http-2 0.6.3 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,83 @@
1
+ require "helper"
2
+ require 'json'
3
+
4
+ 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
+ Dir.exists?(path) or next
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
+ context = story['context'] ? story['context'].to_sym : :request
35
+ @dc = Decompressor.new(table_size: table_size)
36
+ cases.each do |c|
37
+ wire = [c['wire']].pack("H*").force_encoding('binary')
38
+ @emitted = @dc.decode(HTTP2::Buffer.new(wire))
39
+ headers = c['headers'].flat_map(&:to_a)
40
+ @emitted.should eq headers
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ context "Compressor" do
49
+ %w[
50
+ LINEAR
51
+ NAIVE
52
+ SHORTER
53
+ STATIC
54
+ ].each do |mode|
55
+ next if mode =~ /#/
56
+ ['', 'H'].each do |huffman|
57
+ [4096, 512].each do |table_size|
58
+ context "with #{mode}#{huffman} mode and table_size #{table_size}" do
59
+ options = eval("HTTP2::Header::#{mode}#{huffman}")
60
+ path = File.expand_path("hpack-test-case/raw-data", File.dirname(__FILE__))
61
+ Dir.foreach(path) do |file|
62
+ next if file !~ /\.json/
63
+ it "should encode #{file}" do
64
+ story = JSON.parse(File.read("#{path}/#{file}"))
65
+ cases = story['cases']
66
+ context = story['context'] ? story['context'].to_sym : :request
67
+ @cc = Compressor .new(table_size: table_size)
68
+ @dc = Decompressor.new(table_size: table_size)
69
+ cases.each do |c|
70
+ headers = c['headers'].flat_map(&:to_a)
71
+ wire = @cc.encode(headers)
72
+ decoded = @dc.decode(HTTP2::Buffer.new(wire))
73
+ decoded.should eq headers
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,68 @@
1
+ require "helper"
2
+
3
+ describe HTTP2::Header::Huffman do
4
+ huffman_examples = [# plain, encoded
5
+ ["www.example.com", "f1e3c2e5f23a6ba0ab90f4ff"],
6
+ ["no-cache", "a8eb10649cbf"],
7
+ ["Mon, 21 Oct 2013 20:13:21 GMT", "d07abe941054d444a8200595040b8166e082a62d1bff"],
8
+ ]
9
+ context "encode" do
10
+ before(:all) { @encoder = HTTP2::Header::Huffman.new }
11
+ huffman_examples.each do |plain, encoded|
12
+ it "should encode #{plain} into #{encoded}" do
13
+ @encoder.encode(plain).unpack("H*").first.should eq encoded
14
+ end
15
+ end
16
+ end
17
+ context "decode" do
18
+ before(:all) { @encoder = HTTP2::Header::Huffman.new }
19
+ huffman_examples.each do |plain, encoded|
20
+ it "should decode #{encoded} into #{plain}" do
21
+ @encoder.decode(HTTP2::Buffer.new([encoded].pack("H*"))).should eq plain
22
+ end
23
+ end
24
+
25
+ [
26
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0",
27
+ "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
28
+ "http://www.craigslist.org/about/sites/",
29
+ "cl_b=AB2BKbsl4hGM7M4nH5PYWghTM5A; cl_def_lang=en; cl_def_hp=shoals",
30
+ "image/png,image/*;q=0.8,*/*;q=0.5",
31
+ "BX=c99r6jp89a7no&b=3&s=q4; localization=en-us%3Bus%3Bus",
32
+ "UTF-8でエンコードした日本語文字列",
33
+ ].each do |string|
34
+ it "should encode then decode '#{string}' into the same" do
35
+ s = string.dup.force_encoding('binary')
36
+ encoded = @encoder.encode(s)
37
+ @encoder.decode(HTTP2::Buffer.new(encoded)).should eq s
38
+ end
39
+ end
40
+
41
+ it "should encode/decode all_possible 2-byte sequences" do
42
+ (2**16).times do |n|
43
+ str = [n].pack("V")[0,2].force_encoding('binary')
44
+ @encoder.decode(HTTP2::Buffer.new(@encoder.encode(str))).should eq str
45
+ end
46
+ end
47
+
48
+ it "should raise when input is shorter than expected" do
49
+ plain, encoded = huffman_examples[0]
50
+ encoded = [encoded].pack("H*")
51
+ expect { @encoder.decode(HTTP2::Buffer.new(encoded[0...-1])) }.to raise_error(/EOS invalid/)
52
+ end
53
+ it "should raise when input is not padded by 1s" do
54
+ plain, encoded = ["www.example.com", "f1e3c2e5f23a6ba0ab90f4fe"] # note the fe at end
55
+ encoded = [encoded].pack("H*")
56
+ expect { @encoder.decode(HTTP2::Buffer.new(encoded)) }.to raise_error(/EOS invalid/)
57
+ end
58
+ it "should raise when exceedingly padded" do
59
+ plain, encoded = ["www.example.com", "e7cf9bebe89b6fb16fa9b6ffff"] # note the extra ff
60
+ encoded = [encoded].pack("H*")
61
+ expect { @encoder.decode(HTTP2::Buffer.new(encoded)) }.to raise_error(/EOS invalid/)
62
+ end
63
+ it "should raise when EOS is explicitly encoded" do
64
+ encoded = ["1c7fffffffff"].pack("H*") # a b EOS
65
+ expect { @encoder.decode(HTTP2::Buffer.new(encoded)) }.to raise_error(/EOS found/)
66
+ end
67
+ end
68
+ end
@@ -15,7 +15,7 @@ describe HTTP2::Server do
15
15
  it "should emit SETTINGS on new connection" do
16
16
  frames = []
17
17
  @srv.on(:frame) { |recv| frames << recv }
18
- @srv << CONNECTION_HEADER
18
+ @srv << CONNECTION_PREFACE_MAGIC
19
19
 
20
20
  f.parse(frames[0])[:type].should eq :settings
21
21
  end
@@ -23,14 +23,15 @@ describe HTTP2::Server do
23
23
  it "should initialize client with custom connection settings" do
24
24
  frames = []
25
25
 
26
- @srv = Server.new(streams: 200, window: 2**10)
26
+ @srv = Server.new(settings_max_concurrent_streams: 200,
27
+ settings_initial_window_size: 2**10)
27
28
  @srv.on(:frame) { |recv| frames << recv }
28
- @srv << CONNECTION_HEADER
29
+ @srv << CONNECTION_PREFACE_MAGIC
29
30
 
30
31
  frame = f.parse(frames[0])
31
32
  frame[:type].should eq :settings
32
- frame[:payload][:settings_max_concurrent_streams].should eq 200
33
- frame[:payload][:settings_initial_window_size].should eq 2**10
33
+ frame[:payload].should include([:settings_max_concurrent_streams, 200])
34
+ frame[:payload].should include([:settings_initial_window_size, 2**10])
34
35
  end
35
36
  end
36
37
 
@@ -40,7 +41,7 @@ describe HTTP2::Server do
40
41
 
41
42
  @srv.on(:stream) do |stream|
42
43
  expect {
43
- stream.promise({}) {}
44
+ stream.promise(':method' => 'GET') {}
44
45
  }.to_not raise_error
45
46
  end
46
47
 
@@ -12,8 +12,8 @@ describe HTTP2::Stream do
12
12
  end
13
13
 
14
14
  it "should set custom stream priority" do
15
- stream = @client.new_stream(priority: 3)
16
- stream.priority.should eq 3
15
+ stream = @client.new_stream(weight: 3, dependency: 2, exclusive: true)
16
+ stream.weight.should eq 3
17
17
  end
18
18
 
19
19
  context "reserved (local)" do
@@ -55,8 +55,8 @@ describe HTTP2::Stream do
55
55
  end
56
56
 
57
57
  it "should reprioritize stream on PRIORITY" do
58
- @stream.receive PRIORITY.merge({priority: 30})
59
- @stream.priority.should eq 30
58
+ expect { @stream.receive PRIORITY }.to_not raise_error
59
+ @stream.weight.should eq 20
60
60
  end
61
61
  end
62
62
 
@@ -95,8 +95,8 @@ describe HTTP2::Stream do
95
95
  end
96
96
 
97
97
  it "should reprioritize stream on PRIORITY" do
98
- @stream.send PRIORITY
99
- @stream.priority.should eq 15
98
+ expect { @stream.send PRIORITY }.to_not raise_error
99
+ @stream.weight.should eq 20
100
100
  end
101
101
  end
102
102
 
@@ -116,7 +116,7 @@ describe HTTP2::Stream do
116
116
  end
117
117
 
118
118
  it "should transition to half closed (local) if sending END_STREAM" do
119
- [DATA, HEADERS, CONTINUATION].each do |frame|
119
+ [DATA, HEADERS].each do |frame|
120
120
  s, f = @stream.dup, frame.dup
121
121
  f[:flags] = [:end_stream]
122
122
 
@@ -126,7 +126,7 @@ describe HTTP2::Stream do
126
126
  end
127
127
 
128
128
  it "should transition to half closed (remote) if receiving END_STREAM" do
129
- [DATA, HEADERS, CONTINUATION].each do |frame|
129
+ [DATA, HEADERS].each do |frame|
130
130
  s, f = @stream.dup, frame.dup
131
131
  f[:flags] = [:end_stream]
132
132
 
@@ -173,8 +173,8 @@ describe HTTP2::Stream do
173
173
  sp.receive HEADERS
174
174
  sr.send HEADERS
175
175
 
176
- openp.should be_true
177
- openr.should be_true
176
+ openp.should be_truthy
177
+ openr.should be_truthy
178
178
  end
179
179
 
180
180
  it "should not emit :active on transition from open" do
@@ -202,8 +202,8 @@ describe HTTP2::Stream do
202
202
  sp.receive RST_STREAM
203
203
  sr.close
204
204
 
205
- closep.should be_true
206
- closer.should be_true
205
+ closep.should be_truthy
206
+ closer.should be_truthy
207
207
  end
208
208
 
209
209
  it "should emit :close after frame is processed" do
@@ -236,7 +236,7 @@ describe HTTP2::Stream do
236
236
  before(:each) { @stream.send HEADERS_END_STREAM }
237
237
 
238
238
  it "should raise error on attempt to send frames" do
239
- (FRAME_TYPES - [RST_STREAM]).each do |frame|
239
+ (FRAME_TYPES - [PRIORITY, RST_STREAM]).each do |frame|
240
240
  expect { @stream.dup.send frame }.to raise_error StreamError
241
241
  end
242
242
  end
@@ -267,6 +267,11 @@ describe HTTP2::Stream do
267
267
  @stream.state.should eq :half_closed_local
268
268
  end
269
269
 
270
+ it "should reprioritize stream on PRIORITY" do
271
+ expect { @stream.send PRIORITY }.to_not raise_error
272
+ @stream.weight.should eq 20
273
+ end
274
+
270
275
  it "should emit :half_close event on transition" do
271
276
  order = []
272
277
  stream = @client.new_stream
@@ -286,7 +291,7 @@ describe HTTP2::Stream do
286
291
  @stream.receive RST_STREAM
287
292
 
288
293
  @stream.state.should eq :closed
289
- closed.should be_true
294
+ closed.should be_truthy
290
295
  end
291
296
  end
292
297
 
@@ -294,7 +299,7 @@ describe HTTP2::Stream do
294
299
  before(:each) { @stream.receive HEADERS_END_STREAM }
295
300
 
296
301
  it "should raise STREAM_CLOSED error on reciept of frames" do
297
- (FRAME_TYPES - [RST_STREAM, WINDOW_UPDATE]).each do |frame|
302
+ (FRAME_TYPES - [PRIORITY, RST_STREAM, WINDOW_UPDATE]).each do |frame|
298
303
  expect {
299
304
  @stream.dup.receive frame
300
305
  }.to raise_error(StreamClosed)
@@ -302,7 +307,7 @@ describe HTTP2::Stream do
302
307
  end
303
308
 
304
309
  it "should transition to closed if END_STREAM flag is sent" do
305
- [DATA, HEADERS, CONTINUATION].each do |frame|
310
+ [DATA, HEADERS].each do |frame|
306
311
  s, f = @stream.dup, frame.dup
307
312
  f[:flags] = [:end_stream]
308
313
 
@@ -327,6 +332,11 @@ describe HTTP2::Stream do
327
332
  @stream.state.should eq :half_closed_remote
328
333
  end
329
334
 
335
+ it "should reprioritize stream on PRIORITY" do
336
+ expect { @stream.receive PRIORITY }.to_not raise_error
337
+ @stream.weight.should eq 20
338
+ end
339
+
330
340
  it "should emit :half_close event on transition" do
331
341
  order = []
332
342
  stream = @client.new_stream
@@ -346,7 +356,7 @@ describe HTTP2::Stream do
346
356
  @stream.close
347
357
 
348
358
  @stream.state.should eq :closed
349
- closed.should be_true
359
+ closed.should be_truthy
350
360
  end
351
361
  end
352
362
 
@@ -358,7 +368,7 @@ describe HTTP2::Stream do
358
368
  end
359
369
 
360
370
  it "should raise STREAM_CLOSED on attempt to send frames" do
361
- (FRAME_TYPES - [RST_STREAM]).each do |frame|
371
+ (FRAME_TYPES - [PRIORITY, RST_STREAM]).each do |frame|
362
372
  expect {
363
373
  @stream.dup.send frame
364
374
  }.to raise_error(StreamClosed)
@@ -366,20 +376,28 @@ describe HTTP2::Stream do
366
376
  end
367
377
 
368
378
  it "should raise STREAM_CLOSED on receipt of frame" do
369
- (FRAME_TYPES - [RST_STREAM]).each do |frame|
379
+ (FRAME_TYPES - [PRIORITY, RST_STREAM, WINDOW_UPDATE]).each do |frame|
370
380
  expect {
371
381
  @stream.dup.receive frame
372
382
  }.to raise_error(StreamClosed)
373
383
  end
374
384
  end
375
385
 
376
- it "should allow RST_STREAM to be sent" do
386
+ it "should allow PRIORITY, RST_STREAM to be sent" do
387
+ expect { @stream.send PRIORITY }.to_not raise_error
377
388
  expect { @stream.send RST_STREAM }.to_not raise_error
378
389
  end
379
390
 
380
- it "should not send RST_STREAM on receipt of RST_STREAM" do
391
+ it "should allow PRIORITY, RST_STREAM to be received" do
392
+ expect { @stream.receive PRIORITY }.to_not raise_error
381
393
  expect { @stream.receive RST_STREAM }.to_not raise_error
382
394
  end
395
+
396
+ it "should reprioritize stream on PRIORITY" do
397
+ expect { @stream.receive PRIORITY }.to_not raise_error
398
+ @stream.weight.should eq 20
399
+ end
400
+
383
401
  end
384
402
 
385
403
  context "local closed via RST_STREAM frame" do
@@ -407,40 +425,45 @@ describe HTTP2::Stream do
407
425
  # We're auto RST'ing PUSH streams in connection class, hence
408
426
  # skipping this transition for now.
409
427
  #end
428
+
410
429
  end
411
430
 
412
- context "local closed via END_STREAM flag" do
413
- before(:each) do
414
- @stream.send HEADERS # open
415
- @stream.send DATA # contains end_stream flag
416
- end
431
+ # FIXME: Isn't this test same as "half closed (local)"?
432
+ # context "local closed via END_STREAM flag" do
433
+ # before(:each) do
434
+ # @stream.send HEADERS # open
435
+ # @stream.send DATA # contains end_stream flag
436
+ # end
437
+
438
+ # it "should ignore received frames" do
439
+ # FRAME_TYPES.each do |frame|
440
+ # expect { @stream.dup.receive frame }.to_not raise_error
441
+ # end
442
+ # end
443
+ # end
417
444
 
418
- it "should ignore received frames" do
419
- FRAME_TYPES.each do |frame|
420
- expect { @stream.dup.receive frame }.to_not raise_error
421
- end
422
- end
423
- end
424
445
  end
425
446
  end # end stream states
426
447
 
448
+ # TODO: add test cases to ensure on(:priority) emitted after close
449
+
427
450
  context "flow control" do
428
451
  it "should initialize to default flow control window" do
429
- @stream.window.should eq DEFAULT_FLOW_WINDOW
452
+ @stream.remote_window.should eq DEFAULT_FLOW_WINDOW
430
453
  end
431
454
 
432
455
  it "should update window size on DATA frames only" do
433
456
  @stream.send HEADERS # go to open
434
- @stream.window.should eq DEFAULT_FLOW_WINDOW
457
+ @stream.remote_window.should eq DEFAULT_FLOW_WINDOW
435
458
 
436
459
  (FRAME_TYPES - [DATA,PING,GOAWAY,SETTINGS]).each do |frame|
437
460
  s = @stream.dup
438
461
  s.send frame
439
- s.window.should eq DEFAULT_FLOW_WINDOW
462
+ s.remote_window.should eq DEFAULT_FLOW_WINDOW
440
463
  end
441
464
 
442
465
  @stream.send DATA
443
- @stream.window.should eq DEFAULT_FLOW_WINDOW - DATA[:payload].bytesize
466
+ @stream.remote_window.should eq DEFAULT_FLOW_WINDOW - DATA[:payload].bytesize
444
467
  end
445
468
 
446
469
  it "should update window size on receipt of WINDOW_UPDATE" do
@@ -448,14 +471,14 @@ describe HTTP2::Stream do
448
471
  @stream.send DATA
449
472
  @stream.receive WINDOW_UPDATE
450
473
 
451
- @stream.window.should eq (
474
+ @stream.remote_window.should eq (
452
475
  DEFAULT_FLOW_WINDOW - DATA[:payload].bytesize + WINDOW_UPDATE[:increment]
453
476
  )
454
477
  end
455
478
 
456
479
  it "should observe session flow control" do
457
480
  settings, data = SETTINGS.dup, DATA.dup
458
- settings[:payload] = { settings_initial_window_size: 1000 }
481
+ settings[:payload] = [[:settings_initial_window_size, 1000]]
459
482
  settings[:stream] = 0
460
483
 
461
484
  framer = Framer.new
@@ -464,17 +487,17 @@ describe HTTP2::Stream do
464
487
  s1 = @client.new_stream
465
488
  s1.send HEADERS
466
489
  s1.send data.merge({payload: "x" * 900, flags: []})
467
- s1.window.should eq 100
490
+ s1.remote_window.should eq 100
468
491
 
469
492
  s1.send data.merge({payload: "x" * 200})
470
- s1.window.should eq 0
493
+ s1.remote_window.should eq 0
471
494
  s1.buffered_amount.should eq 100
472
495
 
473
496
  @client << framer.generate(WINDOW_UPDATE.merge({
474
497
  stream: s1.id, increment: 1000
475
498
  }))
476
499
  s1.buffered_amount.should eq 0
477
- s1.window.should eq 900
500
+ s1.remote_window.should eq 900
478
501
  end
479
502
  end
480
503
 
@@ -482,17 +505,17 @@ describe HTTP2::Stream do
482
505
  it ".reprioritize should emit PRIORITY frame" do
483
506
  @stream.should_receive(:send) do |frame|
484
507
  frame[:type].should eq :priority
485
- frame[:priority].should eq 30
508
+ frame[:weight].should eq 30
486
509
  end
487
510
 
488
- @stream.reprioritize 30
511
+ @stream.reprioritize weight: 30
489
512
  end
490
513
 
491
514
  it ".reprioritize should raise error if invoked by server" do
492
515
  srv = Server.new
493
516
  stream = srv.new_stream
494
517
 
495
- expect { stream.reprioritize(10) }.to raise_error(StreamError)
518
+ expect { stream.reprioritize(weight: 10) }.to raise_error(StreamError)
496
519
  end
497
520
 
498
521
  it ".headers should emit HEADERS frames" do
@@ -528,7 +551,7 @@ describe HTTP2::Stream do
528
551
  end
529
552
 
530
553
  it ".data should split large DATA frames" do
531
- data = "x" * HTTP2::MAX_FRAME_SIZE * 2
554
+ data = "x" * 16384 * 2
532
555
 
533
556
  @stream.stub(:send)
534
557
  @stream.should_receive(:send).exactly(3).times
@@ -564,7 +587,7 @@ describe HTTP2::Stream do
564
587
  end
565
588
 
566
589
  it "should emit received headers via on(:headers)" do
567
- headers, recv = {"header" => "value"}, nil
590
+ headers, recv = [["header", "value"]], nil
568
591
  @srv.on(:stream) do |stream|
569
592
  stream.on(:headers) {|h| recv = h}
570
593
  end
@@ -585,16 +608,21 @@ describe HTTP2::Stream do
585
608
  @client_stream.data(payload)
586
609
  end
587
610
 
588
- it "should emit received priority via on(:priority)" do
589
- new_priority, recv = 15, 0
611
+ it "should emit received priority parameters via on(:priority)" do
612
+ new_weight, new_dependency = 15, @client_stream.id + 2
613
+ callback_called = false
590
614
  @srv.on(:stream) do |stream|
591
615
  stream.on(:priority) do |pri|
592
- pri.should eq new_priority
616
+ callback_called = true
617
+ pri.is_a?(Hash).should be
618
+ pri[:weight].should eq new_weight
619
+ pri[:dependency].should eq new_dependency
593
620
  end
594
621
  end
595
622
 
596
623
  @client_stream.headers({"key" => "value"})
597
- @client_stream.reprioritize(new_priority)
624
+ @client_stream.reprioritize(weight: new_weight, dependency: new_dependency)
625
+ callback_called.should be
598
626
  end
599
627
 
600
628
  context "push" do
@@ -604,7 +632,6 @@ describe HTTP2::Stream do
604
632
  @server_stream = stream
605
633
  end
606
634
 
607
- # @srv << @frm.generate(SETTINGS)
608
635
  @client_stream.headers({"key" => "value"})
609
636
  end
610
637
 
@@ -643,7 +670,7 @@ describe HTTP2::Stream do
643
670
  end
644
671
 
645
672
  it "client: headers > active > headers > .. > data > close" do
646
- order, headers = [], {}
673
+ order, headers = [], []
647
674
  @client.on(:promise) do |push|
648
675
  order << :reserved
649
676
 
@@ -654,7 +681,7 @@ describe HTTP2::Stream do
654
681
 
655
682
  push.on(:headers) do |h|
656
683
  order << :headers
657
- headers.merge!(h)
684
+ headers += h
658
685
  end
659
686
 
660
687
  push.id.should be_even
@@ -665,7 +692,7 @@ describe HTTP2::Stream do
665
692
  push.data("somedata")
666
693
  end
667
694
 
668
- headers.should eq({"key" => "val", "key2" => "val2"})
695
+ headers.should eq([["key", "val"], ["key2", "val2"]])
669
696
  order.should eq [:reserved, :headers, :active, :headers,
670
697
  :half_close, :data, :close]
671
698
  end