protocol-http1 0.37.0 → 0.37.1

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: 97a6f38e50bdc78455b7f57895ef25df4dde6492142c2c60c40373b632c3db3b
4
+ data.tar.gz: 0fb9af13fd1beeb21082c7d1860604b1bc947d343598c6347387d99707990894
5
5
  SHA512:
6
- metadata.gz: d21bc4fbd87fcb529f4eb367ce740fa31389f352dc7c9e5739e07be8b75c0233f97efdd28f8f9dffaa58c9fb482625c4c176eaae7b61ca1d38e665212a709128
7
- data.tar.gz: 6d2784704a5e2a54f0ab742daf0514c14989bedd9578226d7ac74c6416deefa30c59e8bf7e0e715275c340ff6c7bd92275d9444918a21f799c4e899b695c844c
6
+ metadata.gz: b3cb82192723452081180be601e36491fe2a5d2b5f05fcc3556080a9311c846bdf2844c499981ac52338170cefdd097584ba8b53c52153af54cc29fbb4a405d9
7
+ data.tar.gz: a4013c3278818c2a632d3f41e46a6625c286f59391990819bda5b29c3c5d4fd82c328acae59107a4abeb5268deebc16bf91e3d3584a3b1be42ab3105a941a352
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.
@@ -614,32 +614,34 @@ module Protocol
614
614
 
615
615
  if head
616
616
  @stream.flush
617
+ else
618
+ @stream.flush unless body.ready?
617
619
 
618
- body.close
620
+ chunk_length = 0
621
+ # 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.
622
+ while chunk = body.read
623
+ chunk_length += chunk.bytesize
624
+
625
+ if chunk_length > length
626
+ raise ContentLengthError, "Trying to write #{chunk_length} bytes, but content length was #{length} bytes!"
627
+ end
628
+
629
+ @stream.write(chunk)
630
+ @stream.flush unless body.ready?
631
+ end
619
632
 
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
633
+ @stream.flush
628
634
 
629
- if chunk_length > length
630
- raise ContentLengthError, "Trying to write #{chunk_length} bytes, but content length was #{length} bytes!"
635
+ if chunk_length != length
636
+ raise ContentLengthError, "Wrote #{chunk_length} bytes, but content length was #{length} bytes!"
631
637
  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
638
  end
639
+ rescue => error
640
+ raise
642
641
  ensure
642
+ # Close the body after the response is fully flushed, so that completion callbacks run after the client has received the response:
643
+ body.close(error)
644
+
643
645
  self.send_end_stream!
644
646
  end
645
647
 
@@ -657,34 +659,36 @@ module Protocol
657
659
 
658
660
  if head
659
661
  @stream.flush
662
+ else
663
+ @stream.flush unless body.ready?
660
664
 
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
665
+ # 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.
666
+ while chunk = body.read
667
+ next if chunk.size == 0
668
+
669
+ @stream.write("#{chunk.bytesize.to_s(16).upcase}\r\n")
670
+ @stream.write(chunk)
671
+ @stream.write(CRLF)
672
+
673
+ @stream.flush unless body.ready?
674
+ end
670
675
 
671
- @stream.write("#{chunk.bytesize.to_s(16).upcase}\r\n")
672
- @stream.write(chunk)
673
- @stream.write(CRLF)
676
+ if trailer&.any?
677
+ @stream.write("0\r\n")
678
+ write_headers(trailer)
679
+ @stream.write("\r\n")
680
+ else
681
+ @stream.write("0\r\n\r\n")
682
+ end
674
683
 
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")
684
+ @stream.flush
684
685
  end
685
-
686
- @stream.flush
686
+ rescue => error
687
+ raise
687
688
  ensure
689
+ # 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:
690
+ body.close(error)
691
+
688
692
  self.send_end_stream!
689
693
  end
690
694
 
@@ -697,12 +701,11 @@ module Protocol
697
701
  @persistent = false
698
702
 
699
703
  @stream.write("\r\n")
700
- @stream.flush unless body.ready?
701
704
 
702
- if head
703
- body.close
704
- else
705
- body.each do |chunk|
705
+ unless head
706
+ @stream.flush unless body.ready?
707
+
708
+ while chunk = body.read
706
709
  @stream.write(chunk)
707
710
 
708
711
  @stream.flush unless body.ready?
@@ -711,7 +714,12 @@ module Protocol
711
714
 
712
715
  @stream.flush
713
716
  @stream.close_write
717
+ rescue => error
718
+ raise
714
719
  ensure
720
+ # 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:
721
+ body.close(error)
722
+
715
723
  self.send_end_stream!
716
724
  end
717
725
 
@@ -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.37.1"
9
9
  end
10
10
  end
data/readme.md CHANGED
@@ -30,6 +30,10 @@ 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.37.1
34
+
35
+ - 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.
36
+
33
37
  ### v0.37.0
34
38
 
35
39
  - `Protocol::HTTP1::BadRequest` now includes `Protocol::HTTP::BadRequest` for better interoperability and handling of bad request errors across different HTTP protocol implementations.
@@ -69,10 +73,6 @@ Please see the [project releases](https://socketry.github.io/protocol-http1/rele
69
73
 
70
74
  - Enforce one-way transition for persistent connections to prevent invalid state changes.
71
75
 
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,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.37.1
4
+
5
+ - 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.
6
+
3
7
  ## v0.37.0
4
8
 
5
9
  - `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.37.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -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