biryani 0.0.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 +7 -0
- data/.github/workflows/ci.yml +30 -0
- data/.gitignore +17 -0
- data/.rubocop.yml +36 -0
- data/.ruby-version +1 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +48 -0
- data/Rakefile +8 -0
- data/biryani.gemspec +21 -0
- data/example/echo.rb +27 -0
- data/example/hello_world.rb +22 -0
- data/lib/biryani/connection.rb +464 -0
- data/lib/biryani/connection_error.rb +17 -0
- data/lib/biryani/data_buffer.rb +42 -0
- data/lib/biryani/frame/continuation.rb +48 -0
- data/lib/biryani/frame/data.rb +70 -0
- data/lib/biryani/frame/goaway.rb +44 -0
- data/lib/biryani/frame/headers.rb +110 -0
- data/lib/biryani/frame/ping.rb +49 -0
- data/lib/biryani/frame/priority.rb +44 -0
- data/lib/biryani/frame/push_promise.rb +75 -0
- data/lib/biryani/frame/rst_stream.rb +40 -0
- data/lib/biryani/frame/settings.rb +66 -0
- data/lib/biryani/frame/unknown.rb +42 -0
- data/lib/biryani/frame/window_update.rb +43 -0
- data/lib/biryani/frame.rb +146 -0
- data/lib/biryani/hpack/decoder.rb +22 -0
- data/lib/biryani/hpack/dynamic_table.rb +65 -0
- data/lib/biryani/hpack/encoder.rb +22 -0
- data/lib/biryani/hpack/error.rb +12 -0
- data/lib/biryani/hpack/field.rb +357 -0
- data/lib/biryani/hpack/fields.rb +28 -0
- data/lib/biryani/hpack/huffman.rb +309 -0
- data/lib/biryani/hpack/integer.rb +66 -0
- data/lib/biryani/hpack/option.rb +24 -0
- data/lib/biryani/hpack/string.rb +40 -0
- data/lib/biryani/hpack.rb +84 -0
- data/lib/biryani/http_request.rb +83 -0
- data/lib/biryani/http_response.rb +61 -0
- data/lib/biryani/server.rb +19 -0
- data/lib/biryani/state.rb +224 -0
- data/lib/biryani/stream.rb +19 -0
- data/lib/biryani/stream_error.rb +17 -0
- data/lib/biryani/streams_context.rb +89 -0
- data/lib/biryani/version.rb +3 -0
- data/lib/biryani/window.rb +29 -0
- data/lib/biryani.rb +17 -0
- data/spec/connection/close_all_streams_spec.rb +17 -0
- data/spec/connection/handle_connection_window_update_spec.rb +16 -0
- data/spec/connection/handle_ping_spec.rb +21 -0
- data/spec/connection/handle_rst_stream_spec.rb +21 -0
- data/spec/connection/handle_settings_spec.rb +31 -0
- data/spec/connection/handle_stream_window_update_spec.rb +20 -0
- data/spec/connection/read_http2_magic_spec.rb +26 -0
- data/spec/connection/remove_closed_streams_spec.rb +51 -0
- data/spec/connection/send_spec.rb +114 -0
- data/spec/connection/transition_state_send_spec.rb +39 -0
- data/spec/connection/unwrap_spec.rb +28 -0
- data/spec/data_buffer_spec.rb +135 -0
- data/spec/frame/continuation_spec.rb +39 -0
- data/spec/frame/data_spec.rb +25 -0
- data/spec/frame/goaway_spec.rb +23 -0
- data/spec/frame/headers_spec.rb +52 -0
- data/spec/frame/ping_spec.rb +22 -0
- data/spec/frame/priority_spec.rb +22 -0
- data/spec/frame/push_promise_spec.rb +24 -0
- data/spec/frame/read_spec.rb +30 -0
- data/spec/frame/rst_stream_spec.rb +21 -0
- data/spec/frame/settings_spec.rb +23 -0
- data/spec/frame/window_update_spec.rb +21 -0
- data/spec/hpack/decoder_spec.rb +170 -0
- data/spec/hpack/encoder_spec.rb +48 -0
- data/spec/hpack/field_spec.rb +42 -0
- data/spec/hpack/fields_spec.rb +17 -0
- data/spec/hpack/huffman_spec.rb +20 -0
- data/spec/hpack/integer_spec.rb +27 -0
- data/spec/hpack/string_spec.rb +19 -0
- data/spec/http_request_builder_spec.rb +45 -0
- data/spec/spec_helper.rb +9 -0
- metadata +165 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
module Biryani
|
|
2
|
+
class State
|
|
3
|
+
def initialize
|
|
4
|
+
@state = :idle
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# @param frame [Object]
|
|
8
|
+
# @param direction [:send, :recv]
|
|
9
|
+
def transition!(frame, direction)
|
|
10
|
+
obj = self.class.next(@state, frame, direction)
|
|
11
|
+
return obj if obj.is_a?(StreamError) || obj.is_a?(ConnectionError)
|
|
12
|
+
|
|
13
|
+
@state = obj
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# +--------+
|
|
17
|
+
# send PP | | recv PP
|
|
18
|
+
# ,--------+ idle +--------.
|
|
19
|
+
# / | | \
|
|
20
|
+
# v +--------+ v
|
|
21
|
+
# +----------+ | +----------+
|
|
22
|
+
# | | | send H / | |
|
|
23
|
+
# ,------+ reserved | | recv H | reserved +------.
|
|
24
|
+
# | | (local) | | | (remote) | |
|
|
25
|
+
# | +---+------+ v +------+---+ |
|
|
26
|
+
# | | +--------+ | |
|
|
27
|
+
# | | recv ES | | send ES | |
|
|
28
|
+
# | send H | ,-------+ open +-------. | recv H |
|
|
29
|
+
# | | / | | \ | |
|
|
30
|
+
# | v v +---+----+ v v |
|
|
31
|
+
# | +----------+ | +----------+ |
|
|
32
|
+
# | | half- | | | half- | |
|
|
33
|
+
# | | closed | | send R / | closed | |
|
|
34
|
+
# | | (remote) | | recv R | (local) | |
|
|
35
|
+
# | +----+-----+ | +-----+----+ |
|
|
36
|
+
# | | | | |
|
|
37
|
+
# | | send ES / | recv ES / | |
|
|
38
|
+
# | | send R / v send R / | |
|
|
39
|
+
# | | recv R +--------+ recv R | |
|
|
40
|
+
# | send R / `----------->| |<-----------' send R / |
|
|
41
|
+
# | recv R | closed | recv R |
|
|
42
|
+
# `----------------------->| |<-----------------------'
|
|
43
|
+
# +--------+
|
|
44
|
+
# https://datatracker.ietf.org/doc/html/rfc9113#section-5.1
|
|
45
|
+
#
|
|
46
|
+
# @param state [Symbol]
|
|
47
|
+
# @param frame [Object]
|
|
48
|
+
# @param direction [:send, :recv]
|
|
49
|
+
#
|
|
50
|
+
# @raise [StandardError]
|
|
51
|
+
#
|
|
52
|
+
# @return [Symbol, ConnectionError]
|
|
53
|
+
# rubocop: disable Metrics/AbcSize
|
|
54
|
+
# rubocop: disable Metrics/CyclomaticComplexity
|
|
55
|
+
# rubocop: disable Metrics/MethodLength
|
|
56
|
+
# rubocop: disable Metrics/PerceivedComplexity
|
|
57
|
+
def self.next(state, frame, direction)
|
|
58
|
+
typ = frame.f_type
|
|
59
|
+
case [state, typ, direction]
|
|
60
|
+
# idle
|
|
61
|
+
in [:idle, FrameType::HEADERS, :recv] if frame.end_stream? && frame.end_headers?
|
|
62
|
+
:half_closed_remote
|
|
63
|
+
in [:idle, FrameType::HEADERS, :recv] if frame.end_headers?
|
|
64
|
+
:receiving_data
|
|
65
|
+
in [:idle, FrameType::HEADERS, :recv] if frame.end_stream?
|
|
66
|
+
:receiving_continuation
|
|
67
|
+
in [:idle, FrameType::HEADERS, :recv]
|
|
68
|
+
:receiving_continuation_data
|
|
69
|
+
in [:idle, FrameType::PRIORITY, :recv]
|
|
70
|
+
state
|
|
71
|
+
in [:idle, FrameType::PUSH_PROMISE, :send]
|
|
72
|
+
:reserved_local
|
|
73
|
+
in [:idle, _, _]
|
|
74
|
+
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
75
|
+
|
|
76
|
+
# receiving_continuation_data
|
|
77
|
+
in [:receiving_continuation_data, FrameType::RST_STREAM, _]
|
|
78
|
+
:closed
|
|
79
|
+
in [:receiving_continuation_data, FrameType::CONTINUATION, :recv] if frame.end_headers?
|
|
80
|
+
:receiving_data
|
|
81
|
+
in [:receiving_continuation_data, FrameType::CONTINUATION, :recv]
|
|
82
|
+
state
|
|
83
|
+
in [:receiving_continuation_data, _, _]
|
|
84
|
+
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
85
|
+
|
|
86
|
+
# receiving_continuation
|
|
87
|
+
in [:receiving_continuation, FrameType::RST_STREAM, _]
|
|
88
|
+
:closed
|
|
89
|
+
in [:receiving_continuation, FrameType::CONTINUATION, :recv] if frame.end_headers?
|
|
90
|
+
:half_closed_remote
|
|
91
|
+
in [:receiving_continuation, FrameType::CONTINUATION, :recv]
|
|
92
|
+
state
|
|
93
|
+
in [:receiving_continuation, _, _]
|
|
94
|
+
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
95
|
+
|
|
96
|
+
# receiving_data
|
|
97
|
+
in [:receiving_data, FrameType::DATA, :recv] if frame.end_stream?
|
|
98
|
+
:half_closed_remote
|
|
99
|
+
in [:receiving_data, FrameType::DATA, :recv]
|
|
100
|
+
state
|
|
101
|
+
in [:receiving_data, FrameType::RST_STREAM, _]
|
|
102
|
+
:closed
|
|
103
|
+
in [:receiving_data, _, _]
|
|
104
|
+
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
105
|
+
|
|
106
|
+
# reserved_remote
|
|
107
|
+
in [:reserved_remote, _, _]
|
|
108
|
+
# TODO
|
|
109
|
+
state
|
|
110
|
+
|
|
111
|
+
# reserved_local
|
|
112
|
+
in [:reserved_local, _, _]
|
|
113
|
+
# TODO
|
|
114
|
+
state
|
|
115
|
+
|
|
116
|
+
# half_closed_remote
|
|
117
|
+
in [:half_closed_remote, FrameType::HEADERS, :send] if frame.end_stream? && frame.end_headers?
|
|
118
|
+
:closed
|
|
119
|
+
in [:half_closed_remote, FrameType::HEADERS, :send] if frame.end_headers?
|
|
120
|
+
:sending_data
|
|
121
|
+
in [:half_closed_remote, FrameType::HEADERS, :send] if frame.end_stream?
|
|
122
|
+
:sending_continuation
|
|
123
|
+
in [:half_closed_remote, FrameType::HEADERS, :send]
|
|
124
|
+
:sending_continuation_data
|
|
125
|
+
in [:half_closed_remote, FrameType::PRIORITY, :recv]
|
|
126
|
+
state
|
|
127
|
+
in [:half_closed_remote, FrameType::RST_STREAM, _]
|
|
128
|
+
:closed
|
|
129
|
+
in [half_closed_remote, FrameType::WINDOW_UPDATE, :recv]
|
|
130
|
+
state
|
|
131
|
+
in [:half_closed_remote, _, :recv]
|
|
132
|
+
unexpected(ErrorCode::STREAM_CLOSED, state, typ, direction)
|
|
133
|
+
in [:half_closed_local, _, :send]
|
|
134
|
+
unreachable(state, typ, direction)
|
|
135
|
+
|
|
136
|
+
# sending_continuation_data
|
|
137
|
+
in [:sending_continuation_data, FrameType::RST_STREAM, :send]
|
|
138
|
+
:closed
|
|
139
|
+
in [:sending_continuation_data, FrameType::CONTINUATION, :send] if frame.end_headers?
|
|
140
|
+
:sending_data
|
|
141
|
+
in [:sending_continuation_data, FrameType::CONTINUATION, :send]
|
|
142
|
+
state
|
|
143
|
+
in [:sending_continuation_data, _, :send]
|
|
144
|
+
unreachable(state, typ, direction)
|
|
145
|
+
in [:sending_continuation_data, _, :recv]
|
|
146
|
+
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
147
|
+
|
|
148
|
+
# sending_continuation
|
|
149
|
+
in [:sending_continuation, FrameType::RST_STREAM, :send]
|
|
150
|
+
:closed
|
|
151
|
+
in [:sending_continuation, FrameType::CONTINUATION, :send] if frame.end_headers?
|
|
152
|
+
:closed
|
|
153
|
+
in [:sending_continuation, FrameType::CONTINUATION, :send]
|
|
154
|
+
state
|
|
155
|
+
in [:sending_continuation, _, :send]
|
|
156
|
+
unreachable(state, typ, direction)
|
|
157
|
+
in [:sending_continuation, _, :recv]
|
|
158
|
+
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
159
|
+
|
|
160
|
+
# sending_data
|
|
161
|
+
in [:sending_data, FrameType::DATA, :send] if frame.end_stream?
|
|
162
|
+
:closed
|
|
163
|
+
in [:sending_data, FrameType::DATA, :send]
|
|
164
|
+
state
|
|
165
|
+
in [:sending_data, FrameType::RST_STREAM, :send]
|
|
166
|
+
:closed
|
|
167
|
+
in [:sending_continuation, _, :send]
|
|
168
|
+
unreachable(state, typ, direction)
|
|
169
|
+
in [:sending_continuation, _, :recv]
|
|
170
|
+
unexpected(ErrorCode::PROTOCOL_ERROR, state, typ, direction)
|
|
171
|
+
|
|
172
|
+
# closed
|
|
173
|
+
in [:closed, FrameType::RST_STREAM, :recv]
|
|
174
|
+
state
|
|
175
|
+
in [:closed, _, :send]
|
|
176
|
+
unreachable(state, typ, direction)
|
|
177
|
+
in [:closed, _, :recv]
|
|
178
|
+
unexpected(ErrorCode::STREAM_CLOSED, state, typ, direction)
|
|
179
|
+
|
|
180
|
+
# other
|
|
181
|
+
in [_, _, :send]
|
|
182
|
+
unreachable(state, typ, direction)
|
|
183
|
+
in [_, _, :recv]
|
|
184
|
+
unexpected(ErrorCode::STREAM_CLOSED, state, typ, direction)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
# rubocop: enable Metrics/AbcSize
|
|
188
|
+
# rubocop: enable Metrics/CyclomaticComplexity
|
|
189
|
+
# rubocop: enable Metrics/MethodLength
|
|
190
|
+
# rubocop: enable Metrics/PerceivedComplexity
|
|
191
|
+
|
|
192
|
+
def self.unexpected(error_code, state, typ, direction)
|
|
193
|
+
ConnectionError.new(error_code, "#{direction} unexpected #{format('0x%02x', typ)} frame in #{state}")
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def self.unreachable(state, typ, direction)
|
|
197
|
+
raise "#{direction} unexpected #{format('0x%02x', typ)} frame in #{state}"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def close
|
|
201
|
+
@state = :closed
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# @return [Boolean]
|
|
205
|
+
def closed?
|
|
206
|
+
@state == :closed
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
# @return [Boolean]
|
|
210
|
+
def idle?
|
|
211
|
+
@state == :idle
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# @return [Boolean]
|
|
215
|
+
def active?
|
|
216
|
+
@state == :open || @state == :half_closed_local || @state == :half_closed_remote
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# @return [Boolean]
|
|
220
|
+
def half_closed_remote?
|
|
221
|
+
@state == :half_closed_remote
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Biryani
|
|
2
|
+
class Stream
|
|
3
|
+
attr_accessor :rx
|
|
4
|
+
|
|
5
|
+
# @param tx [Port]
|
|
6
|
+
# @param stream_id [Integer]
|
|
7
|
+
# @param proc [Proc]
|
|
8
|
+
def initialize(tx, stream_id, proc)
|
|
9
|
+
@rx = Ractor.new(tx, stream_id, proc) do |tx, stream_id, proc|
|
|
10
|
+
unless (req = Ractor.recv).nil?
|
|
11
|
+
res = HTTPResponse.new(0, {}, '')
|
|
12
|
+
|
|
13
|
+
proc.call(req, res)
|
|
14
|
+
tx << [res, stream_id]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Biryani
|
|
2
|
+
class StreamError
|
|
3
|
+
# @param code [Integer]
|
|
4
|
+
# @param stream_id [stream_id]
|
|
5
|
+
# @param debug [String]
|
|
6
|
+
def initialize(code, stream_id, debug)
|
|
7
|
+
@code = code
|
|
8
|
+
@stream_id = stream_id
|
|
9
|
+
@debug = debug
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @return [RstStream]
|
|
13
|
+
def rst_stream
|
|
14
|
+
Frame::RstStream.new(@stream_id, @code)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
module Biryani
|
|
2
|
+
class StreamsContext
|
|
3
|
+
def initialize
|
|
4
|
+
@h = {}
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# @param stream_id [Integer]
|
|
8
|
+
# @param proc [Proc]
|
|
9
|
+
#
|
|
10
|
+
# @return [StreamContext]
|
|
11
|
+
def new_context(stream_id, proc)
|
|
12
|
+
ctx = StreamContext.new(stream_id, proc)
|
|
13
|
+
@h[stream_id] = ctx
|
|
14
|
+
ctx
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @param stream_id [Integer]
|
|
18
|
+
def delete(stream_id)
|
|
19
|
+
@h.delete(stream_id)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @param stream_id [Integer]
|
|
23
|
+
#
|
|
24
|
+
# @return [StreamContext, nil]
|
|
25
|
+
def [](stream_id)
|
|
26
|
+
@h[stream_id]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# @return [Integer]
|
|
30
|
+
def length
|
|
31
|
+
@h.length
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def each(&block)
|
|
35
|
+
@h.each_value(&block)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @return [Array<Port>]
|
|
39
|
+
def txs
|
|
40
|
+
@h.values.filter { |ctx| !ctx.closed? }.map(&:tx)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [Integer]
|
|
44
|
+
def count_active
|
|
45
|
+
@h.values.filter(&:active?).length
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [Array<Integer>]
|
|
49
|
+
def closed_stream_ids
|
|
50
|
+
@h.filter { |_, ctx| ctx.closed? }.keys
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @return [Integer]
|
|
54
|
+
def last_stream_id
|
|
55
|
+
@h.reject { |_, ctx| ctx.idle? }.keys.max || 0
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class StreamContext
|
|
60
|
+
attr_accessor :stream, :tx, :send_window, :recv_window, :fragment, :content, :state
|
|
61
|
+
|
|
62
|
+
# @param stream_id [Integer]
|
|
63
|
+
# @param proc [Proc]
|
|
64
|
+
def initialize(stream_id, proc)
|
|
65
|
+
@tx = Ractor::Port.new
|
|
66
|
+
@stream = Stream.new(@tx, stream_id, proc)
|
|
67
|
+
@send_window = Window.new
|
|
68
|
+
@recv_window = Window.new
|
|
69
|
+
@fragment = StringIO.new
|
|
70
|
+
@content = StringIO.new
|
|
71
|
+
@state = State.new
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# @return [Boolean]
|
|
75
|
+
def closed?
|
|
76
|
+
@state.closed?
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @return [Boolean]
|
|
80
|
+
def idle?
|
|
81
|
+
@state.idle?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# @return [Boolean]
|
|
85
|
+
def active?
|
|
86
|
+
@state.active?
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Biryani
|
|
2
|
+
class Window
|
|
3
|
+
def initialize
|
|
4
|
+
@window = 2**16 - 1
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
# @param length [Integer]
|
|
8
|
+
#
|
|
9
|
+
# @return [Boolean]
|
|
10
|
+
def available?(length)
|
|
11
|
+
@window > length
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @param length [Integer]
|
|
15
|
+
def consume!(length)
|
|
16
|
+
@window -= length
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @param length [Integer]
|
|
20
|
+
def increase!(length)
|
|
21
|
+
@window += length
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @return [Integer]
|
|
25
|
+
def length
|
|
26
|
+
@window
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/biryani.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require 'stringio'
|
|
2
|
+
require 'uri'
|
|
3
|
+
|
|
4
|
+
require_relative 'biryani/connection'
|
|
5
|
+
require_relative 'biryani/connection_error'
|
|
6
|
+
require_relative 'biryani/data_buffer'
|
|
7
|
+
require_relative 'biryani/frame'
|
|
8
|
+
require_relative 'biryani/hpack'
|
|
9
|
+
require_relative 'biryani/http_request'
|
|
10
|
+
require_relative 'biryani/http_response'
|
|
11
|
+
require_relative 'biryani/server'
|
|
12
|
+
require_relative 'biryani/state'
|
|
13
|
+
require_relative 'biryani/streams_context'
|
|
14
|
+
require_relative 'biryani/stream_error'
|
|
15
|
+
require_relative 'biryani/stream'
|
|
16
|
+
require_relative 'biryani/version'
|
|
17
|
+
require_relative 'biryani/window'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Connection do
|
|
4
|
+
context 'close_all_streams' do
|
|
5
|
+
let(:streams_ctx) do
|
|
6
|
+
streams_ctx = StreamsContext.new
|
|
7
|
+
streams_ctx.new_context(1, do_nothing_proc)
|
|
8
|
+
streams_ctx.new_context(2, do_nothing_proc)
|
|
9
|
+
streams_ctx
|
|
10
|
+
end
|
|
11
|
+
it 'should close' do
|
|
12
|
+
Connection.close_all_streams(streams_ctx)
|
|
13
|
+
expect { streams_ctx[1].tx << nil }.to raise_error Ractor::ClosedError
|
|
14
|
+
expect { streams_ctx[2].tx << nil }.to raise_error Ractor::ClosedError
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Connection do
|
|
4
|
+
context 'handle_connection_window_update' do
|
|
5
|
+
let(:window_update) do
|
|
6
|
+
Frame::WindowUpdate.new(0, 1000)
|
|
7
|
+
end
|
|
8
|
+
let(:send_window) do
|
|
9
|
+
Window.new
|
|
10
|
+
end
|
|
11
|
+
it 'should handle' do
|
|
12
|
+
expect { Connection.handle_connection_window_update(window_update, send_window) }.not_to raise_error
|
|
13
|
+
expect(send_window.length).to eq 2**16 - 1 + 1000
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Connection do
|
|
4
|
+
context 'handle_ping' do
|
|
5
|
+
let(:ping1) do
|
|
6
|
+
Frame::Ping.new(true, 0, "\x00" * 8)
|
|
7
|
+
end
|
|
8
|
+
it 'should handle' do
|
|
9
|
+
expect(Connection.handle_ping(ping1)).to eq nil
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let(:ping2) do
|
|
13
|
+
Frame::Ping.new(false, 0, "\x00" * 8)
|
|
14
|
+
end
|
|
15
|
+
it 'should handle' do
|
|
16
|
+
expect(Connection.handle_ping(ping2)).to_not eq nil
|
|
17
|
+
expect(Connection.handle_ping(ping2).ack?).to eq true
|
|
18
|
+
expect(Connection.handle_ping(ping2).opaque).to eq "\x00" * 8
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Connection do
|
|
4
|
+
context 'handle_rst_stream' do
|
|
5
|
+
let(:rst_stream) do
|
|
6
|
+
Frame::RstStream.new(2, 0)
|
|
7
|
+
end
|
|
8
|
+
let(:streams_ctx) do
|
|
9
|
+
streams_ctx = StreamsContext.new
|
|
10
|
+
streams_ctx.new_context(1, do_nothing_proc)
|
|
11
|
+
streams_ctx.new_context(2, do_nothing_proc)
|
|
12
|
+
streams_ctx
|
|
13
|
+
end
|
|
14
|
+
it 'should handle' do
|
|
15
|
+
Connection.handle_rst_stream(rst_stream, streams_ctx)
|
|
16
|
+
expect(streams_ctx.length).to eq 2
|
|
17
|
+
expect(streams_ctx.count_active).to eq 0
|
|
18
|
+
expect(streams_ctx.closed_stream_ids.length).to eq 1
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Connection do
|
|
4
|
+
context 'handle_settings' do
|
|
5
|
+
let(:send_settings) do
|
|
6
|
+
Connection.default_settings
|
|
7
|
+
end
|
|
8
|
+
let(:decoder) do
|
|
9
|
+
HPACK::Decoder.new(4_096)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let(:settings1) do
|
|
13
|
+
Frame::Settings.new(false, 0, { SettingsID::SETTINGS_HEADER_TABLE_SIZE => 8_192 })
|
|
14
|
+
end
|
|
15
|
+
it 'should handle' do
|
|
16
|
+
reply_settings = Connection.handle_settings(settings1, send_settings, decoder)
|
|
17
|
+
expect(reply_settings.ack?).to eq true
|
|
18
|
+
expect(reply_settings.setting.empty?).to eq true
|
|
19
|
+
expect(send_settings[SettingsID::SETTINGS_MAX_CONCURRENT_STREAMS]).to eq 0xffffffff
|
|
20
|
+
expect(send_settings[SettingsID::SETTINGS_MAX_FRAME_SIZE]).to eq 16_384
|
|
21
|
+
expect(send_settings[SettingsID::SETTINGS_HEADER_TABLE_SIZE]).to eq 8_192
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
let(:settings2) do
|
|
25
|
+
Frame::Settings.new(true, 0, {})
|
|
26
|
+
end
|
|
27
|
+
it 'should handle' do
|
|
28
|
+
expect(Connection.handle_settings(settings2, send_settings, decoder)).to eq nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Connection do
|
|
4
|
+
context 'handle_stream_window_update' do
|
|
5
|
+
let(:window_update) do
|
|
6
|
+
Frame::WindowUpdate.new(1, 1000)
|
|
7
|
+
end
|
|
8
|
+
let(:streams_ctx) do
|
|
9
|
+
streams_ctx = StreamsContext.new
|
|
10
|
+
streams_ctx.new_context(1, do_nothing_proc)
|
|
11
|
+
streams_ctx.new_context(2, do_nothing_proc)
|
|
12
|
+
streams_ctx
|
|
13
|
+
end
|
|
14
|
+
it 'should handle' do
|
|
15
|
+
expect { Connection.handle_stream_window_update(window_update, streams_ctx) }.not_to raise_error
|
|
16
|
+
expect(streams_ctx[1].send_window.length).to eq 2**16 - 1 + 1000
|
|
17
|
+
expect(streams_ctx[2].send_window.length).to eq 2**16 - 1
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Connection do
|
|
4
|
+
context 'read_http2_magic' do
|
|
5
|
+
let(:io1) do
|
|
6
|
+
StringIO.new("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
|
7
|
+
end
|
|
8
|
+
it 'should read' do
|
|
9
|
+
expect(Connection.read_http2_magic(io1)).to eq nil
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
let(:io2) do
|
|
13
|
+
StringIO.new("PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n\x00xff")
|
|
14
|
+
end
|
|
15
|
+
it 'should read' do
|
|
16
|
+
expect(Connection.read_http2_magic(io2)).to eq nil
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:io3) do
|
|
20
|
+
StringIO.new("\x00xffPRI * HTTP/2.0\r\n\r\nSM\r\n\r\n")
|
|
21
|
+
end
|
|
22
|
+
it 'should not read' do
|
|
23
|
+
expect(Connection.read_http2_magic(io3)).to be_kind_of ConnectionError
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require_relative '../spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe Connection do
|
|
4
|
+
context 'remove_closed_streams' do
|
|
5
|
+
let(:streams_ctx1) do
|
|
6
|
+
streams_ctx = StreamsContext.new
|
|
7
|
+
streams_ctx.new_context(1, do_nothing_proc)
|
|
8
|
+
streams_ctx.new_context(2, do_nothing_proc)
|
|
9
|
+
streams_ctx
|
|
10
|
+
end
|
|
11
|
+
let(:data_buffer1) do
|
|
12
|
+
DataBuffer.new
|
|
13
|
+
end
|
|
14
|
+
it 'should close' do
|
|
15
|
+
Connection.remove_closed_streams(streams_ctx1, data_buffer1)
|
|
16
|
+
expect(streams_ctx1.length).to eq 2
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
let(:streams_ctx2) do
|
|
20
|
+
streams_ctx = StreamsContext.new
|
|
21
|
+
streams_ctx.new_context(1, do_nothing_proc)
|
|
22
|
+
streams_ctx.new_context(2, do_nothing_proc)
|
|
23
|
+
streams_ctx[2].state.close
|
|
24
|
+
streams_ctx
|
|
25
|
+
end
|
|
26
|
+
let(:data_buffer2) do
|
|
27
|
+
DataBuffer.new
|
|
28
|
+
end
|
|
29
|
+
it 'should close' do
|
|
30
|
+
Connection.remove_closed_streams(streams_ctx2, data_buffer2)
|
|
31
|
+
expect(streams_ctx2.length).to eq 2 # remain stream_id
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
let(:streams_ctx3) do
|
|
35
|
+
streams_ctx = StreamsContext.new
|
|
36
|
+
streams_ctx.new_context(1, do_nothing_proc)
|
|
37
|
+
streams_ctx.new_context(2, do_nothing_proc)
|
|
38
|
+
streams_ctx[2].state.close
|
|
39
|
+
streams_ctx
|
|
40
|
+
end
|
|
41
|
+
let(:data_buffer3) do
|
|
42
|
+
data_buffer = DataBuffer.new
|
|
43
|
+
data_buffer << Frame::Data.new(false, 2, 'two', nil)
|
|
44
|
+
data_buffer
|
|
45
|
+
end
|
|
46
|
+
it 'should close' do
|
|
47
|
+
Connection.remove_closed_streams(streams_ctx3, data_buffer3)
|
|
48
|
+
expect(streams_ctx3.length).to eq 2
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|