http-2 0.11.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -9
  3. data/lib/http/2/base64.rb +45 -0
  4. data/lib/http/2/client.rb +19 -6
  5. data/lib/http/2/connection.rb +235 -163
  6. data/lib/http/2/emitter.rb +7 -5
  7. data/lib/http/2/error.rb +24 -1
  8. data/lib/http/2/extensions.rb +53 -0
  9. data/lib/http/2/flow_buffer.rb +91 -33
  10. data/lib/http/2/framer.rb +184 -157
  11. data/lib/http/2/header/compressor.rb +157 -0
  12. data/lib/http/2/header/decompressor.rb +144 -0
  13. data/lib/http/2/header/encoding_context.rb +337 -0
  14. data/lib/http/2/{huffman.rb → header/huffman.rb} +25 -19
  15. data/lib/http/2/{huffman_statemachine.rb → header/huffman_statemachine.rb} +2 -0
  16. data/lib/http/2/header.rb +35 -0
  17. data/lib/http/2/server.rb +47 -20
  18. data/lib/http/2/stream.rb +130 -61
  19. data/lib/http/2/version.rb +3 -1
  20. data/lib/http/2.rb +14 -13
  21. data/sig/client.rbs +9 -0
  22. data/sig/connection.rbs +93 -0
  23. data/sig/emitter.rbs +13 -0
  24. data/sig/error.rbs +35 -0
  25. data/sig/extensions.rbs +5 -0
  26. data/sig/flow_buffer.rbs +21 -0
  27. data/sig/frame_buffer.rbs +13 -0
  28. data/sig/framer.rbs +54 -0
  29. data/sig/header/compressor.rbs +27 -0
  30. data/sig/header/decompressor.rbs +22 -0
  31. data/sig/header/encoding_context.rbs +34 -0
  32. data/sig/header/huffman.rbs +9 -0
  33. data/sig/header.rbs +27 -0
  34. data/sig/next.rbs +101 -0
  35. data/sig/server.rbs +12 -0
  36. data/sig/stream.rbs +91 -0
  37. metadata +38 -79
  38. data/.autotest +0 -20
  39. data/.coveralls.yml +0 -1
  40. data/.gitignore +0 -20
  41. data/.gitmodules +0 -3
  42. data/.rspec +0 -5
  43. data/.rubocop.yml +0 -93
  44. data/.rubocop_todo.yml +0 -131
  45. data/.travis.yml +0 -17
  46. data/Gemfile +0 -16
  47. data/Guardfile +0 -18
  48. data/Guardfile.h2spec +0 -12
  49. data/LICENSE +0 -21
  50. data/Rakefile +0 -49
  51. data/example/Gemfile +0 -3
  52. data/example/README.md +0 -44
  53. data/example/client.rb +0 -122
  54. data/example/helper.rb +0 -19
  55. data/example/keys/server.crt +0 -20
  56. data/example/keys/server.key +0 -27
  57. data/example/server.rb +0 -139
  58. data/example/upgrade_client.rb +0 -153
  59. data/example/upgrade_server.rb +0 -203
  60. data/http-2.gemspec +0 -22
  61. data/lib/http/2/buffer.rb +0 -76
  62. data/lib/http/2/compressor.rb +0 -572
  63. data/lib/tasks/generate_huffman_table.rb +0 -166
  64. data/spec/buffer_spec.rb +0 -28
  65. data/spec/client_spec.rb +0 -188
  66. data/spec/compressor_spec.rb +0 -666
  67. data/spec/connection_spec.rb +0 -681
  68. data/spec/emitter_spec.rb +0 -54
  69. data/spec/framer_spec.rb +0 -487
  70. data/spec/h2spec/h2spec.darwin +0 -0
  71. data/spec/h2spec/output/non_secure.txt +0 -317
  72. data/spec/helper.rb +0 -147
  73. data/spec/hpack_test_spec.rb +0 -84
  74. data/spec/huffman_spec.rb +0 -68
  75. data/spec/server_spec.rb +0 -52
  76. data/spec/stream_spec.rb +0 -878
  77. data/spec/support/deep_dup.rb +0 -55
  78. data/spec/support/duplicable.rb +0 -98
@@ -1,666 +0,0 @@
1
- require 'helper'
2
-
3
- RSpec.describe HTTP2::Header do
4
- let(:c) { Compressor.new }
5
- let(:d) { Decompressor.new }
6
-
7
- context 'literal representation' do
8
- context 'integer' do
9
- it 'should encode 10 using a 5-bit prefix' do
10
- buf = c.integer(10, 5)
11
- expect(buf).to eq [10].pack('C')
12
- expect(d.integer(Buffer.new(buf), 5)).to eq 10
13
- end
14
-
15
- it 'should encode 10 using a 0-bit prefix' do
16
- buf = c.integer(10, 0)
17
- expect(buf).to eq [10].pack('C')
18
- expect(d.integer(Buffer.new(buf), 0)).to eq 10
19
- end
20
-
21
- it 'should encode 1337 using a 5-bit prefix' do
22
- buf = c.integer(1337, 5)
23
- expect(buf).to eq [31, 128 + 26, 10].pack('C*')
24
- expect(d.integer(Buffer.new(buf), 5)).to eq 1337
25
- end
26
-
27
- it 'should encode 1337 using a 0-bit prefix' do
28
- buf = c.integer(1337, 0)
29
- expect(buf).to eq [128 + 57, 10].pack('C*')
30
- expect(d.integer(Buffer.new(buf), 0)).to eq 1337
31
- end
32
- end
33
-
34
- context 'string' do
35
- [
36
- ['with huffman', :always, 0x80],
37
- ['without huffman', :never, 0],
38
- ].each do |desc, option, msb|
39
- let(:trailer) { 'trailer' }
40
-
41
- [
42
- ['ascii codepoints', 'abcdefghij'],
43
- ['utf-8 codepoints', 'éáűőúöüó€'],
44
- ['long utf-8 strings', 'éáűőúöüó€' * 100],
45
- ].each do |datatype, plain|
46
- it "should handle #{datatype} #{desc}" do
47
- # NOTE: don't put this new in before{} because of test case shuffling
48
- @c = Compressor.new(huffman: option)
49
- str = @c.string(plain)
50
- expect(str.getbyte(0) & 0x80).to eq msb
51
-
52
- buf = Buffer.new(str + trailer)
53
- expect(d.string(buf)).to eq plain
54
- expect(buf).to eq trailer
55
- end
56
- end
57
- end
58
- context 'choosing shorter representation' do
59
- [['日本語', :plain],
60
- ['200', :huffman],
61
- ['xq', :plain], # prefer plain if equal size
62
- ].each do |string, choice|
63
- before { @c = Compressor.new(huffman: :shorter) }
64
-
65
- it "should return #{choice} representation" do
66
- wire = @c.string(string)
67
- expect(wire.getbyte(0) & 0x80).to eq(choice == :plain ? 0 : 0x80)
68
- end
69
- end
70
- end
71
- end
72
- end
73
-
74
- context 'header representation' do
75
- it 'should handle indexed representation' do
76
- h = { name: 10, type: :indexed }
77
- wire = c.header(h)
78
- expect(wire.readbyte(0) & 0x80).to eq 0x80
79
- expect(wire.readbyte(0) & 0x7f).to eq h[:name] + 1
80
- expect(d.header(wire)).to eq h
81
- end
82
- it 'should raise when decoding indexed representation with index zero' do
83
- h = { name: 10, type: :indexed }
84
- wire = c.header(h)
85
- wire[0] = 0x80.chr(Encoding::BINARY)
86
- expect { d.header(wire) }.to raise_error CompressionError
87
- end
88
-
89
- context 'literal w/o indexing representation' do
90
- it 'should handle indexed header' do
91
- h = { name: 10, value: 'my-value', type: :noindex }
92
- wire = c.header(h)
93
- expect(wire.readbyte(0) & 0xf0).to eq 0x0
94
- expect(wire.readbyte(0) & 0x0f).to eq h[:name] + 1
95
- expect(d.header(wire)).to eq h
96
- end
97
-
98
- it 'should handle literal header' do
99
- h = { name: 'x-custom', value: 'my-value', type: :noindex }
100
- wire = c.header(h)
101
- expect(wire.readbyte(0) & 0xf0).to eq 0x0
102
- expect(wire.readbyte(0) & 0x0f).to eq 0
103
- expect(d.header(wire)).to eq h
104
- end
105
- end
106
-
107
- context 'literal w/ incremental indexing' do
108
- it 'should handle indexed header' do
109
- h = { name: 10, value: 'my-value', type: :incremental }
110
- wire = c.header(h)
111
- expect(wire.readbyte(0) & 0xc0).to eq 0x40
112
- expect(wire.readbyte(0) & 0x3f).to eq h[:name] + 1
113
- expect(d.header(wire)).to eq h
114
- end
115
-
116
- it 'should handle literal header' do
117
- h = { name: 'x-custom', value: 'my-value', type: :incremental }
118
- wire = c.header(h)
119
- expect(wire.readbyte(0) & 0xc0).to eq 0x40
120
- expect(wire.readbyte(0) & 0x3f).to eq 0
121
- expect(d.header(wire)).to eq h
122
- end
123
- end
124
-
125
- context 'literal never indexed' do
126
- it 'should handle indexed header' do
127
- h = { name: 10, value: 'my-value', type: :neverindexed }
128
- wire = c.header(h)
129
- expect(wire.readbyte(0) & 0xf0).to eq 0x10
130
- expect(wire.readbyte(0) & 0x0f).to eq h[:name] + 1
131
- expect(d.header(wire)).to eq h
132
- end
133
-
134
- it 'should handle literal header' do
135
- h = { name: 'x-custom', value: 'my-value', type: :neverindexed }
136
- wire = c.header(h)
137
- expect(wire.readbyte(0) & 0xf0).to eq 0x10
138
- expect(wire.readbyte(0) & 0x0f).to eq 0
139
- expect(d.header(wire)).to eq h
140
- end
141
- end
142
- end
143
-
144
- context 'shared compression context' do
145
- before(:each) { @cc = EncodingContext.new }
146
-
147
- it 'should be initialized with empty headers' do
148
- cc = EncodingContext.new
149
- expect(cc.table).to be_empty
150
- end
151
-
152
- context 'processing' do
153
- [
154
- ['no indexing', :noindex],
155
- ['never indexed', :neverindexed],
156
- ].each do |desc, type|
157
- context "#{desc}" do
158
- it 'should process indexed header with literal value' do
159
- original_table = @cc.table.dup
160
-
161
- emit = @cc.process(name: 4, value: '/path', type: type)
162
- expect(emit).to eq [':path', '/path']
163
- expect(@cc.table).to eq original_table
164
- end
165
-
166
- it 'should process literal header with literal value' do
167
- original_table = @cc.table.dup
168
-
169
- emit = @cc.process(name: 'x-custom', value: 'random', type: type)
170
- expect(emit).to eq ['x-custom', 'random']
171
- expect(@cc.table).to eq original_table
172
- end
173
- end
174
- end
175
-
176
- context 'incremental indexing' do
177
- it 'should process indexed header with literal value' do
178
- original_table = @cc.table.dup
179
-
180
- emit = @cc.process(name: 4, value: '/path', type: :incremental)
181
- expect(emit).to eq [':path', '/path']
182
- expect(@cc.table - original_table).to eq [[':path', '/path']]
183
- end
184
-
185
- it 'should process literal header with literal value' do
186
- original_table = @cc.table.dup
187
-
188
- @cc.process(name: 'x-custom', value: 'random', type: :incremental)
189
- expect(@cc.table - original_table).to eq [['x-custom', 'random']]
190
- end
191
- end
192
-
193
- context 'size bounds' do
194
- it 'should drop headers from end of table' do
195
- cc = EncodingContext.new(table_size: 2048)
196
- cc.instance_eval do
197
- add_to_table(['test1', '1' * 1024])
198
- add_to_table(['test2', '2' * 500])
199
- end
200
-
201
- original_table = cc.table.dup
202
- original_size = original_table.join.bytesize + original_table.size * 32
203
-
204
- cc.process(name: 'x-custom',
205
- value: 'a' * (2048 - original_size),
206
- type: :incremental)
207
-
208
- expect(cc.table.first[0]).to eq 'x-custom'
209
- expect(cc.table.size).to eq original_table.size # number of entries
210
- end
211
- end
212
-
213
- it 'should clear table if entry exceeds table size' do
214
- cc = EncodingContext.new(table_size: 2048)
215
- cc.instance_eval do
216
- add_to_table(['test1', '1' * 1024])
217
- add_to_table(['test2', '2' * 500])
218
- end
219
-
220
- h = { name: 'x-custom', value: 'a', index: 0, type: :incremental }
221
- e = { name: 'large', value: 'a' * 2048, index: 0 }
222
-
223
- cc.process(h)
224
- cc.process(e.merge(type: :incremental))
225
- expect(cc.table).to be_empty
226
- end
227
-
228
- it 'should shrink table if set smaller size' do
229
- cc = EncodingContext.new(table_size: 2048)
230
- cc.instance_eval do
231
- add_to_table(['test1', '1' * 1024])
232
- add_to_table(['test2', '2' * 500])
233
- end
234
-
235
- cc.process(type: :changetablesize, value: 1500)
236
- expect(cc.table.size).to be 1
237
- expect(cc.table.first[0]).to eq 'test2'
238
- end
239
-
240
- it 'should reject table size update if exceed limit' do
241
- cc = EncodingContext.new(table_size: 4096)
242
-
243
- expect { cc.process(type: :changetablesize, value: 150_000_000) }.to raise_error(CompressionError)
244
- end
245
- end
246
-
247
- context 'encode' do
248
- it 'downcases the field' do
249
- expect(EncodingContext.new.encode([['Content-Length', '5']]))
250
- .to eq(EncodingContext.new.encode([['content-length', '5']]))
251
- end
252
-
253
- it 'fills :path if empty' do
254
- expect(EncodingContext.new.encode([[':path', '']]))
255
- .to eq(EncodingContext.new.encode([[':path', '/']]))
256
- end
257
- end
258
- end
259
-
260
- spec_examples = [
261
- { title: 'D.3. Request Examples without Huffman',
262
- type: :request,
263
- table_size: 4096,
264
- huffman: :never,
265
- streams: [
266
- { wire: "8286 8441 0f77 7777 2e65 7861 6d70 6c65
267
- 2e63 6f6d",
268
- emitted: [
269
- [':method', 'GET'],
270
- [':scheme', 'http'],
271
- [':path', '/'],
272
- [':authority', 'www.example.com'],
273
- ],
274
- table: [
275
- [':authority', 'www.example.com'],
276
- ],
277
- table_size: 57,
278
- },
279
- { wire: '8286 84be 5808 6e6f 2d63 6163 6865',
280
- emitted: [
281
- [':method', 'GET'],
282
- [':scheme', 'http'],
283
- [':path', '/'],
284
- [':authority', 'www.example.com'],
285
- ['cache-control', 'no-cache'],
286
- ],
287
- table: [
288
- ['cache-control', 'no-cache'],
289
- [':authority', 'www.example.com'],
290
- ],
291
- table_size: 110,
292
- },
293
- { wire: "8287 85bf 400a 6375 7374 6f6d 2d6b 6579
294
- 0c63 7573 746f 6d2d 7661 6c75 65",
295
- emitted: [
296
- [':method', 'GET'],
297
- [':scheme', 'https'],
298
- [':path', '/index.html'],
299
- [':authority', 'www.example.com'],
300
- ['custom-key', 'custom-value'],
301
- ],
302
- table: [
303
- ['custom-key', 'custom-value'],
304
- ['cache-control', 'no-cache'],
305
- [':authority', 'www.example.com'],
306
- ],
307
- table_size: 164,
308
- },
309
- ],
310
- },
311
- { title: 'D.4. Request Examples with Huffman',
312
- type: :request,
313
- table_size: 4096,
314
- huffman: :always,
315
- streams: [
316
- { wire: '8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff',
317
- emitted: [
318
- [':method', 'GET'],
319
- [':scheme', 'http'],
320
- [':path', '/'],
321
- [':authority', 'www.example.com'],
322
- ],
323
- table: [
324
- [':authority', 'www.example.com'],
325
- ],
326
- table_size: 57,
327
- },
328
- { wire: '8286 84be 5886 a8eb 1064 9cbf',
329
- emitted: [
330
- [':method', 'GET'],
331
- [':scheme', 'http'],
332
- [':path', '/'],
333
- [':authority', 'www.example.com'],
334
- ['cache-control', 'no-cache'],
335
- ],
336
- table: [
337
- ['cache-control', 'no-cache'],
338
- [':authority', 'www.example.com'],
339
- ],
340
- table_size: 110,
341
- },
342
- { wire: "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925
343
- a849 e95b b8e8 b4bf",
344
- emitted: [
345
- [':method', 'GET'],
346
- [':scheme', 'https'],
347
- [':path', '/index.html'],
348
- [':authority', 'www.example.com'],
349
- ['custom-key', 'custom-value'],
350
- ],
351
- table: [
352
- ['custom-key', 'custom-value'],
353
- ['cache-control', 'no-cache'],
354
- [':authority', 'www.example.com'],
355
- ],
356
- table_size: 164,
357
- },
358
- ],
359
- },
360
- { title: 'D.4.a. Request Examples with Huffman - Client Handling of Improperly Ordered Headers',
361
- type: :request,
362
- table_size: 4096,
363
- huffman: :always,
364
- streams: [
365
- { wire: '8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff',
366
- emitted: [
367
- [':method', 'GET'],
368
- [':scheme', 'http'],
369
- [':path', '/'],
370
- [':authority', 'www.example.com'],
371
- ],
372
- table: [
373
- [':authority', 'www.example.com'],
374
- ],
375
- table_size: 57,
376
- },
377
- { wire: '8286 84be 5886 a8eb 1064 9cbf',
378
- emitted: [
379
- [':method', 'GET'],
380
- [':scheme', 'http'],
381
- ['cache-control', 'no-cache'],
382
- [':path', '/'],
383
- [':authority', 'www.example.com'],
384
- ],
385
- table: [
386
- ['cache-control', 'no-cache'],
387
- [':authority', 'www.example.com'],
388
- ],
389
- table_size: 110,
390
- },
391
- { wire: "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925
392
- a849 e95b b8e8 b4bf",
393
- emitted: [
394
- [':method', 'GET'],
395
- [':scheme', 'https'],
396
- ['custom-key', 'custom-value'],
397
- [':path', '/index.html'],
398
- [':authority', 'www.example.com'],
399
- ],
400
- table: [
401
- ['custom-key', 'custom-value'],
402
- ['cache-control', 'no-cache'],
403
- [':authority', 'www.example.com'],
404
- ],
405
- table_size: 164,
406
- },
407
- ],
408
- },
409
- { title: 'D.4.b. Request Examples with Huffman - Server Handling of Improperly Ordered Headers',
410
- type: :request,
411
- bypass_encoder: true,
412
- table_size: 4096,
413
- huffman: :always,
414
- streams: [
415
- { wire: '8286408825a849e95ba97d7f8925a849e95bb8e8b4bf84418cf1e3c2e5f23a6ba0ab90f4ff',
416
- emitted: [
417
- [':method', 'GET'],
418
- [':scheme', 'http'],
419
- ['custom-key', 'custom-value'],
420
- [':path', '/'],
421
- [':authority', 'www.example.com'],
422
- ],
423
- table: [
424
- ['custom-key', 'custom-value'],
425
- [':authority', 'www.example.com'],
426
- ],
427
- table_size: 111,
428
- has_bad_headers: true,
429
- },
430
- ],
431
- },
432
- { title: 'D.5. Response Examples without Huffman',
433
- type: :response,
434
- table_size: 256,
435
- huffman: :never,
436
- streams: [
437
- { wire: "4803 3330 3258 0770 7269 7661 7465 611d
438
- 4d6f 6e2c 2032 3120 4f63 7420 3230 3133
439
- 2032 303a 3133 3a32 3120 474d 546e 1768
440
- 7474 7073 3a2f 2f77 7777 2e65 7861 6d70
441
- 6c65 2e63 6f6d",
442
- emitted: [
443
- [':status', '302'],
444
- ['cache-control', 'private'],
445
- ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'],
446
- ['location', 'https://www.example.com'],
447
- ],
448
- table: [
449
- ['location', 'https://www.example.com'],
450
- ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'],
451
- ['cache-control', 'private'],
452
- [':status', '302'],
453
- ],
454
- table_size: 222,
455
- },
456
- { wire: '4803 3330 37c1 c0bf',
457
- emitted: [
458
- [':status', '307'],
459
- ['cache-control', 'private'],
460
- ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'],
461
- ['location', 'https://www.example.com'],
462
- ],
463
- table: [
464
- [':status', '307'],
465
- ['location', 'https://www.example.com'],
466
- ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'],
467
- ['cache-control', 'private'],
468
- ],
469
- table_size: 222,
470
- },
471
- { wire: "88c1 611d 4d6f 6e2c 2032 3120 4f63 7420
472
- 3230 3133 2032 303a 3133 3a32 3220 474d
473
- 54c0 5a04 677a 6970 7738 666f 6f3d 4153
474
- 444a 4b48 514b 425a 584f 5157 454f 5049
475
- 5541 5851 5745 4f49 553b 206d 6178 2d61
476
- 6765 3d33 3630 303b 2076 6572 7369 6f6e
477
- 3d31",
478
- emitted: [
479
- [':status', '200'],
480
- ['cache-control', 'private'],
481
- ['date', 'Mon, 21 Oct 2013 20:13:22 GMT'],
482
- ['location', 'https://www.example.com'],
483
- ['content-encoding', 'gzip'],
484
- ['set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1'],
485
- ],
486
- table: [
487
- ['set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1'],
488
- ['content-encoding', 'gzip'],
489
- ['date', 'Mon, 21 Oct 2013 20:13:22 GMT'],
490
- ],
491
- table_size: 215,
492
- },
493
- ],
494
- },
495
- { title: 'D.6. Response Examples with Huffman',
496
- type: :response,
497
- table_size: 256,
498
- huffman: :always,
499
- streams: [
500
- { wire: "4882 6402 5885 aec3 771a 4b61 96d0 7abe
501
- 9410 54d4 44a8 2005 9504 0b81 66e0 82a6
502
- 2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8
503
- e9ae 82ae 43d3",
504
- emitted: [
505
- [':status', '302'],
506
- ['cache-control', 'private'],
507
- ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'],
508
- ['location', 'https://www.example.com'],
509
- ],
510
- table: [
511
- ['location', 'https://www.example.com'],
512
- ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'],
513
- ['cache-control', 'private'],
514
- [':status', '302'],
515
- ],
516
- table_size: 222,
517
- },
518
- { wire: '4883 640e ffc1 c0bf',
519
- emitted: [
520
- [':status', '307'],
521
- ['cache-control', 'private'],
522
- ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'],
523
- ['location', 'https://www.example.com'],
524
- ],
525
- table: [
526
- [':status', '307'],
527
- ['location', 'https://www.example.com'],
528
- ['date', 'Mon, 21 Oct 2013 20:13:21 GMT'],
529
- ['cache-control', 'private'],
530
- ],
531
- table_size: 222,
532
- },
533
- { wire: "88c1 6196 d07a be94 1054 d444 a820 0595
534
- 040b 8166 e084 a62d 1bff c05a 839b d9ab
535
- 77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b
536
- 3960 d5af 2708 7f36 72c1 ab27 0fb5 291f
537
- 9587 3160 65c0 03ed 4ee5 b106 3d50 07",
538
- emitted: [
539
- [':status', '200'],
540
- ['cache-control', 'private'],
541
- ['date', 'Mon, 21 Oct 2013 20:13:22 GMT'],
542
- ['location', 'https://www.example.com'],
543
- ['content-encoding', 'gzip'],
544
- ['set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1'],
545
- ],
546
- table: [
547
- ['set-cookie', 'foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1'],
548
- ['content-encoding', 'gzip'],
549
- ['date', 'Mon, 21 Oct 2013 20:13:22 GMT'],
550
- ],
551
- table_size: 215,
552
- },
553
- ],
554
- },
555
- { title: 'D.6.a. Response Examples with Huffman - dynamic table size updates should not trigger exceptions',
556
- type: :response,
557
- table_size: 4096,
558
- huffman: :always,
559
- bypass_encoder: true,
560
- streams: [
561
- { wire: '2088 7689 aa63 55e5 80ae 16d7 17',
562
- emitted: [
563
- [':status', '200'],
564
- ['server', 'nginx/1.15.2'],
565
- ],
566
- table: [],
567
- table_size: 0,
568
- },
569
- ],
570
- },
571
- ]
572
-
573
- context 'decode' do
574
- spec_examples.each do |ex|
575
- context "spec example #{ex[:title]}" do
576
- ex[:streams].size.times do |nth|
577
- context "request #{nth + 1}" do
578
- before { @dc = Decompressor.new(table_size: ex[:table_size]) }
579
- before do
580
- (0...nth).each do |i|
581
- bytes = [ex[:streams][i][:wire].delete(" \n")].pack('H*')
582
- if ex[:streams][i][:has_bad_headers]
583
- expect { @dc.decode(HTTP2::Buffer.new(bytes)) }.to raise_error ProtocolError
584
- else
585
- @dc.decode(HTTP2::Buffer.new(bytes))
586
- end
587
- end
588
- end
589
- if ex[:streams][nth][:has_bad_headers]
590
- it 'should raise CompressionError' do
591
- bytes = [ex[:streams][nth][:wire].delete(" \n")].pack('H*')
592
- expect { @dc.decode(HTTP2::Buffer.new(bytes)) }.to raise_error ProtocolError
593
- end
594
- else
595
- subject do
596
- bytes = [ex[:streams][nth][:wire].delete(" \n")].pack('H*')
597
- @emitted = @dc.decode(HTTP2::Buffer.new(bytes))
598
- end
599
- it 'should emit expected headers' do
600
- subject
601
- # partitioned compare
602
- pseudo_headers, headers = ex[:streams][nth][:emitted].partition { |f, _| f.start_with? ':' }
603
- partitioned_headers = pseudo_headers + headers
604
- expect(@emitted).to eq partitioned_headers
605
- end
606
- it 'should update header table' do
607
- subject
608
- expect(@dc.instance_eval { @cc.table }).to eq ex[:streams][nth][:table]
609
- end
610
- it 'should compute header table size' do
611
- subject
612
- expect(@dc.instance_eval { @cc.current_table_size }).to eq ex[:streams][nth][:table_size]
613
- end
614
- end
615
- end
616
- end
617
- end
618
- end
619
- end
620
-
621
- context 'encode' do
622
- spec_examples.each do |ex|
623
- next if ex[:bypass_encoder]
624
- context "spec example #{ex[:title]}" do
625
- ex[:streams].size.times do |nth|
626
- context "request #{nth + 1}" do
627
- before do
628
- @cc = Compressor.new(table_size: ex[:table_size],
629
- huffman: ex[:huffman])
630
- end
631
- before do
632
- (0...nth).each do |i|
633
- if ex[:streams][i][:has_bad_headers]
634
- @cc.encode(ex[:streams][i][:emitted], ensure_proper_ordering: false)
635
- else
636
- @cc.encode(ex[:streams][i][:emitted])
637
- end
638
- end
639
- end
640
- subject do
641
- if ex[:streams][nth][:has_bad_headers]
642
- @cc.encode(ex[:streams][nth][:emitted], ensure_proper_ordering: false)
643
- else
644
- @cc.encode(ex[:streams][nth][:emitted])
645
- end
646
- end
647
- it 'should emit expected bytes on wire' do
648
- puts subject.unpack('H*').first
649
- expect(subject.unpack('H*').first).to eq ex[:streams][nth][:wire].delete(" \n")
650
- end
651
- unless ex[:streams][nth][:has_bad_headers]
652
- it 'should update header table' do
653
- subject
654
- expect(@cc.instance_eval { @cc.table }).to eq ex[:streams][nth][:table]
655
- end
656
- it 'should compute header table size' do
657
- subject
658
- expect(@cc.instance_eval { @cc.current_table_size }).to eq ex[:streams][nth][:table_size]
659
- end
660
- end
661
- end
662
- end
663
- end
664
- end
665
- end
666
- end