protocol-http1 0.40.2 → 0.41.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: fff1a36300fb1ea0775689411e9b86a45093e6d064d10868c419e38422754bea
4
- data.tar.gz: c5204b0d2f701c834685b84c405ae1a98da109ec8ccdaf0c6d4a99a04b489ccb
3
+ metadata.gz: aa2e943387ba60e2d581cbdebf2dbd4ae864fb848965f30dff7e383a684a731f
4
+ data.tar.gz: 1824ea1d12202dd3d5dc65192229492a4ad45900dabdbe2b311677878d91866c
5
5
  SHA512:
6
- metadata.gz: 454ec4c8703447ad07cc199d482210d40b3abd79961b456eb1d0680869e35f87b72a0f0ba7bea5739178a40130007397b3d71fe9f467c83507628c2625e0cc98
7
- data.tar.gz: f0c33d4fb8815e1f46e447ab5c693da0baaed0cf08e288d05030157b4bdaf6e792f306efbdaa3db21b80e1d1731d624647c4b3c13e5e5b308deac7b7bbf58346
6
+ metadata.gz: 5fac180bc79a47ad08969214bf46c6fef9c431608e643f6a707cf479fc3f4617f08e87b0057be350d768ad1151107f9147a6a35cd403a70e227d6dde2cbb14be
7
+ data.tar.gz: 76e0fd036db9afcddccc25f49a8dac62019f3f43e8710d8aa66ca73d2179a2da0d8efb6d3d7ddbd12032b6368fa0de2949e21613149e4f38fd7b8f53cb2a6734
checksums.yaml.gz.sig CHANGED
Binary file
@@ -177,15 +177,15 @@ module Protocol
177
177
  # Write the appropriate header for connection persistence.
178
178
  def write_connection_header(version)
179
179
  if version == HTTP10
180
- @stream.write("connection: keep-alive\r\n") if @persistent
180
+ write_stream("connection: keep-alive\r\n") if @persistent
181
181
  else
182
- @stream.write("connection: close\r\n") unless @persistent
182
+ write_stream("connection: close\r\n") unless @persistent
183
183
  end
184
184
  end
185
185
 
186
186
  # Write the appropriate header for connection upgrade.
187
187
  def write_upgrade_header(upgrade)
188
- @stream.write("connection: upgrade\r\nupgrade: #{upgrade}\r\n")
188
+ write_stream("connection: upgrade\r\nupgrade: #{upgrade}\r\n")
189
189
  end
190
190
 
191
191
  # Indicates whether the connection has been hijacked meaning its IO has been handed over and is not usable anymore.
@@ -262,8 +262,8 @@ module Protocol
262
262
  def write_request(authority, method, target, version, headers)
263
263
  open!
264
264
 
265
- @stream.write("#{method} #{target} #{version}\r\n")
266
- @stream.write("host: #{authority}\r\n") if authority
265
+ write_stream("#{method} #{target} #{version}\r\n")
266
+ write_stream("host: #{authority}\r\n") if authority
267
267
 
268
268
  write_headers(headers)
269
269
  rescue
@@ -284,7 +284,7 @@ module Protocol
284
284
  end
285
285
 
286
286
  # Safari WebSockets break if no reason is given:
287
- @stream.write("#{version} #{status} #{reason}\r\n")
287
+ write_stream("#{version} #{status} #{reason}\r\n")
288
288
 
289
289
  write_headers(headers)
290
290
  end
@@ -303,12 +303,12 @@ module Protocol
303
303
  raise ProtocolError, "Cannot write interim response in state: #{@state}!"
304
304
  end
305
305
 
306
- @stream.write("#{version} #{status} #{reason}\r\n")
306
+ write_stream("#{version} #{status} #{reason}\r\n")
307
307
 
308
308
  write_headers(headers)
309
309
 
310
- @stream.write("\r\n")
311
- @stream.flush
310
+ write_stream("\r\n")
311
+ flush_stream
312
312
  end
313
313
 
314
314
  # Write headers to the connection.
@@ -331,7 +331,7 @@ module Protocol
331
331
  end
332
332
 
333
333
  # Write it:
334
- @stream.write("#{name}: #{value}\r\n")
334
+ write_stream("#{name}: #{value}\r\n")
335
335
  end
336
336
  end
337
337
 
@@ -340,6 +340,8 @@ module Protocol
340
340
  # @parameter length [Integer] the maximum number of bytes to read.
341
341
  def readpartial(length)
342
342
  @stream.readpartial(length)
343
+ rescue Errno::EPIPE, Errno::ECONNRESET
344
+ raise HTTP::RemoteError, "Remote connection closed during readpartial!"
343
345
  end
344
346
 
345
347
  # Read some data from the connection.
@@ -347,6 +349,8 @@ module Protocol
347
349
  # @parameter length [Integer] the number of bytes to read.
348
350
  def read(length)
349
351
  @stream.read(length)
352
+ rescue Errno::EPIPE, Errno::ECONNRESET
353
+ raise HTTP::RemoteError, "Remote connection closed during read!"
350
354
  end
351
355
 
352
356
  # Read a line from the connection.
@@ -541,16 +545,16 @@ module Protocol
541
545
 
542
546
  write_upgrade_header(protocol)
543
547
 
544
- @stream.write("\r\n")
545
- @stream.flush # Don't remove me!
548
+ write_stream("\r\n")
549
+ flush_stream # Don't remove me!
546
550
 
547
551
  if body
548
552
  body.each do |chunk|
549
- @stream.write(chunk)
550
- @stream.flush
553
+ write_stream(chunk)
554
+ flush_stream
551
555
  end
552
556
 
553
- @stream.close_write
557
+ close_write_stream
554
558
  end
555
559
 
556
560
  return @stream
@@ -572,16 +576,16 @@ module Protocol
572
576
 
573
577
  write_connection_header(version)
574
578
 
575
- @stream.write("\r\n")
576
- @stream.flush # Don't remove me!
579
+ write_stream("\r\n")
580
+ flush_stream # Don't remove me!
577
581
 
578
582
  if body
579
583
  body.each do |chunk|
580
- @stream.write(chunk)
581
- @stream.flush
584
+ write_stream(chunk)
585
+ flush_stream
582
586
  end
583
587
 
584
- @stream.close_write
588
+ close_write_stream
585
589
  end
586
590
 
587
591
  return @stream
@@ -595,8 +599,8 @@ module Protocol
595
599
  #
596
600
  # @parameter body [Object | Nil] the body to write.
597
601
  def write_empty_body(body = nil)
598
- @stream.write("content-length: 0\r\n\r\n")
599
- @stream.flush
602
+ write_stream("content-length: 0\r\n\r\n")
603
+ flush_stream
600
604
 
601
605
  body&.close
602
606
  ensure
@@ -612,12 +616,12 @@ module Protocol
612
616
  # @parameter head [Boolean] whether the request was a `HEAD` request.
613
617
  # @raises [ContentLengthError] if the body length does not match the content length specified.
614
618
  def write_fixed_length_body(body, length, head)
615
- @stream.write("content-length: #{length}\r\n\r\n")
619
+ write_stream("content-length: #{length}\r\n\r\n")
616
620
 
617
621
  if head
618
- @stream.flush
622
+ flush_stream
619
623
  else
620
- @stream.flush unless body.ready?
624
+ flush_stream unless body.ready?
621
625
 
622
626
  chunk_length = 0
623
627
  # 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.
@@ -628,11 +632,11 @@ module Protocol
628
632
  raise ContentLengthError, "Trying to write #{chunk_length} bytes, but content length was #{length} bytes!"
629
633
  end
630
634
 
631
- @stream.write(chunk)
632
- @stream.flush unless body.ready?
635
+ write_stream(chunk)
636
+ flush_stream unless body.ready?
633
637
  end
634
638
 
635
- @stream.flush
639
+ flush_stream
636
640
 
637
641
  if chunk_length != length
638
642
  raise ContentLengthError, "Wrote #{chunk_length} bytes, but content length was #{length} bytes!"
@@ -657,33 +661,33 @@ module Protocol
657
661
  # @parameter head [Boolean] whether the request was a `HEAD` request.
658
662
  # @parameter trailer [Hash | Nil] the trailers to write.
659
663
  def write_chunked_body(body, head, trailer = nil)
660
- @stream.write("transfer-encoding: chunked\r\n\r\n")
664
+ write_stream("transfer-encoding: chunked\r\n\r\n")
661
665
 
662
666
  if head
663
- @stream.flush
667
+ flush_stream
664
668
  else
665
- @stream.flush unless body.ready?
669
+ flush_stream unless body.ready?
666
670
 
667
671
  # 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
672
  while chunk = body.read
669
673
  next if chunk.size == 0
670
674
 
671
- @stream.write("#{chunk.bytesize.to_s(16).upcase}\r\n")
672
- @stream.write(chunk)
673
- @stream.write(CRLF)
675
+ write_stream("#{chunk.bytesize.to_s(16).upcase}\r\n")
676
+ write_stream(chunk)
677
+ write_stream(CRLF)
674
678
 
675
- @stream.flush unless body.ready?
679
+ flush_stream unless body.ready?
676
680
  end
677
681
 
678
682
  if trailer&.any?
679
- @stream.write("0\r\n")
683
+ write_stream("0\r\n")
680
684
  write_headers(trailer)
681
- @stream.write("\r\n")
685
+ write_stream("\r\n")
682
686
  else
683
- @stream.write("0\r\n\r\n")
687
+ write_stream("0\r\n\r\n")
684
688
  end
685
689
 
686
- @stream.flush
690
+ flush_stream
687
691
  end
688
692
  rescue => error
689
693
  raise
@@ -702,20 +706,20 @@ module Protocol
702
706
  # We can't be persistent because we don't know the data length:
703
707
  @persistent = false
704
708
 
705
- @stream.write("\r\n")
709
+ write_stream("\r\n")
706
710
 
707
711
  unless head
708
- @stream.flush unless body.ready?
712
+ flush_stream unless body.ready?
709
713
 
710
714
  while chunk = body.read
711
- @stream.write(chunk)
715
+ write_stream(chunk)
712
716
 
713
- @stream.flush unless body.ready?
717
+ flush_stream unless body.ready?
714
718
  end
715
719
  end
716
720
 
717
- @stream.flush
718
- @stream.close_write
721
+ flush_stream
722
+ close_write_stream
719
723
  rescue => error
720
724
  raise
721
725
  ensure
@@ -1026,6 +1030,26 @@ module Protocol
1026
1030
  return read_remainder_body
1027
1031
  end
1028
1032
  end
1033
+
1034
+ private
1035
+
1036
+ def write_stream(data)
1037
+ @stream.write(data)
1038
+ rescue Errno::EPIPE, Errno::ECONNRESET
1039
+ raise HTTP::RemoteError, "Remote connection closed during write!"
1040
+ end
1041
+
1042
+ def flush_stream
1043
+ @stream.flush
1044
+ rescue Errno::EPIPE, Errno::ECONNRESET
1045
+ raise HTTP::RemoteError, "Remote connection closed during flush!"
1046
+ end
1047
+
1048
+ def close_write_stream
1049
+ @stream.close_write
1050
+ rescue Errno::EPIPE, Errno::ECONNRESET
1051
+ raise HTTP::RemoteError, "Remote connection closed during close_write!"
1052
+ end
1029
1053
  end
1030
1054
  end
1031
1055
  end
@@ -5,6 +5,6 @@
5
5
 
6
6
  module Protocol
7
7
  module HTTP1
8
- VERSION = "0.40.2"
8
+ VERSION = "0.41.0"
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.41.0
34
+
35
+ - Map remote socket failures to `Protocol::HTTP::RemoteError` and prevent connection reuse.
36
+
33
37
  ### v0.40.2
34
38
 
35
39
  - Handle unexpected EOF while reading chunked trailers.
@@ -67,10 +71,6 @@ Please see the [project releases](https://socketry.github.io/protocol-http1/rele
67
71
  - Tidy up implementation of `read_line?` to handle line length errors and protocol violations more clearly.
68
72
  - Improve error handling for unexpected connection closures (`Errno::ECONNRESET`) in `read_line?`.
69
73
 
70
- ### v0.35.0
71
-
72
- - Add traces provider for `Protocol::HTTP1::Connection`.
73
-
74
74
  ## Contributing
75
75
 
76
76
  We welcome contributions to this project.
data/releases.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Releases
2
2
 
3
+ ## v0.41.0
4
+
5
+ - Map remote socket failures to `Protocol::HTTP::RemoteError` and prevent connection reuse.
6
+
3
7
  ## v0.40.2
4
8
 
5
9
  - Handle unexpected EOF while reading chunked trailers.
data.tar.gz.sig CHANGED
@@ -1,2 +1,4 @@
1
- ��~��CP9
2
- ���'��uxϦ���Ѳ���(KAQ��k�/FwR��� ��~��<���%���P����@��hz\�[�
1
+ @�&qq�$q���:LvI��~�_x��q������2"�t�
2
+
3
+ ��ԩ��c�Sx ��(W���G�C���mH ��0��%:W��<�$iAֶ��5��c��,�c�'�)>Ʒ������-�Ug�d+�T��ޚVC��'��08�qƇ�\%:�3����1�<R�,�P��d@ ��kF����b�I��d�(�sSTckG�s�T��>i�!hN��I��n;�5�ŧ1I�nhz�e����W)؟��Hr�)$%v�ܱ�mK�b��u�gƔu]���F���"s0n�u�a�Υ؏�)����ɐh���ԭ���c���(y�k��P?���uZ��h�~�zr�s��*�,�J=�
4
+ Q��r J~�4[d7N/,;�e�.sɱ[
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.40.2
4
+ version: 0.41.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
metadata.gz.sig CHANGED
Binary file