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.
- checksums.yaml +4 -4
 - data/README.md +0 -2
 - data/lib/http/2/buffer.rb +6 -4
 - data/lib/http/2/client.rb +5 -1
 - data/lib/http/2/compressor.rb +42 -34
 - data/lib/http/2/connection.rb +72 -86
 - data/lib/http/2/emitter.rb +4 -1
 - data/lib/http/2/error.rb +2 -0
 - data/lib/http/2/flow_buffer.rb +8 -3
 - data/lib/http/2/framer.rb +83 -94
 - data/lib/http/2/huffman.rb +19 -17
 - data/lib/http/2/server.rb +9 -7
 - data/lib/http/2/stream.rb +48 -48
 - data/lib/http/2/version.rb +3 -1
 - data/lib/http/2.rb +2 -0
 - metadata +7 -60
 - data/.autotest +0 -20
 - data/.coveralls.yml +0 -1
 - data/.gitignore +0 -20
 - data/.gitmodules +0 -3
 - data/.rspec +0 -5
 - data/.rubocop.yml +0 -93
 - data/.rubocop_todo.yml +0 -131
 - data/.travis.yml +0 -17
 - data/Gemfile +0 -16
 - data/Guardfile +0 -18
 - data/Guardfile.h2spec +0 -12
 - data/Rakefile +0 -49
 - data/example/Gemfile +0 -3
 - data/example/README.md +0 -44
 - data/example/client.rb +0 -122
 - data/example/helper.rb +0 -19
 - data/example/keys/server.crt +0 -20
 - data/example/keys/server.key +0 -27
 - data/example/server.rb +0 -139
 - data/example/upgrade_client.rb +0 -153
 - data/example/upgrade_server.rb +0 -203
 - data/http-2.gemspec +0 -22
 - data/lib/tasks/generate_huffman_table.rb +0 -166
 - data/spec/buffer_spec.rb +0 -28
 - data/spec/client_spec.rb +0 -188
 - data/spec/compressor_spec.rb +0 -666
 - data/spec/connection_spec.rb +0 -681
 - data/spec/emitter_spec.rb +0 -54
 - data/spec/framer_spec.rb +0 -487
 - data/spec/h2spec/h2spec.darwin +0 -0
 - data/spec/h2spec/output/non_secure.txt +0 -317
 - data/spec/helper.rb +0 -147
 - data/spec/hpack_test_spec.rb +0 -84
 - data/spec/huffman_spec.rb +0 -68
 - data/spec/server_spec.rb +0 -52
 - data/spec/stream_spec.rb +0 -878
 - data/spec/support/deep_dup.rb +0 -55
 - data/spec/support/duplicable.rb +0 -98
 
    
        data/spec/connection_spec.rb
    DELETED
    
    | 
         @@ -1,681 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'helper'
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            RSpec.describe HTTP2::Connection do
         
     | 
| 
       4 
     | 
    
         
            -
              include FrameHelpers
         
     | 
| 
       5 
     | 
    
         
            -
              before(:each) do
         
     | 
| 
       6 
     | 
    
         
            -
                @conn = Client.new
         
     | 
| 
       7 
     | 
    
         
            -
              end
         
     | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
              let(:f) { Framer.new }
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
              context 'initialization and settings' do
         
     | 
| 
       12 
     | 
    
         
            -
                it 'should raise error if first frame is not settings' do
         
     | 
| 
       13 
     | 
    
         
            -
                  (frame_types - [settings_frame]).each do |frame|
         
     | 
| 
       14 
     | 
    
         
            -
                    expect { @conn << frame }.to raise_error(ProtocolError)
         
     | 
| 
       15 
     | 
    
         
            -
                    expect(@conn).to be_closed
         
     | 
| 
       16 
     | 
    
         
            -
                  end
         
     | 
| 
       17 
     | 
    
         
            -
                end
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                it 'should not raise error if first frame is SETTINGS' do
         
     | 
| 
       20 
     | 
    
         
            -
                  expect { @conn << f.generate(settings_frame) }.to_not raise_error
         
     | 
| 
       21 
     | 
    
         
            -
                  expect(@conn.state).to eq :connected
         
     | 
| 
       22 
     | 
    
         
            -
                  expect(@conn).to_not be_closed
         
     | 
| 
       23 
     | 
    
         
            -
                end
         
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
                it 'should raise error if SETTINGS stream != 0' do
         
     | 
| 
       26 
     | 
    
         
            -
                  frame = set_stream_id(f.generate(settings_frame), 0x1)
         
     | 
| 
       27 
     | 
    
         
            -
                  expect { @conn << frame }.to raise_error(ProtocolError)
         
     | 
| 
       28 
     | 
    
         
            -
                end
         
     | 
| 
       29 
     | 
    
         
            -
              end
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
              context 'settings synchronization' do
         
     | 
| 
       32 
     | 
    
         
            -
                it 'should reflect outgoing settings when ack is received' do
         
     | 
| 
       33 
     | 
    
         
            -
                  expect(@conn.local_settings[:settings_header_table_size]).to eq 4096
         
     | 
| 
       34 
     | 
    
         
            -
                  @conn.settings(settings_header_table_size: 256)
         
     | 
| 
       35 
     | 
    
         
            -
                  expect(@conn.local_settings[:settings_header_table_size]).to eq 4096
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                  ack = { type: :settings, stream: 0, payload: [], flags: [:ack] }
         
     | 
| 
       38 
     | 
    
         
            -
                  @conn << f.generate(ack)
         
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                  expect(@conn.local_settings[:settings_header_table_size]).to eq 256
         
     | 
| 
       41 
     | 
    
         
            -
                end
         
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
                it 'should reflect incoming settings when SETTINGS is received' do
         
     | 
| 
       44 
     | 
    
         
            -
                  expect(@conn.remote_settings[:settings_header_table_size]).to eq 4096
         
     | 
| 
       45 
     | 
    
         
            -
                  settings = settings_frame
         
     | 
| 
       46 
     | 
    
         
            -
                  settings[:payload] = [[:settings_header_table_size, 256]]
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                  @conn << f.generate(settings)
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                  expect(@conn.remote_settings[:settings_header_table_size]).to eq 256
         
     | 
| 
       51 
     | 
    
         
            -
                end
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
                it 'should reflect settings_max_frame_size recevied from peer' do
         
     | 
| 
       54 
     | 
    
         
            -
                  settings = settings_frame
         
     | 
| 
       55 
     | 
    
         
            -
                  settings[:payload] = [[:settings_max_frame_size, 16_385]]
         
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
                  @conn << f.generate(settings)
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
                  frame = {
         
     | 
| 
       60 
     | 
    
         
            -
                    length: 16_385,
         
     | 
| 
       61 
     | 
    
         
            -
                    type: :data,
         
     | 
| 
       62 
     | 
    
         
            -
                    flags: [:end_stream],
         
     | 
| 
       63 
     | 
    
         
            -
                    stream: 1,
         
     | 
| 
       64 
     | 
    
         
            -
                    payload: 'a' * 16_385,
         
     | 
| 
       65 
     | 
    
         
            -
                  }
         
     | 
| 
       66 
     | 
    
         
            -
                  expect { @conn.send(frame) }.not_to raise_error(CompressionError)
         
     | 
| 
       67 
     | 
    
         
            -
                end
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
                it 'should send SETTINGS ACK when SETTINGS is received' do
         
     | 
| 
       70 
     | 
    
         
            -
                  settings = settings_frame
         
     | 
| 
       71 
     | 
    
         
            -
                  settings[:payload] = [[:settings_header_table_size, 256]]
         
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
                  # We should expect two frames here (append .twice) - one for the connection setup, and one for the settings ack.
         
     | 
| 
       74 
     | 
    
         
            -
                  frames = []
         
     | 
| 
       75 
     | 
    
         
            -
                  expect(@conn).to receive(:send).twice do |frame|
         
     | 
| 
       76 
     | 
    
         
            -
                    frames << frame
         
     | 
| 
       77 
     | 
    
         
            -
                  end
         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                  @conn.send_connection_preface
         
     | 
| 
       80 
     | 
    
         
            -
                  @conn << f.generate(settings)
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
                  frame = frames.last
         
     | 
| 
       83 
     | 
    
         
            -
                  expect(frame[:type]).to eq :settings
         
     | 
| 
       84 
     | 
    
         
            -
                  expect(frame[:flags]).to eq [:ack]
         
     | 
| 
       85 
     | 
    
         
            -
                  expect(frame[:payload]).to eq []
         
     | 
| 
       86 
     | 
    
         
            -
                end
         
     | 
| 
       87 
     | 
    
         
            -
              end
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
              context 'stream management' do
         
     | 
| 
       90 
     | 
    
         
            -
                it 'should initialize to default stream limit (100)' do
         
     | 
| 
       91 
     | 
    
         
            -
                  expect(@conn.local_settings[:settings_max_concurrent_streams]).to eq 100
         
     | 
| 
       92 
     | 
    
         
            -
                end
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
                it 'should change stream limit to received SETTINGS value' do
         
     | 
| 
       95 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       96 
     | 
    
         
            -
                  expect(@conn.remote_settings[:settings_max_concurrent_streams]).to eq 10
         
     | 
| 
       97 
     | 
    
         
            -
                end
         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
                it 'should count open streams against stream limit' do
         
     | 
| 
       100 
     | 
    
         
            -
                  s = @conn.new_stream
         
     | 
| 
       101 
     | 
    
         
            -
                  expect(@conn.active_stream_count).to eq 0
         
     | 
| 
       102 
     | 
    
         
            -
                  s.receive headers_frame
         
     | 
| 
       103 
     | 
    
         
            -
                  expect(@conn.active_stream_count).to eq 1
         
     | 
| 
       104 
     | 
    
         
            -
                end
         
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
                it 'should not count reserved streams against stream limit' do
         
     | 
| 
       107 
     | 
    
         
            -
                  s1 = @conn.new_stream
         
     | 
| 
       108 
     | 
    
         
            -
                  s1.receive push_promise_frame
         
     | 
| 
       109 
     | 
    
         
            -
                  expect(@conn.active_stream_count).to eq 0
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                  s2 = @conn.new_stream
         
     | 
| 
       112 
     | 
    
         
            -
                  s2.send push_promise_frame
         
     | 
| 
       113 
     | 
    
         
            -
                  expect(@conn.active_stream_count).to eq 0
         
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
                  # transition to half closed
         
     | 
| 
       116 
     | 
    
         
            -
                  s1.receive headers_frame
         
     | 
| 
       117 
     | 
    
         
            -
                  s2.send headers_frame
         
     | 
| 
       118 
     | 
    
         
            -
                  expect(@conn.active_stream_count).to eq 2
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
                  # transition to closed
         
     | 
| 
       121 
     | 
    
         
            -
                  s1.receive data_frame
         
     | 
| 
       122 
     | 
    
         
            -
                  s2.send data_frame
         
     | 
| 
       123 
     | 
    
         
            -
                  expect(@conn.active_stream_count).to eq 0
         
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
                  expect(s1).to be_closed
         
     | 
| 
       126 
     | 
    
         
            -
                  expect(s2).to be_closed
         
     | 
| 
       127 
     | 
    
         
            -
                end
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
                it 'should not exceed stream limit set by peer' do
         
     | 
| 
       130 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
       132 
     | 
    
         
            -
                  expect do
         
     | 
| 
       133 
     | 
    
         
            -
                    10.times do
         
     | 
| 
       134 
     | 
    
         
            -
                      s = @conn.new_stream
         
     | 
| 
       135 
     | 
    
         
            -
                      s.send headers_frame
         
     | 
| 
       136 
     | 
    
         
            -
                    end
         
     | 
| 
       137 
     | 
    
         
            -
                  end.to_not raise_error
         
     | 
| 
       138 
     | 
    
         
            -
             
     | 
| 
       139 
     | 
    
         
            -
                  expect { @conn.new_stream }.to raise_error(StreamLimitExceeded)
         
     | 
| 
       140 
     | 
    
         
            -
                end
         
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
     | 
    
         
            -
                it 'should initialize stream with HEADERS priority value' do
         
     | 
| 
       143 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
                  stream, headers = nil, headers_frame
         
     | 
| 
       146 
     | 
    
         
            -
                  headers[:weight] = 20
         
     | 
| 
       147 
     | 
    
         
            -
                  headers[:stream_dependency] = 0
         
     | 
| 
       148 
     | 
    
         
            -
                  headers[:exclusive] = false
         
     | 
| 
       149 
     | 
    
         
            -
             
     | 
| 
       150 
     | 
    
         
            -
                  @conn.on(:stream) { |s| stream = s }
         
     | 
| 
       151 
     | 
    
         
            -
                  @conn << f.generate(headers)
         
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
                  expect(stream.weight).to eq 20
         
     | 
| 
       154 
     | 
    
         
            -
                end
         
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
                it 'should initialize idle stream on PRIORITY frame' do
         
     | 
| 
       157 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       158 
     | 
    
         
            -
             
     | 
| 
       159 
     | 
    
         
            -
                  stream = nil
         
     | 
| 
       160 
     | 
    
         
            -
                  @conn.on(:stream) { |s| stream = s }
         
     | 
| 
       161 
     | 
    
         
            -
                  @conn << f.generate(priority_frame)
         
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
                  expect(stream.state).to eq :idle
         
     | 
| 
       164 
     | 
    
         
            -
                end
         
     | 
| 
       165 
     | 
    
         
            -
              end
         
     | 
| 
       166 
     | 
    
         
            -
             
     | 
| 
       167 
     | 
    
         
            -
              context 'cleanup_recently_closed' do
         
     | 
| 
       168 
     | 
    
         
            -
                it 'should cleanup old connections' do
         
     | 
| 
       169 
     | 
    
         
            -
                  now_ts = Time.now.to_i
         
     | 
| 
       170 
     | 
    
         
            -
                  stream_ids = Array.new(4) { @conn.new_stream.id }
         
     | 
| 
       171 
     | 
    
         
            -
                  expect(@conn.instance_variable_get('@streams').size).to eq(4)
         
     | 
| 
       172 
     | 
    
         
            -
             
     | 
| 
       173 
     | 
    
         
            -
                  # Assume that the first 3 streams were closed in different time
         
     | 
| 
       174 
     | 
    
         
            -
                  recently_closed = stream_ids[0, 3].zip([now_ts - 100, now_ts - 50, now_ts - 5]).to_h
         
     | 
| 
       175 
     | 
    
         
            -
                  @conn.instance_variable_set('@streams_recently_closed', recently_closed)
         
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
                  # Cleanup should delete streams that were closed earlier than 15s ago
         
     | 
| 
       178 
     | 
    
         
            -
                  @conn.__send__(:cleanup_recently_closed)
         
     | 
| 
       179 
     | 
    
         
            -
                  expect(@conn.instance_variable_get('@streams').size).to eq(2)
         
     | 
| 
       180 
     | 
    
         
            -
                  expect(@conn.instance_variable_get('@streams_recently_closed')).to eq(stream_ids[2] => now_ts - 5)
         
     | 
| 
       181 
     | 
    
         
            -
                end
         
     | 
| 
       182 
     | 
    
         
            -
              end
         
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
       184 
     | 
    
         
            -
              context 'Headers pre/post processing' do
         
     | 
| 
       185 
     | 
    
         
            -
                it 'should not concatenate multiple occurences of a header field with the same name' do
         
     | 
| 
       186 
     | 
    
         
            -
                  input = [
         
     | 
| 
       187 
     | 
    
         
            -
                    ['Content-Type', 'text/html'],
         
     | 
| 
       188 
     | 
    
         
            -
                    ['Cache-Control', 'max-age=60, private'],
         
     | 
| 
       189 
     | 
    
         
            -
                    ['Cache-Control', 'must-revalidate'],
         
     | 
| 
       190 
     | 
    
         
            -
                  ]
         
     | 
| 
       191 
     | 
    
         
            -
                  expected = [
         
     | 
| 
       192 
     | 
    
         
            -
                    ['content-type', 'text/html'],
         
     | 
| 
       193 
     | 
    
         
            -
                    ['cache-control', 'max-age=60, private'],
         
     | 
| 
       194 
     | 
    
         
            -
                    ['cache-control', 'must-revalidate'],
         
     | 
| 
       195 
     | 
    
         
            -
                  ]
         
     | 
| 
       196 
     | 
    
         
            -
                  headers = []
         
     | 
| 
       197 
     | 
    
         
            -
                  @conn.on(:frame) do |bytes|
         
     | 
| 
       198 
     | 
    
         
            -
                    headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
         
     | 
| 
       199 
     | 
    
         
            -
                  end
         
     | 
| 
       200 
     | 
    
         
            -
             
     | 
| 
       201 
     | 
    
         
            -
                  stream = @conn.new_stream
         
     | 
| 
       202 
     | 
    
         
            -
                  stream.headers(input)
         
     | 
| 
       203 
     | 
    
         
            -
             
     | 
| 
       204 
     | 
    
         
            -
                  expect(headers.size).to eq 1
         
     | 
| 
       205 
     | 
    
         
            -
                  emitted = Decompressor.new.decode(headers.first[:payload])
         
     | 
| 
       206 
     | 
    
         
            -
                  expect(emitted).to match_array(expected)
         
     | 
| 
       207 
     | 
    
         
            -
                end
         
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
                it 'should not split zero-concatenated header field values' do
         
     | 
| 
       210 
     | 
    
         
            -
                  input = [*RESPONSE_HEADERS,
         
     | 
| 
       211 
     | 
    
         
            -
                           ['cache-control', "max-age=60, private\0must-revalidate"],
         
     | 
| 
       212 
     | 
    
         
            -
                           ['content-type', 'text/html'],
         
     | 
| 
       213 
     | 
    
         
            -
                           ['cookie', "a=b\0c=d; e=f"]]
         
     | 
| 
       214 
     | 
    
         
            -
                  expected = [*RESPONSE_HEADERS,
         
     | 
| 
       215 
     | 
    
         
            -
                              ['cache-control', "max-age=60, private\0must-revalidate"],
         
     | 
| 
       216 
     | 
    
         
            -
                              ['content-type', 'text/html'],
         
     | 
| 
       217 
     | 
    
         
            -
                              ['cookie', "a=b\0c=d; e=f"]]
         
     | 
| 
       218 
     | 
    
         
            -
             
     | 
| 
       219 
     | 
    
         
            -
                  result = nil
         
     | 
| 
       220 
     | 
    
         
            -
                  @conn.on(:stream) do |stream|
         
     | 
| 
       221 
     | 
    
         
            -
                    stream.on(:headers) { |h| result = h }
         
     | 
| 
       222 
     | 
    
         
            -
                  end
         
     | 
| 
       223 
     | 
    
         
            -
             
     | 
| 
       224 
     | 
    
         
            -
                  srv = Server.new
         
     | 
| 
       225 
     | 
    
         
            -
                  srv.on(:frame) { |bytes| @conn << bytes }
         
     | 
| 
       226 
     | 
    
         
            -
                  stream = srv.new_stream
         
     | 
| 
       227 
     | 
    
         
            -
                  stream.headers(input)
         
     | 
| 
       228 
     | 
    
         
            -
             
     | 
| 
       229 
     | 
    
         
            -
                  expect(result).to eq expected
         
     | 
| 
       230 
     | 
    
         
            -
                end
         
     | 
| 
       231 
     | 
    
         
            -
              end
         
     | 
| 
       232 
     | 
    
         
            -
             
     | 
| 
       233 
     | 
    
         
            -
              context 'flow control' do
         
     | 
| 
       234 
     | 
    
         
            -
                it 'should initialize to default flow window' do
         
     | 
| 
       235 
     | 
    
         
            -
                  expect(@conn.remote_window).to eq DEFAULT_FLOW_WINDOW
         
     | 
| 
       236 
     | 
    
         
            -
                end
         
     | 
| 
       237 
     | 
    
         
            -
             
     | 
| 
       238 
     | 
    
         
            -
                it 'should update connection and stream windows on SETTINGS' do
         
     | 
| 
       239 
     | 
    
         
            -
                  settings, data = settings_frame, data_frame
         
     | 
| 
       240 
     | 
    
         
            -
                  settings[:payload] = [[:settings_initial_window_size, 1024]]
         
     | 
| 
       241 
     | 
    
         
            -
                  data[:payload] = 'x' * 2048
         
     | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
       243 
     | 
    
         
            -
                  stream = @conn.new_stream
         
     | 
| 
       244 
     | 
    
         
            -
             
     | 
| 
       245 
     | 
    
         
            -
                  stream.send headers_frame
         
     | 
| 
       246 
     | 
    
         
            -
                  stream.send data
         
     | 
| 
       247 
     | 
    
         
            -
                  expect(stream.remote_window).to eq(DEFAULT_FLOW_WINDOW - 2048)
         
     | 
| 
       248 
     | 
    
         
            -
                  expect(@conn.remote_window).to eq(DEFAULT_FLOW_WINDOW - 2048)
         
     | 
| 
       249 
     | 
    
         
            -
             
     | 
| 
       250 
     | 
    
         
            -
                  @conn << f.generate(settings)
         
     | 
| 
       251 
     | 
    
         
            -
                  expect(@conn.remote_window).to eq(-1024)
         
     | 
| 
       252 
     | 
    
         
            -
                  expect(stream.remote_window).to eq(-1024)
         
     | 
| 
       253 
     | 
    
         
            -
                end
         
     | 
| 
       254 
     | 
    
         
            -
             
     | 
| 
       255 
     | 
    
         
            -
                it 'should initialize streams with window specified by peer' do
         
     | 
| 
       256 
     | 
    
         
            -
                  settings = settings_frame
         
     | 
| 
       257 
     | 
    
         
            -
                  settings[:payload] = [[:settings_initial_window_size, 1024]]
         
     | 
| 
       258 
     | 
    
         
            -
             
     | 
| 
       259 
     | 
    
         
            -
                  @conn << f.generate(settings)
         
     | 
| 
       260 
     | 
    
         
            -
                  expect(@conn.new_stream.remote_window).to eq 1024
         
     | 
| 
       261 
     | 
    
         
            -
                end
         
     | 
| 
       262 
     | 
    
         
            -
             
     | 
| 
       263 
     | 
    
         
            -
                it 'should observe connection flow control' do
         
     | 
| 
       264 
     | 
    
         
            -
                  settings, data = settings_frame, data_frame
         
     | 
| 
       265 
     | 
    
         
            -
                  settings[:payload] = [[:settings_initial_window_size, 1000]]
         
     | 
| 
       266 
     | 
    
         
            -
             
     | 
| 
       267 
     | 
    
         
            -
                  @conn << f.generate(settings)
         
     | 
| 
       268 
     | 
    
         
            -
                  s1 = @conn.new_stream
         
     | 
| 
       269 
     | 
    
         
            -
                  s2 = @conn.new_stream
         
     | 
| 
       270 
     | 
    
         
            -
             
     | 
| 
       271 
     | 
    
         
            -
                  s1.send headers_frame
         
     | 
| 
       272 
     | 
    
         
            -
                  s1.send data.merge(payload: 'x' * 900)
         
     | 
| 
       273 
     | 
    
         
            -
                  expect(@conn.remote_window).to eq 100
         
     | 
| 
       274 
     | 
    
         
            -
             
     | 
| 
       275 
     | 
    
         
            -
                  s2.send headers_frame
         
     | 
| 
       276 
     | 
    
         
            -
                  s2.send data.merge(payload: 'x' * 200)
         
     | 
| 
       277 
     | 
    
         
            -
                  expect(@conn.remote_window).to eq 0
         
     | 
| 
       278 
     | 
    
         
            -
                  expect(@conn.buffered_amount).to eq 100
         
     | 
| 
       279 
     | 
    
         
            -
             
     | 
| 
       280 
     | 
    
         
            -
                  @conn << f.generate(window_update_frame.merge(stream: 0, increment: 1000))
         
     | 
| 
       281 
     | 
    
         
            -
                  expect(@conn.buffered_amount).to eq 0
         
     | 
| 
       282 
     | 
    
         
            -
                  expect(@conn.remote_window).to eq 900
         
     | 
| 
       283 
     | 
    
         
            -
                end
         
     | 
| 
       284 
     | 
    
         
            -
             
     | 
| 
       285 
     | 
    
         
            -
                it 'should update window when data received is over half of the maximum local window size' do
         
     | 
| 
       286 
     | 
    
         
            -
                  settings, data = settings_frame, data_frame
         
     | 
| 
       287 
     | 
    
         
            -
                  conn = Client.new(settings_initial_window_size: 500)
         
     | 
| 
       288 
     | 
    
         
            -
             
     | 
| 
       289 
     | 
    
         
            -
                  conn.receive f.generate(settings)
         
     | 
| 
       290 
     | 
    
         
            -
                  s1 = conn.new_stream
         
     | 
| 
       291 
     | 
    
         
            -
                  s2 = conn.new_stream
         
     | 
| 
       292 
     | 
    
         
            -
             
     | 
| 
       293 
     | 
    
         
            -
                  s1.send headers_frame
         
     | 
| 
       294 
     | 
    
         
            -
                  s2.send headers_frame
         
     | 
| 
       295 
     | 
    
         
            -
                  expect(conn).to receive(:send) do |frame|
         
     | 
| 
       296 
     | 
    
         
            -
                    expect(frame[:type]).to eq :window_update
         
     | 
| 
       297 
     | 
    
         
            -
                    expect(frame[:stream]).to eq 0
         
     | 
| 
       298 
     | 
    
         
            -
                    expect(frame[:increment]).to eq 400
         
     | 
| 
       299 
     | 
    
         
            -
                  end
         
     | 
| 
       300 
     | 
    
         
            -
                  conn.receive f.generate(data.merge(payload: 'x' * 200, end_stream: false, stream: s1.id))
         
     | 
| 
       301 
     | 
    
         
            -
                  conn.receive f.generate(data.merge(payload: 'x' * 200, end_stream: false, stream: s2.id))
         
     | 
| 
       302 
     | 
    
         
            -
                  expect(s1.local_window).to eq 300
         
     | 
| 
       303 
     | 
    
         
            -
                  expect(s2.local_window).to eq 300
         
     | 
| 
       304 
     | 
    
         
            -
                  expect(conn.local_window).to eq 500
         
     | 
| 
       305 
     | 
    
         
            -
                end
         
     | 
| 
       306 
     | 
    
         
            -
              end
         
     | 
| 
       307 
     | 
    
         
            -
             
     | 
| 
       308 
     | 
    
         
            -
              context 'framing' do
         
     | 
| 
       309 
     | 
    
         
            -
                it 'should buffer incomplete frames' do
         
     | 
| 
       310 
     | 
    
         
            -
                  settings = settings_frame
         
     | 
| 
       311 
     | 
    
         
            -
                  settings[:payload] = [[:settings_initial_window_size, 1000]]
         
     | 
| 
       312 
     | 
    
         
            -
                  @conn << f.generate(settings)
         
     | 
| 
       313 
     | 
    
         
            -
             
     | 
| 
       314 
     | 
    
         
            -
                  frame = f.generate(window_update_frame.merge(stream: 0, increment: 1000))
         
     | 
| 
       315 
     | 
    
         
            -
                  @conn << frame
         
     | 
| 
       316 
     | 
    
         
            -
                  expect(@conn.remote_window).to eq 2000
         
     | 
| 
       317 
     | 
    
         
            -
             
     | 
| 
       318 
     | 
    
         
            -
                  @conn << frame.slice!(0, 1)
         
     | 
| 
       319 
     | 
    
         
            -
                  @conn << frame
         
     | 
| 
       320 
     | 
    
         
            -
                  expect(@conn.remote_window).to eq 3000
         
     | 
| 
       321 
     | 
    
         
            -
                end
         
     | 
| 
       322 
     | 
    
         
            -
             
     | 
| 
       323 
     | 
    
         
            -
                it 'should decompress header blocks regardless of stream state' do
         
     | 
| 
       324 
     | 
    
         
            -
                  req_headers = [
         
     | 
| 
       325 
     | 
    
         
            -
                    ['content-length', '20'],
         
     | 
| 
       326 
     | 
    
         
            -
                    ['x-my-header', 'first'],
         
     | 
| 
       327 
     | 
    
         
            -
                  ]
         
     | 
| 
       328 
     | 
    
         
            -
             
     | 
| 
       329 
     | 
    
         
            -
                  cc = Compressor.new
         
     | 
| 
       330 
     | 
    
         
            -
                  headers = headers_frame
         
     | 
| 
       331 
     | 
    
         
            -
                  headers[:payload] = cc.encode(req_headers)
         
     | 
| 
       332 
     | 
    
         
            -
             
     | 
| 
       333 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       334 
     | 
    
         
            -
                  @conn.on(:stream) do |stream|
         
     | 
| 
       335 
     | 
    
         
            -
                    expect(stream).to receive(:<<) do |frame|
         
     | 
| 
       336 
     | 
    
         
            -
                      expect(frame[:payload]).to eq req_headers
         
     | 
| 
       337 
     | 
    
         
            -
                    end
         
     | 
| 
       338 
     | 
    
         
            -
                  end
         
     | 
| 
       339 
     | 
    
         
            -
             
     | 
| 
       340 
     | 
    
         
            -
                  @conn << f.generate(headers)
         
     | 
| 
       341 
     | 
    
         
            -
                end
         
     | 
| 
       342 
     | 
    
         
            -
             
     | 
| 
       343 
     | 
    
         
            -
                it 'should decode non-contiguous header blocks' do
         
     | 
| 
       344 
     | 
    
         
            -
                  req_headers = [
         
     | 
| 
       345 
     | 
    
         
            -
                    ['content-length', '15'],
         
     | 
| 
       346 
     | 
    
         
            -
                    ['x-my-header', 'first'],
         
     | 
| 
       347 
     | 
    
         
            -
                  ]
         
     | 
| 
       348 
     | 
    
         
            -
             
     | 
| 
       349 
     | 
    
         
            -
                  cc = Compressor.new
         
     | 
| 
       350 
     | 
    
         
            -
                  h1, h2 = headers_frame, continuation_frame
         
     | 
| 
       351 
     | 
    
         
            -
             
     | 
| 
       352 
     | 
    
         
            -
                  # Header block fragment might not complete for decompression
         
     | 
| 
       353 
     | 
    
         
            -
                  payload = cc.encode(req_headers)
         
     | 
| 
       354 
     | 
    
         
            -
                  h1[:payload] = payload.slice!(0, payload.size / 2) # first half
         
     | 
| 
       355 
     | 
    
         
            -
                  h1[:stream] = 5
         
     | 
| 
       356 
     | 
    
         
            -
                  h1[:flags] = []
         
     | 
| 
       357 
     | 
    
         
            -
             
     | 
| 
       358 
     | 
    
         
            -
                  h2[:payload] = payload # the remaining
         
     | 
| 
       359 
     | 
    
         
            -
                  h2[:stream] = 5
         
     | 
| 
       360 
     | 
    
         
            -
             
     | 
| 
       361 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       362 
     | 
    
         
            -
                  @conn.on(:stream) do |stream|
         
     | 
| 
       363 
     | 
    
         
            -
                    expect(stream).to receive(:<<) do |frame|
         
     | 
| 
       364 
     | 
    
         
            -
                      expect(frame[:payload]).to eq req_headers
         
     | 
| 
       365 
     | 
    
         
            -
                    end
         
     | 
| 
       366 
     | 
    
         
            -
                  end
         
     | 
| 
       367 
     | 
    
         
            -
             
     | 
| 
       368 
     | 
    
         
            -
                  @conn << f.generate(h1)
         
     | 
| 
       369 
     | 
    
         
            -
                  @conn << f.generate(h2)
         
     | 
| 
       370 
     | 
    
         
            -
                end
         
     | 
| 
       371 
     | 
    
         
            -
             
     | 
| 
       372 
     | 
    
         
            -
                it 'should require that split header blocks are a contiguous sequence' do
         
     | 
| 
       373 
     | 
    
         
            -
                  headers = headers_frame
         
     | 
| 
       374 
     | 
    
         
            -
                  headers[:flags] = []
         
     | 
| 
       375 
     | 
    
         
            -
             
     | 
| 
       376 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       377 
     | 
    
         
            -
                  @conn << f.generate(headers)
         
     | 
| 
       378 
     | 
    
         
            -
                  (frame_types - [continuation_frame]).each do |frame|
         
     | 
| 
       379 
     | 
    
         
            -
                    expect { @conn << f.generate(frame.deep_dup) }.to raise_error(ProtocolError)
         
     | 
| 
       380 
     | 
    
         
            -
                  end
         
     | 
| 
       381 
     | 
    
         
            -
                end
         
     | 
| 
       382 
     | 
    
         
            -
             
     | 
| 
       383 
     | 
    
         
            -
                it 'should raise compression error on encode of invalid frame' do
         
     | 
| 
       384 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       385 
     | 
    
         
            -
                  stream = @conn.new_stream
         
     | 
| 
       386 
     | 
    
         
            -
             
     | 
| 
       387 
     | 
    
         
            -
                  expect do
         
     | 
| 
       388 
     | 
    
         
            -
                    stream.headers({ 'name' => Float::INFINITY })
         
     | 
| 
       389 
     | 
    
         
            -
                  end.to raise_error(CompressionError)
         
     | 
| 
       390 
     | 
    
         
            -
                end
         
     | 
| 
       391 
     | 
    
         
            -
             
     | 
| 
       392 
     | 
    
         
            -
                it 'should raise connection error on decode of invalid frame' do
         
     | 
| 
       393 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       394 
     | 
    
         
            -
                  frame = f.generate(data_frame) # Receiving DATA on unopened stream 1 is an error.
         
     | 
| 
       395 
     | 
    
         
            -
                  # Connection errors emit protocol error frames
         
     | 
| 
       396 
     | 
    
         
            -
                  expect { @conn << frame }.to raise_error(ProtocolError)
         
     | 
| 
       397 
     | 
    
         
            -
                end
         
     | 
| 
       398 
     | 
    
         
            -
             
     | 
| 
       399 
     | 
    
         
            -
                it 'should emit encoded frames via on(:frame)' do
         
     | 
| 
       400 
     | 
    
         
            -
                  bytes = nil
         
     | 
| 
       401 
     | 
    
         
            -
                  @conn.on(:frame) { |d| bytes = d }
         
     | 
| 
       402 
     | 
    
         
            -
                  @conn.settings(settings_max_concurrent_streams: 10,
         
     | 
| 
       403 
     | 
    
         
            -
                                 settings_initial_window_size: 0x7fffffff)
         
     | 
| 
       404 
     | 
    
         
            -
             
     | 
| 
       405 
     | 
    
         
            -
                  expect(bytes).to eq f.generate(settings_frame)
         
     | 
| 
       406 
     | 
    
         
            -
                end
         
     | 
| 
       407 
     | 
    
         
            -
             
     | 
| 
       408 
     | 
    
         
            -
                it 'should compress stream headers' do
         
     | 
| 
       409 
     | 
    
         
            -
                  @conn.on(:frame) do |bytes|
         
     | 
| 
       410 
     | 
    
         
            -
                    expect(bytes).not_to include('get')
         
     | 
| 
       411 
     | 
    
         
            -
                    expect(bytes).not_to include('http')
         
     | 
| 
       412 
     | 
    
         
            -
                    expect(bytes).not_to include('www.example.org') # should be huffman encoded
         
     | 
| 
       413 
     | 
    
         
            -
                  end
         
     | 
| 
       414 
     | 
    
         
            -
             
     | 
| 
       415 
     | 
    
         
            -
                  stream = @conn.new_stream
         
     | 
| 
       416 
     | 
    
         
            -
                  stream.headers({ ':method' => 'get',
         
     | 
| 
       417 
     | 
    
         
            -
                                   ':scheme' => 'http',
         
     | 
| 
       418 
     | 
    
         
            -
                                   ':authority' => 'www.example.org',
         
     | 
| 
       419 
     | 
    
         
            -
                                   ':path'   => '/resource' })
         
     | 
| 
       420 
     | 
    
         
            -
                end
         
     | 
| 
       421 
     | 
    
         
            -
             
     | 
| 
       422 
     | 
    
         
            -
                it 'should generate CONTINUATION if HEADERS is too long' do
         
     | 
| 
       423 
     | 
    
         
            -
                  headers = []
         
     | 
| 
       424 
     | 
    
         
            -
                  @conn.on(:frame) do |bytes|
         
     | 
| 
       425 
     | 
    
         
            -
                    # bytes[3]: frame's type field
         
     | 
| 
       426 
     | 
    
         
            -
                    headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
         
     | 
| 
       427 
     | 
    
         
            -
                  end
         
     | 
| 
       428 
     | 
    
         
            -
             
     | 
| 
       429 
     | 
    
         
            -
                  stream = @conn.new_stream
         
     | 
| 
       430 
     | 
    
         
            -
                  stream.headers({
         
     | 
| 
       431 
     | 
    
         
            -
                    ':method' => 'get',
         
     | 
| 
       432 
     | 
    
         
            -
                    ':scheme' => 'http',
         
     | 
| 
       433 
     | 
    
         
            -
                    ':authority' => 'www.example.org',
         
     | 
| 
       434 
     | 
    
         
            -
                    ':path'   => '/resource',
         
     | 
| 
       435 
     | 
    
         
            -
                    'custom' => 'q' * 44_000,
         
     | 
| 
       436 
     | 
    
         
            -
                  }, end_stream: true)
         
     | 
| 
       437 
     | 
    
         
            -
                  expect(headers.size).to eq 3
         
     | 
| 
       438 
     | 
    
         
            -
                  expect(headers[0][:type]).to eq :headers
         
     | 
| 
       439 
     | 
    
         
            -
                  expect(headers[1][:type]).to eq :continuation
         
     | 
| 
       440 
     | 
    
         
            -
                  expect(headers[2][:type]).to eq :continuation
         
     | 
| 
       441 
     | 
    
         
            -
                  expect(headers[0][:flags]).to eq [:end_stream]
         
     | 
| 
       442 
     | 
    
         
            -
                  expect(headers[1][:flags]).to eq []
         
     | 
| 
       443 
     | 
    
         
            -
                  expect(headers[2][:flags]).to eq [:end_headers]
         
     | 
| 
       444 
     | 
    
         
            -
                end
         
     | 
| 
       445 
     | 
    
         
            -
             
     | 
| 
       446 
     | 
    
         
            -
                it 'should not generate CONTINUATION if HEADERS fits exactly in a frame' do
         
     | 
| 
       447 
     | 
    
         
            -
                  headers = []
         
     | 
| 
       448 
     | 
    
         
            -
                  @conn.on(:frame) do |bytes|
         
     | 
| 
       449 
     | 
    
         
            -
                    # bytes[3]: frame's type field
         
     | 
| 
       450 
     | 
    
         
            -
                    headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
         
     | 
| 
       451 
     | 
    
         
            -
                  end
         
     | 
| 
       452 
     | 
    
         
            -
             
     | 
| 
       453 
     | 
    
         
            -
                  stream = @conn.new_stream
         
     | 
| 
       454 
     | 
    
         
            -
                  stream.headers({
         
     | 
| 
       455 
     | 
    
         
            -
                    ':method' => 'get',
         
     | 
| 
       456 
     | 
    
         
            -
                    ':scheme' => 'http',
         
     | 
| 
       457 
     | 
    
         
            -
                    ':authority' => 'www.example.org',
         
     | 
| 
       458 
     | 
    
         
            -
                    ':path'   => '/resource',
         
     | 
| 
       459 
     | 
    
         
            -
                    'custom' => 'q' * 18_682, # this number should be updated when Huffman table is changed
         
     | 
| 
       460 
     | 
    
         
            -
                  }, end_stream: true)
         
     | 
| 
       461 
     | 
    
         
            -
                  expect(headers[0][:length]).to eq @conn.remote_settings[:settings_max_frame_size]
         
     | 
| 
       462 
     | 
    
         
            -
                  expect(headers.size).to eq 1
         
     | 
| 
       463 
     | 
    
         
            -
                  expect(headers[0][:type]).to eq :headers
         
     | 
| 
       464 
     | 
    
         
            -
                  expect(headers[0][:flags]).to include(:end_headers)
         
     | 
| 
       465 
     | 
    
         
            -
                  expect(headers[0][:flags]).to include(:end_stream)
         
     | 
| 
       466 
     | 
    
         
            -
                end
         
     | 
| 
       467 
     | 
    
         
            -
             
     | 
| 
       468 
     | 
    
         
            -
                it 'should not generate CONTINUATION if HEADERS fits exactly in a frame' do
         
     | 
| 
       469 
     | 
    
         
            -
                  headers = []
         
     | 
| 
       470 
     | 
    
         
            -
                  @conn.on(:frame) do |bytes|
         
     | 
| 
       471 
     | 
    
         
            -
                    # bytes[3]: frame's type field
         
     | 
| 
       472 
     | 
    
         
            -
                    headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
         
     | 
| 
       473 
     | 
    
         
            -
                  end
         
     | 
| 
       474 
     | 
    
         
            -
             
     | 
| 
       475 
     | 
    
         
            -
                  stream = @conn.new_stream
         
     | 
| 
       476 
     | 
    
         
            -
                  stream.headers({
         
     | 
| 
       477 
     | 
    
         
            -
                    ':method' => 'get',
         
     | 
| 
       478 
     | 
    
         
            -
                    ':scheme' => 'http',
         
     | 
| 
       479 
     | 
    
         
            -
                    ':authority' => 'www.example.org',
         
     | 
| 
       480 
     | 
    
         
            -
                    ':path'   => '/resource',
         
     | 
| 
       481 
     | 
    
         
            -
                    'custom' => 'q' * 18_682, # this number should be updated when Huffman table is changed
         
     | 
| 
       482 
     | 
    
         
            -
                  }, end_stream: true)
         
     | 
| 
       483 
     | 
    
         
            -
                  expect(headers[0][:length]).to eq @conn.remote_settings[:settings_max_frame_size]
         
     | 
| 
       484 
     | 
    
         
            -
                  expect(headers.size).to eq 1
         
     | 
| 
       485 
     | 
    
         
            -
                  expect(headers[0][:type]).to eq :headers
         
     | 
| 
       486 
     | 
    
         
            -
                  expect(headers[0][:flags]).to include(:end_headers)
         
     | 
| 
       487 
     | 
    
         
            -
                  expect(headers[0][:flags]).to include(:end_stream)
         
     | 
| 
       488 
     | 
    
         
            -
                end
         
     | 
| 
       489 
     | 
    
         
            -
             
     | 
| 
       490 
     | 
    
         
            -
                it 'should generate CONTINUATION if HEADERS exceed the max payload by one byte' do
         
     | 
| 
       491 
     | 
    
         
            -
                  headers = []
         
     | 
| 
       492 
     | 
    
         
            -
                  @conn.on(:frame) do |bytes|
         
     | 
| 
       493 
     | 
    
         
            -
                    headers << f.parse(bytes) if [1, 5, 9].include?(bytes[3].ord)
         
     | 
| 
       494 
     | 
    
         
            -
                  end
         
     | 
| 
       495 
     | 
    
         
            -
             
     | 
| 
       496 
     | 
    
         
            -
                  stream = @conn.new_stream
         
     | 
| 
       497 
     | 
    
         
            -
                  stream.headers({
         
     | 
| 
       498 
     | 
    
         
            -
                    ':method' => 'get',
         
     | 
| 
       499 
     | 
    
         
            -
                    ':scheme' => 'http',
         
     | 
| 
       500 
     | 
    
         
            -
                    ':authority' => 'www.example.org',
         
     | 
| 
       501 
     | 
    
         
            -
                    ':path'   => '/resource',
         
     | 
| 
       502 
     | 
    
         
            -
                    'custom' => 'q' * 18_683, # this number should be updated when Huffman table is changed
         
     | 
| 
       503 
     | 
    
         
            -
                  }, end_stream: true)
         
     | 
| 
       504 
     | 
    
         
            -
                  expect(headers[0][:length]).to eq @conn.remote_settings[:settings_max_frame_size]
         
     | 
| 
       505 
     | 
    
         
            -
                  expect(headers[1][:length]).to eq 1
         
     | 
| 
       506 
     | 
    
         
            -
                  expect(headers.size).to eq 2
         
     | 
| 
       507 
     | 
    
         
            -
                  expect(headers[0][:type]).to eq :headers
         
     | 
| 
       508 
     | 
    
         
            -
                  expect(headers[1][:type]).to eq :continuation
         
     | 
| 
       509 
     | 
    
         
            -
                  expect(headers[0][:flags]).to eq [:end_stream]
         
     | 
| 
       510 
     | 
    
         
            -
                  expect(headers[1][:flags]).to eq [:end_headers]
         
     | 
| 
       511 
     | 
    
         
            -
                end
         
     | 
| 
       512 
     | 
    
         
            -
              end
         
     | 
| 
       513 
     | 
    
         
            -
             
     | 
| 
       514 
     | 
    
         
            -
              context 'connection management' do
         
     | 
| 
       515 
     | 
    
         
            -
                it 'should raise error on invalid connection header' do
         
     | 
| 
       516 
     | 
    
         
            -
                  srv = Server.new
         
     | 
| 
       517 
     | 
    
         
            -
                  expect { srv << f.generate(settings_frame) }.to raise_error(HandshakeError)
         
     | 
| 
       518 
     | 
    
         
            -
             
     | 
| 
       519 
     | 
    
         
            -
                  srv = Server.new
         
     | 
| 
       520 
     | 
    
         
            -
                  expect do
         
     | 
| 
       521 
     | 
    
         
            -
                    srv << CONNECTION_PREFACE_MAGIC
         
     | 
| 
       522 
     | 
    
         
            -
                    srv << f.generate(settings_frame)
         
     | 
| 
       523 
     | 
    
         
            -
                  end.to_not raise_error
         
     | 
| 
       524 
     | 
    
         
            -
                end
         
     | 
| 
       525 
     | 
    
         
            -
             
     | 
| 
       526 
     | 
    
         
            -
                it 'should respond to PING frames' do
         
     | 
| 
       527 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       528 
     | 
    
         
            -
                  expect(@conn).to receive(:send) do |frame|
         
     | 
| 
       529 
     | 
    
         
            -
                    expect(frame[:type]).to eq :ping
         
     | 
| 
       530 
     | 
    
         
            -
                    expect(frame[:flags]).to eq [:ack]
         
     | 
| 
       531 
     | 
    
         
            -
                    expect(frame[:payload]).to eq '12345678'
         
     | 
| 
       532 
     | 
    
         
            -
                  end
         
     | 
| 
       533 
     | 
    
         
            -
             
     | 
| 
       534 
     | 
    
         
            -
                  @conn << f.generate(ping_frame)
         
     | 
| 
       535 
     | 
    
         
            -
                end
         
     | 
| 
       536 
     | 
    
         
            -
             
     | 
| 
       537 
     | 
    
         
            -
                it 'should fire callback on PONG' do
         
     | 
| 
       538 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       539 
     | 
    
         
            -
             
     | 
| 
       540 
     | 
    
         
            -
                  pong = nil
         
     | 
| 
       541 
     | 
    
         
            -
                  @conn.ping('12345678') { |d| pong = d }
         
     | 
| 
       542 
     | 
    
         
            -
                  @conn << f.generate(pong_frame)
         
     | 
| 
       543 
     | 
    
         
            -
                  expect(pong).to eq '12345678'
         
     | 
| 
       544 
     | 
    
         
            -
                end
         
     | 
| 
       545 
     | 
    
         
            -
             
     | 
| 
       546 
     | 
    
         
            -
                it 'should fire callback on receipt of GOAWAY' do
         
     | 
| 
       547 
     | 
    
         
            -
                  last_stream, payload, error = nil
         
     | 
| 
       548 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       549 
     | 
    
         
            -
                  @conn.on(:goaway) do |s, e, p|
         
     | 
| 
       550 
     | 
    
         
            -
                    last_stream = s
         
     | 
| 
       551 
     | 
    
         
            -
                    error = e
         
     | 
| 
       552 
     | 
    
         
            -
                    payload = p
         
     | 
| 
       553 
     | 
    
         
            -
                  end
         
     | 
| 
       554 
     | 
    
         
            -
                  @conn << f.generate(goaway_frame.merge(last_stream: 17, payload: 'test'))
         
     | 
| 
       555 
     | 
    
         
            -
             
     | 
| 
       556 
     | 
    
         
            -
                  expect(last_stream).to eq 17
         
     | 
| 
       557 
     | 
    
         
            -
                  expect(error).to eq :no_error
         
     | 
| 
       558 
     | 
    
         
            -
                  expect(payload).to eq 'test'
         
     | 
| 
       559 
     | 
    
         
            -
             
     | 
| 
       560 
     | 
    
         
            -
                  expect(@conn).to be_closed
         
     | 
| 
       561 
     | 
    
         
            -
                end
         
     | 
| 
       562 
     | 
    
         
            -
             
     | 
| 
       563 
     | 
    
         
            -
                it 'should raise error when opening new stream after sending GOAWAY' do
         
     | 
| 
       564 
     | 
    
         
            -
                  @conn.goaway
         
     | 
| 
       565 
     | 
    
         
            -
                  expect(@conn).to be_closed
         
     | 
| 
       566 
     | 
    
         
            -
             
     | 
| 
       567 
     | 
    
         
            -
                  expect { @conn.new_stream }.to raise_error(ConnectionClosed)
         
     | 
| 
       568 
     | 
    
         
            -
                end
         
     | 
| 
       569 
     | 
    
         
            -
             
     | 
| 
       570 
     | 
    
         
            -
                it 'should raise error when opening new stream after receiving GOAWAY' do
         
     | 
| 
       571 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       572 
     | 
    
         
            -
                  @conn << f.generate(goaway_frame)
         
     | 
| 
       573 
     | 
    
         
            -
                  expect { @conn.new_stream }.to raise_error(ConnectionClosed)
         
     | 
| 
       574 
     | 
    
         
            -
                end
         
     | 
| 
       575 
     | 
    
         
            -
             
     | 
| 
       576 
     | 
    
         
            -
                it 'should not raise error when receiving connection management frames immediately after emitting goaway' do
         
     | 
| 
       577 
     | 
    
         
            -
                  @conn.goaway
         
     | 
| 
       578 
     | 
    
         
            -
                  expect(@conn).to be_closed
         
     | 
| 
       579 
     | 
    
         
            -
             
     | 
| 
       580 
     | 
    
         
            -
                  expect { @conn << f.generate(settings_frame) }.not_to raise_error(ProtocolError)
         
     | 
| 
       581 
     | 
    
         
            -
                  expect { @conn << f.generate(ping_frame) }.not_to raise_error(ProtocolError)
         
     | 
| 
       582 
     | 
    
         
            -
                  expect { @conn << f.generate(goaway_frame) }.not_to raise_error(ProtocolError)
         
     | 
| 
       583 
     | 
    
         
            -
                end
         
     | 
| 
       584 
     | 
    
         
            -
             
     | 
| 
       585 
     | 
    
         
            -
                it 'should process connection management frames after GOAWAY' do
         
     | 
| 
       586 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       587 
     | 
    
         
            -
                  @conn << f.generate(headers_frame)
         
     | 
| 
       588 
     | 
    
         
            -
                  @conn << f.generate(goaway_frame)
         
     | 
| 
       589 
     | 
    
         
            -
                  @conn << f.generate(headers_frame.merge(stream: 7))
         
     | 
| 
       590 
     | 
    
         
            -
                  @conn << f.generate(push_promise_frame)
         
     | 
| 
       591 
     | 
    
         
            -
             
     | 
| 
       592 
     | 
    
         
            -
                  expect(@conn.active_stream_count).to eq 1
         
     | 
| 
       593 
     | 
    
         
            -
                end
         
     | 
| 
       594 
     | 
    
         
            -
             
     | 
| 
       595 
     | 
    
         
            -
                it 'should raise error on frame for invalid stream ID' do
         
     | 
| 
       596 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       597 
     | 
    
         
            -
             
     | 
| 
       598 
     | 
    
         
            -
                  expect do
         
     | 
| 
       599 
     | 
    
         
            -
                    @conn << f.generate(data_frame.merge(stream: 31))
         
     | 
| 
       600 
     | 
    
         
            -
                  end.to raise_error(ProtocolError)
         
     | 
| 
       601 
     | 
    
         
            -
                end
         
     | 
| 
       602 
     | 
    
         
            -
             
     | 
| 
       603 
     | 
    
         
            -
                it 'should not raise an error on frame for a closed stream ID' do
         
     | 
| 
       604 
     | 
    
         
            -
                  srv = Server.new
         
     | 
| 
       605 
     | 
    
         
            -
                  srv << CONNECTION_PREFACE_MAGIC
         
     | 
| 
       606 
     | 
    
         
            -
             
     | 
| 
       607 
     | 
    
         
            -
                  stream = srv.new_stream
         
     | 
| 
       608 
     | 
    
         
            -
                  stream.send headers_frame
         
     | 
| 
       609 
     | 
    
         
            -
                  stream.send data_frame
         
     | 
| 
       610 
     | 
    
         
            -
                  stream.close
         
     | 
| 
       611 
     | 
    
         
            -
             
     | 
| 
       612 
     | 
    
         
            -
                  expect do
         
     | 
| 
       613 
     | 
    
         
            -
                    srv << f.generate(rst_stream_frame.merge(stream: stream.id))
         
     | 
| 
       614 
     | 
    
         
            -
                  end.to_not raise_error
         
     | 
| 
       615 
     | 
    
         
            -
                end
         
     | 
| 
       616 
     | 
    
         
            -
             
     | 
| 
       617 
     | 
    
         
            -
                it 'should send GOAWAY frame on connection error' do
         
     | 
| 
       618 
     | 
    
         
            -
                  stream = @conn.new_stream
         
     | 
| 
       619 
     | 
    
         
            -
             
     | 
| 
       620 
     | 
    
         
            -
                  expect(@conn).to receive(:encode) do |frame|
         
     | 
| 
       621 
     | 
    
         
            -
                    expect(frame[:type]).to eq :settings
         
     | 
| 
       622 
     | 
    
         
            -
                    [frame]
         
     | 
| 
       623 
     | 
    
         
            -
                  end
         
     | 
| 
       624 
     | 
    
         
            -
                  expect(@conn).to receive(:encode) do |frame|
         
     | 
| 
       625 
     | 
    
         
            -
                    expect(frame[:type]).to eq :goaway
         
     | 
| 
       626 
     | 
    
         
            -
                    expect(frame[:last_stream]).to eq stream.id
         
     | 
| 
       627 
     | 
    
         
            -
                    expect(frame[:error]).to eq :protocol_error
         
     | 
| 
       628 
     | 
    
         
            -
                    [frame]
         
     | 
| 
       629 
     | 
    
         
            -
                  end
         
     | 
| 
       630 
     | 
    
         
            -
             
     | 
| 
       631 
     | 
    
         
            -
                  expect { @conn << f.generate(data_frame) }.to raise_error(ProtocolError)
         
     | 
| 
       632 
     | 
    
         
            -
                end
         
     | 
| 
       633 
     | 
    
         
            -
              end
         
     | 
| 
       634 
     | 
    
         
            -
             
     | 
| 
       635 
     | 
    
         
            -
              context 'API' do
         
     | 
| 
       636 
     | 
    
         
            -
                it '.settings should emit SETTINGS frames' do
         
     | 
| 
       637 
     | 
    
         
            -
                  expect(@conn).to receive(:send) do |frame|
         
     | 
| 
       638 
     | 
    
         
            -
                    expect(frame[:type]).to eq :settings
         
     | 
| 
       639 
     | 
    
         
            -
                    expect(frame[:payload]).to eq([
         
     | 
| 
       640 
     | 
    
         
            -
                      [:settings_max_concurrent_streams, 10],
         
     | 
| 
       641 
     | 
    
         
            -
                      [:settings_initial_window_size, 0x7fffffff],
         
     | 
| 
       642 
     | 
    
         
            -
                    ])
         
     | 
| 
       643 
     | 
    
         
            -
                    expect(frame[:stream]).to eq 0
         
     | 
| 
       644 
     | 
    
         
            -
                  end
         
     | 
| 
       645 
     | 
    
         
            -
             
     | 
| 
       646 
     | 
    
         
            -
                  @conn.settings(settings_max_concurrent_streams: 10,
         
     | 
| 
       647 
     | 
    
         
            -
                                 settings_initial_window_size: 0x7fffffff)
         
     | 
| 
       648 
     | 
    
         
            -
                end
         
     | 
| 
       649 
     | 
    
         
            -
             
     | 
| 
       650 
     | 
    
         
            -
                it '.ping should generate PING frames' do
         
     | 
| 
       651 
     | 
    
         
            -
                  expect(@conn).to receive(:send) do |frame|
         
     | 
| 
       652 
     | 
    
         
            -
                    expect(frame[:type]).to eq :ping
         
     | 
| 
       653 
     | 
    
         
            -
                    expect(frame[:payload]).to eq 'somedata'
         
     | 
| 
       654 
     | 
    
         
            -
                  end
         
     | 
| 
       655 
     | 
    
         
            -
             
     | 
| 
       656 
     | 
    
         
            -
                  @conn.ping('somedata')
         
     | 
| 
       657 
     | 
    
         
            -
                end
         
     | 
| 
       658 
     | 
    
         
            -
             
     | 
| 
       659 
     | 
    
         
            -
                it '.goaway should generate GOAWAY frame with last processed stream ID' do
         
     | 
| 
       660 
     | 
    
         
            -
                  @conn << f.generate(settings_frame)
         
     | 
| 
       661 
     | 
    
         
            -
                  @conn << f.generate(headers_frame.merge(stream: 17))
         
     | 
| 
       662 
     | 
    
         
            -
             
     | 
| 
       663 
     | 
    
         
            -
                  expect(@conn).to receive(:send) do |frame|
         
     | 
| 
       664 
     | 
    
         
            -
                    expect(frame[:type]).to eq :goaway
         
     | 
| 
       665 
     | 
    
         
            -
                    expect(frame[:last_stream]).to eq 17
         
     | 
| 
       666 
     | 
    
         
            -
                    expect(frame[:error]).to eq :internal_error
         
     | 
| 
       667 
     | 
    
         
            -
                    expect(frame[:payload]).to eq 'payload'
         
     | 
| 
       668 
     | 
    
         
            -
                  end
         
     | 
| 
       669 
     | 
    
         
            -
             
     | 
| 
       670 
     | 
    
         
            -
                  @conn.goaway(:internal_error, 'payload')
         
     | 
| 
       671 
     | 
    
         
            -
                end
         
     | 
| 
       672 
     | 
    
         
            -
                it '.window_update should emit WINDOW_UPDATE frames' do
         
     | 
| 
       673 
     | 
    
         
            -
                  expect(@conn).to receive(:send) do |frame|
         
     | 
| 
       674 
     | 
    
         
            -
                    expect(frame[:type]).to eq :window_update
         
     | 
| 
       675 
     | 
    
         
            -
                    expect(frame[:increment]).to eq 20
         
     | 
| 
       676 
     | 
    
         
            -
                    expect(frame[:stream]).to eq 0
         
     | 
| 
       677 
     | 
    
         
            -
                  end
         
     | 
| 
       678 
     | 
    
         
            -
                  @conn.window_update(20)
         
     | 
| 
       679 
     | 
    
         
            -
                end
         
     | 
| 
       680 
     | 
    
         
            -
              end
         
     | 
| 
       681 
     | 
    
         
            -
            end
         
     |