protocol-http1 0.40.1 → 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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/protocol/http1/body/chunked.rb +2 -2
- data/lib/protocol/http1/connection.rb +69 -45
- data/lib/protocol/http1/version.rb +1 -1
- data/readme.md +8 -9
- data/releases.md +8 -0
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: aa2e943387ba60e2d581cbdebf2dbd4ae864fb848965f30dff7e383a684a731f
|
|
4
|
+
data.tar.gz: 1824ea1d12202dd3d5dc65192229492a4ad45900dabdbe2b311677878d91866c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5fac180bc79a47ad08969214bf46c6fef9c431608e643f6a707cf479fc3f4617f08e87b0057be350d768ad1151107f9147a6a35cd403a70e227d6dde2cbb14be
|
|
7
|
+
data.tar.gz: 76e0fd036db9afcddccc25f49a8dac62019f3f43e8710d8aa66ca73d2179a2da0d8efb6d3d7ddbd12032b6368fa0de2949e21613149e4f38fd7b8f53cb2a6734
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -128,7 +128,7 @@ module Protocol
|
|
|
128
128
|
connection.close_read
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
raise
|
|
131
|
+
raise
|
|
132
132
|
end
|
|
133
133
|
|
|
134
134
|
# @returns [String] a human-readable representation of the body.
|
|
@@ -149,7 +149,7 @@ module Protocol
|
|
|
149
149
|
|
|
150
150
|
# Read the trailer from the connection, and add any headers to the trailer.
|
|
151
151
|
def read_trailer
|
|
152
|
-
while line = @connection.read_line
|
|
152
|
+
while line = @connection.read_line
|
|
153
153
|
# Empty line indicates end of trailer:
|
|
154
154
|
break if line.empty?
|
|
155
155
|
|
|
@@ -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
|
-
|
|
180
|
+
write_stream("connection: keep-alive\r\n") if @persistent
|
|
181
181
|
else
|
|
182
|
-
|
|
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
|
-
|
|
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
|
-
|
|
266
|
-
|
|
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
|
-
|
|
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
|
-
|
|
306
|
+
write_stream("#{version} #{status} #{reason}\r\n")
|
|
307
307
|
|
|
308
308
|
write_headers(headers)
|
|
309
309
|
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
|
|
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
|
-
|
|
545
|
-
|
|
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
|
-
|
|
550
|
-
|
|
553
|
+
write_stream(chunk)
|
|
554
|
+
flush_stream
|
|
551
555
|
end
|
|
552
556
|
|
|
553
|
-
|
|
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
|
-
|
|
576
|
-
|
|
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
|
-
|
|
581
|
-
|
|
584
|
+
write_stream(chunk)
|
|
585
|
+
flush_stream
|
|
582
586
|
end
|
|
583
587
|
|
|
584
|
-
|
|
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
|
-
|
|
599
|
-
|
|
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
|
-
|
|
619
|
+
write_stream("content-length: #{length}\r\n\r\n")
|
|
616
620
|
|
|
617
621
|
if head
|
|
618
|
-
|
|
622
|
+
flush_stream
|
|
619
623
|
else
|
|
620
|
-
|
|
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
|
-
|
|
632
|
-
|
|
635
|
+
write_stream(chunk)
|
|
636
|
+
flush_stream unless body.ready?
|
|
633
637
|
end
|
|
634
638
|
|
|
635
|
-
|
|
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
|
-
|
|
664
|
+
write_stream("transfer-encoding: chunked\r\n\r\n")
|
|
661
665
|
|
|
662
666
|
if head
|
|
663
|
-
|
|
667
|
+
flush_stream
|
|
664
668
|
else
|
|
665
|
-
|
|
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
|
-
|
|
672
|
-
|
|
673
|
-
|
|
675
|
+
write_stream("#{chunk.bytesize.to_s(16).upcase}\r\n")
|
|
676
|
+
write_stream(chunk)
|
|
677
|
+
write_stream(CRLF)
|
|
674
678
|
|
|
675
|
-
|
|
679
|
+
flush_stream unless body.ready?
|
|
676
680
|
end
|
|
677
681
|
|
|
678
682
|
if trailer&.any?
|
|
679
|
-
|
|
683
|
+
write_stream("0\r\n")
|
|
680
684
|
write_headers(trailer)
|
|
681
|
-
|
|
685
|
+
write_stream("\r\n")
|
|
682
686
|
else
|
|
683
|
-
|
|
687
|
+
write_stream("0\r\n\r\n")
|
|
684
688
|
end
|
|
685
689
|
|
|
686
|
-
|
|
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
|
-
|
|
709
|
+
write_stream("\r\n")
|
|
706
710
|
|
|
707
711
|
unless head
|
|
708
|
-
|
|
712
|
+
flush_stream unless body.ready?
|
|
709
713
|
|
|
710
714
|
while chunk = body.read
|
|
711
|
-
|
|
715
|
+
write_stream(chunk)
|
|
712
716
|
|
|
713
|
-
|
|
717
|
+
flush_stream unless body.ready?
|
|
714
718
|
end
|
|
715
719
|
end
|
|
716
720
|
|
|
717
|
-
|
|
718
|
-
|
|
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
|
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.41.0
|
|
34
|
+
|
|
35
|
+
- Map remote socket failures to `Protocol::HTTP::RemoteError` and prevent connection reuse.
|
|
36
|
+
|
|
37
|
+
### v0.40.2
|
|
38
|
+
|
|
39
|
+
- Handle unexpected EOF while reading chunked trailers.
|
|
40
|
+
|
|
33
41
|
### v0.40.1
|
|
34
42
|
|
|
35
43
|
- Bound fixed-length and chunked body reads, and validate chunk terminators.
|
|
@@ -63,15 +71,6 @@ Please see the [project releases](https://socketry.github.io/protocol-http1/rele
|
|
|
63
71
|
- Tidy up implementation of `read_line?` to handle line length errors and protocol violations more clearly.
|
|
64
72
|
- Improve error handling for unexpected connection closures (`Errno::ECONNRESET`) in `read_line?`.
|
|
65
73
|
|
|
66
|
-
### v0.35.0
|
|
67
|
-
|
|
68
|
-
- Add traces provider for `Protocol::HTTP1::Connection`.
|
|
69
|
-
|
|
70
|
-
### v0.34.1
|
|
71
|
-
|
|
72
|
-
- Fix connection state handling to allow idempotent response body closing.
|
|
73
|
-
- Add `kisaten` fuzzing integration for improved security testing.
|
|
74
|
-
|
|
75
74
|
## Contributing
|
|
76
75
|
|
|
77
76
|
We welcome contributions to this project.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.41.0
|
|
4
|
+
|
|
5
|
+
- Map remote socket failures to `Protocol::HTTP::RemoteError` and prevent connection reuse.
|
|
6
|
+
|
|
7
|
+
## v0.40.2
|
|
8
|
+
|
|
9
|
+
- Handle unexpected EOF while reading chunked trailers.
|
|
10
|
+
|
|
3
11
|
## v0.40.1
|
|
4
12
|
|
|
5
13
|
- Bound fixed-length and chunked body reads, and validate chunk terminators.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|