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