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
data/spec/emitter_spec.rb DELETED
@@ -1,54 +0,0 @@
1
- require 'helper'
2
-
3
- RSpec.describe HTTP2::Emitter do
4
- class Worker
5
- include Emitter
6
- end
7
-
8
- before(:each) do
9
- @w = Worker.new
10
- @cnt = 0
11
- end
12
-
13
- it 'should raise error on missing callback' do
14
- expect { @w.on(:a) {} }.to_not raise_error
15
- expect { @w.on(:a) }.to raise_error
16
- end
17
-
18
- it 'should allow multiple callbacks on single event' do
19
- @w.on(:a) { @cnt += 1 }
20
- @w.on(:a) { @cnt += 1 }
21
- @w.emit(:a)
22
-
23
- expect(@cnt).to eq 2
24
- end
25
-
26
- it 'should execute callback with optional args' do
27
- args = nil
28
- @w.on(:a) { |a| args = a }
29
- @w.emit(:a, 123)
30
-
31
- expect(args).to eq 123
32
- end
33
-
34
- it 'should pass emitted callbacks to listeners' do
35
- @w.on(:a) { |&block| block.call }
36
- @w.once(:a) { |&block| block.call }
37
- @w.emit(:a) { @cnt += 1 }
38
-
39
- expect(@cnt).to eq 2
40
- end
41
-
42
- it 'should allow events with no callbacks' do
43
- expect { @w.emit(:missing) }.to_not raise_error
44
- end
45
-
46
- it 'should execute callback exactly once' do
47
- @w.on(:a) { @cnt += 1 }
48
- @w.once(:a) { @cnt += 1 }
49
- @w.emit(:a)
50
- @w.emit(:a)
51
-
52
- expect(@cnt).to eq 3
53
- end
54
- end
data/spec/framer_spec.rb DELETED
@@ -1,487 +0,0 @@
1
- require 'helper'
2
-
3
- RSpec.describe HTTP2::Framer do
4
- let(:f) { Framer.new }
5
-
6
- context 'common header' do
7
- let(:frame) do
8
- {
9
- length: 4,
10
- type: :headers,
11
- flags: [:end_stream, :end_headers],
12
- stream: 15,
13
- }
14
- end
15
-
16
- let(:bytes) { [0, 0x04, 0x01, 0x5, 0x0000000F].pack('CnCCN') }
17
-
18
- it 'should generate common 9 byte header' do
19
- expect(f.common_header(frame)).to eq bytes
20
- end
21
-
22
- it 'should parse common 9 byte header' do
23
- expect(f.read_common_header(Buffer.new(bytes))).to eq frame
24
- end
25
-
26
- it 'should generate a large frame' do
27
- f = Framer.new
28
- f.max_frame_size = 2**24 - 1
29
- frame = {
30
- length: 2**18 + 2**16 + 17,
31
- type: :headers,
32
- flags: [:end_stream, :end_headers],
33
- stream: 15,
34
- }
35
- bytes = [5, 17, 0x01, 0x5, 0x0000000F].pack('CnCCN')
36
- expect(f.common_header(frame)).to eq bytes
37
- expect(f.read_common_header(Buffer.new(bytes))).to eq frame
38
- end
39
-
40
- it 'should raise exception on invalid frame type when sending' do
41
- expect do
42
- frame[:type] = :bogus
43
- f.common_header(frame)
44
- end.to raise_error(CompressionError, /invalid.*type/i)
45
- end
46
-
47
- it 'should raise exception on invalid stream ID' do
48
- expect do
49
- frame[:stream] = Framer::MAX_STREAM_ID + 1
50
- f.common_header(frame)
51
- end.to raise_error(CompressionError, /stream/i)
52
- end
53
-
54
- it 'should raise exception on invalid frame flag' do
55
- expect do
56
- frame[:flags] = [:bogus]
57
- f.common_header(frame)
58
- end.to raise_error(CompressionError, /frame flag/)
59
- end
60
-
61
- it 'should raise exception on invalid frame size' do
62
- expect do
63
- frame[:length] = 2**24
64
- f.common_header(frame)
65
- end.to raise_error(CompressionError, /too large/)
66
- end
67
- end
68
-
69
- context 'DATA' do
70
- it 'should generate and parse bytes' do
71
- frame = {
72
- length: 4,
73
- type: :data,
74
- flags: [:end_stream],
75
- stream: 1,
76
- payload: 'text',
77
- }
78
-
79
- bytes = f.generate(frame)
80
- expect(bytes).to eq [0, 0x4, 0x0, 0x1, 0x1, *'text'.bytes].pack('CnCCNC*')
81
-
82
- expect(f.parse(bytes)).to eq frame
83
- end
84
- end
85
-
86
- context 'HEADERS' do
87
- it 'should generate and parse bytes' do
88
- frame = {
89
- length: 12,
90
- type: :headers,
91
- flags: [:end_stream, :end_headers],
92
- stream: 1,
93
- payload: 'header-block',
94
- }
95
-
96
- bytes = f.generate(frame)
97
- expect(bytes).to eq [0, 0xc, 0x1, 0x5, 0x1, *'header-block'.bytes].pack('CnCCNC*')
98
- expect(f.parse(bytes)).to eq frame
99
- end
100
-
101
- it 'should carry an optional stream priority' do
102
- frame = {
103
- length: 16,
104
- type: :headers,
105
- flags: [:end_headers],
106
- stream: 1,
107
- stream_dependency: 15,
108
- weight: 12,
109
- exclusive: false,
110
- payload: 'header-block',
111
- }
112
-
113
- bytes = f.generate(frame)
114
- expect(bytes).to eq [0, 0x11, 0x1, 0x24, 0x1, 0xf, 0xb, *'header-block'.bytes].pack('CnCCNNCC*')
115
- expect(f.parse(bytes)).to eq frame
116
- end
117
- end
118
-
119
- context 'PRIORITY' do
120
- it 'should generate and parse bytes' do
121
- frame = {
122
- length: 5,
123
- type: :priority,
124
- stream: 1,
125
- stream_dependency: 15,
126
- weight: 12,
127
- exclusive: true,
128
- }
129
-
130
- bytes = f.generate(frame)
131
- expect(bytes).to eq [0, 0x5, 0x2, 0x0, 0x1, 0x8000000f, 0xb].pack('CnCCNNC')
132
- expect(f.parse(bytes)).to eq frame
133
- end
134
- end
135
-
136
- context 'RST_STREAM' do
137
- it 'should generate and parse bytes' do
138
- frame = {
139
- length: 4,
140
- type: :rst_stream,
141
- stream: 1,
142
- error: :stream_closed,
143
- }
144
-
145
- bytes = f.generate(frame)
146
- expect(bytes).to eq [0, 0x4, 0x3, 0x0, 0x1, 0x5].pack('CnCCNN')
147
- expect(f.parse(bytes)).to eq frame
148
- end
149
- end
150
-
151
- context 'SETTINGS' do
152
- let(:frame) do
153
- {
154
- type: :settings,
155
- flags: [],
156
- stream: 0,
157
- payload: [
158
- [:settings_max_concurrent_streams, 10],
159
- [:settings_header_table_size, 2048],
160
- ],
161
- }
162
- end
163
-
164
- it 'should generate and parse bytes' do
165
- bytes = f.generate(frame)
166
- expect(bytes).to eq [0, 12, 0x4, 0x0, 0x0, 3, 10, 1, 2048].pack('CnCCNnNnN')
167
- parsed = f.parse(bytes)
168
- parsed.delete(:length)
169
- frame.delete(:length)
170
- expect(parsed).to eq frame
171
- end
172
-
173
- it 'should generate settings when id is given as an integer' do
174
- frame[:payload][1][0] = 1
175
- bytes = f.generate(frame)
176
- expect(bytes).to eq [0, 12, 0x4, 0x0, 0x0, 3, 10, 1, 2048].pack('CnCCNnNnN')
177
- end
178
-
179
- it 'should ignore custom settings when sending' do
180
- frame[:payload] = [
181
- [:settings_max_concurrent_streams, 10],
182
- [:settings_initial_window_size, 20],
183
- [55, 30],
184
- ]
185
-
186
- buf = f.generate(frame)
187
- frame[:payload].slice!(2) # cut off the extension
188
- frame[:length] = 12 # frame length should be computed WITHOUT extensions
189
- expect(f.parse(buf)).to eq frame
190
- end
191
-
192
- it 'should ignore custom settings when receiving' do
193
- frame[:payload] = [
194
- [:settings_max_concurrent_streams, 10],
195
- [:settings_initial_window_size, 20],
196
- ]
197
-
198
- buf = f.generate(frame)
199
- buf.setbyte(2, 18) # add 6 to the frame length
200
- buf << "\x00\x37\x00\x00\x00\x1e"
201
- parsed = f.parse(buf)
202
- parsed.delete(:length)
203
- frame.delete(:length)
204
- expect(parsed).to eq frame
205
- end
206
-
207
- it 'should raise exception on sending invalid stream ID' do
208
- expect do
209
- frame[:stream] = 1
210
- f.generate(frame)
211
- end.to raise_error(CompressionError, /Invalid stream ID/)
212
- end
213
-
214
- it 'should raise exception on receiving invalid stream ID' do
215
- expect do
216
- buf = f.generate(frame)
217
- buf.setbyte(8, 1)
218
- f.parse(buf)
219
- end.to raise_error(ProtocolError, /Invalid stream ID/)
220
- end
221
-
222
- it 'should raise exception on sending invalid setting' do
223
- expect do
224
- frame[:payload] = [[:random, 23]]
225
- f.generate(frame)
226
- end.to raise_error(CompressionError, /Unknown settings ID/)
227
- end
228
-
229
- it 'should raise exception on receiving invalid payload length' do
230
- expect do
231
- buf = f.generate(frame)
232
- buf.setbyte(2, 11) # change payload length
233
- f.parse(buf)
234
- end.to raise_error(ProtocolError, /Invalid settings payload length/)
235
- end
236
- end
237
-
238
- context 'PUSH_PROMISE' do
239
- it 'should generate and parse bytes' do
240
- frame = {
241
- length: 11,
242
- type: :push_promise,
243
- flags: [:end_headers],
244
- stream: 1,
245
- promise_stream: 2,
246
- payload: 'headers',
247
- }
248
-
249
- bytes = f.generate(frame)
250
- expect(bytes).to eq [0, 0xb, 0x5, 0x4, 0x1, 0x2, *'headers'.bytes].pack('CnCCNNC*')
251
- expect(f.parse(bytes)).to eq frame
252
- end
253
- end
254
-
255
- context 'PING' do
256
- let(:frame) do
257
- {
258
- length: 8,
259
- stream: 1,
260
- type: :ping,
261
- flags: [:ack],
262
- payload: '12345678',
263
- }
264
- end
265
-
266
- it 'should generate and parse bytes' do
267
- bytes = f.generate(frame)
268
- expect(bytes).to eq [0, 0x8, 0x6, 0x1, 0x1, *'12345678'.bytes].pack('CnCCNC*')
269
- expect(f.parse(bytes)).to eq frame
270
- end
271
-
272
- it 'should raise exception on invalid payload' do
273
- expect do
274
- frame[:payload] = '1234'
275
- f.generate(frame)
276
- end.to raise_error(CompressionError, /Invalid payload size/)
277
- end
278
- end
279
-
280
- context 'GOAWAY' do
281
- let(:frame) do
282
- {
283
- length: 13,
284
- stream: 1,
285
- type: :goaway,
286
- last_stream: 2,
287
- error: :no_error,
288
- payload: 'debug',
289
- }
290
- end
291
-
292
- it 'should generate and parse bytes' do
293
- bytes = f.generate(frame)
294
- expect(bytes).to eq [0, 0xd, 0x7, 0x0, 0x1, 0x2, 0x0, *'debug'.bytes].pack('CnCCNNNC*')
295
- expect(f.parse(bytes)).to eq frame
296
- end
297
-
298
- it 'should treat debug payload as optional' do
299
- frame.delete :payload
300
- frame[:length] = 0x8
301
-
302
- bytes = f.generate(frame)
303
- expect(bytes).to eq [0, 0x8, 0x7, 0x0, 0x1, 0x2, 0x0].pack('CnCCNNN')
304
- expect(f.parse(bytes)).to eq frame
305
- end
306
- end
307
-
308
- context 'WINDOW_UPDATE' do
309
- it 'should generate and parse bytes' do
310
- frame = {
311
- length: 4,
312
- type: :window_update,
313
- increment: 10,
314
- }
315
-
316
- bytes = f.generate(frame)
317
- expect(bytes).to eq [0, 0x4, 0x8, 0x0, 0x0, 0xa].pack('CnCCNN')
318
- expect(f.parse(bytes)).to eq frame
319
- end
320
- end
321
-
322
- context 'CONTINUATION' do
323
- it 'should generate and parse bytes' do
324
- frame = {
325
- length: 12,
326
- type: :continuation,
327
- stream: 1,
328
- flags: [:end_headers],
329
- payload: 'header-block',
330
- }
331
-
332
- bytes = f.generate(frame)
333
- expect(bytes).to eq [0, 0xc, 0x9, 0x4, 0x1, *'header-block'.bytes].pack('CnCCNC*')
334
- expect(f.parse(bytes)).to eq frame
335
- end
336
- end
337
-
338
- context 'ALTSVC' do
339
- it 'should generate and parse bytes' do
340
- frame = {
341
- length: 44,
342
- type: :altsvc,
343
- stream: 1,
344
- max_age: 1_402_290_402, # 4
345
- port: 8080, # 2
346
- proto: 'h2-13', # 1 + 5
347
- host: 'www.example.com', # 1 + 15
348
- origin: 'www.example.com', # 15
349
- }
350
- bytes = f.generate(frame)
351
- expected = [0, 43, 0xa, 0, 1, 1_402_290_402, 8080].pack('CnCCNNn')
352
- expected << [5, *'h2-13'.bytes].pack('CC*')
353
- expected << [15, *'www.example.com'.bytes].pack('CC*')
354
- expected << [*'www.example.com'.bytes].pack('C*')
355
- expect(bytes).to eq expected
356
- expect(f.parse(bytes)).to eq frame
357
- end
358
- end
359
-
360
- context 'Padding' do
361
- [:data, :headers, :push_promise].each do |type|
362
- [1, 256].each do |padlen|
363
- context "generating #{type} frame padded #{padlen}" do
364
- before do
365
- @frame = {
366
- length: 12,
367
- type: type,
368
- stream: 1,
369
- payload: 'example data',
370
- }
371
- @frame[:promise_stream] = 2 if type == :push_promise
372
- @normal = f.generate(@frame)
373
- @padded = f.generate(@frame.merge(padding: padlen))
374
- end
375
- it 'should generate a frame with padding' do
376
- expect(@padded.bytesize).to eq @normal.bytesize + padlen
377
- end
378
- it 'should fill padded octets with zero' do
379
- trailer_len = padlen - 1
380
- expect(@padded[-trailer_len, trailer_len]).to match(/\A\0*\z/)
381
- end
382
- it 'should parse a frame with padding' do
383
- expect(f.parse(Buffer.new(@padded))).to eq \
384
- f.parse(Buffer.new(@normal)).merge(padding: padlen)
385
- end
386
- it 'should preserve payload' do
387
- expect(f.parse(Buffer.new(@padded))[:payload]).to eq @frame[:payload]
388
- end
389
- end
390
- end
391
- end
392
- context 'generating with invalid padding length' do
393
- before do
394
- @frame = {
395
- length: 12,
396
- type: :data,
397
- stream: 1,
398
- payload: 'example data',
399
- }
400
- end
401
- [0, 257, 1334].each do |padlen|
402
- it "should raise error on trying to generate data frame padded with invalid #{padlen}" do
403
- expect do
404
- f.generate(@frame.merge(padding: padlen))
405
- end.to raise_error(CompressionError, /padding/i)
406
- end
407
- end
408
- it 'should raise error when adding a padding would make frame too large' do
409
- @frame[:payload] = 'q' * (f.max_frame_size - 200)
410
- @frame[:length] = @frame[:payload].size
411
- @frame[:padding] = 210 # would exceed 4096
412
- expect do
413
- f.generate(@frame)
414
- end.to raise_error(CompressionError, /padding/i)
415
- end
416
- end
417
- context 'parsing frames with invalid paddings' do
418
- before do
419
- @frame = {
420
- length: 12,
421
- type: :data,
422
- stream: 1,
423
- payload: 'example data',
424
- }
425
- @padlen = 123
426
- @padded = f.generate(@frame.merge(padding: @padlen))
427
- end
428
- it 'should raise exception when the given padding is longer than the payload' do
429
- @padded.setbyte(9, 240)
430
- expect { f.parse(Buffer.new(@padded)) }.to raise_error(ProtocolError, /padding/)
431
- end
432
- end
433
- end
434
-
435
- it 'should determine frame length' do
436
- frames = [
437
- [{ type: :data, stream: 1, flags: [:end_stream], payload: 'abc' }, 3],
438
- [{ type: :headers, stream: 1, payload: 'abc' }, 3],
439
- [{ type: :priority, stream: 3, stream_dependency: 30, exclusive: false, weight: 1 }, 5],
440
- [{ type: :rst_stream, stream: 3, error: 100 }, 4],
441
- [{ type: :settings, payload: [[:settings_max_concurrent_streams, 10]] }, 6],
442
- [{ type: :push_promise, promise_stream: 5, payload: 'abc' }, 7],
443
- [{ type: :ping, payload: 'blob' * 2 }, 8],
444
- [{ type: :goaway, last_stream: 5, error: 20, payload: 'blob' }, 12],
445
- [{ type: :window_update, stream: 1, increment: 1024 }, 4],
446
- [{ type: :continuation, stream: 1, payload: 'abc' }, 3],
447
- ]
448
-
449
- frames.each do |(frame, size)|
450
- bytes = f.generate(frame)
451
- expect(bytes.slice(1, 2).unpack('n').first).to eq size
452
- expect(bytes.readbyte(0)).to eq 0
453
- end
454
- end
455
-
456
- it 'should parse single frame at a time' do
457
- frames = [
458
- { type: :headers, stream: 1, payload: 'headers' },
459
- { type: :data, stream: 1, flags: [:end_stream], payload: 'abc' },
460
- ]
461
-
462
- buf = f.generate(frames[0]) << f.generate(frames[1])
463
-
464
- expect(f.parse(buf)).to eq frames[0]
465
- expect(f.parse(buf)).to eq frames[1]
466
- end
467
-
468
- it 'should process full frames only' do
469
- frame = { type: :headers, stream: 1, payload: 'headers' }
470
- bytes = f.generate(frame)
471
-
472
- expect(f.parse(bytes[0...-1])).to be_nil
473
- expect(f.parse(bytes)).to eq frame
474
- expect(bytes).to be_empty
475
- end
476
-
477
- it 'should ignore unknown extension frames' do
478
- frame = { type: :headers, stream: 1, payload: 'headers' }
479
- bytes = f.generate(frame)
480
- bytes = Buffer.new(bytes + bytes) # Two HEADERS frames in bytes
481
- bytes.setbyte(3, 42) # Make the first unknown type 42
482
-
483
- expect(f.parse(bytes)).to be_nil # first frame should be ignored
484
- expect(f.parse(bytes)).to eq frame # should generate only one HEADERS
485
- expect(bytes).to be_empty
486
- end
487
- end
Binary file