biryani 0.0.12 → 0.0.14
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 +25 -18
- data/lib/biryani/data_buffer.rb +2 -2
- data/lib/biryani/frame/data.rb +1 -4
- data/lib/biryani/frame/headers.rb +3 -10
- data/lib/biryani/frame/push_promise.rb +2 -4
- data/lib/biryani/frame.rb +16 -0
- data/lib/biryani/hpack/dynamic_table.rb +20 -0
- data/lib/biryani/hpack/field.rb +4 -29
- data/lib/biryani/stream.rb +1 -1
- data/lib/biryani/streams_context.rb +2 -2
- data/lib/biryani/version.rb +1 -1
- 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: 96b9dc1294ce1840c02e6cf1700b1ebae51409b586dffd25b7df814acdd65034
|
|
4
|
+
data.tar.gz: 66d417773c04e090101661650748415566e9f4ca0f91ff95720b2cb7f8668f7b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '079d3ab7c12394a9fd340757c5f076cbe937bd1f1a1e60cd24e98f7535526e320189a35f6baa2a5dab3c621b467fec52566e2cfec43a9b994d9f4e5c9dd818cf'
|
|
7
|
+
data.tar.gz: 7eed1c453cef71438ee5e625bd7b954c38c8cb4aa417db79c00894bf00e4f1f500a759a16d8c6ea58d3c90f273b5bcbbbefbe3a2de1059367b087095921aaec8
|
data/lib/biryani/connection.rb
CHANGED
|
@@ -12,20 +12,23 @@ module Biryani
|
|
|
12
12
|
class Connection
|
|
13
13
|
CONNECTION_PREFACE = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".freeze
|
|
14
14
|
CONNECTION_PREFACE_LENGTH = CONNECTION_PREFACE.length
|
|
15
|
+
DEFAULT_HEADER_TABLE_SIZE = 4_096
|
|
16
|
+
INITIAL_CONNECTION_WINDOW_SIZE = 65_535
|
|
15
17
|
|
|
16
|
-
private_constant :CONNECTION_PREFACE, :CONNECTION_PREFACE_LENGTH
|
|
18
|
+
private_constant :CONNECTION_PREFACE, :CONNECTION_PREFACE_LENGTH, :DEFAULT_HEADER_TABLE_SIZE, :INITIAL_CONNECTION_WINDOW_SIZE
|
|
17
19
|
Ractor.make_shareable(CONNECTION_PREFACE)
|
|
18
20
|
Ractor.make_shareable(CONNECTION_PREFACE_LENGTH)
|
|
19
21
|
|
|
20
22
|
# proc [Proc]
|
|
21
23
|
def initialize(proc)
|
|
22
|
-
@
|
|
24
|
+
@port = Ractor::Port.new
|
|
25
|
+
@recv_done = false
|
|
23
26
|
@proc = proc
|
|
24
|
-
@streams_ctx = StreamsContext.new(proc)
|
|
25
|
-
@encoder = HPACK::Encoder.new(
|
|
26
|
-
@decoder = HPACK::Decoder.new(
|
|
27
|
-
@send_window = Window.new(
|
|
28
|
-
@recv_window = Window.new(
|
|
27
|
+
@streams_ctx = StreamsContext.new(proc, @port)
|
|
28
|
+
@encoder = HPACK::Encoder.new(DEFAULT_HEADER_TABLE_SIZE)
|
|
29
|
+
@decoder = HPACK::Decoder.new(DEFAULT_HEADER_TABLE_SIZE)
|
|
30
|
+
@send_window = Window.new(INITIAL_CONNECTION_WINDOW_SIZE)
|
|
31
|
+
@recv_window = Window.new(INITIAL_CONNECTION_WINDOW_SIZE)
|
|
29
32
|
@data_buffer = DataBuffer.new
|
|
30
33
|
@settings = self.class.default_settings # Hash<Integer, Integer>
|
|
31
34
|
@peer_settings = self.class.default_settings # Hash<Integer, Integer>
|
|
@@ -53,12 +56,15 @@ module Biryani
|
|
|
53
56
|
|
|
54
57
|
# @param io [IO]
|
|
55
58
|
def recv_loop(io)
|
|
56
|
-
Ractor.new(io, @
|
|
59
|
+
Ractor.new(io, @port) do |io_, port_|
|
|
57
60
|
loop do
|
|
58
61
|
obj = Frame.read(io_)
|
|
59
|
-
|
|
62
|
+
if obj.nil?
|
|
63
|
+
port_.send(:eof, move: true)
|
|
64
|
+
break
|
|
65
|
+
end
|
|
60
66
|
|
|
61
|
-
|
|
67
|
+
port_.send([:frame, obj], move: true)
|
|
62
68
|
end
|
|
63
69
|
end
|
|
64
70
|
end
|
|
@@ -66,13 +72,14 @@ module Biryani
|
|
|
66
72
|
# @param io [IO]
|
|
67
73
|
def select_loop(io)
|
|
68
74
|
loop do
|
|
69
|
-
break if @
|
|
75
|
+
break if @recv_done && @streams_ctx.empty?
|
|
70
76
|
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
case @port.receive
|
|
78
|
+
in :eof
|
|
79
|
+
@recv_done = true
|
|
80
|
+
in [:frame, obj]
|
|
73
81
|
recv_dispatch(io, obj)
|
|
74
|
-
|
|
75
|
-
res, stream_id = obj
|
|
82
|
+
in [:response, res, stream_id]
|
|
76
83
|
handle_response(io, res, stream_id)
|
|
77
84
|
end
|
|
78
85
|
|
|
@@ -118,7 +125,7 @@ module Biryani
|
|
|
118
125
|
# @return [Array<Object>, Array<ConnectionError>, Array<StreamError>] frames or errors
|
|
119
126
|
def do_recv_dispatch(frame)
|
|
120
127
|
receiving_continuation_stream_id = @streams_ctx.receiving_continuation_stream_id
|
|
121
|
-
return [ConnectionError.new(ErrorCode::PROTOCOL_ERROR, "invalid frame type #{format('0x%02x',
|
|
128
|
+
return [ConnectionError.new(ErrorCode::PROTOCOL_ERROR, "invalid frame type #{format('0x%02x', frame.f_type)} for stream identifier #{format('0x%02x', frame.stream_id)}")] \
|
|
122
129
|
if !receiving_continuation_stream_id.nil? && frame.stream_id != receiving_continuation_stream_id
|
|
123
130
|
|
|
124
131
|
if frame.stream_id.zero?
|
|
@@ -135,7 +142,7 @@ module Biryani
|
|
|
135
142
|
def handle_connection_frame(frame)
|
|
136
143
|
case frame.f_type
|
|
137
144
|
when FrameType::DATA, FrameType::HEADERS, FrameType::PRIORITY, FrameType::RST_STREAM, FrameType::PUSH_PROMISE, FrameType::CONTINUATION
|
|
138
|
-
[ConnectionError.new(ErrorCode::PROTOCOL_ERROR, "invalid frame type #{format('0x%02x',
|
|
145
|
+
[ConnectionError.new(ErrorCode::PROTOCOL_ERROR, "invalid frame type #{format('0x%02x', frame.f_type)} for stream identifier 0x00")]
|
|
139
146
|
when FrameType::SETTINGS
|
|
140
147
|
obj = self.class.handle_settings(frame, @peer_settings, @decoder, @streams_ctx)
|
|
141
148
|
return [] if obj.nil?
|
|
@@ -486,7 +493,7 @@ module Biryani
|
|
|
486
493
|
def self.default_settings
|
|
487
494
|
# https://datatracker.ietf.org/doc/html/rfc9113#section-6.5.2
|
|
488
495
|
{
|
|
489
|
-
SettingsID::SETTINGS_HEADER_TABLE_SIZE =>
|
|
496
|
+
SettingsID::SETTINGS_HEADER_TABLE_SIZE => DEFAULT_HEADER_TABLE_SIZE,
|
|
490
497
|
SettingsID::SETTINGS_ENABLE_PUSH => 1,
|
|
491
498
|
SettingsID::SETTINGS_MAX_CONCURRENT_STREAMS => 0xffffffff,
|
|
492
499
|
SettingsID::SETTINGS_INITIAL_WINDOW_SIZE => 65_535,
|
data/lib/biryani/data_buffer.rb
CHANGED
|
@@ -20,14 +20,14 @@ module Biryani
|
|
|
20
20
|
datas = []
|
|
21
21
|
@buffer.each do |stream_id, data|
|
|
22
22
|
frames, remains = streams_ctx.sendable_datas(stream_id, data, send_window, max_frame_size)
|
|
23
|
-
datas
|
|
23
|
+
datas.concat(frames)
|
|
24
24
|
if remains.empty?
|
|
25
25
|
@buffer.delete(stream_id)
|
|
26
26
|
else
|
|
27
27
|
@buffer[stream_id] = remains
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
len = frames.
|
|
30
|
+
len = frames.sum(&:length)
|
|
31
31
|
send_window.consume!(len)
|
|
32
32
|
streams_ctx[stream_id].send_window.consume!(len)
|
|
33
33
|
end
|
data/lib/biryani/frame/data.rb
CHANGED
|
@@ -51,10 +51,7 @@ module Biryani
|
|
|
51
51
|
|
|
52
52
|
if padded
|
|
53
53
|
io = IO::Buffer.for(s)
|
|
54
|
-
pad_length =
|
|
55
|
-
data_length = s.bytesize - pad_length - 1
|
|
56
|
-
data = io.get_string(1, data_length)
|
|
57
|
-
padding = io.get_string(1 + data_length)
|
|
54
|
+
data, padding, pad_length = Frame.read_padded_payload(io, s, 1)
|
|
58
55
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if padding.bytesize != pad_length
|
|
59
56
|
else
|
|
60
57
|
data = s
|
|
@@ -64,7 +64,6 @@ module Biryani
|
|
|
64
64
|
# @return [Headers]
|
|
65
65
|
# rubocop: disable Metrics/AbcSize
|
|
66
66
|
# rubocop: disable Metrics/CyclomaticComplexity
|
|
67
|
-
# rubocop: disable Metrics/MethodLength
|
|
68
67
|
# rubocop: disable Metrics/PerceivedComplexity
|
|
69
68
|
def self.read(s, flags, stream_id)
|
|
70
69
|
priority = Frame.read_priority(flags)
|
|
@@ -74,14 +73,12 @@ module Biryani
|
|
|
74
73
|
|
|
75
74
|
if priority && padded
|
|
76
75
|
io = IO::Buffer.for(s)
|
|
77
|
-
|
|
78
|
-
fragment_length = s.bytesize - pad_length - 6
|
|
76
|
+
_pad_length, stream_dependency, weight = io.get_values(%i[U8 U32 U8], 0)
|
|
79
77
|
# exclusive = (stream_dependency / 2**31).positive?
|
|
80
78
|
stream_dependency %= 2**31
|
|
81
79
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'cannot depend on itself') if stream_dependency == stream_id
|
|
82
80
|
|
|
83
|
-
fragment =
|
|
84
|
-
padding = io.get_string(6 + fragment_length)
|
|
81
|
+
fragment, padding, pad_length = Frame.read_padded_payload(io, s, 6)
|
|
85
82
|
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'invalid frame') if padding.bytesize != pad_length
|
|
86
83
|
elsif priority
|
|
87
84
|
io = IO::Buffer.for(s)
|
|
@@ -94,10 +91,7 @@ module Biryani
|
|
|
94
91
|
return ConnectionError.new(ErrorCode::FRAME_SIZE_ERROR, 'invalid frame') if fragment.bytesize + 5 != s.bytesize
|
|
95
92
|
elsif padded
|
|
96
93
|
io = IO::Buffer.for(s)
|
|
97
|
-
pad_length =
|
|
98
|
-
fragment_length = s.bytesize - pad_length - 1
|
|
99
|
-
fragment = io.get_string(1, fragment_length)
|
|
100
|
-
padding = io.get_string(1 + fragment_length)
|
|
94
|
+
fragment, padding, pad_length = Frame.read_padded_payload(io, s, 1)
|
|
101
95
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if pad_length >= s.bytesize
|
|
102
96
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if padding.bytesize != pad_length
|
|
103
97
|
else
|
|
@@ -108,7 +102,6 @@ module Biryani
|
|
|
108
102
|
end
|
|
109
103
|
# rubocop: enable Metrics/AbcSize
|
|
110
104
|
# rubocop: enable Metrics/CyclomaticComplexity
|
|
111
|
-
# rubocop: enable Metrics/MethodLength
|
|
112
105
|
# rubocop: enable Metrics/PerceivedComplexity
|
|
113
106
|
end
|
|
114
107
|
end
|
|
@@ -53,11 +53,9 @@ module Biryani
|
|
|
53
53
|
|
|
54
54
|
io = IO::Buffer.for(s)
|
|
55
55
|
if padded
|
|
56
|
-
|
|
56
|
+
_pad_length, promised_stream_id = io.get_values(%i[U8 U32], 0)
|
|
57
57
|
promised_stream_id %= 2**31 # Promised Stream ID (31)
|
|
58
|
-
|
|
59
|
-
fragment = io.get_string(5, fragment_length)
|
|
60
|
-
padding = io.get_string(5 + fragment_length)
|
|
58
|
+
fragment, padding, pad_length = Frame.read_padded_payload(io, s, 5)
|
|
61
59
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if pad_length >= s.bytesize
|
|
62
60
|
return ConnectionError.new(ErrorCode::PROTOCOL_ERROR, 'invalid frame') if padding.bytesize != pad_length
|
|
63
61
|
else
|
data/lib/biryani/frame.rb
CHANGED
|
@@ -98,6 +98,22 @@ module Biryani
|
|
|
98
98
|
def self.to_binary_s_header(payload_length, f_type, flags, stream_id)
|
|
99
99
|
[payload_length, f_type, flags, stream_id].pack('NCCN')[1..]
|
|
100
100
|
end
|
|
101
|
+
|
|
102
|
+
# @param io [IO::Buffer]
|
|
103
|
+
# @param s [String]
|
|
104
|
+
# @param header_length [Integer] bytes preceding the payload, including the 1-byte Pad Length field
|
|
105
|
+
#
|
|
106
|
+
# @return [String] payload
|
|
107
|
+
# @return [String] padding
|
|
108
|
+
# @return [Integer] pad_length
|
|
109
|
+
def self.read_padded_payload(io, s, header_length)
|
|
110
|
+
pad_length = io.get_value(:U8, 0)
|
|
111
|
+
payload_length = s.bytesize - pad_length - header_length
|
|
112
|
+
payload = io.get_string(header_length, payload_length)
|
|
113
|
+
padding = io.get_string(header_length + payload_length)
|
|
114
|
+
|
|
115
|
+
[payload, padding, pad_length]
|
|
116
|
+
end
|
|
101
117
|
end
|
|
102
118
|
end
|
|
103
119
|
|
|
@@ -47,6 +47,26 @@ module Biryani
|
|
|
47
47
|
@table[index]
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# @param index [Integer]
|
|
51
|
+
#
|
|
52
|
+
# @return [Array] [name, value]
|
|
53
|
+
def entry_at(index)
|
|
54
|
+
raise Error::HPACKDecodeError if index.zero? || index > STATIC_TABLE_SIZE + count_entries
|
|
55
|
+
|
|
56
|
+
if index <= STATIC_TABLE_SIZE
|
|
57
|
+
STATIC_TABLE[index - 1]
|
|
58
|
+
else
|
|
59
|
+
self[index - 1 - STATIC_TABLE_SIZE]
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @param index [Integer]
|
|
64
|
+
#
|
|
65
|
+
# @return [String]
|
|
66
|
+
def name_at(index)
|
|
67
|
+
entry_at(index)[0]
|
|
68
|
+
end
|
|
69
|
+
|
|
50
70
|
# @param new_limit [Integer]
|
|
51
71
|
def chomp!(new_limit)
|
|
52
72
|
while @size > new_limit
|
data/lib/biryani/hpack/field.rb
CHANGED
|
@@ -145,15 +145,8 @@ module Biryani
|
|
|
145
145
|
# @return [Integer]
|
|
146
146
|
def self.decode_indexed(io, cursor, dynamic_table)
|
|
147
147
|
index, c = Integer.decode(io, 7, cursor)
|
|
148
|
-
raise Error::HPACKDecodeError if index.zero? || index > STATIC_TABLE_SIZE + dynamic_table.count_entries
|
|
149
148
|
|
|
150
|
-
|
|
151
|
-
STATIC_TABLE[index - 1]
|
|
152
|
-
else
|
|
153
|
-
dynamic_table[index - 1 - STATIC_TABLE_SIZE]
|
|
154
|
-
end
|
|
155
|
-
|
|
156
|
-
[field, c]
|
|
149
|
+
[dynamic_table.entry_at(index), c]
|
|
157
150
|
end
|
|
158
151
|
|
|
159
152
|
# 0 1 2 3 4 5 6 7
|
|
@@ -202,13 +195,7 @@ module Biryani
|
|
|
202
195
|
# @return [Integer]
|
|
203
196
|
def self.decode_literal_value_incremental_indexing(io, cursor, dynamic_table)
|
|
204
197
|
index, c = Integer.decode(io, 6, cursor)
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
name = if index <= STATIC_TABLE_SIZE
|
|
208
|
-
STATIC_TABLE[index - 1][0]
|
|
209
|
-
else
|
|
210
|
-
dynamic_table[index - 1 - STATIC_TABLE_SIZE][0]
|
|
211
|
-
end
|
|
198
|
+
name = dynamic_table.name_at(index)
|
|
212
199
|
value, c = String.decode(io, c)
|
|
213
200
|
dynamic_table.store(name, value)
|
|
214
201
|
|
|
@@ -281,13 +268,7 @@ module Biryani
|
|
|
281
268
|
# @return [Integer]
|
|
282
269
|
def self.decode_literal_value_never_indexed(io, cursor, dynamic_table)
|
|
283
270
|
index, c = Integer.decode(io, 4, cursor)
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
name = if index <= STATIC_TABLE_SIZE
|
|
287
|
-
STATIC_TABLE[index - 1][0]
|
|
288
|
-
else
|
|
289
|
-
dynamic_table[index - 1 - STATIC_TABLE_SIZE][0]
|
|
290
|
-
end
|
|
271
|
+
name = dynamic_table.name_at(index)
|
|
291
272
|
value, c = String.decode(io, c)
|
|
292
273
|
|
|
293
274
|
[[name, value], c]
|
|
@@ -336,13 +317,7 @@ module Biryani
|
|
|
336
317
|
# @return [Integer]
|
|
337
318
|
def self.decode_literal_value_without_indexing(io, cursor, dynamic_table)
|
|
338
319
|
index, c = Integer.decode(io, 4, cursor)
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
name = if index <= STATIC_TABLE_SIZE
|
|
342
|
-
STATIC_TABLE[index - 1][0]
|
|
343
|
-
else
|
|
344
|
-
dynamic_table[index - 1 - STATIC_TABLE_SIZE][0]
|
|
345
|
-
end
|
|
320
|
+
name = dynamic_table.name_at(index)
|
|
346
321
|
value, c = String.decode(io, c)
|
|
347
322
|
|
|
348
323
|
[[name, value], c]
|
data/lib/biryani/stream.rb
CHANGED
data/lib/biryani/version.rb
CHANGED