biryani 0.0.4 → 0.0.5
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
- data/lib/biryani/connection.rb +2 -2
- data/lib/biryani/data_buffer.rb +1 -1
- data/lib/biryani/frame/continuation.rb +4 -7
- data/lib/biryani/frame/data.rb +9 -12
- data/lib/biryani/frame/goaway.rb +6 -6
- data/lib/biryani/frame/headers.rb +25 -18
- data/lib/biryani/frame/ping.rb +5 -7
- data/lib/biryani/frame/priority.rb +6 -5
- data/lib/biryani/frame/push_promise.rb +14 -15
- data/lib/biryani/frame/rst_stream.rb +5 -5
- data/lib/biryani/frame/settings.rb +5 -5
- data/lib/biryani/frame/unknown.rb +0 -12
- data/lib/biryani/frame/window_update.rb +5 -5
- data/lib/biryani/frame.rb +5 -4
- data/lib/biryani/hpack/field.rb +42 -42
- data/lib/biryani/hpack/fields.rb +2 -1
- data/lib/biryani/hpack/huffman.rb +290 -21
- data/lib/biryani/hpack/integer.rb +10 -6
- data/lib/biryani/hpack/string.rb +6 -6
- data/lib/biryani/http_request.rb +1 -1
- data/lib/biryani/state.rb +2 -0
- data/lib/biryani/streams_context.rb +2 -6
- data/lib/biryani/version.rb +1 -1
- data/lib/biryani.rb +0 -1
- data/spec/connection/handle_data_spec.rb +2 -2
- data/spec/connection/handle_headers_spec.rb +1 -1
- data/spec/frame/continuation_spec.rb +2 -2
- data/spec/frame/data_spec.rb +1 -1
- data/spec/frame/goaway_spec.rb +1 -1
- data/spec/frame/headers_spec.rb +2 -2
- data/spec/frame/ping_spec.rb +1 -1
- data/spec/frame/priority_spec.rb +1 -1
- data/spec/frame/push_promise_spec.rb +1 -1
- data/spec/frame/rst_stream_spec.rb +1 -1
- data/spec/frame/settings_spec.rb +1 -1
- data/spec/frame/window_update_spec.rb +1 -1
- data/spec/hpack/field_spec.rb +10 -9
- data/spec/hpack/huffman_spec.rb +4 -4
- data/spec/hpack/integer_spec.rb +5 -5
- data/spec/hpack/string_spec.rb +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: be854e5993fe96b137d1d4db219551155b3103b213a97058c64f756ad2971ba3
|
|
4
|
+
data.tar.gz: 2256eea9aee63134060398084d904651a1d0fde583564dc211e13ddb5b134e78
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0abdc8a1ce8d2b0a4d149830e14c0435e08b884950c026ab7d67af0726aeff53eff9d8dc06da83c4819badd967d6e1f0f2fb0b5833432c508ec3874c117ceaf8
|
|
7
|
+
data.tar.gz: e11b1fdde4debd807fb8ddfd7c9b7eaec3be56510be8758e1c42b815589418c5711848ae2ded681247578509aad0f20951558ee54a0b0d6d92825f7704bb3256
|
data/lib/biryani/connection.rb
CHANGED
|
@@ -349,7 +349,7 @@ module Biryani
|
|
|
349
349
|
|
|
350
350
|
ctx.content << data
|
|
351
351
|
if ctx.half_closed_remote?
|
|
352
|
-
obj = http_request(ctx.fragment
|
|
352
|
+
obj = http_request(ctx.fragment, ctx.content, decoder)
|
|
353
353
|
return obj if Biryani.err?(obj)
|
|
354
354
|
|
|
355
355
|
ctx.stream.rx << obj
|
|
@@ -370,7 +370,7 @@ module Biryani
|
|
|
370
370
|
def self.handle_headers(headers, ctx, decoder)
|
|
371
371
|
ctx.fragment << headers.fragment
|
|
372
372
|
if ctx.half_closed_remote?
|
|
373
|
-
obj = http_request(ctx.fragment
|
|
373
|
+
obj = http_request(ctx.fragment, ctx.content, decoder)
|
|
374
374
|
return [obj] if Biryani.err?(obj)
|
|
375
375
|
|
|
376
376
|
ctx.stream.rx << obj
|
data/lib/biryani/data_buffer.rb
CHANGED
|
@@ -32,16 +32,13 @@ module Biryani
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# @param s [String]
|
|
35
|
+
# @param flags [Integer]
|
|
36
|
+
# @param stream_id [Integer]
|
|
35
37
|
#
|
|
36
38
|
# @return [Continuation]
|
|
37
|
-
def self.read(s)
|
|
38
|
-
payload_length, _, flags, stream_id = Frame.read_header(s)
|
|
39
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
40
|
-
|
|
39
|
+
def self.read(s, flags, stream_id)
|
|
41
40
|
end_headers = Frame.read_end_headers(flags)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Continuation.new(end_headers, stream_id, fragment)
|
|
41
|
+
Continuation.new(end_headers, stream_id, s)
|
|
45
42
|
end
|
|
46
43
|
end
|
|
47
44
|
end
|
data/lib/biryani/frame/data.rb
CHANGED
|
@@ -41,26 +41,23 @@ module Biryani
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
# @param s [String]
|
|
44
|
+
# @param flags [Integer]
|
|
45
|
+
# @param stream_id [Integer]
|
|
44
46
|
#
|
|
45
47
|
# @return [Data]
|
|
46
|
-
def self.read(s)
|
|
47
|
-
payload_length, _, flags, stream_id = Frame.read_header(s)
|
|
48
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
49
|
-
|
|
48
|
+
def self.read(s, flags, stream_id)
|
|
50
49
|
padded = Frame.read_padded(flags)
|
|
51
50
|
end_stream = Frame.read_end_stream(flags)
|
|
52
51
|
|
|
53
52
|
if padded
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
io = IO::Buffer.for(s)
|
|
54
|
+
pad_length = io.get_value(:U8, 0)
|
|
55
|
+
data_length = s.bytesize - pad_length - 1
|
|
56
|
+
data = io.get_string(1, data_length)
|
|
57
|
+
padding = io.get_string(1 + data_length)
|
|
59
58
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if padding.bytesize != pad_length
|
|
60
59
|
else
|
|
61
|
-
data = s
|
|
62
|
-
padding = nil
|
|
63
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if data.bytesize != payload_length
|
|
60
|
+
data = s
|
|
64
61
|
end
|
|
65
62
|
|
|
66
63
|
Data.new(end_stream, stream_id, data, padding)
|
data/lib/biryani/frame/goaway.rb
CHANGED
|
@@ -28,14 +28,14 @@ module Biryani
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
# @param s [String]
|
|
31
|
+
# @param _flags [Integer]
|
|
32
|
+
# @param stream_id [Integer]
|
|
31
33
|
#
|
|
32
34
|
# @return [Goaway]
|
|
33
|
-
def self.read(s)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
last_stream_id, error_code = s[9..16].unpack('NN')
|
|
38
|
-
debug = s[17..]
|
|
35
|
+
def self.read(s, _flags, stream_id)
|
|
36
|
+
io = IO::Buffer.for(s)
|
|
37
|
+
last_stream_id, error_code = io.get_values(%i[U32 U32], 0)
|
|
38
|
+
debug = io.get_string(8)
|
|
39
39
|
|
|
40
40
|
Goaway.new(stream_id, last_stream_id, error_code, debug)
|
|
41
41
|
end
|
|
@@ -58,50 +58,57 @@ module Biryani
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
# @param s [String]
|
|
61
|
+
# @param flags [Integer]
|
|
62
|
+
# @param stream_id [Integer]
|
|
61
63
|
#
|
|
62
64
|
# @return [Headers]
|
|
63
65
|
# rubocop: disable Metrics/AbcSize
|
|
64
66
|
# rubocop: disable Metrics/CyclomaticComplexity
|
|
67
|
+
# rubocop: disable Metrics/MethodLength
|
|
65
68
|
# rubocop: disable Metrics/PerceivedComplexity
|
|
66
|
-
def self.read(s)
|
|
67
|
-
payload_length, _, flags, stream_id = Frame.read_header(s)
|
|
68
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
69
|
-
|
|
69
|
+
def self.read(s, flags, stream_id)
|
|
70
70
|
priority = Frame.read_priority(flags)
|
|
71
71
|
padded = Frame.read_padded(flags)
|
|
72
72
|
end_headers = Frame.read_end_headers(flags)
|
|
73
73
|
end_stream = Frame.read_end_stream(flags)
|
|
74
74
|
|
|
75
75
|
if priority && padded
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
io = IO::Buffer.for(s)
|
|
77
|
+
pad_length, stream_dependency, weight = io.get_values(%i[U8 U32 U8], 0)
|
|
78
|
+
fragment_length = s.bytesize - pad_length - 6
|
|
78
79
|
# exclusive = (stream_dependency / 2**31).positive?
|
|
79
80
|
stream_dependency %= 2**31
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'cannot depend on itself') if stream_dependency == stream_id
|
|
82
|
+
|
|
83
|
+
fragment = io.get_string(6, fragment_length)
|
|
84
|
+
padding = io.get_string(6 + fragment_length)
|
|
82
85
|
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'invalid frame') if padding.bytesize != pad_length
|
|
83
86
|
elsif priority
|
|
84
|
-
|
|
87
|
+
io = IO::Buffer.for(s)
|
|
88
|
+
stream_dependency, weight = io.get_values(%i[U32 U8], 0)
|
|
85
89
|
# exclusive = (stream_dependency / 2**31).positive?
|
|
86
90
|
stream_dependency %= 2**31
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'cannot depend on itself') if stream_dependency == stream_id
|
|
92
|
+
|
|
93
|
+
fragment = io.get_string(5)
|
|
94
|
+
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'invalid frame') if fragment.bytesize + 5 != s.bytesize
|
|
89
95
|
elsif padded
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
96
|
+
io = IO::Buffer.for(s)
|
|
97
|
+
pad_length = io.get_value(:U8, 0)
|
|
98
|
+
fragment_length = s.bytesize - pad_length - 1
|
|
99
|
+
fragment = io.get_string(1, fragment_length)
|
|
100
|
+
padding = io.get_string(1 + fragment_length)
|
|
101
|
+
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if pad_length >= s.bytesize
|
|
95
102
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if padding.bytesize != pad_length
|
|
96
103
|
else
|
|
97
|
-
fragment = s
|
|
98
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if fragment.bytesize != payload_length
|
|
104
|
+
fragment = s
|
|
99
105
|
end
|
|
100
106
|
|
|
101
107
|
Headers.new(end_headers, end_stream, stream_id, stream_dependency, weight, fragment, padding)
|
|
102
108
|
end
|
|
103
109
|
# rubocop: enable Metrics/AbcSize
|
|
104
110
|
# rubocop: enable Metrics/CyclomaticComplexity
|
|
111
|
+
# rubocop: enable Metrics/MethodLength
|
|
105
112
|
# rubocop: enable Metrics/PerceivedComplexity
|
|
106
113
|
end
|
|
107
114
|
end
|
data/lib/biryani/frame/ping.rb
CHANGED
|
@@ -32,17 +32,15 @@ module Biryani
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
# @param s [String]
|
|
35
|
+
# @param flags [Integer]
|
|
36
|
+
# @param stream_id [Integer]
|
|
35
37
|
#
|
|
36
38
|
# @return [Ping]
|
|
37
|
-
def self.read(s)
|
|
38
|
-
|
|
39
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
40
|
-
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'PING payload length MUST be 8') if s[9..].bytesize != 8
|
|
39
|
+
def self.read(s, flags, stream_id)
|
|
40
|
+
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'PING payload length MUST be 8') if s.bytesize != 8
|
|
41
41
|
|
|
42
42
|
ack = Frame.read_ack(flags)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
Ping.new(ack, stream_id, opaque)
|
|
43
|
+
Ping.new(ack, stream_id, s)
|
|
46
44
|
end
|
|
47
45
|
end
|
|
48
46
|
end
|
|
@@ -27,15 +27,16 @@ module Biryani
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# @param s [String]
|
|
30
|
+
# @param _flags [Integer]
|
|
31
|
+
# @param stream_id [Integer]
|
|
30
32
|
#
|
|
31
33
|
# @return [Priority]
|
|
32
|
-
def self.read(s)
|
|
33
|
-
|
|
34
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
35
|
-
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'PRIORITY payload length MUST be 5') if payload_length != 5
|
|
34
|
+
def self.read(s, _flags, stream_id)
|
|
35
|
+
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'PRIORITY payload length MUST be 5') if s.bytesize != 5
|
|
36
36
|
|
|
37
|
-
stream_dependency, weight = s
|
|
37
|
+
stream_dependency, weight = s.unpack('NC')
|
|
38
38
|
stream_dependency %= 2**31
|
|
39
|
+
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'cannot depend on itself') if stream_dependency == stream_id
|
|
39
40
|
|
|
40
41
|
Priority.new(stream_id, stream_dependency, weight)
|
|
41
42
|
end
|
|
@@ -43,33 +43,32 @@ module Biryani
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# @param s [String]
|
|
46
|
+
# @param flags [Integer]
|
|
47
|
+
# @param stream_id [Integer]
|
|
46
48
|
#
|
|
47
49
|
# @return [PushPromise]
|
|
48
|
-
|
|
49
|
-
def self.read(s)
|
|
50
|
-
payload_length, _, flags, stream_id = Frame.read_header(s)
|
|
51
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
52
|
-
|
|
50
|
+
def self.read(s, flags, stream_id)
|
|
53
51
|
padded = Frame.read_padded(flags)
|
|
54
52
|
end_headers = Frame.read_end_headers(flags)
|
|
55
53
|
|
|
54
|
+
io = IO::Buffer.for(s)
|
|
56
55
|
if padded
|
|
57
|
-
pad_length =
|
|
58
|
-
promised_stream_id
|
|
59
|
-
fragment_length =
|
|
60
|
-
fragment =
|
|
61
|
-
padding =
|
|
62
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if pad_length >=
|
|
56
|
+
pad_length, promised_stream_id = io.get_values(%i[U8 U32], 0)
|
|
57
|
+
promised_stream_id %= 2**31 # Promised Stream ID (31)
|
|
58
|
+
fragment_length = s.bytesize - pad_length - 5
|
|
59
|
+
fragment = io.get_string(5, fragment_length)
|
|
60
|
+
padding = io.get_string(5 + fragment_length)
|
|
61
|
+
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if pad_length >= s.bytesize
|
|
63
62
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if padding.bytesize != pad_length
|
|
64
63
|
else
|
|
65
|
-
promised_stream_id =
|
|
66
|
-
|
|
67
|
-
|
|
64
|
+
promised_stream_id = io.get_value(:U32, 0)
|
|
65
|
+
promised_stream_id %= 2**31 # Promised Stream ID (31)
|
|
66
|
+
fragment = io.get_string(4)
|
|
67
|
+
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if fragment.bytesize + 4 != s.bytesize
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
PushPromise.new(end_headers, stream_id, promised_stream_id, fragment, padding)
|
|
71
71
|
end
|
|
72
|
-
# rubocop: enable Metrics/AbcSize
|
|
73
72
|
end
|
|
74
73
|
end
|
|
75
74
|
end
|
|
@@ -25,14 +25,14 @@ module Biryani
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# @param s [String]
|
|
28
|
+
# @param _flags [Integer]
|
|
29
|
+
# @param stream_id [Integer]
|
|
28
30
|
#
|
|
29
31
|
# @return [RstStream]
|
|
30
|
-
def self.read(s)
|
|
31
|
-
|
|
32
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
33
|
-
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'RST_STREAM payload length MUST be 4') if s[9..].bytesize != 4
|
|
32
|
+
def self.read(s, _flags, stream_id)
|
|
33
|
+
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'RST_STREAM payload length MUST be 4') if s.bytesize != 4
|
|
34
34
|
|
|
35
|
-
error_code = s
|
|
35
|
+
error_code = s.unpack1('N')
|
|
36
36
|
RstStream.new(stream_id, error_code)
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -33,18 +33,18 @@ module Biryani
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# @param s [String]
|
|
36
|
+
# @param flags [Integer]
|
|
37
|
+
# @param stream_id [Integer]
|
|
36
38
|
#
|
|
37
39
|
# @return [Settings]
|
|
38
40
|
# rubocop: disable Metrics/AbcSize
|
|
39
41
|
# rubocop: disable Metrics/CyclomaticComplexity
|
|
40
42
|
# rubocop: disable Metrics/PerceivedComplexity
|
|
41
|
-
def self.read(s)
|
|
42
|
-
|
|
43
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
44
|
-
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'SETTINGS payload length MUST be a multiple of 6') if payload_length % 6 != 0
|
|
43
|
+
def self.read(s, flags, stream_id)
|
|
44
|
+
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'SETTINGS payload length MUST be a multiple of 6') if s.bytesize % 6 != 0
|
|
45
45
|
|
|
46
46
|
ack = Frame.read_ack(flags)
|
|
47
|
-
setting = s
|
|
47
|
+
setting = s.unpack('nN' * (s.bytesize / 6)).each_slice(2).to_h
|
|
48
48
|
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'SETTINGS MUST NOT have setting with ack') \
|
|
49
49
|
if ack && setting.any?
|
|
50
50
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid SETTINGS_ENABLE_PUSH') \
|
|
@@ -25,18 +25,6 @@ module Biryani
|
|
|
25
25
|
|
|
26
26
|
Frame.to_binary_s_header(payload_length, @f_type, flags, @stream_id) + @payload
|
|
27
27
|
end
|
|
28
|
-
|
|
29
|
-
# @param s [String]
|
|
30
|
-
#
|
|
31
|
-
# @return [Unknown]
|
|
32
|
-
def self.read(s)
|
|
33
|
-
payload_length, f_type, flags, stream_id = Frame.read_header(s)
|
|
34
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
35
|
-
|
|
36
|
-
payload = s[9..]
|
|
37
|
-
|
|
38
|
-
Unknown.new(f_type, flags, stream_id, payload)
|
|
39
|
-
end
|
|
40
28
|
end
|
|
41
29
|
end
|
|
42
30
|
end
|
|
@@ -25,14 +25,14 @@ module Biryani
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
# @param s [String]
|
|
28
|
+
# @param _flags [Integer]
|
|
29
|
+
# @param stream_id [Integer]
|
|
28
30
|
#
|
|
29
31
|
# @return [WindowUpdate]
|
|
30
|
-
def self.read(s)
|
|
31
|
-
|
|
32
|
-
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if s[9..].bytesize != payload_length
|
|
33
|
-
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'WINDOW_UPDATE payload length MUST be 4') if s[9..].bytesize != 4
|
|
32
|
+
def self.read(s, _flags, stream_id)
|
|
33
|
+
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'WINDOW_UPDATE payload length MUST be 4') if s.bytesize != 4
|
|
34
34
|
|
|
35
|
-
window_size_increment = s
|
|
35
|
+
window_size_increment = s.unpack1('N')
|
|
36
36
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'WINDOW_UPDATE invalid window size increment 0') if window_size_increment.zero?
|
|
37
37
|
return ConnectionError.new(ErrorCode::FLOW_CONTROL_ERROR, 'WINDOW_UPDATE invalid window size increment greater than 2^31-1') if window_size_increment > 2**31 - 1
|
|
38
38
|
|
data/lib/biryani/frame.rb
CHANGED
|
@@ -37,11 +37,11 @@ module Biryani
|
|
|
37
37
|
# @return [Integer]
|
|
38
38
|
# @return [Integer]
|
|
39
39
|
def self.read_header(s)
|
|
40
|
-
b0, b1, b2, f_type,
|
|
40
|
+
b0, b1, b2, f_type, flags, stream_id = s.unpack('CCCCCN')
|
|
41
41
|
payload_length = (b0 << 16) | (b1 << 8) | b2
|
|
42
|
-
stream_id
|
|
42
|
+
stream_id %= 2**31 # Stream Identifier (31)
|
|
43
43
|
|
|
44
|
-
[payload_length, f_type,
|
|
44
|
+
[payload_length, f_type, flags, stream_id]
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
# @param uint8 [Integer]
|
|
@@ -136,9 +136,10 @@ module Biryani
|
|
|
136
136
|
|
|
137
137
|
payload_length, f_type, flags, stream_id = read_header(s)
|
|
138
138
|
payload = io.read(payload_length)
|
|
139
|
+
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if payload.bytesize != payload_length
|
|
139
140
|
return Frame::Unknown.new(f_type, flags, stream_id, payload) unless FRAME_MAP.key?(f_type)
|
|
140
141
|
|
|
141
|
-
FRAME_MAP[f_type].read(
|
|
142
|
+
FRAME_MAP[f_type].read(payload, flags, stream_id)
|
|
142
143
|
rescue StandardError
|
|
143
144
|
ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'invalid frame')
|
|
144
145
|
end
|
data/lib/biryani/hpack/field.rb
CHANGED
|
@@ -98,7 +98,7 @@ module Biryani
|
|
|
98
98
|
"\x40#{String.encode(name)}#{String.encode(value)}"
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
# @param
|
|
101
|
+
# @param io [IO::Buffer]
|
|
102
102
|
# @param cursor [Integer]
|
|
103
103
|
# @param dynamic_table [DynamicTable]
|
|
104
104
|
#
|
|
@@ -106,24 +106,24 @@ module Biryani
|
|
|
106
106
|
# @return [Integer]
|
|
107
107
|
# rubocop: disable Metrics/CyclomaticComplexity
|
|
108
108
|
# rubocop: disable Metrics/PerceivedComplexity
|
|
109
|
-
def self.decode(
|
|
110
|
-
byte =
|
|
109
|
+
def self.decode(io, cursor, dynamic_table)
|
|
110
|
+
byte = io.get_value(:U8, cursor)
|
|
111
111
|
if (byte & 0b10000000).positive?
|
|
112
|
-
decode_indexed(
|
|
112
|
+
decode_indexed(io, cursor, dynamic_table)
|
|
113
113
|
elsif byte == 0b01000000
|
|
114
|
-
decode_literal_field_incremental_indexing(
|
|
114
|
+
decode_literal_field_incremental_indexing(io, cursor, dynamic_table)
|
|
115
115
|
elsif (byte & 0b01000000).positive?
|
|
116
|
-
decode_literal_value_incremental_indexing(
|
|
116
|
+
decode_literal_value_incremental_indexing(io, cursor, dynamic_table)
|
|
117
117
|
elsif (byte & 0b00100000).positive?
|
|
118
|
-
decode_dynamic_table_size_update(
|
|
118
|
+
decode_dynamic_table_size_update(io, cursor, dynamic_table)
|
|
119
119
|
elsif byte == 0b00010000
|
|
120
|
-
decode_literal_field_never_indexed(
|
|
120
|
+
decode_literal_field_never_indexed(io, cursor)
|
|
121
121
|
elsif (byte & 0b00010000).positive?
|
|
122
|
-
decode_literal_value_never_indexed(
|
|
122
|
+
decode_literal_value_never_indexed(io, cursor, dynamic_table)
|
|
123
123
|
elsif byte.zero?
|
|
124
|
-
decode_literal_field_without_indexing(
|
|
124
|
+
decode_literal_field_without_indexing(io, cursor)
|
|
125
125
|
elsif (byte & 0b11110000).zero?
|
|
126
|
-
decode_literal_value_without_indexing(
|
|
126
|
+
decode_literal_value_without_indexing(io, cursor, dynamic_table)
|
|
127
127
|
else
|
|
128
128
|
raise 'unreachable'
|
|
129
129
|
end
|
|
@@ -137,14 +137,14 @@ module Biryani
|
|
|
137
137
|
# +---+---------------------------+
|
|
138
138
|
# https://datatracker.ietf.org/doc/html/rfc7541#section-6.1
|
|
139
139
|
#
|
|
140
|
-
# @param
|
|
140
|
+
# @param io [IO::Buffer]
|
|
141
141
|
# @param cursor [Integer]
|
|
142
142
|
# @param dynamic_table [DynamicTable]
|
|
143
143
|
#
|
|
144
144
|
# @return [Array]
|
|
145
145
|
# @return [Integer]
|
|
146
|
-
def self.decode_indexed(
|
|
147
|
-
index, c = Integer.decode(
|
|
146
|
+
def self.decode_indexed(io, cursor, dynamic_table)
|
|
147
|
+
index, c = Integer.decode(io, 7, cursor)
|
|
148
148
|
raise Error::HPACKDecodeError if index.zero?
|
|
149
149
|
raise Error::HPACKDecodeError if index > STATIC_TABLE_SIZE + dynamic_table.count_entries
|
|
150
150
|
|
|
@@ -171,15 +171,15 @@ module Biryani
|
|
|
171
171
|
# +-------------------------------+
|
|
172
172
|
# https://datatracker.ietf.org/doc/html/rfc7541#section-6.2.1
|
|
173
173
|
#
|
|
174
|
-
# @param
|
|
174
|
+
# @param io [IO::Buffer]
|
|
175
175
|
# @param cursor [Integer]
|
|
176
176
|
# @param dynamic_table [DynamicTable]
|
|
177
177
|
#
|
|
178
178
|
# @return [Array]
|
|
179
179
|
# @return [Integer]
|
|
180
|
-
def self.decode_literal_field_incremental_indexing(
|
|
181
|
-
name, c = String.decode(
|
|
182
|
-
value, c = String.decode(
|
|
180
|
+
def self.decode_literal_field_incremental_indexing(io, cursor, dynamic_table)
|
|
181
|
+
name, c = String.decode(io, cursor + 1)
|
|
182
|
+
value, c = String.decode(io, c)
|
|
183
183
|
dynamic_table.store(name, value)
|
|
184
184
|
|
|
185
185
|
[[name, value], c]
|
|
@@ -195,14 +195,14 @@ module Biryani
|
|
|
195
195
|
# +-------------------------------+
|
|
196
196
|
# https://datatracker.ietf.org/doc/html/rfc7541#section-6.2.1
|
|
197
197
|
#
|
|
198
|
-
# @param
|
|
198
|
+
# @param io [IO::Buffer]
|
|
199
199
|
# @param cursor [Integer]
|
|
200
200
|
# @param dynamic_table [DynamicTable]
|
|
201
201
|
#
|
|
202
202
|
# @return [Array]
|
|
203
203
|
# @return [Integer]
|
|
204
|
-
def self.decode_literal_value_incremental_indexing(
|
|
205
|
-
index, c = Integer.decode(
|
|
204
|
+
def self.decode_literal_value_incremental_indexing(io, cursor, dynamic_table)
|
|
205
|
+
index, c = Integer.decode(io, 6, cursor)
|
|
206
206
|
raise Error::HPACKDecodeError if index.zero?
|
|
207
207
|
raise Error::HPACKDecodeError if index > STATIC_TABLE_SIZE + dynamic_table.count_entries
|
|
208
208
|
|
|
@@ -211,7 +211,7 @@ module Biryani
|
|
|
211
211
|
else
|
|
212
212
|
dynamic_table[index - 1 - STATIC_TABLE_SIZE][0]
|
|
213
213
|
end
|
|
214
|
-
value, c = String.decode(
|
|
214
|
+
value, c = String.decode(io, c)
|
|
215
215
|
dynamic_table.store(name, value)
|
|
216
216
|
|
|
217
217
|
[[name, value], c]
|
|
@@ -223,16 +223,16 @@ module Biryani
|
|
|
223
223
|
# +---+---------------------------+
|
|
224
224
|
# https://datatracker.ietf.org/doc/html/rfc7541#section-6.3
|
|
225
225
|
#
|
|
226
|
-
# @param
|
|
226
|
+
# @param io [IO::Buffer]
|
|
227
227
|
# @param cursor [Integer]
|
|
228
228
|
# @param dynamic_table [DynamicTable]
|
|
229
229
|
#
|
|
230
230
|
# @return [nil]
|
|
231
231
|
# @return [Integer]
|
|
232
|
-
def self.decode_dynamic_table_size_update(
|
|
233
|
-
raise Error::HPACKDecodeError unless cursor.zero? || (
|
|
232
|
+
def self.decode_dynamic_table_size_update(io, cursor, dynamic_table)
|
|
233
|
+
raise Error::HPACKDecodeError unless cursor.zero? || (io.get_value(:U8, 0) & 0b00100000).positive? && Integer.decode(io, 5, 0)[1] == cursor
|
|
234
234
|
|
|
235
|
-
max_size, c = Integer.decode(
|
|
235
|
+
max_size, c = Integer.decode(io, 5, cursor)
|
|
236
236
|
raise Error::HPACKDecodeError if max_size > dynamic_table.limit
|
|
237
237
|
|
|
238
238
|
dynamic_table.chomp!(max_size)
|
|
@@ -253,14 +253,14 @@ module Biryani
|
|
|
253
253
|
# +-------------------------------+
|
|
254
254
|
# https://datatracker.ietf.org/doc/html/rfc7541#section-6.2.3
|
|
255
255
|
#
|
|
256
|
-
# @param
|
|
256
|
+
# @param io [IO::Buffer]
|
|
257
257
|
# @param cursor [Integer]
|
|
258
258
|
#
|
|
259
259
|
# @return [Array]
|
|
260
260
|
# @return [Integer]
|
|
261
|
-
def self.decode_literal_field_never_indexed(
|
|
262
|
-
name, c = String.decode(
|
|
263
|
-
value, c = String.decode(
|
|
261
|
+
def self.decode_literal_field_never_indexed(io, cursor)
|
|
262
|
+
name, c = String.decode(io, cursor + 1)
|
|
263
|
+
value, c = String.decode(io, c)
|
|
264
264
|
|
|
265
265
|
[[name, value], c]
|
|
266
266
|
end
|
|
@@ -275,14 +275,14 @@ module Biryani
|
|
|
275
275
|
# +-------------------------------+
|
|
276
276
|
# https://datatracker.ietf.org/doc/html/rfc7541#section-6.2.3
|
|
277
277
|
#
|
|
278
|
-
# @param
|
|
278
|
+
# @param io [IO::Buffer]
|
|
279
279
|
# @param cursor [Integer]
|
|
280
280
|
# @param dynamic_table [DynamicTable]
|
|
281
281
|
#
|
|
282
282
|
# @return [Array]
|
|
283
283
|
# @return [Integer]
|
|
284
|
-
def self.decode_literal_value_never_indexed(
|
|
285
|
-
index, c = Integer.decode(
|
|
284
|
+
def self.decode_literal_value_never_indexed(io, cursor, dynamic_table)
|
|
285
|
+
index, c = Integer.decode(io, 4, cursor)
|
|
286
286
|
raise Error::HPACKDecodeError if index.zero?
|
|
287
287
|
raise Error::HPACKDecodeError if index > STATIC_TABLE_SIZE + dynamic_table.count_entries
|
|
288
288
|
|
|
@@ -291,7 +291,7 @@ module Biryani
|
|
|
291
291
|
else
|
|
292
292
|
dynamic_table[index - 1 - STATIC_TABLE_SIZE][0]
|
|
293
293
|
end
|
|
294
|
-
value, c = String.decode(
|
|
294
|
+
value, c = String.decode(io, c)
|
|
295
295
|
|
|
296
296
|
[[name, value], c]
|
|
297
297
|
end
|
|
@@ -310,14 +310,14 @@ module Biryani
|
|
|
310
310
|
# +-------------------------------+
|
|
311
311
|
# https://datatracker.ietf.org/doc/html/rfc7541#section-6.2.2
|
|
312
312
|
#
|
|
313
|
-
# @param
|
|
313
|
+
# @param io [IO::Buffer]
|
|
314
314
|
# @param cursor [Integer]
|
|
315
315
|
#
|
|
316
316
|
# @return [Array]
|
|
317
317
|
# @return [Integer]
|
|
318
|
-
def self.decode_literal_field_without_indexing(
|
|
319
|
-
name, c = String.decode(
|
|
320
|
-
value, c = String.decode(
|
|
318
|
+
def self.decode_literal_field_without_indexing(io, cursor)
|
|
319
|
+
name, c = String.decode(io, cursor + 1)
|
|
320
|
+
value, c = String.decode(io, c)
|
|
321
321
|
|
|
322
322
|
[[name, value], c]
|
|
323
323
|
end
|
|
@@ -331,14 +331,14 @@ module Biryani
|
|
|
331
331
|
# | Value String (Length octets) |
|
|
332
332
|
# +-------------------------------+
|
|
333
333
|
#
|
|
334
|
-
# @param
|
|
334
|
+
# @param io [IO::Buffer]
|
|
335
335
|
# @param cursor [Integer]
|
|
336
336
|
# @param dynamic_table [DynamicTable]
|
|
337
337
|
#
|
|
338
338
|
# @return [Array]
|
|
339
339
|
# @return [Integer]
|
|
340
|
-
def self.decode_literal_value_without_indexing(
|
|
341
|
-
index, c = Integer.decode(
|
|
340
|
+
def self.decode_literal_value_without_indexing(io, cursor, dynamic_table)
|
|
341
|
+
index, c = Integer.decode(io, 4, cursor)
|
|
342
342
|
raise Error::HPACKDecodeError if index.zero?
|
|
343
343
|
raise Error::HPACKDecodeError if index > STATIC_TABLE_SIZE + dynamic_table.count_entries
|
|
344
344
|
|
|
@@ -347,7 +347,7 @@ module Biryani
|
|
|
347
347
|
else
|
|
348
348
|
dynamic_table[index - 1 - STATIC_TABLE_SIZE][0]
|
|
349
349
|
end
|
|
350
|
-
value, c = String.decode(
|
|
350
|
+
value, c = String.decode(io, c)
|
|
351
351
|
|
|
352
352
|
[[name, value], c]
|
|
353
353
|
end
|
data/lib/biryani/hpack/fields.rb
CHANGED
|
@@ -14,10 +14,11 @@ module Biryani
|
|
|
14
14
|
#
|
|
15
15
|
# @return [Array]
|
|
16
16
|
def self.decode(s, dynamic_table)
|
|
17
|
+
io = IO::Buffer.for(s)
|
|
17
18
|
cursor = 0
|
|
18
19
|
fields = []
|
|
19
20
|
while cursor < s.bytesize
|
|
20
|
-
field, cursor = Field.decode(
|
|
21
|
+
field, cursor = Field.decode(io, cursor, dynamic_table)
|
|
21
22
|
fields << field unless field.nil?
|
|
22
23
|
end
|
|
23
24
|
|