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.
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