protocol-http1 0.37.0 → 0.38.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b8ed289fdd31ce464d53ec0d87533f948cb000c05a966a53280f0d666a3f61e8
4
- data.tar.gz: 88b8c7cd528012574849c4a221a6040139f884355affeebb8e1b9fd1666b69a2
3
+ metadata.gz: 9b20437da24d1840f9eba2a6cd768d224b1279856db05b08d4dac174cc36d26f
4
+ data.tar.gz: 1a0651bae0777c40bfbfee99b617492010e3c01e96c7fd7242f2a519d04c37d9
5
5
  SHA512:
6
- metadata.gz: d21bc4fbd87fcb529f4eb367ce740fa31389f352dc7c9e5739e07be8b75c0233f97efdd28f8f9dffaa58c9fb482625c4c176eaae7b61ca1d38e665212a709128
7
- data.tar.gz: 6d2784704a5e2a54f0ab742daf0514c14989bedd9578226d7ac74c6416deefa30c59e8bf7e0e715275c340ff6c7bd92275d9444918a21f799c4e899b695c844c
6
+ metadata.gz: a22e2a5e9cf67e34e6577f1ec76c2be8a4b471f89bbeb01544e8bf9cfb2f5e1ed3b548d34b72af6b4769370f16a9f266d2a857d2196cba95a86fad2bc47ee3fe
7
+ data.tar.gz: c9fe0c68fa411bacdb8219af302d5b3d042f8bf763e83936561dedce2a0a31136ad442188084d0830fb563fa4ec057c97dd9bdca64e80826ba17331fa5e472ff
checksums.yaml.gz.sig CHANGED
Binary file
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2025, by Samuel Williams.
4
+ # Copyright, 2019-2026, by Samuel Williams.
5
5
  # Copyright, 2019, by Brian Morearty.
6
6
  # Copyright, 2020, by Bruno Sutic.
7
7
  # Copyright, 2023-2024, by Thomas Morgan.
@@ -266,6 +266,8 @@ module Protocol
266
266
  @stream.write("host: #{authority}\r\n") if authority
267
267
 
268
268
  write_headers(headers)
269
+ rescue
270
+ raise ::Protocol::HTTP::RequestRefusedError
269
271
  end
270
272
 
271
273
  # Write a response to the connection. It is expected you will write the body after this method.
@@ -614,32 +616,34 @@ module Protocol
614
616
 
615
617
  if head
616
618
  @stream.flush
619
+ else
620
+ @stream.flush unless body.ready?
617
621
 
618
- body.close
622
+ chunk_length = 0
623
+ # Use a manual read loop (not body.each) so that body.close runs after the response is fully written and flushed. This ensures completion callbacks (e.g. rack.response_finished) don't delay the client.
624
+ while chunk = body.read
625
+ chunk_length += chunk.bytesize
626
+
627
+ if chunk_length > length
628
+ raise ContentLengthError, "Trying to write #{chunk_length} bytes, but content length was #{length} bytes!"
629
+ end
630
+
631
+ @stream.write(chunk)
632
+ @stream.flush unless body.ready?
633
+ end
619
634
 
620
- return
621
- end
622
-
623
- @stream.flush unless body.ready?
624
-
625
- chunk_length = 0
626
- body.each do |chunk|
627
- chunk_length += chunk.bytesize
635
+ @stream.flush
628
636
 
629
- if chunk_length > length
630
- raise ContentLengthError, "Trying to write #{chunk_length} bytes, but content length was #{length} bytes!"
637
+ if chunk_length != length
638
+ raise ContentLengthError, "Wrote #{chunk_length} bytes, but content length was #{length} bytes!"
631
639
  end
632
-
633
- @stream.write(chunk)
634
- @stream.flush unless body.ready?
635
- end
636
-
637
- @stream.flush
638
-
639
- if chunk_length != length
640
- raise ContentLengthError, "Wrote #{chunk_length} bytes, but content length was #{length} bytes!"
641
640
  end
641
+ rescue => error
642
+ raise
642
643
  ensure
644
+ # Close the body after the response is fully flushed, so that completion callbacks run after the client has received the response:
645
+ body.close(error)
646
+
643
647
  self.send_end_stream!
644
648
  end
645
649
 
@@ -657,34 +661,36 @@ module Protocol
657
661
 
658
662
  if head
659
663
  @stream.flush
664
+ else
665
+ @stream.flush unless body.ready?
660
666
 
661
- body.close
662
-
663
- return
664
- end
665
-
666
- @stream.flush unless body.ready?
667
-
668
- body.each do |chunk|
669
- next if chunk.size == 0
667
+ # Use a manual read loop (not body.each) so that body.close runs after the terminal chunk is written. With body.each, the ensure { close } fires before the terminal "0\r\n\r\n" is sent, delaying the client.
668
+ while chunk = body.read
669
+ next if chunk.size == 0
670
+
671
+ @stream.write("#{chunk.bytesize.to_s(16).upcase}\r\n")
672
+ @stream.write(chunk)
673
+ @stream.write(CRLF)
674
+
675
+ @stream.flush unless body.ready?
676
+ end
670
677
 
671
- @stream.write("#{chunk.bytesize.to_s(16).upcase}\r\n")
672
- @stream.write(chunk)
673
- @stream.write(CRLF)
678
+ if trailer&.any?
679
+ @stream.write("0\r\n")
680
+ write_headers(trailer)
681
+ @stream.write("\r\n")
682
+ else
683
+ @stream.write("0\r\n\r\n")
684
+ end
674
685
 
675
- @stream.flush unless body.ready?
676
- end
677
-
678
- if trailer&.any?
679
- @stream.write("0\r\n")
680
- write_headers(trailer)
681
- @stream.write("\r\n")
682
- else
683
- @stream.write("0\r\n\r\n")
686
+ @stream.flush
684
687
  end
685
-
686
- @stream.flush
688
+ rescue => error
689
+ raise
687
690
  ensure
691
+ # Close the body after the complete chunked response (including terminal chunk) is flushed, so that completion callbacks don't block the client from seeing the response as complete:
692
+ body.close(error)
693
+
688
694
  self.send_end_stream!
689
695
  end
690
696
 
@@ -697,12 +703,11 @@ module Protocol
697
703
  @persistent = false
698
704
 
699
705
  @stream.write("\r\n")
700
- @stream.flush unless body.ready?
701
706
 
702
- if head
703
- body.close
704
- else
705
- body.each do |chunk|
707
+ unless head
708
+ @stream.flush unless body.ready?
709
+
710
+ while chunk = body.read
706
711
  @stream.write(chunk)
707
712
 
708
713
  @stream.flush unless body.ready?
@@ -711,7 +716,12 @@ module Protocol
711
716
 
712
717
  @stream.flush
713
718
  @stream.close_write
719
+ rescue => error
720
+ raise
714
721
  ensure
722
+ # Close the body after the stream is fully written and half-closed, so that completion callbacks run after the client has received the full response:
723
+ body.close(error)
724
+
715
725
  self.send_end_stream!
716
726
  end
717
727
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2025, by Samuel Williams.
4
+ # Copyright, 2019-2026, by Samuel Williams.
5
5
 
6
6
  require "protocol/http/error"
7
7
 
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2019-2025, by Samuel Williams.
4
+ # Copyright, 2019-2026, by Samuel Williams.
5
5
 
6
6
  module Protocol
7
7
  module HTTP1
8
- VERSION = "0.37.0"
8
+ VERSION = "0.38.0"
9
9
  end
10
10
  end
data/readme.md CHANGED
@@ -30,6 +30,14 @@ Please see the [project documentation](https://socketry.github.io/protocol-http1
30
30
 
31
31
  Please see the [project releases](https://socketry.github.io/protocol-http1/releases/index) for all releases.
32
32
 
33
+ ### v0.38.0
34
+
35
+ - `write_request` now raises `Protocol::HTTP::RequestRefusedError` if the request line or headers cannot be written, indicating the request was not processed and can be safely retried.
36
+
37
+ ### v0.37.1
38
+
39
+ - Defer `body.close` in `write_chunked_body`, `write_fixed_length_body`, and `write_body_and_close` until after the response is fully written and flushed. Previously, `body.each` called `close` in its `ensure` block before the terminal chunk (chunked encoding) or final flush was written, causing `rack.response_finished` callbacks to delay the client-visible response completion.
40
+
33
41
  ### v0.37.0
34
42
 
35
43
  - `Protocol::HTTP1::BadRequest` now includes `Protocol::HTTP::BadRequest` for better interoperability and handling of bad request errors across different HTTP protocol implementations.
@@ -65,14 +73,6 @@ Please see the [project releases](https://socketry.github.io/protocol-http1/rele
65
73
  - Fix header parsing to handle tab characters between values correctly.
66
74
  - Complete documentation coverage for all public APIs.
67
75
 
68
- ### v0.31.0
69
-
70
- - Enforce one-way transition for persistent connections to prevent invalid state changes.
71
-
72
- ### v0.30.0
73
-
74
- - Make `authority` header optional in HTTP requests for improved flexibility.
75
-
76
76
  ## Contributing
77
77
 
78
78
  We welcome contributions to this project.
data/releases.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Releases
2
2
 
3
+ ## v0.38.0
4
+
5
+ - `write_request` now raises `Protocol::HTTP::RequestRefusedError` if the request line or headers cannot be written, indicating the request was not processed and can be safely retried.
6
+
7
+ ## v0.37.1
8
+
9
+ - Defer `body.close` in `write_chunked_body`, `write_fixed_length_body`, and `write_body_and_close` until after the response is fully written and flushed. Previously, `body.each` called `close` in its `ensure` block before the terminal chunk (chunked encoding) or final flush was written, causing `rack.response_finished` callbacks to delay the client-visible response completion.
10
+
3
11
  ## v0.37.0
4
12
 
5
13
  - `Protocol::HTTP1::BadRequest` now includes `Protocol::HTTP::BadRequest` for better interoperability and handling of bad request errors across different HTTP protocol implementations.
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: protocol-http1
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.37.0
4
+ version: 0.38.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -49,14 +49,14 @@ dependencies:
49
49
  requirements:
50
50
  - - "~>"
51
51
  - !ruby/object:Gem::Version
52
- version: '0.58'
52
+ version: '0.61'
53
53
  type: :runtime
54
54
  prerelease: false
55
55
  version_requirements: !ruby/object:Gem::Requirement
56
56
  requirements:
57
57
  - - "~>"
58
58
  - !ruby/object:Gem::Version
59
- version: '0.58'
59
+ version: '0.61'
60
60
  executables: []
61
61
  extensions: []
62
62
  extra_rdoc_files: []
@@ -90,14 +90,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
90
90
  requirements:
91
91
  - - ">="
92
92
  - !ruby/object:Gem::Version
93
- version: '3.2'
93
+ version: '3.3'
94
94
  required_rubygems_version: !ruby/object:Gem::Requirement
95
95
  requirements:
96
96
  - - ">="
97
97
  - !ruby/object:Gem::Version
98
98
  version: '0'
99
99
  requirements: []
100
- rubygems_version: 4.0.3
100
+ rubygems_version: 4.0.6
101
101
  specification_version: 4
102
102
  summary: A low level implementation of the HTTP/1 protocol.
103
103
  test_files: []
metadata.gz.sig CHANGED
Binary file