mieps_http-2 0.8.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.
@@ -0,0 +1,487 @@
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
data/spec/helper.rb ADDED
@@ -0,0 +1,128 @@
1
+ require 'active_support/core_ext/object/deep_dup'
2
+
3
+ RSpec.configure(&:disable_monkey_patching!)
4
+
5
+ require 'json'
6
+ require 'coveralls'
7
+
8
+ Coveralls.wear! if ENV['CI']
9
+
10
+ require 'http/2'
11
+
12
+ include HTTP2
13
+ include HTTP2::Header
14
+ include HTTP2::Error
15
+
16
+ DATA = {
17
+ type: :data,
18
+ flags: [:end_stream].freeze,
19
+ stream: 1,
20
+ payload: 'text'.freeze,
21
+ }.freeze
22
+
23
+ HEADERS = {
24
+ type: :headers,
25
+ flags: [:end_headers].freeze,
26
+ stream: 1,
27
+ payload: Compressor.new.encode([%w(a b)]).freeze,
28
+ }.freeze
29
+
30
+ HEADERS_END_STREAM = {
31
+ type: :headers,
32
+ flags: [:end_headers, :end_stream].freeze,
33
+ stream: 1,
34
+ payload: Compressor.new.encode([%w(a b)]).freeze,
35
+ }.freeze
36
+
37
+ PRIORITY = {
38
+ type: :priority,
39
+ stream: 1,
40
+ exclusive: false,
41
+ stream_dependency: 0,
42
+ weight: 20,
43
+ }.freeze
44
+
45
+ RST_STREAM = {
46
+ type: :rst_stream,
47
+ stream: 1,
48
+ error: :stream_closed,
49
+ }.freeze
50
+
51
+ SETTINGS = {
52
+ type: :settings,
53
+ stream: 0,
54
+ payload: [
55
+ [:settings_max_concurrent_streams, 10].freeze,
56
+ [:settings_initial_window_size, 0x7fffffff].freeze,
57
+ ].freeze,
58
+ }.freeze
59
+
60
+ PUSH_PROMISE = {
61
+ type: :push_promise,
62
+ flags: [:end_headers].freeze,
63
+ stream: 1,
64
+ promise_stream: 2,
65
+ payload: Compressor.new.encode([%w(a b)]).freeze,
66
+ }.freeze
67
+
68
+ PING = {
69
+ stream: 0,
70
+ type: :ping,
71
+ payload: '12345678'.freeze,
72
+ }.freeze
73
+
74
+ PONG = {
75
+ stream: 0,
76
+ type: :ping,
77
+ flags: [:ack].freeze,
78
+ payload: '12345678'.freeze,
79
+ }.freeze
80
+
81
+ GOAWAY = {
82
+ type: :goaway,
83
+ last_stream: 2,
84
+ error: :no_error,
85
+ payload: 'debug'.freeze,
86
+ }.freeze
87
+
88
+ WINDOW_UPDATE = {
89
+ type: :window_update,
90
+ increment: 10,
91
+ }.freeze
92
+
93
+ CONTINUATION = {
94
+ type: :continuation,
95
+ flags: [:end_headers].freeze,
96
+ payload: '-second-block'.freeze,
97
+ }.freeze
98
+
99
+ ALTSVC = {
100
+ type: :altsvc,
101
+ max_age: 1_402_290_402, # 4
102
+ port: 8080, # 2 reserved 1
103
+ proto: 'h2-12'.freeze, # 1 + 5
104
+ host: 'www.example.com'.freeze, # 1 + 15
105
+ origin: 'www.example.com'.freeze, # 15
106
+ }.freeze
107
+
108
+ FRAME_TYPES = [
109
+ DATA,
110
+ HEADERS,
111
+ PRIORITY,
112
+ RST_STREAM,
113
+ SETTINGS,
114
+ PUSH_PROMISE,
115
+ PING,
116
+ GOAWAY,
117
+ WINDOW_UPDATE,
118
+ CONTINUATION,
119
+ ALTSVC,
120
+ ].freeze
121
+
122
+ def set_stream_id(bytes, id)
123
+ scheme = 'CnCCN'.freeze
124
+ head = bytes.slice!(0, 9).unpack(scheme)
125
+ head[4] = id
126
+
127
+ head.pack(scheme) + bytes
128
+ end
@@ -0,0 +1,79 @@
1
+ require 'helper'
2
+ require 'json'
3
+
4
+ RSpec.describe HTTP2::Header do
5
+ folders = %w(
6
+ go-hpack
7
+ haskell-http2-diff
8
+ haskell-http2-diff-huffman
9
+ haskell-http2-linear
10
+ haskell-http2-linear-huffman
11
+ haskell-http2-naive
12
+ haskell-http2-naive-huffman
13
+ haskell-http2-static
14
+ haskell-http2-static-huffman
15
+ #hyper-hpack
16
+ nghttp2
17
+ nghttp2-16384-4096
18
+ nghttp2-change-table-size
19
+ node-http2-hpack
20
+ )
21
+
22
+ context 'Decompressor' do
23
+ folders.each do |folder|
24
+ next if folder =~ /#/
25
+ path = File.expand_path("hpack-test-case/#{folder}", File.dirname(__FILE__))
26
+ next unless Dir.exist?(path)
27
+ context "#{folder}" do
28
+ Dir.foreach(path) do |file|
29
+ next if file !~ /\.json/
30
+ it "should decode #{file}" do
31
+ story = JSON.parse(File.read("#{path}/#{file}"))
32
+ cases = story['cases']
33
+ table_size = cases[0]['header_table_size'] || 4096
34
+ @dc = Decompressor.new(table_size: table_size)
35
+ cases.each do |c|
36
+ wire = [c['wire']].pack('H*').force_encoding(Encoding::BINARY)
37
+ @emitted = @dc.decode(HTTP2::Buffer.new(wire))
38
+ headers = c['headers'].flat_map(&:to_a)
39
+ expect(@emitted).to eq headers
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'Compressor' do
48
+ %w(
49
+ LINEAR
50
+ NAIVE
51
+ SHORTER
52
+ STATIC
53
+ ).each do |mode|
54
+ next if mode =~ /#/
55
+ ['', 'H'].each do |huffman|
56
+ [4096, 512].each do |table_size|
57
+ context "with #{mode}#{huffman} mode and table_size #{table_size}" do
58
+ path = File.expand_path('hpack-test-case/raw-data', File.dirname(__FILE__))
59
+ Dir.foreach(path) do |file|
60
+ next if file !~ /\.json/
61
+ it "should encode #{file}" do
62
+ story = JSON.parse(File.read("#{path}/#{file}"))
63
+ cases = story['cases']
64
+ @cc = Compressor .new(table_size: table_size)
65
+ @dc = Decompressor.new(table_size: table_size)
66
+ cases.each do |c|
67
+ headers = c['headers'].flat_map(&:to_a)
68
+ wire = @cc.encode(headers)
69
+ decoded = @dc.decode(HTTP2::Buffer.new(wire))
70
+ expect(decoded).to eq headers
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end