http-2 0.11.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -2
  3. data/lib/http/2/buffer.rb +6 -4
  4. data/lib/http/2/client.rb +5 -1
  5. data/lib/http/2/compressor.rb +42 -34
  6. data/lib/http/2/connection.rb +72 -86
  7. data/lib/http/2/emitter.rb +4 -1
  8. data/lib/http/2/error.rb +2 -0
  9. data/lib/http/2/flow_buffer.rb +8 -3
  10. data/lib/http/2/framer.rb +83 -94
  11. data/lib/http/2/huffman.rb +19 -17
  12. data/lib/http/2/server.rb +9 -7
  13. data/lib/http/2/stream.rb +48 -48
  14. data/lib/http/2/version.rb +3 -1
  15. data/lib/http/2.rb +2 -0
  16. metadata +7 -60
  17. data/.autotest +0 -20
  18. data/.coveralls.yml +0 -1
  19. data/.gitignore +0 -20
  20. data/.gitmodules +0 -3
  21. data/.rspec +0 -5
  22. data/.rubocop.yml +0 -93
  23. data/.rubocop_todo.yml +0 -131
  24. data/.travis.yml +0 -17
  25. data/Gemfile +0 -16
  26. data/Guardfile +0 -18
  27. data/Guardfile.h2spec +0 -12
  28. data/Rakefile +0 -49
  29. data/example/Gemfile +0 -3
  30. data/example/README.md +0 -44
  31. data/example/client.rb +0 -122
  32. data/example/helper.rb +0 -19
  33. data/example/keys/server.crt +0 -20
  34. data/example/keys/server.key +0 -27
  35. data/example/server.rb +0 -139
  36. data/example/upgrade_client.rb +0 -153
  37. data/example/upgrade_server.rb +0 -203
  38. data/http-2.gemspec +0 -22
  39. data/lib/tasks/generate_huffman_table.rb +0 -166
  40. data/spec/buffer_spec.rb +0 -28
  41. data/spec/client_spec.rb +0 -188
  42. data/spec/compressor_spec.rb +0 -666
  43. data/spec/connection_spec.rb +0 -681
  44. data/spec/emitter_spec.rb +0 -54
  45. data/spec/framer_spec.rb +0 -487
  46. data/spec/h2spec/h2spec.darwin +0 -0
  47. data/spec/h2spec/output/non_secure.txt +0 -317
  48. data/spec/helper.rb +0 -147
  49. data/spec/hpack_test_spec.rb +0 -84
  50. data/spec/huffman_spec.rb +0 -68
  51. data/spec/server_spec.rb +0 -52
  52. data/spec/stream_spec.rb +0 -878
  53. data/spec/support/deep_dup.rb +0 -55
  54. data/spec/support/duplicable.rb +0 -98
@@ -1,681 +0,0 @@
1
- require 'helper'
2
-
3
- RSpec.describe HTTP2::Connection do
4
- include FrameHelpers
5
- before(:each) do
6
- @conn = Client.new
7
- end
8
-
9
- let(:f) { Framer.new }
10
-
11
- context 'initialization and settings' do
12
- it 'should raise error if first frame is not settings' do
13
- (frame_types - [settings_frame]).each do |frame|
14
- expect { @conn << frame }.to raise_error(ProtocolError)
15
- expect(@conn).to be_closed
16
- end
17
- end
18
-
19
- it 'should not raise error if first frame is SETTINGS' do
20
- expect { @conn << f.generate(settings_frame) }.to_not raise_error
21
- expect(@conn.state).to eq :connected
22
- expect(@conn).to_not be_closed
23
- end
24
-
25
- it 'should raise error if SETTINGS stream != 0' do
26
- frame = set_stream_id(f.generate(settings_frame), 0x1)
27
- expect { @conn << frame }.to raise_error(ProtocolError)
28
- end
29
- end
30
-
31
- context 'settings synchronization' do
32
- it 'should reflect outgoing settings when ack is received' do
33
- expect(@conn.local_settings[:settings_header_table_size]).to eq 4096
34
- @conn.settings(settings_header_table_size: 256)
35
- expect(@conn.local_settings[:settings_header_table_size]).to eq 4096
36
-
37
- ack = { type: :settings, stream: 0, payload: [], flags: [:ack] }
38
- @conn << f.generate(ack)
39
-
40
- expect(@conn.local_settings[:settings_header_table_size]).to eq 256
41
- end
42
-
43
- it 'should reflect incoming settings when SETTINGS is received' do
44
- expect(@conn.remote_settings[:settings_header_table_size]).to eq 4096
45
- settings = settings_frame
46
- settings[:payload] = [[:settings_header_table_size, 256]]
47
-
48
- @conn << f.generate(settings)
49
-
50
- expect(@conn.remote_settings[:settings_header_table_size]).to eq 256
51
- end
52
-
53
- it 'should reflect settings_max_frame_size recevied from peer' do
54
- settings = settings_frame
55
- settings[:payload] = [[:settings_max_frame_size, 16_385]]
56
-
57
- @conn << f.generate(settings)
58
-
59
- frame = {
60
- length: 16_385,
61
- type: :data,
62
- flags: [:end_stream],
63
- stream: 1,
64
- payload: 'a' * 16_385,
65
- }
66
- expect { @conn.send(frame) }.not_to raise_error(CompressionError)
67
- end
68
-
69
- it 'should send SETTINGS ACK when SETTINGS is received' do
70
- settings = settings_frame
71
- settings[:payload] = [[:settings_header_table_size, 256]]
72
-
73
- # We should expect two frames here (append .twice) - one for the connection setup, and one for the settings ack.
74
- frames = []
75
- expect(@conn).to receive(:send).twice do |frame|
76
- frames << frame
77
- end
78
-
79
- @conn.send_connection_preface
80
- @conn << f.generate(settings)
81
-
82
- frame = frames.last
83
- expect(frame[:type]).to eq :settings
84
- expect(frame[:flags]).to eq [:ack]
85
- expect(frame[:payload]).to eq []
86
- end
87
- end
88
-
89
- context 'stream management' do
90
- it 'should initialize to default stream limit (100)' do
91
- expect(@conn.local_settings[:settings_max_concurrent_streams]).to eq 100
92
- end
93
-
94
- it 'should change stream limit to received SETTINGS value' do
95
- @conn << f.generate(settings_frame)
96
- expect(@conn.remote_settings[:settings_max_concurrent_streams]).to eq 10
97
- end
98
-
99
- it 'should count open streams against stream limit' do
100
- s = @conn.new_stream
101
- expect(@conn.active_stream_count).to eq 0
102
- s.receive headers_frame
103
- expect(@conn.active_stream_count).to eq 1
104
- end
105
-
106
- it 'should not count reserved streams against stream limit' do
107
- s1 = @conn.new_stream
108
- s1.receive push_promise_frame
109
- expect(@conn.active_stream_count).to eq 0
110
-
111
- s2 = @conn.new_stream
112
- s2.send push_promise_frame
113
- expect(@conn.active_stream_count).to eq 0
114
-
115
- # transition to half closed
116
- s1.receive headers_frame
117
- s2.send headers_frame
118
- expect(@conn.active_stream_count).to eq 2
119
-
120
- # transition to closed
121
- s1.receive data_frame
122
- s2.send data_frame
123
- expect(@conn.active_stream_count).to eq 0
124
-
125
- expect(s1).to be_closed
126
- expect(s2).to be_closed
127
- end
128
-
129
- it 'should not exceed stream limit set by peer' do
130
- @conn << f.generate(settings_frame)
131
-
132
- expect do
133
- 10.times do
134
- s = @conn.new_stream
135
- s.send headers_frame
136
- end
137
- end.to_not raise_error
138
-
139
- expect { @conn.new_stream }.to raise_error(StreamLimitExceeded)
140
- end
141
-
142
- it 'should initialize stream with HEADERS priority value' do
143
- @conn << f.generate(settings_frame)
144
-
145
- stream, headers = nil, headers_frame
146
- headers[:weight] = 20
147
- headers[:stream_dependency] = 0
148
- headers[:exclusive] = false
149
-
150
- @conn.on(:stream) { |s| stream = s }
151
- @conn << f.generate(headers)
152
-
153
- expect(stream.weight).to eq 20
154
- end
155
-
156
- it 'should initialize idle stream on PRIORITY frame' do
157
- @conn << f.generate(settings_frame)
158
-
159
- stream = nil
160
- @conn.on(:stream) { |s| stream = s }
161
- @conn << f.generate(priority_frame)
162
-
163
- expect(stream.state).to eq :idle
164
- end
165
- end
166
-
167
- context 'cleanup_recently_closed' do
168
- it 'should cleanup old connections' do
169
- now_ts = Time.now.to_i
170
- stream_ids = Array.new(4) { @conn.new_stream.id }
171
- expect(@conn.instance_variable_get('@streams').size).to eq(4)
172
-
173
- # Assume that the first 3 streams were closed in different time
174
- recently_closed = stream_ids[0, 3].zip([now_ts - 100, now_ts - 50, now_ts - 5]).to_h
175
- @conn.instance_variable_set('@streams_recently_closed', recently_closed)
176
-
177
- # Cleanup should delete streams that were closed earlier than 15s ago
178
- @conn.__send__(:cleanup_recently_closed)
179
- expect(@conn.instance_variable_get('@streams').size).to eq(2)
180
- expect(@conn.instance_variable_get('@streams_recently_closed')).to eq(stream_ids[2] => now_ts - 5)
181
- end
182
- end
183
-
184
- context 'Headers pre/post processing' do
185
- it 'should not concatenate multiple occurences of a header field with the same name' do
186
- input = [
187
- ['Content-Type', 'text/html'],
188
- ['Cache-Control', 'max-age=60, private'],
189
- ['Cache-Control', 'must-revalidate'],
190
- ]
191
- expected = [
192
- ['content-type', 'text/html'],
193
- ['cache-control', 'max-age=60, private'],
194
- ['cache-control', 'must-revalidate'],
195
- ]
196
- headers = []
197
- @conn.on(:frame) do |bytes|
198
- headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
199
- end
200
-
201
- stream = @conn.new_stream
202
- stream.headers(input)
203
-
204
- expect(headers.size).to eq 1
205
- emitted = Decompressor.new.decode(headers.first[:payload])
206
- expect(emitted).to match_array(expected)
207
- end
208
-
209
- it 'should not split zero-concatenated header field values' do
210
- input = [*RESPONSE_HEADERS,
211
- ['cache-control', "max-age=60, private\0must-revalidate"],
212
- ['content-type', 'text/html'],
213
- ['cookie', "a=b\0c=d; e=f"]]
214
- expected = [*RESPONSE_HEADERS,
215
- ['cache-control', "max-age=60, private\0must-revalidate"],
216
- ['content-type', 'text/html'],
217
- ['cookie', "a=b\0c=d; e=f"]]
218
-
219
- result = nil
220
- @conn.on(:stream) do |stream|
221
- stream.on(:headers) { |h| result = h }
222
- end
223
-
224
- srv = Server.new
225
- srv.on(:frame) { |bytes| @conn << bytes }
226
- stream = srv.new_stream
227
- stream.headers(input)
228
-
229
- expect(result).to eq expected
230
- end
231
- end
232
-
233
- context 'flow control' do
234
- it 'should initialize to default flow window' do
235
- expect(@conn.remote_window).to eq DEFAULT_FLOW_WINDOW
236
- end
237
-
238
- it 'should update connection and stream windows on SETTINGS' do
239
- settings, data = settings_frame, data_frame
240
- settings[:payload] = [[:settings_initial_window_size, 1024]]
241
- data[:payload] = 'x' * 2048
242
-
243
- stream = @conn.new_stream
244
-
245
- stream.send headers_frame
246
- stream.send data
247
- expect(stream.remote_window).to eq(DEFAULT_FLOW_WINDOW - 2048)
248
- expect(@conn.remote_window).to eq(DEFAULT_FLOW_WINDOW - 2048)
249
-
250
- @conn << f.generate(settings)
251
- expect(@conn.remote_window).to eq(-1024)
252
- expect(stream.remote_window).to eq(-1024)
253
- end
254
-
255
- it 'should initialize streams with window specified by peer' do
256
- settings = settings_frame
257
- settings[:payload] = [[:settings_initial_window_size, 1024]]
258
-
259
- @conn << f.generate(settings)
260
- expect(@conn.new_stream.remote_window).to eq 1024
261
- end
262
-
263
- it 'should observe connection flow control' do
264
- settings, data = settings_frame, data_frame
265
- settings[:payload] = [[:settings_initial_window_size, 1000]]
266
-
267
- @conn << f.generate(settings)
268
- s1 = @conn.new_stream
269
- s2 = @conn.new_stream
270
-
271
- s1.send headers_frame
272
- s1.send data.merge(payload: 'x' * 900)
273
- expect(@conn.remote_window).to eq 100
274
-
275
- s2.send headers_frame
276
- s2.send data.merge(payload: 'x' * 200)
277
- expect(@conn.remote_window).to eq 0
278
- expect(@conn.buffered_amount).to eq 100
279
-
280
- @conn << f.generate(window_update_frame.merge(stream: 0, increment: 1000))
281
- expect(@conn.buffered_amount).to eq 0
282
- expect(@conn.remote_window).to eq 900
283
- end
284
-
285
- it 'should update window when data received is over half of the maximum local window size' do
286
- settings, data = settings_frame, data_frame
287
- conn = Client.new(settings_initial_window_size: 500)
288
-
289
- conn.receive f.generate(settings)
290
- s1 = conn.new_stream
291
- s2 = conn.new_stream
292
-
293
- s1.send headers_frame
294
- s2.send headers_frame
295
- expect(conn).to receive(:send) do |frame|
296
- expect(frame[:type]).to eq :window_update
297
- expect(frame[:stream]).to eq 0
298
- expect(frame[:increment]).to eq 400
299
- end
300
- conn.receive f.generate(data.merge(payload: 'x' * 200, end_stream: false, stream: s1.id))
301
- conn.receive f.generate(data.merge(payload: 'x' * 200, end_stream: false, stream: s2.id))
302
- expect(s1.local_window).to eq 300
303
- expect(s2.local_window).to eq 300
304
- expect(conn.local_window).to eq 500
305
- end
306
- end
307
-
308
- context 'framing' do
309
- it 'should buffer incomplete frames' do
310
- settings = settings_frame
311
- settings[:payload] = [[:settings_initial_window_size, 1000]]
312
- @conn << f.generate(settings)
313
-
314
- frame = f.generate(window_update_frame.merge(stream: 0, increment: 1000))
315
- @conn << frame
316
- expect(@conn.remote_window).to eq 2000
317
-
318
- @conn << frame.slice!(0, 1)
319
- @conn << frame
320
- expect(@conn.remote_window).to eq 3000
321
- end
322
-
323
- it 'should decompress header blocks regardless of stream state' do
324
- req_headers = [
325
- ['content-length', '20'],
326
- ['x-my-header', 'first'],
327
- ]
328
-
329
- cc = Compressor.new
330
- headers = headers_frame
331
- headers[:payload] = cc.encode(req_headers)
332
-
333
- @conn << f.generate(settings_frame)
334
- @conn.on(:stream) do |stream|
335
- expect(stream).to receive(:<<) do |frame|
336
- expect(frame[:payload]).to eq req_headers
337
- end
338
- end
339
-
340
- @conn << f.generate(headers)
341
- end
342
-
343
- it 'should decode non-contiguous header blocks' do
344
- req_headers = [
345
- ['content-length', '15'],
346
- ['x-my-header', 'first'],
347
- ]
348
-
349
- cc = Compressor.new
350
- h1, h2 = headers_frame, continuation_frame
351
-
352
- # Header block fragment might not complete for decompression
353
- payload = cc.encode(req_headers)
354
- h1[:payload] = payload.slice!(0, payload.size / 2) # first half
355
- h1[:stream] = 5
356
- h1[:flags] = []
357
-
358
- h2[:payload] = payload # the remaining
359
- h2[:stream] = 5
360
-
361
- @conn << f.generate(settings_frame)
362
- @conn.on(:stream) do |stream|
363
- expect(stream).to receive(:<<) do |frame|
364
- expect(frame[:payload]).to eq req_headers
365
- end
366
- end
367
-
368
- @conn << f.generate(h1)
369
- @conn << f.generate(h2)
370
- end
371
-
372
- it 'should require that split header blocks are a contiguous sequence' do
373
- headers = headers_frame
374
- headers[:flags] = []
375
-
376
- @conn << f.generate(settings_frame)
377
- @conn << f.generate(headers)
378
- (frame_types - [continuation_frame]).each do |frame|
379
- expect { @conn << f.generate(frame.deep_dup) }.to raise_error(ProtocolError)
380
- end
381
- end
382
-
383
- it 'should raise compression error on encode of invalid frame' do
384
- @conn << f.generate(settings_frame)
385
- stream = @conn.new_stream
386
-
387
- expect do
388
- stream.headers({ 'name' => Float::INFINITY })
389
- end.to raise_error(CompressionError)
390
- end
391
-
392
- it 'should raise connection error on decode of invalid frame' do
393
- @conn << f.generate(settings_frame)
394
- frame = f.generate(data_frame) # Receiving DATA on unopened stream 1 is an error.
395
- # Connection errors emit protocol error frames
396
- expect { @conn << frame }.to raise_error(ProtocolError)
397
- end
398
-
399
- it 'should emit encoded frames via on(:frame)' do
400
- bytes = nil
401
- @conn.on(:frame) { |d| bytes = d }
402
- @conn.settings(settings_max_concurrent_streams: 10,
403
- settings_initial_window_size: 0x7fffffff)
404
-
405
- expect(bytes).to eq f.generate(settings_frame)
406
- end
407
-
408
- it 'should compress stream headers' do
409
- @conn.on(:frame) do |bytes|
410
- expect(bytes).not_to include('get')
411
- expect(bytes).not_to include('http')
412
- expect(bytes).not_to include('www.example.org') # should be huffman encoded
413
- end
414
-
415
- stream = @conn.new_stream
416
- stream.headers({ ':method' => 'get',
417
- ':scheme' => 'http',
418
- ':authority' => 'www.example.org',
419
- ':path' => '/resource' })
420
- end
421
-
422
- it 'should generate CONTINUATION if HEADERS is too long' do
423
- headers = []
424
- @conn.on(:frame) do |bytes|
425
- # bytes[3]: frame's type field
426
- headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
427
- end
428
-
429
- stream = @conn.new_stream
430
- stream.headers({
431
- ':method' => 'get',
432
- ':scheme' => 'http',
433
- ':authority' => 'www.example.org',
434
- ':path' => '/resource',
435
- 'custom' => 'q' * 44_000,
436
- }, end_stream: true)
437
- expect(headers.size).to eq 3
438
- expect(headers[0][:type]).to eq :headers
439
- expect(headers[1][:type]).to eq :continuation
440
- expect(headers[2][:type]).to eq :continuation
441
- expect(headers[0][:flags]).to eq [:end_stream]
442
- expect(headers[1][:flags]).to eq []
443
- expect(headers[2][:flags]).to eq [:end_headers]
444
- end
445
-
446
- it 'should not generate CONTINUATION if HEADERS fits exactly in a frame' do
447
- headers = []
448
- @conn.on(:frame) do |bytes|
449
- # bytes[3]: frame's type field
450
- headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
451
- end
452
-
453
- stream = @conn.new_stream
454
- stream.headers({
455
- ':method' => 'get',
456
- ':scheme' => 'http',
457
- ':authority' => 'www.example.org',
458
- ':path' => '/resource',
459
- 'custom' => 'q' * 18_682, # this number should be updated when Huffman table is changed
460
- }, end_stream: true)
461
- expect(headers[0][:length]).to eq @conn.remote_settings[:settings_max_frame_size]
462
- expect(headers.size).to eq 1
463
- expect(headers[0][:type]).to eq :headers
464
- expect(headers[0][:flags]).to include(:end_headers)
465
- expect(headers[0][:flags]).to include(:end_stream)
466
- end
467
-
468
- it 'should not generate CONTINUATION if HEADERS fits exactly in a frame' do
469
- headers = []
470
- @conn.on(:frame) do |bytes|
471
- # bytes[3]: frame's type field
472
- headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
473
- end
474
-
475
- stream = @conn.new_stream
476
- stream.headers({
477
- ':method' => 'get',
478
- ':scheme' => 'http',
479
- ':authority' => 'www.example.org',
480
- ':path' => '/resource',
481
- 'custom' => 'q' * 18_682, # this number should be updated when Huffman table is changed
482
- }, end_stream: true)
483
- expect(headers[0][:length]).to eq @conn.remote_settings[:settings_max_frame_size]
484
- expect(headers.size).to eq 1
485
- expect(headers[0][:type]).to eq :headers
486
- expect(headers[0][:flags]).to include(:end_headers)
487
- expect(headers[0][:flags]).to include(:end_stream)
488
- end
489
-
490
- it 'should generate CONTINUATION if HEADERS exceed the max payload by one byte' do
491
- headers = []
492
- @conn.on(:frame) do |bytes|
493
- headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
494
- end
495
-
496
- stream = @conn.new_stream
497
- stream.headers({
498
- ':method' => 'get',
499
- ':scheme' => 'http',
500
- ':authority' => 'www.example.org',
501
- ':path' => '/resource',
502
- 'custom' => 'q' * 18_683, # this number should be updated when Huffman table is changed
503
- }, end_stream: true)
504
- expect(headers[0][:length]).to eq @conn.remote_settings[:settings_max_frame_size]
505
- expect(headers[1][:length]).to eq 1
506
- expect(headers.size).to eq 2
507
- expect(headers[0][:type]).to eq :headers
508
- expect(headers[1][:type]).to eq :continuation
509
- expect(headers[0][:flags]).to eq [:end_stream]
510
- expect(headers[1][:flags]).to eq [:end_headers]
511
- end
512
- end
513
-
514
- context 'connection management' do
515
- it 'should raise error on invalid connection header' do
516
- srv = Server.new
517
- expect { srv << f.generate(settings_frame) }.to raise_error(HandshakeError)
518
-
519
- srv = Server.new
520
- expect do
521
- srv << CONNECTION_PREFACE_MAGIC
522
- srv << f.generate(settings_frame)
523
- end.to_not raise_error
524
- end
525
-
526
- it 'should respond to PING frames' do
527
- @conn << f.generate(settings_frame)
528
- expect(@conn).to receive(:send) do |frame|
529
- expect(frame[:type]).to eq :ping
530
- expect(frame[:flags]).to eq [:ack]
531
- expect(frame[:payload]).to eq '12345678'
532
- end
533
-
534
- @conn << f.generate(ping_frame)
535
- end
536
-
537
- it 'should fire callback on PONG' do
538
- @conn << f.generate(settings_frame)
539
-
540
- pong = nil
541
- @conn.ping('12345678') { |d| pong = d }
542
- @conn << f.generate(pong_frame)
543
- expect(pong).to eq '12345678'
544
- end
545
-
546
- it 'should fire callback on receipt of GOAWAY' do
547
- last_stream, payload, error = nil
548
- @conn << f.generate(settings_frame)
549
- @conn.on(:goaway) do |s, e, p|
550
- last_stream = s
551
- error = e
552
- payload = p
553
- end
554
- @conn << f.generate(goaway_frame.merge(last_stream: 17, payload: 'test'))
555
-
556
- expect(last_stream).to eq 17
557
- expect(error).to eq :no_error
558
- expect(payload).to eq 'test'
559
-
560
- expect(@conn).to be_closed
561
- end
562
-
563
- it 'should raise error when opening new stream after sending GOAWAY' do
564
- @conn.goaway
565
- expect(@conn).to be_closed
566
-
567
- expect { @conn.new_stream }.to raise_error(ConnectionClosed)
568
- end
569
-
570
- it 'should raise error when opening new stream after receiving GOAWAY' do
571
- @conn << f.generate(settings_frame)
572
- @conn << f.generate(goaway_frame)
573
- expect { @conn.new_stream }.to raise_error(ConnectionClosed)
574
- end
575
-
576
- it 'should not raise error when receiving connection management frames immediately after emitting goaway' do
577
- @conn.goaway
578
- expect(@conn).to be_closed
579
-
580
- expect { @conn << f.generate(settings_frame) }.not_to raise_error(ProtocolError)
581
- expect { @conn << f.generate(ping_frame) }.not_to raise_error(ProtocolError)
582
- expect { @conn << f.generate(goaway_frame) }.not_to raise_error(ProtocolError)
583
- end
584
-
585
- it 'should process connection management frames after GOAWAY' do
586
- @conn << f.generate(settings_frame)
587
- @conn << f.generate(headers_frame)
588
- @conn << f.generate(goaway_frame)
589
- @conn << f.generate(headers_frame.merge(stream: 7))
590
- @conn << f.generate(push_promise_frame)
591
-
592
- expect(@conn.active_stream_count).to eq 1
593
- end
594
-
595
- it 'should raise error on frame for invalid stream ID' do
596
- @conn << f.generate(settings_frame)
597
-
598
- expect do
599
- @conn << f.generate(data_frame.merge(stream: 31))
600
- end.to raise_error(ProtocolError)
601
- end
602
-
603
- it 'should not raise an error on frame for a closed stream ID' do
604
- srv = Server.new
605
- srv << CONNECTION_PREFACE_MAGIC
606
-
607
- stream = srv.new_stream
608
- stream.send headers_frame
609
- stream.send data_frame
610
- stream.close
611
-
612
- expect do
613
- srv << f.generate(rst_stream_frame.merge(stream: stream.id))
614
- end.to_not raise_error
615
- end
616
-
617
- it 'should send GOAWAY frame on connection error' do
618
- stream = @conn.new_stream
619
-
620
- expect(@conn).to receive(:encode) do |frame|
621
- expect(frame[:type]).to eq :settings
622
- [frame]
623
- end
624
- expect(@conn).to receive(:encode) do |frame|
625
- expect(frame[:type]).to eq :goaway
626
- expect(frame[:last_stream]).to eq stream.id
627
- expect(frame[:error]).to eq :protocol_error
628
- [frame]
629
- end
630
-
631
- expect { @conn << f.generate(data_frame) }.to raise_error(ProtocolError)
632
- end
633
- end
634
-
635
- context 'API' do
636
- it '.settings should emit SETTINGS frames' do
637
- expect(@conn).to receive(:send) do |frame|
638
- expect(frame[:type]).to eq :settings
639
- expect(frame[:payload]).to eq([
640
- [:settings_max_concurrent_streams, 10],
641
- [:settings_initial_window_size, 0x7fffffff],
642
- ])
643
- expect(frame[:stream]).to eq 0
644
- end
645
-
646
- @conn.settings(settings_max_concurrent_streams: 10,
647
- settings_initial_window_size: 0x7fffffff)
648
- end
649
-
650
- it '.ping should generate PING frames' do
651
- expect(@conn).to receive(:send) do |frame|
652
- expect(frame[:type]).to eq :ping
653
- expect(frame[:payload]).to eq 'somedata'
654
- end
655
-
656
- @conn.ping('somedata')
657
- end
658
-
659
- it '.goaway should generate GOAWAY frame with last processed stream ID' do
660
- @conn << f.generate(settings_frame)
661
- @conn << f.generate(headers_frame.merge(stream: 17))
662
-
663
- expect(@conn).to receive(:send) do |frame|
664
- expect(frame[:type]).to eq :goaway
665
- expect(frame[:last_stream]).to eq 17
666
- expect(frame[:error]).to eq :internal_error
667
- expect(frame[:payload]).to eq 'payload'
668
- end
669
-
670
- @conn.goaway(:internal_error, 'payload')
671
- end
672
- it '.window_update should emit WINDOW_UPDATE frames' do
673
- expect(@conn).to receive(:send) do |frame|
674
- expect(frame[:type]).to eq :window_update
675
- expect(frame[:increment]).to eq 20
676
- expect(frame[:stream]).to eq 0
677
- end
678
- @conn.window_update(20)
679
- end
680
- end
681
- end