mieps_http-2 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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