http-2 0.11.0 → 0.12.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
- data/README.md +0 -2
- data/lib/http/2/buffer.rb +6 -4
- data/lib/http/2/client.rb +5 -1
- data/lib/http/2/compressor.rb +42 -34
- data/lib/http/2/connection.rb +72 -86
- data/lib/http/2/emitter.rb +4 -1
- data/lib/http/2/error.rb +2 -0
- data/lib/http/2/flow_buffer.rb +8 -3
- data/lib/http/2/framer.rb +83 -94
- data/lib/http/2/huffman.rb +19 -17
- data/lib/http/2/server.rb +9 -7
- data/lib/http/2/stream.rb +48 -48
- data/lib/http/2/version.rb +3 -1
- data/lib/http/2.rb +2 -0
- metadata +7 -60
- data/.autotest +0 -20
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -20
- data/.gitmodules +0 -3
- data/.rspec +0 -5
- data/.rubocop.yml +0 -93
- data/.rubocop_todo.yml +0 -131
- data/.travis.yml +0 -17
- data/Gemfile +0 -16
- data/Guardfile +0 -18
- data/Guardfile.h2spec +0 -12
- data/Rakefile +0 -49
- data/example/Gemfile +0 -3
- data/example/README.md +0 -44
- data/example/client.rb +0 -122
- data/example/helper.rb +0 -19
- data/example/keys/server.crt +0 -20
- data/example/keys/server.key +0 -27
- data/example/server.rb +0 -139
- data/example/upgrade_client.rb +0 -153
- data/example/upgrade_server.rb +0 -203
- data/http-2.gemspec +0 -22
- data/lib/tasks/generate_huffman_table.rb +0 -166
- data/spec/buffer_spec.rb +0 -28
- data/spec/client_spec.rb +0 -188
- data/spec/compressor_spec.rb +0 -666
- data/spec/connection_spec.rb +0 -681
- data/spec/emitter_spec.rb +0 -54
- data/spec/framer_spec.rb +0 -487
- data/spec/h2spec/h2spec.darwin +0 -0
- data/spec/h2spec/output/non_secure.txt +0 -317
- data/spec/helper.rb +0 -147
- data/spec/hpack_test_spec.rb +0 -84
- data/spec/huffman_spec.rb +0 -68
- data/spec/server_spec.rb +0 -52
- data/spec/stream_spec.rb +0 -878
- data/spec/support/deep_dup.rb +0 -55
- data/spec/support/duplicable.rb +0 -98
data/spec/stream_spec.rb
DELETED
@@ -1,878 +0,0 @@
|
|
1
|
-
require 'helper'
|
2
|
-
|
3
|
-
RSpec.describe HTTP2::Stream do
|
4
|
-
include FrameHelpers
|
5
|
-
before(:each) do
|
6
|
-
@client = Client.new
|
7
|
-
@stream = @client.new_stream
|
8
|
-
end
|
9
|
-
|
10
|
-
context 'stream states' do
|
11
|
-
it 'should initiliaze all streams to IDLE' do
|
12
|
-
expect(@stream.state).to eq :idle
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'should set custom stream priority' do
|
16
|
-
stream = @client.new_stream(weight: 3, dependency: 2, exclusive: true)
|
17
|
-
expect(stream.weight).to eq 3
|
18
|
-
end
|
19
|
-
|
20
|
-
context 'idle' do
|
21
|
-
it 'should transition to open on sent HEADERS' do
|
22
|
-
@stream.send headers_frame
|
23
|
-
expect(@stream.state).to eq :open
|
24
|
-
end
|
25
|
-
it 'should transition to open on received HEADERS' do
|
26
|
-
@stream.receive headers_frame
|
27
|
-
expect(@stream.state).to eq :open
|
28
|
-
end
|
29
|
-
it 'should transition to reserved (local) on sent PUSH_PROMISE' do
|
30
|
-
@stream.send push_promise_frame
|
31
|
-
expect(@stream.state).to eq :reserved_local
|
32
|
-
end
|
33
|
-
it 'should transition to reserved (remote) on received PUSH_PROMISE' do
|
34
|
-
@stream.receive push_promise_frame
|
35
|
-
expect(@stream.state).to eq :reserved_remote
|
36
|
-
end
|
37
|
-
it 'should reprioritize stream on sent PRIORITY' do
|
38
|
-
expect { @stream.send priority_frame }.to_not raise_error
|
39
|
-
expect(@stream.weight).to eq 20
|
40
|
-
end
|
41
|
-
it 'should reprioritize stream on received PRIORITY' do
|
42
|
-
expect { @stream.send priority_frame }.to_not raise_error
|
43
|
-
expect(@stream.weight).to eq 20
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
context 'reserved (local)' do
|
48
|
-
before(:each) { @stream.send push_promise_frame }
|
49
|
-
|
50
|
-
it 'should transition on sent PUSH_PROMISE' do
|
51
|
-
expect(@stream.state).to eq :reserved_local
|
52
|
-
end
|
53
|
-
|
54
|
-
it 'should allow HEADERS to be sent' do
|
55
|
-
expect { @stream.send headers_frame }.to_not raise_error
|
56
|
-
end
|
57
|
-
|
58
|
-
it 'should raise error if sending invalid frames' do
|
59
|
-
frame_types.reject { |frame| %i[headers rst_stream].include?(frame[:type]) }.each do |type|
|
60
|
-
expect { @stream.dup.send type }.to raise_error InternalError
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'should raise error on receipt of invalid frames' do
|
65
|
-
what_types = frame_types.reject { |frame| %i[priority window_update rst_stream].include?(frame[:type]) }
|
66
|
-
what_types.each do |type|
|
67
|
-
expect { @stream.dup.receive type }.to raise_error InternalError
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'should transition to half closed (remote) on sent HEADERS' do
|
72
|
-
@stream.send headers_frame
|
73
|
-
expect(@stream.state).to eq :half_closed_remote
|
74
|
-
end
|
75
|
-
|
76
|
-
it 'should transition to closed on sent RST_STREAM' do
|
77
|
-
@stream.close
|
78
|
-
expect(@stream.state).to eq :closed
|
79
|
-
end
|
80
|
-
|
81
|
-
it 'should transition to closed on received RST_STREAM' do
|
82
|
-
@stream.receive rst_stream_frame
|
83
|
-
expect(@stream.state).to eq :closed
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'should reprioritize stream on PRIORITY' do
|
87
|
-
expect { @stream.receive priority_frame }.to_not raise_error
|
88
|
-
expect(@stream.weight).to eq 20
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'should increment remote_window on received WINDOW_UPDATE' do
|
92
|
-
expect { @stream.receive window_update_frame }.to_not raise_error
|
93
|
-
expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW + window_update_frame[:increment]
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
context 'reserved (remote)' do
|
98
|
-
before(:each) { @stream.receive push_promise_frame }
|
99
|
-
|
100
|
-
it 'should transition on received PUSH_PROMISE' do
|
101
|
-
expect(@stream.state).to eq :reserved_remote
|
102
|
-
end
|
103
|
-
|
104
|
-
it 'should raise error if sending invalid frames' do
|
105
|
-
frame_types.reject { |frame| %i[priority rst_stream window_update].include?(frame[:type]) }.each do |type|
|
106
|
-
expect { @stream.dup.send type }.to raise_error InternalError
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
it 'should raise error on receipt of invalid frames' do
|
111
|
-
frame_types.reject { |frame| %i[headers rst_stream].include?(frame[:type]) }.each do |type|
|
112
|
-
expect { @stream.dup.receive type }.to raise_error InternalError
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
it 'should transition to half closed (local) on received HEADERS' do
|
117
|
-
@stream.receive headers_frame
|
118
|
-
expect(@stream.state).to eq :half_closed_local
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'should transition to closed on sent RST_STREAM' do
|
122
|
-
@stream.close
|
123
|
-
expect(@stream.state).to eq :closed
|
124
|
-
end
|
125
|
-
|
126
|
-
it 'should transition to closed on received RST_STREAM' do
|
127
|
-
@stream.receive rst_stream_frame
|
128
|
-
expect(@stream.state).to eq :closed
|
129
|
-
end
|
130
|
-
|
131
|
-
it 'should reprioritize stream on PRIORITY' do
|
132
|
-
expect { @stream.send priority_frame }.to_not raise_error
|
133
|
-
expect(@stream.weight).to eq 20
|
134
|
-
end
|
135
|
-
|
136
|
-
it 'should increment local_window on sent WINDOW_UPDATE' do
|
137
|
-
expect { @stream.send window_update_frame }.to_not raise_error
|
138
|
-
expect(@stream.local_window).to eq DEFAULT_FLOW_WINDOW + window_update_frame[:increment]
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
context 'open' do
|
143
|
-
before(:each) { @stream.receive headers_frame }
|
144
|
-
|
145
|
-
it 'should allow any valid frames types to be sent' do
|
146
|
-
(frame_types - [ping_frame, goaway_frame, settings_frame]).each do |type|
|
147
|
-
expect { @stream.dup.send type.deep_dup }.to_not raise_error
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
it 'should allow frames of any type to be received' do
|
152
|
-
frame_types.each do |type|
|
153
|
-
expect { @stream.dup.receive type }.to_not raise_error
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
it 'should transition to half closed (local) if sending END_STREAM' do
|
158
|
-
[data_frame, headers_frame].each do |frame|
|
159
|
-
s, f = @stream.dup, frame.deep_dup
|
160
|
-
f[:flags] = [:end_stream]
|
161
|
-
|
162
|
-
s.send f
|
163
|
-
expect(s.state).to eq :half_closed_local
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
|
-
it 'should transition to half closed (remote) if receiving END_STREAM' do
|
168
|
-
[data_frame, headers_frame].each do |frame|
|
169
|
-
s, f = @stream.dup, frame.dup
|
170
|
-
f[:flags] = [:end_stream]
|
171
|
-
|
172
|
-
s.receive f
|
173
|
-
expect(s.state).to eq :half_closed_remote
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
it 'should transition to half closed if remote opened with END_STREAM' do
|
178
|
-
s = @client.new_stream
|
179
|
-
hclose = headers_frame
|
180
|
-
hclose[:flags] = [:end_stream]
|
181
|
-
|
182
|
-
s.receive hclose
|
183
|
-
expect(s.state).to eq :half_closed_remote
|
184
|
-
end
|
185
|
-
|
186
|
-
it 'should transition to half closed if local opened with END_STREAM' do
|
187
|
-
s = @client.new_stream
|
188
|
-
hclose = headers_frame
|
189
|
-
hclose[:flags] = [:end_stream]
|
190
|
-
|
191
|
-
s.send hclose
|
192
|
-
expect(s.state).to eq :half_closed_local
|
193
|
-
end
|
194
|
-
|
195
|
-
it 'should transition to closed if sending RST_STREAM' do
|
196
|
-
@stream.close
|
197
|
-
expect(@stream.state).to eq :closed
|
198
|
-
expect(@stream).to be_closed
|
199
|
-
end
|
200
|
-
|
201
|
-
it 'should transition to closed if receiving RST_STREAM' do
|
202
|
-
@stream.receive rst_stream_frame
|
203
|
-
expect(@stream.state).to eq :closed
|
204
|
-
end
|
205
|
-
|
206
|
-
it 'should emit :active on open transition' do
|
207
|
-
openp, openr = false, false
|
208
|
-
sp = @client.new_stream
|
209
|
-
sr = @client.new_stream
|
210
|
-
sp.on(:active) { openp = true }
|
211
|
-
sr.on(:active) { openr = true }
|
212
|
-
|
213
|
-
sp.receive headers_frame
|
214
|
-
sr.send headers_frame
|
215
|
-
|
216
|
-
expect(openp).to be_truthy
|
217
|
-
expect(openr).to be_truthy
|
218
|
-
end
|
219
|
-
|
220
|
-
it 'should not emit :active on transition from open' do
|
221
|
-
order, stream = [], @client.new_stream
|
222
|
-
|
223
|
-
stream.on(:active) { order << :active }
|
224
|
-
stream.on(:half_close) { order << :half_close }
|
225
|
-
stream.on(:close) { order << :close }
|
226
|
-
|
227
|
-
req = headers_frame
|
228
|
-
req[:flags] = [:end_headers]
|
229
|
-
|
230
|
-
stream.send req
|
231
|
-
stream.send data_frame
|
232
|
-
expect(order).to eq [:active, :half_close]
|
233
|
-
end
|
234
|
-
|
235
|
-
it 'should emit :close on close transition' do
|
236
|
-
closep, closer = false, false
|
237
|
-
sp, sr = @stream.dup, @stream.dup
|
238
|
-
|
239
|
-
sp.on(:close) { closep = true }
|
240
|
-
sr.on(:close) { closer = true }
|
241
|
-
|
242
|
-
sp.receive rst_stream_frame
|
243
|
-
sr.close
|
244
|
-
|
245
|
-
expect(closep).to be_truthy
|
246
|
-
expect(closer).to be_truthy
|
247
|
-
end
|
248
|
-
|
249
|
-
it 'should emit :close after frame is processed' do
|
250
|
-
order, stream = [], @client.new_stream
|
251
|
-
|
252
|
-
stream.on(:active) { order << :active }
|
253
|
-
stream.on(:data) { order << :data }
|
254
|
-
stream.on(:half_close) { order << :half_close }
|
255
|
-
stream.on(:close) { order << :close }
|
256
|
-
|
257
|
-
req = headers_frame
|
258
|
-
req[:flags] = [:end_stream, :end_headers]
|
259
|
-
|
260
|
-
stream.send req
|
261
|
-
stream.receive headers_frame
|
262
|
-
stream.receive data_frame
|
263
|
-
|
264
|
-
expect(order).to eq [:active, :half_close, :data, :close]
|
265
|
-
end
|
266
|
-
|
267
|
-
it 'should emit :close with reason' do
|
268
|
-
reason = nil
|
269
|
-
@stream.on(:close) { |r| reason = r }
|
270
|
-
@stream.receive rst_stream_frame
|
271
|
-
expect(reason).not_to be_nil
|
272
|
-
end
|
273
|
-
|
274
|
-
it 'should reprioritize stream on sent PRIORITY' do
|
275
|
-
expect { @stream.send priority_frame }.to_not raise_error
|
276
|
-
expect(@stream.weight).to eq 20
|
277
|
-
end
|
278
|
-
it 'should reprioritize stream on received PRIORITY' do
|
279
|
-
expect { @stream.receive priority_frame }.to_not raise_error
|
280
|
-
expect(@stream.weight).to eq 20
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
context 'half closed (local)' do
|
285
|
-
before(:each) { @stream.send headers_frame.merge(flags: [:end_headers, :end_stream]) }
|
286
|
-
|
287
|
-
it 'should raise error on attempt to send invalid frames' do
|
288
|
-
frame_types.reject { |frame| %i[priority rst_stream window_update].include?(frame[:type]) }.each do |frame|
|
289
|
-
expect { @stream.dup.send frame }.to raise_error InternalError
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
it 'should transition to closed on receipt of END_STREAM flag' do
|
294
|
-
[data_frame, headers_frame, continuation_frame].each do |frame|
|
295
|
-
s, f = @stream.dup, frame.dup
|
296
|
-
f[:flags] = [:end_stream]
|
297
|
-
|
298
|
-
s.receive f
|
299
|
-
expect(s.state).to eq :closed
|
300
|
-
end
|
301
|
-
end
|
302
|
-
|
303
|
-
it 'should transition to closed on receipt of RST_STREAM frame' do
|
304
|
-
@stream.receive rst_stream_frame
|
305
|
-
expect(@stream.state).to eq :closed
|
306
|
-
end
|
307
|
-
|
308
|
-
it 'should transition to closed if RST_STREAM frame is sent' do
|
309
|
-
@stream.send rst_stream_frame
|
310
|
-
expect(@stream.state).to eq :closed
|
311
|
-
end
|
312
|
-
|
313
|
-
it 'should ignore received WINDOW_UPDATE frames' do
|
314
|
-
expect { @stream.receive window_update_frame }.to_not raise_error
|
315
|
-
expect(@stream.state).to eq :half_closed_local
|
316
|
-
end
|
317
|
-
|
318
|
-
it 'should ignore received PRIORITY frames' do
|
319
|
-
expect { @stream.receive priority_frame }.to_not raise_error
|
320
|
-
expect(@stream.state).to eq :half_closed_local
|
321
|
-
end
|
322
|
-
|
323
|
-
it 'should reprioritize stream on sent PRIORITY' do
|
324
|
-
expect { @stream.send priority_frame }.to_not raise_error
|
325
|
-
expect(@stream.weight).to eq 20
|
326
|
-
end
|
327
|
-
|
328
|
-
it 'should reprioritize stream (and decendants) on received PRIORITY' do
|
329
|
-
expect { @stream.receive priority_frame }.to_not raise_error
|
330
|
-
expect(@stream.weight).to eq 20
|
331
|
-
end
|
332
|
-
|
333
|
-
it 'should increment local_window on sent WINDOW_UPDATE' do
|
334
|
-
expect { @stream.send window_update_frame }.to_not raise_error
|
335
|
-
expect(@stream.local_window).to eq DEFAULT_FLOW_WINDOW + window_update_frame[:increment]
|
336
|
-
end
|
337
|
-
|
338
|
-
it 'should emit :half_close event on transition' do
|
339
|
-
order = []
|
340
|
-
stream = @client.new_stream
|
341
|
-
stream.on(:active) { order << :active }
|
342
|
-
stream.on(:half_close) { order << :half_close }
|
343
|
-
|
344
|
-
req = headers_frame
|
345
|
-
req[:flags] = [:end_stream, :end_headers]
|
346
|
-
|
347
|
-
stream.send req
|
348
|
-
expect(order).to eq [:active, :half_close]
|
349
|
-
end
|
350
|
-
|
351
|
-
it 'should emit :close event on transition to closed' do
|
352
|
-
closed = false
|
353
|
-
@stream.on(:close) { closed = true }
|
354
|
-
@stream.receive rst_stream_frame
|
355
|
-
|
356
|
-
expect(@stream.state).to eq :closed
|
357
|
-
expect(closed).to be_truthy
|
358
|
-
end
|
359
|
-
end
|
360
|
-
|
361
|
-
context 'half closed (remote)' do
|
362
|
-
before(:each) { @stream.receive headers_frame.merge(flags: [:end_headers, :end_stream]) }
|
363
|
-
|
364
|
-
it 'should raise STREAM_CLOSED error on reciept of frames' do
|
365
|
-
(frame_types - [priority_frame, rst_stream_frame, window_update_frame]).each do |frame|
|
366
|
-
expect do
|
367
|
-
@stream.dup.receive frame
|
368
|
-
end.to raise_error(StreamClosed)
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
it 'should transition to closed if END_STREAM flag is sent' do
|
373
|
-
[data_frame, headers_frame].each do |frame|
|
374
|
-
s, f = @stream.dup, frame.deep_dup
|
375
|
-
f[:flags] = [:end_stream]
|
376
|
-
|
377
|
-
s.on(:close) { expect(s.state).to eq :closed }
|
378
|
-
s.send f
|
379
|
-
expect(s.state).to eq :closed
|
380
|
-
end
|
381
|
-
end
|
382
|
-
|
383
|
-
it 'should not transition to closed if END_STREAM flag is sent when overflowing window' do
|
384
|
-
@stream.on(:close) { fail 'should not have closed' }
|
385
|
-
data = { type: :data, flags: [], stream: @stream.id }
|
386
|
-
4.times do
|
387
|
-
data = data.merge(flags: [:end_stream]) if @stream.remote_window < 16_384
|
388
|
-
@stream.send data.merge(payload: 'x' * 16_384)
|
389
|
-
end
|
390
|
-
end
|
391
|
-
|
392
|
-
it 'should transition to closed when send buffer is emptied' do
|
393
|
-
o = Object.new
|
394
|
-
expect(o).to receive(:tap).once
|
395
|
-
@stream.on(:close) do
|
396
|
-
expect(@stream.buffered_amount).to eq 0
|
397
|
-
o.tap
|
398
|
-
end
|
399
|
-
data = { type: :data, flags: [], stream: @stream.id }
|
400
|
-
4.times do
|
401
|
-
data = data.merge(flags: [:end_stream]) if @stream.remote_window < 16_384
|
402
|
-
@stream.send data.merge(payload: 'x' * 16_384)
|
403
|
-
end
|
404
|
-
@client << Framer.new.generate(type: :window_update, stream: @stream.id, increment: 16_384)
|
405
|
-
end
|
406
|
-
|
407
|
-
it 'should transition to closed if RST_STREAM is sent' do
|
408
|
-
@stream.close
|
409
|
-
expect(@stream.state).to eq :closed
|
410
|
-
end
|
411
|
-
|
412
|
-
it 'should transition to closed on reciept of RST_STREAM frame' do
|
413
|
-
@stream.receive rst_stream_frame
|
414
|
-
expect(@stream.state).to eq :closed
|
415
|
-
end
|
416
|
-
|
417
|
-
it 'should ignore sent WINDOW_UPDATE frames' do
|
418
|
-
expect { @stream.send window_update_frame }.to_not raise_error
|
419
|
-
expect(@stream.state).to eq :half_closed_remote
|
420
|
-
end
|
421
|
-
|
422
|
-
it 'should increment remote_window on received WINDOW_UPDATE' do
|
423
|
-
expect { @stream.receive window_update_frame }.to_not raise_error
|
424
|
-
expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW + window_update_frame[:increment]
|
425
|
-
end
|
426
|
-
|
427
|
-
it 'should reprioritize stream on sent PRIORITY' do
|
428
|
-
expect { @stream.send priority_frame }.to_not raise_error
|
429
|
-
expect(@stream.weight).to eq 20
|
430
|
-
end
|
431
|
-
it 'should reprioritize stream on received PRIORITY' do
|
432
|
-
expect { @stream.receive priority_frame }.to_not raise_error
|
433
|
-
expect(@stream.weight).to eq 20
|
434
|
-
end
|
435
|
-
|
436
|
-
it 'should emit :half_close event on transition' do
|
437
|
-
order = []
|
438
|
-
stream = @client.new_stream
|
439
|
-
stream.on(:active) { order << :active }
|
440
|
-
stream.on(:half_close) { order << :half_close }
|
441
|
-
|
442
|
-
req = headers_frame
|
443
|
-
req[:flags] = [:end_stream, :end_headers]
|
444
|
-
|
445
|
-
stream.receive req
|
446
|
-
expect(order).to eq [:active, :half_close]
|
447
|
-
end
|
448
|
-
|
449
|
-
it 'should emit :close event on close transition' do
|
450
|
-
closed = false
|
451
|
-
@stream.on(:close) { closed = true }
|
452
|
-
@stream.close
|
453
|
-
|
454
|
-
expect(@stream.state).to eq :closed
|
455
|
-
expect(closed).to be_truthy
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
context 'closed' do
|
460
|
-
context 'remote closed stream' do
|
461
|
-
before(:each) do
|
462
|
-
@stream.send headers_frame.merge(flags: [:end_headers, :end_stream]) # half closed local
|
463
|
-
@stream.receive headers_frame.merge(flags: [:end_headers, :end_stream]) # closed by remote
|
464
|
-
end
|
465
|
-
|
466
|
-
it 'should raise STREAM_CLOSED on attempt to send frames' do
|
467
|
-
(frame_types - [priority_frame, rst_stream_frame]).each do |frame|
|
468
|
-
expect do
|
469
|
-
@stream.dup.send frame
|
470
|
-
end.to raise_error(StreamClosed)
|
471
|
-
end
|
472
|
-
end
|
473
|
-
|
474
|
-
it 'should raise STREAM_CLOSED on receipt of frame' do
|
475
|
-
(frame_types - [priority_frame, rst_stream_frame, window_update_frame]).each do |frame|
|
476
|
-
expect do
|
477
|
-
@stream.dup.receive frame
|
478
|
-
end.to raise_error(StreamClosed)
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
|
-
it 'should allow PRIORITY, RST_STREAM to be sent' do
|
483
|
-
expect { @stream.send priority_frame }.to_not raise_error
|
484
|
-
expect { @stream.send rst_stream_frame }.to_not raise_error
|
485
|
-
end
|
486
|
-
|
487
|
-
it 'should allow PRIORITY, RST_STREAM to be received' do
|
488
|
-
expect { @stream.receive priority_frame }.to_not raise_error
|
489
|
-
expect { @stream.receive rst_stream_frame }.to_not raise_error
|
490
|
-
end
|
491
|
-
|
492
|
-
it 'should reprioritize stream on sent PRIORITY' do
|
493
|
-
expect { @stream.send priority_frame }.to_not raise_error
|
494
|
-
expect(@stream.weight).to eq 20
|
495
|
-
end
|
496
|
-
it 'should reprioritize stream on received PRIORITY' do
|
497
|
-
expect { @stream.receive priority_frame }.to_not raise_error
|
498
|
-
expect(@stream.weight).to eq 20
|
499
|
-
end
|
500
|
-
|
501
|
-
it 'should ignore received WINDOW_UPDATE frames' do
|
502
|
-
expect { @stream.receive window_update_frame }.to_not raise_error
|
503
|
-
expect(@stream.state).to eq :closed
|
504
|
-
end
|
505
|
-
end
|
506
|
-
|
507
|
-
context 'local closed via RST_STREAM frame' do
|
508
|
-
before(:each) do
|
509
|
-
@stream.send headers_frame # open
|
510
|
-
@stream.send rst_stream_frame # closed by local
|
511
|
-
end
|
512
|
-
|
513
|
-
it 'should ignore received frames' do
|
514
|
-
(frame_types - [push_promise_frame]).each do |frame|
|
515
|
-
expect do
|
516
|
-
cb = []
|
517
|
-
@stream.on(:data) { cb << :data }
|
518
|
-
@stream.on(:headers) { cb << :headers }
|
519
|
-
@stream.dup.receive frame.dup
|
520
|
-
expect(cb).to be_empty
|
521
|
-
end.to_not raise_error
|
522
|
-
end
|
523
|
-
end
|
524
|
-
|
525
|
-
# it "should transition to reserved remote on PUSH_PROMISE" do
|
526
|
-
# An endpoint might receive a PUSH_PROMISE frame after it sends
|
527
|
-
# RST_STREAM. PUSH_PROMISE causes a stream to become "reserved".
|
528
|
-
# ...
|
529
|
-
# We're auto RST'ing PUSH streams in connection class, hence
|
530
|
-
# skipping this transition for now.
|
531
|
-
# end
|
532
|
-
end
|
533
|
-
|
534
|
-
# FIXME: Isn't this test same as "half closed (local)"?
|
535
|
-
# context "local closed via END_STREAM flag" do
|
536
|
-
# before(:each) do
|
537
|
-
# @stream.send headers_frame # open
|
538
|
-
# @stream.send data_frame # contains end_stream flag
|
539
|
-
# end
|
540
|
-
|
541
|
-
# it "should ignore received frames" do
|
542
|
-
# frame_types.each do |frame|
|
543
|
-
# expect { @stream.dup.receive frame }.to_not raise_error
|
544
|
-
# end
|
545
|
-
# end
|
546
|
-
# end
|
547
|
-
end
|
548
|
-
end # end stream states
|
549
|
-
|
550
|
-
# TODO: add test cases to ensure on(:priority) emitted after close
|
551
|
-
|
552
|
-
context 'flow control' do
|
553
|
-
it 'should initialize to default flow control window' do
|
554
|
-
expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW
|
555
|
-
end
|
556
|
-
|
557
|
-
it 'should update window size on DATA frames only' do
|
558
|
-
@stream.send headers_frame # go to open
|
559
|
-
expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW
|
560
|
-
|
561
|
-
(frame_types - [data_frame, ping_frame, goaway_frame, settings_frame]).each do |frame|
|
562
|
-
s = @stream.dup
|
563
|
-
s.send frame.deep_dup
|
564
|
-
expect(s.remote_window).to eq DEFAULT_FLOW_WINDOW
|
565
|
-
end
|
566
|
-
|
567
|
-
@stream.send data_frame
|
568
|
-
expect(@stream.remote_window).to eq DEFAULT_FLOW_WINDOW - data_frame[:payload].bytesize
|
569
|
-
end
|
570
|
-
|
571
|
-
it 'should update window size on receipt of WINDOW_UPDATE' do
|
572
|
-
@stream.send headers_frame
|
573
|
-
@stream.send data_frame
|
574
|
-
@stream.receive window_update_frame
|
575
|
-
|
576
|
-
expect(@stream.remote_window).to eq(
|
577
|
-
DEFAULT_FLOW_WINDOW - data_frame[:payload].bytesize + window_update_frame[:increment],
|
578
|
-
)
|
579
|
-
end
|
580
|
-
|
581
|
-
it 'should observe session flow control' do
|
582
|
-
settings, data = settings_frame, data_frame
|
583
|
-
settings[:payload] = [[:settings_initial_window_size, 1000]]
|
584
|
-
settings[:stream] = 0
|
585
|
-
|
586
|
-
framer = Framer.new
|
587
|
-
@client << framer.generate(settings)
|
588
|
-
|
589
|
-
s1 = @client.new_stream
|
590
|
-
s1.send headers_frame
|
591
|
-
s1.send data.merge(payload: 'x' * 900, flags: [])
|
592
|
-
expect(s1.remote_window).to eq 100
|
593
|
-
|
594
|
-
s1.send data.merge(payload: 'x' * 200)
|
595
|
-
expect(s1.remote_window).to eq 0
|
596
|
-
expect(s1.buffered_amount).to eq 100
|
597
|
-
|
598
|
-
@client << framer.generate(window_update_frame.merge(stream: s1.id, increment: 1000))
|
599
|
-
expect(s1.buffered_amount).to eq 0
|
600
|
-
expect(s1.remote_window).to eq 900
|
601
|
-
end
|
602
|
-
|
603
|
-
it 'should not update window when data received is less than half of maximum local window size' do
|
604
|
-
data = data_frame
|
605
|
-
datalen = data[:payload].bytesize
|
606
|
-
expect(@stream).not_to receive(:send) do |frame|
|
607
|
-
expect(frame[:type]).to eq :window_update
|
608
|
-
expect(frame[:increment]).to eq datalen
|
609
|
-
end
|
610
|
-
@stream.receive headers_frame
|
611
|
-
@stream.receive data
|
612
|
-
end
|
613
|
-
|
614
|
-
it 'should update window when data received is over half of the maximum local window size' do
|
615
|
-
data1 = data_frame.merge(payload: 'a'*16_384, flags: [])
|
616
|
-
data2 = data_frame.merge(payload: 'a'*16_384)
|
617
|
-
datalen = 16_384 * 2
|
618
|
-
expect(@stream).to receive(:send) do |frame|
|
619
|
-
expect(frame[:type]).to eq :window_update
|
620
|
-
expect(frame[:increment]).to eq datalen
|
621
|
-
end
|
622
|
-
@stream.receive headers_frame
|
623
|
-
@stream.receive data1
|
624
|
-
@stream.receive data2
|
625
|
-
end
|
626
|
-
end
|
627
|
-
|
628
|
-
context 'client API' do
|
629
|
-
it '.reprioritize should emit PRIORITY frame' do
|
630
|
-
expect(@stream).to receive(:send) do |frame|
|
631
|
-
expect(frame[:type]).to eq :priority
|
632
|
-
expect(frame[:weight]).to eq 30
|
633
|
-
end
|
634
|
-
|
635
|
-
@stream.reprioritize weight: 30
|
636
|
-
end
|
637
|
-
|
638
|
-
it '.reprioritize should raise error if invoked by server' do
|
639
|
-
srv = Server.new
|
640
|
-
stream = srv.new_stream
|
641
|
-
|
642
|
-
expect { stream.reprioritize(weight: 10) }.to raise_error(InternalError)
|
643
|
-
end
|
644
|
-
|
645
|
-
it '.headers should emit HEADERS frames' do
|
646
|
-
payload = {
|
647
|
-
':method' => 'GET',
|
648
|
-
':scheme' => 'http',
|
649
|
-
':host' => 'www.example.org',
|
650
|
-
':path' => '/resource',
|
651
|
-
'custom' => 'value',
|
652
|
-
}
|
653
|
-
|
654
|
-
expect(@stream).to receive(:send) do |frame|
|
655
|
-
expect(frame[:type]).to eq :headers
|
656
|
-
expect(frame[:payload]).to eq payload
|
657
|
-
expect(frame[:flags]).to eq [:end_headers]
|
658
|
-
end
|
659
|
-
|
660
|
-
@stream.headers(payload, end_stream: false, end_headers: true)
|
661
|
-
end
|
662
|
-
|
663
|
-
it '.data should emit DATA frames' do
|
664
|
-
expect(@stream).to receive(:send) do |frame|
|
665
|
-
expect(frame[:type]).to eq :data
|
666
|
-
expect(frame[:payload]).to eq 'text'
|
667
|
-
expect(frame[:flags]).to be_empty
|
668
|
-
end
|
669
|
-
@stream.data('text', end_stream: false)
|
670
|
-
|
671
|
-
expect(@stream).to receive(:send) do |frame|
|
672
|
-
expect(frame[:flags]).to eq [:end_stream]
|
673
|
-
end
|
674
|
-
@stream.data('text')
|
675
|
-
end
|
676
|
-
|
677
|
-
it '.data should split large DATA frames' do
|
678
|
-
data = 'x' * 16_384 * 2
|
679
|
-
|
680
|
-
want = [
|
681
|
-
{ type: :data, flags: [], length: 16_384 },
|
682
|
-
{ type: :data, flags: [], length: 16_384 },
|
683
|
-
{ type: :data, flags: [:end_stream], length: 1 },
|
684
|
-
]
|
685
|
-
want.each do |w|
|
686
|
-
expect(@stream).to receive(:send) do |frame|
|
687
|
-
expect(frame[:type]).to eq w[:type]
|
688
|
-
expect(frame[:flags]).to eq w[:flags]
|
689
|
-
expect(frame[:payload].bytesize).to eq w[:length]
|
690
|
-
end
|
691
|
-
end
|
692
|
-
|
693
|
-
@stream.data(data + 'x')
|
694
|
-
end
|
695
|
-
|
696
|
-
it '.data should split large multibyte DATA frames' do
|
697
|
-
data = '🐼' * 16_384
|
698
|
-
|
699
|
-
want = [
|
700
|
-
{ type: :data, flags: [], length: 16_384 },
|
701
|
-
{ type: :data, flags: [], length: 16_384 },
|
702
|
-
{ type: :data, flags: [], length: 16_384 },
|
703
|
-
{ type: :data, flags: [], length: 16_384 },
|
704
|
-
{ type: :data, flags: [:end_stream], length: 1 },
|
705
|
-
]
|
706
|
-
want.each do |w|
|
707
|
-
expect(@stream).to receive(:send) do |frame|
|
708
|
-
expect(frame[:type]).to eq w[:type]
|
709
|
-
expect(frame[:flags]).to eq w[:flags]
|
710
|
-
expect(frame[:payload].bytesize).to eq w[:length]
|
711
|
-
end
|
712
|
-
end
|
713
|
-
|
714
|
-
@stream.data(data + 'x')
|
715
|
-
end
|
716
|
-
|
717
|
-
it '.cancel should reset stream with cancel error code' do
|
718
|
-
expect(@stream).to receive(:send) do |frame|
|
719
|
-
expect(frame[:type]).to eq :rst_stream
|
720
|
-
expect(frame[:error]).to eq :cancel
|
721
|
-
end
|
722
|
-
|
723
|
-
@stream.cancel
|
724
|
-
end
|
725
|
-
|
726
|
-
it '.refuse should reset stream with refused stream error code' do
|
727
|
-
expect(@stream).to receive(:send) do |frame|
|
728
|
-
expect(frame[:type]).to eq :rst_stream
|
729
|
-
expect(frame[:error]).to eq :refused_stream
|
730
|
-
end
|
731
|
-
|
732
|
-
@stream.refuse
|
733
|
-
end
|
734
|
-
|
735
|
-
it '.window_update should emit WINDOW_UPDATE frames' do
|
736
|
-
expect(@stream).to receive(:send) do |frame|
|
737
|
-
expect(frame[:type]).to eq :window_update
|
738
|
-
expect(frame[:increment]).to eq 20
|
739
|
-
end
|
740
|
-
@stream.window_update(20)
|
741
|
-
end
|
742
|
-
end
|
743
|
-
|
744
|
-
context 'server API' do
|
745
|
-
before(:each) do
|
746
|
-
@srv = Server.new
|
747
|
-
@frm = Framer.new
|
748
|
-
|
749
|
-
@client.on(:frame) { |bytes| @srv << bytes }
|
750
|
-
@client_stream = @client.new_stream
|
751
|
-
end
|
752
|
-
|
753
|
-
it 'should emit received headers via on(:headers)' do
|
754
|
-
headers, recv = REQUEST_HEADERS, nil
|
755
|
-
@srv.on(:stream) do |stream|
|
756
|
-
stream.on(:headers) { |h| recv = h }
|
757
|
-
end
|
758
|
-
|
759
|
-
@client_stream.headers(headers)
|
760
|
-
expect(recv).to eq headers
|
761
|
-
end
|
762
|
-
|
763
|
-
it 'should emit received payload via on(:data)' do
|
764
|
-
payload = 'some-payload'
|
765
|
-
@srv.on(:stream) do |stream|
|
766
|
-
stream.on(:data) do |recv|
|
767
|
-
expect(recv).to eq payload
|
768
|
-
end
|
769
|
-
end
|
770
|
-
|
771
|
-
@client_stream.headers(REQUEST_HEADERS)
|
772
|
-
@client_stream.data(payload)
|
773
|
-
end
|
774
|
-
|
775
|
-
it 'should emit received priority parameters via on(:priority)' do
|
776
|
-
new_weight, new_dependency = 15, @client_stream.id + 2
|
777
|
-
callback_called = false
|
778
|
-
@srv.on(:stream) do |stream|
|
779
|
-
stream.on(:priority) do |pri|
|
780
|
-
callback_called = true
|
781
|
-
expect(pri.is_a?(Hash)).to be
|
782
|
-
expect(pri[:weight]).to eq new_weight
|
783
|
-
expect(pri[:dependency]).to eq new_dependency
|
784
|
-
end
|
785
|
-
end
|
786
|
-
|
787
|
-
@client_stream.headers(REQUEST_HEADERS)
|
788
|
-
@client_stream.reprioritize(weight: new_weight, dependency: new_dependency)
|
789
|
-
expect(callback_called).to be
|
790
|
-
end
|
791
|
-
|
792
|
-
context 'push' do
|
793
|
-
before(:each) do
|
794
|
-
@srv.on(:frame) { |bytes| @client << bytes }
|
795
|
-
@srv.on(:stream) do |stream|
|
796
|
-
@server_stream = stream
|
797
|
-
end
|
798
|
-
|
799
|
-
@client_stream.headers(REQUEST_HEADERS)
|
800
|
-
end
|
801
|
-
|
802
|
-
it '.promise should emit server initiated stream' do
|
803
|
-
push = nil
|
804
|
-
@server_stream.promise(REQUEST_HEADERS) { |pstream| push = pstream }
|
805
|
-
expect(push.id).to eq 2
|
806
|
-
end
|
807
|
-
|
808
|
-
it '.promise push stream should have parent stream' do
|
809
|
-
push = nil
|
810
|
-
@server_stream.promise(REQUEST_HEADERS) { |pstream| push = pstream }
|
811
|
-
|
812
|
-
expect(push.state).to eq :reserved_local
|
813
|
-
expect(push.parent.id).to eq @server_stream.id
|
814
|
-
end
|
815
|
-
|
816
|
-
context 'stream states' do
|
817
|
-
it 'server: active > half close > close' do
|
818
|
-
order = []
|
819
|
-
@server_stream.promise(REQUEST_HEADERS) do |push|
|
820
|
-
stream = push
|
821
|
-
|
822
|
-
expect(push.state).to eq :reserved_local
|
823
|
-
order << :reserved
|
824
|
-
|
825
|
-
push.on(:active) { order << :active }
|
826
|
-
push.on(:half_close) { order << :half_close }
|
827
|
-
push.on(:close) { order << :close }
|
828
|
-
|
829
|
-
push.headers(RESPONSE_HEADERS)
|
830
|
-
push.send data_frame.merge(stream: stream.id)
|
831
|
-
end
|
832
|
-
|
833
|
-
expect(order).to eq [:reserved, :active, :half_close, :close]
|
834
|
-
end
|
835
|
-
|
836
|
-
it 'client: promise_headers > active > headers > .. > data > close' do
|
837
|
-
order, headers, promise_headers = [], [], []
|
838
|
-
@client.on(:promise) do |push|
|
839
|
-
order << :reserved
|
840
|
-
|
841
|
-
push.on(:active) { order << :active }
|
842
|
-
push.on(:data) { order << :data }
|
843
|
-
push.on(:half_close) { order << :half_close }
|
844
|
-
push.on(:close) { order << :close }
|
845
|
-
|
846
|
-
push.on(:promise_headers) do |h|
|
847
|
-
order << :promise_headers
|
848
|
-
promise_headers += h
|
849
|
-
end
|
850
|
-
push.on(:headers) do |h|
|
851
|
-
order << :headers
|
852
|
-
headers += h
|
853
|
-
end
|
854
|
-
|
855
|
-
expect(push.id).to be_even
|
856
|
-
end
|
857
|
-
|
858
|
-
@server_stream.promise(REQUEST_HEADERS) do |push|
|
859
|
-
push.headers(RESPONSE_HEADERS)
|
860
|
-
push.data('somedata')
|
861
|
-
end
|
862
|
-
|
863
|
-
expect(promise_headers).to eq(REQUEST_HEADERS)
|
864
|
-
expect(headers).to eq(RESPONSE_HEADERS)
|
865
|
-
expect(order).to eq [
|
866
|
-
:reserved,
|
867
|
-
:promise_headers,
|
868
|
-
:active,
|
869
|
-
:headers,
|
870
|
-
:half_close,
|
871
|
-
:data,
|
872
|
-
:close,
|
873
|
-
]
|
874
|
-
end
|
875
|
-
end
|
876
|
-
end
|
877
|
-
end
|
878
|
-
end
|