http-2 0.10.1 → 0.10.2
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 +5 -5
- data/.rubocop.yml +1 -0
- data/.rubocop_todo.yml +3 -0
- data/LICENSE +21 -0
- data/example/upgrade_client.rb +10 -10
- data/example/upgrade_server.rb +5 -5
- data/http-2.gemspec +1 -1
- data/lib/http/2/compressor.rb +3 -0
- data/lib/http/2/connection.rb +22 -7
- data/lib/http/2/version.rb +1 -1
- data/spec/client_spec.rb +21 -20
- data/spec/compressor_spec.rb +6 -0
- data/spec/connection_spec.rb +95 -79
- data/spec/helper.rb +123 -107
- data/spec/server_spec.rb +2 -1
- data/spec/stream_spec.rb +112 -111
- metadata +8 -8
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 | 
            -
             | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: 8dd78ba6a3468b6a3ceda2ebabc37ceb895a90d675d9fecc035b74d2f479a457
         | 
| 4 | 
            +
              data.tar.gz: d70542d69e57050ab9ee3c837b6123fea81424a03bf3b99ffdc889a54a2fd20c
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: f678316f6235ed93ce5a3902ef3bf4c48854605de0875054d1f3e05196fc161bdfe49d149ff930413bef5ca76c61ab2d193092867a383f1f5dbe309cfef0f012
         | 
| 7 | 
            +
              data.tar.gz: 52fce71379a9299df1cd8c93d44964d668d09c199564482193513b109a6339f220b4ae1e724c5965892b3e53855820a810e95405b8eb198797ea130611d39a05
         | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/.rubocop_todo.yml
    CHANGED
    
    
    
        data/LICENSE
    ADDED
    
    | @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            MIT License
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Copyright (c) 2013 Ilya Grigorik
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| 6 | 
            +
            of this software and associated documentation files (the "Software"), to deal
         | 
| 7 | 
            +
            in the Software without restriction, including without limitation the rights
         | 
| 8 | 
            +
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         | 
| 9 | 
            +
            copies of the Software, and to permit persons to whom the Software is
         | 
| 10 | 
            +
            furnished to do so, subject to the following conditions:
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            The above copyright notice and this permission notice shall be included in all
         | 
| 13 | 
            +
            copies or substantial portions of the Software.
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         | 
| 16 | 
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         | 
| 17 | 
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         | 
| 18 | 
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         | 
| 19 | 
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         | 
| 20 | 
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
         | 
| 21 | 
            +
            SOFTWARE.
         | 
    
        data/example/upgrade_client.rb
    CHANGED
    
    | @@ -34,16 +34,16 @@ end | |
| 34 34 |  | 
| 35 35 | 
             
            # upgrader module
         | 
| 36 36 | 
             
            class UpgradeHandler
         | 
| 37 | 
            -
              UPGRADE_REQUEST =  | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 37 | 
            +
              UPGRADE_REQUEST = <<RESP.freeze
         | 
| 38 | 
            +
            GET %s HTTP/1.1
         | 
| 39 | 
            +
            Connection: Upgrade, HTTP2-Settings
         | 
| 40 | 
            +
            HTTP2-Settings: #{HTTP2::Client.settings_header(settings_max_concurrent_streams: 100)}
         | 
| 41 | 
            +
            Upgrade: h2c
         | 
| 42 | 
            +
            Host: %s
         | 
| 43 | 
            +
            User-Agent: http-2 upgrade
         | 
| 44 | 
            +
            Accept: */*
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            RESP
         | 
| 47 47 |  | 
| 48 48 | 
             
              attr_reader :complete, :parsing
         | 
| 49 49 | 
             
              def initialize(conn, sock)
         | 
    
        data/example/upgrade_server.rb
    CHANGED
    
    | @@ -39,12 +39,12 @@ end | |
| 39 39 |  | 
| 40 40 | 
             
            class UpgradeHandler
         | 
| 41 41 | 
             
              VALID_UPGRADE_METHODS = %w(GET OPTIONS).freeze
         | 
| 42 | 
            -
              UPGRADE_RESPONSE =  | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 42 | 
            +
              UPGRADE_RESPONSE = <<RESP.freeze
         | 
| 43 | 
            +
            HTTP/1.1 101 Switching Protocols
         | 
| 44 | 
            +
            Connection: Upgrade
         | 
| 45 | 
            +
            Upgrade: h2c
         | 
| 46 46 |  | 
| 47 | 
            -
             | 
| 47 | 
            +
            RESP
         | 
| 48 48 |  | 
| 49 49 | 
             
              attr_reader :complete, :headers, :body, :parsing
         | 
| 50 50 |  | 
    
        data/http-2.gemspec
    CHANGED
    
    
    
        data/lib/http/2/compressor.rb
    CHANGED
    
    
    
        data/lib/http/2/connection.rb
    CHANGED
    
    | @@ -33,6 +33,9 @@ module HTTP2 | |
| 33 33 | 
             
              # Default connection "fast-fail" preamble string as defined by the spec.
         | 
| 34 34 | 
             
              CONNECTION_PREFACE_MAGIC = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".freeze
         | 
| 35 35 |  | 
| 36 | 
            +
              # Time to hold recently closed streams until purge (seconds)
         | 
| 37 | 
            +
              RECENTLY_CLOSED_STREAMS_TTL = 15
         | 
| 38 | 
            +
             | 
| 36 39 | 
             
              # Connection encapsulates all of the connection, stream, flow-control,
         | 
| 37 40 | 
             
              # error management, and other processing logic required for a well-behaved
         | 
| 38 41 | 
             
              # HTTP 2.0 endpoint.
         | 
| @@ -683,13 +686,9 @@ module HTTP2 | |
| 683 686 | 
             
                    # Store a reference to the closed stream, such that we can respond
         | 
| 684 687 | 
             
                    # to any in-flight frames while close is registered on both sides.
         | 
| 685 688 | 
             
                    # References to such streams will be purged whenever another stream
         | 
| 686 | 
            -
                    # is closed, with a  | 
| 687 | 
            -
                    @streams_recently_closed[id] = Time.now
         | 
| 688 | 
            -
                     | 
| 689 | 
            -
                    to_delete.each do |stream_id|
         | 
| 690 | 
            -
                      @streams.delete stream_id
         | 
| 691 | 
            -
                      @streams_recently_closed.delete stream_id
         | 
| 692 | 
            -
                    end
         | 
| 689 | 
            +
                    # is closed, with a defined RTT time window.
         | 
| 690 | 
            +
                    @streams_recently_closed[id] = Time.now.to_i
         | 
| 691 | 
            +
                    cleanup_recently_closed
         | 
| 693 692 | 
             
                  end
         | 
| 694 693 |  | 
| 695 694 | 
             
                  stream.on(:promise, &method(:promise)) if self.is_a? Server
         | 
| @@ -698,6 +697,22 @@ module HTTP2 | |
| 698 697 | 
             
                  @streams[id] = stream
         | 
| 699 698 | 
             
                end
         | 
| 700 699 |  | 
| 700 | 
            +
                # Purge recently streams closed within defined RTT time window.
         | 
| 701 | 
            +
                def cleanup_recently_closed
         | 
| 702 | 
            +
                  now_ts = Time.now.to_i
         | 
| 703 | 
            +
                  to_delete = []
         | 
| 704 | 
            +
                  @streams_recently_closed.each do |stream_id, ts|
         | 
| 705 | 
            +
                    # Ruby Hash enumeration is ordered, so once fresh stream is met we can stop searching.
         | 
| 706 | 
            +
                    break if now_ts - ts < RECENTLY_CLOSED_STREAMS_TTL
         | 
| 707 | 
            +
                    to_delete << stream_id
         | 
| 708 | 
            +
                  end
         | 
| 709 | 
            +
             | 
| 710 | 
            +
                  to_delete.each do |stream_id|
         | 
| 711 | 
            +
                    @streams.delete stream_id
         | 
| 712 | 
            +
                    @streams_recently_closed.delete stream_id
         | 
| 713 | 
            +
                  end
         | 
| 714 | 
            +
                end
         | 
| 715 | 
            +
             | 
| 701 716 | 
             
                # Emit GOAWAY error indicating to peer that the connection is being
         | 
| 702 717 | 
             
                # aborted, and once sent, raise a local exception.
         | 
| 703 718 | 
             
                #
         | 
    
        data/lib/http/2/version.rb
    CHANGED
    
    
    
        data/spec/client_spec.rb
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            require 'helper'
         | 
| 2 2 |  | 
| 3 3 | 
             
            RSpec.describe HTTP2::Client do
         | 
| 4 | 
            +
              include FrameHelpers
         | 
| 4 5 | 
             
              before(:each) do
         | 
| 5 6 | 
             
                @client = Client.new
         | 
| 6 7 | 
             
              end
         | 
| @@ -36,7 +37,7 @@ RSpec.describe HTTP2::Client do | |
| 36 37 | 
             
                it 'should initialize client when receiving server settings before sending ack' do
         | 
| 37 38 | 
             
                  frames = []
         | 
| 38 39 | 
             
                  @client.on(:frame) { |bytes| frames << bytes }
         | 
| 39 | 
            -
                  @client << f.generate( | 
| 40 | 
            +
                  @client << f.generate(settings_frame)
         | 
| 40 41 |  | 
| 41 42 | 
             
                  expect(frames[0]).to eq CONNECTION_PREFACE_MAGIC
         | 
| 42 43 | 
             
                  expect(f.parse(frames[1])[:type]).to eq :settings
         | 
| @@ -72,33 +73,33 @@ RSpec.describe HTTP2::Client do | |
| 72 73 |  | 
| 73 74 | 
             
                it 'should raise error on PUSH_PROMISE against stream 0' do
         | 
| 74 75 | 
             
                  expect do
         | 
| 75 | 
            -
                    @client << set_stream_id(f.generate( | 
| 76 | 
            +
                    @client << set_stream_id(f.generate(push_promise_frame), 0)
         | 
| 76 77 | 
             
                  end.to raise_error(ProtocolError)
         | 
| 77 78 | 
             
                end
         | 
| 78 79 |  | 
| 79 80 | 
             
                it 'should raise error on PUSH_PROMISE against bogus stream' do
         | 
| 80 81 | 
             
                  expect do
         | 
| 81 | 
            -
                    @client << set_stream_id(f.generate( | 
| 82 | 
            +
                    @client << set_stream_id(f.generate(push_promise_frame), 31_415)
         | 
| 82 83 | 
             
                  end.to raise_error(ProtocolError)
         | 
| 83 84 | 
             
                end
         | 
| 84 85 |  | 
| 85 86 | 
             
                it 'should raise error on PUSH_PROMISE against non-idle stream' do
         | 
| 86 87 | 
             
                  expect do
         | 
| 87 88 | 
             
                    s = @client.new_stream
         | 
| 88 | 
            -
                    s.send  | 
| 89 | 
            +
                    s.send headers_frame
         | 
| 89 90 |  | 
| 90 | 
            -
                    @client << set_stream_id(f.generate( | 
| 91 | 
            -
                    @client << set_stream_id(f.generate( | 
| 91 | 
            +
                    @client << set_stream_id(f.generate(push_promise_frame), s.id)
         | 
| 92 | 
            +
                    @client << set_stream_id(f.generate(push_promise_frame), s.id)
         | 
| 92 93 | 
             
                  end.to raise_error(ProtocolError)
         | 
| 93 94 | 
             
                end
         | 
| 94 95 |  | 
| 95 96 | 
             
                it 'should emit stream object for received PUSH_PROMISE' do
         | 
| 96 97 | 
             
                  s = @client.new_stream
         | 
| 97 | 
            -
                  s.send  | 
| 98 | 
            +
                  s.send headers_frame
         | 
| 98 99 |  | 
| 99 100 | 
             
                  promise = nil
         | 
| 100 101 | 
             
                  @client.on(:promise) { |stream| promise = stream }
         | 
| 101 | 
            -
                  @client << set_stream_id(f.generate( | 
| 102 | 
            +
                  @client << set_stream_id(f.generate(push_promise_frame), s.id)
         | 
| 102 103 |  | 
| 103 104 | 
             
                  expect(promise.id).to eq 2
         | 
| 104 105 | 
             
                  expect(promise.state).to eq :reserved_remote
         | 
| @@ -107,14 +108,14 @@ RSpec.describe HTTP2::Client do | |
| 107 108 | 
             
                it 'should emit promise headers for received PUSH_PROMISE' do
         | 
| 108 109 | 
             
                  header = nil
         | 
| 109 110 | 
             
                  s = @client.new_stream
         | 
| 110 | 
            -
                  s.send  | 
| 111 | 
            +
                  s.send headers_frame
         | 
| 111 112 |  | 
| 112 113 | 
             
                  @client.on(:promise) do |stream|
         | 
| 113 114 | 
             
                    stream.on(:promise_headers) do |h|
         | 
| 114 115 | 
             
                      header = h
         | 
| 115 116 | 
             
                    end
         | 
| 116 117 | 
             
                  end
         | 
| 117 | 
            -
                  @client << set_stream_id(f.generate( | 
| 118 | 
            +
                  @client << set_stream_id(f.generate(push_promise_frame), s.id)
         | 
| 118 119 |  | 
| 119 120 | 
             
                  expect(header).to be_a(Array)
         | 
| 120 121 | 
             
                  # expect(header).to eq([%w(a b)])
         | 
| @@ -122,7 +123,7 @@ RSpec.describe HTTP2::Client do | |
| 122 123 |  | 
| 123 124 | 
             
                it 'should auto RST_STREAM promises against locally-RST stream' do
         | 
| 124 125 | 
             
                  s = @client.new_stream
         | 
| 125 | 
            -
                  s.send  | 
| 126 | 
            +
                  s.send headers_frame
         | 
| 126 127 | 
             
                  s.close
         | 
| 127 128 |  | 
| 128 129 | 
             
                  allow(@client).to receive(:send)
         | 
| @@ -131,54 +132,54 @@ RSpec.describe HTTP2::Client do | |
| 131 132 | 
             
                    expect(frame[:stream]).to eq 2
         | 
| 132 133 | 
             
                  end
         | 
| 133 134 |  | 
| 134 | 
            -
                  @client << set_stream_id(f.generate( | 
| 135 | 
            +
                  @client << set_stream_id(f.generate(push_promise_frame), s.id)
         | 
| 135 136 | 
             
                end
         | 
| 136 137 | 
             
              end
         | 
| 137 138 |  | 
| 138 139 | 
             
              context 'alt-svc' do
         | 
| 139 140 | 
             
                context 'received in the connection' do
         | 
| 140 141 | 
             
                  it 'should emit :altsvc when receiving one' do
         | 
| 141 | 
            -
                    @client << f.generate( | 
| 142 | 
            +
                    @client << f.generate(settings_frame)
         | 
| 142 143 | 
             
                    frame = nil
         | 
| 143 144 | 
             
                    @client.on(:altsvc) do |f|
         | 
| 144 145 | 
             
                      frame = f
         | 
| 145 146 | 
             
                    end
         | 
| 146 | 
            -
                    @client << f.generate( | 
| 147 | 
            +
                    @client << f.generate(altsvc_frame)
         | 
| 147 148 | 
             
                    expect(frame).to be_a(Hash)
         | 
| 148 149 | 
             
                  end
         | 
| 149 150 | 
             
                  it 'should not emit :altsvc when the frame when contains no host' do
         | 
| 150 | 
            -
                    @client << f.generate( | 
| 151 | 
            +
                    @client << f.generate(settings_frame)
         | 
| 151 152 | 
             
                    frame = nil
         | 
| 152 153 | 
             
                    @client.on(:altsvc) do |f|
         | 
| 153 154 | 
             
                      frame = f
         | 
| 154 155 | 
             
                    end
         | 
| 155 156 |  | 
| 156 | 
            -
                    @client << f.generate( | 
| 157 | 
            +
                    @client << f.generate(altsvc_frame.merge(origin: nil))
         | 
| 157 158 | 
             
                    expect(frame).to be_nil
         | 
| 158 159 | 
             
                  end
         | 
| 159 160 | 
             
                end
         | 
| 160 161 | 
             
                context 'received in a stream' do
         | 
| 161 162 | 
             
                  it 'should emit :altsvc' do
         | 
| 162 163 | 
             
                    s = @client.new_stream
         | 
| 163 | 
            -
                    s.send  | 
| 164 | 
            +
                    s.send headers_frame
         | 
| 164 165 | 
             
                    s.close
         | 
| 165 166 |  | 
| 166 167 | 
             
                    frame = nil
         | 
| 167 168 | 
             
                    s.on(:altsvc) { |f| frame = f }
         | 
| 168 169 |  | 
| 169 | 
            -
                    @client << set_stream_id(f.generate( | 
| 170 | 
            +
                    @client << set_stream_id(f.generate(altsvc_frame.merge(origin: nil)), s.id)
         | 
| 170 171 |  | 
| 171 172 | 
             
                    expect(frame).to be_a(Hash)
         | 
| 172 173 | 
             
                  end
         | 
| 173 174 | 
             
                  it 'should not emit :alt_svc when the frame when contains a origin' do
         | 
| 174 175 | 
             
                    s = @client.new_stream
         | 
| 175 | 
            -
                    s.send  | 
| 176 | 
            +
                    s.send headers_frame
         | 
| 176 177 | 
             
                    s.close
         | 
| 177 178 |  | 
| 178 179 | 
             
                    frame = nil
         | 
| 179 180 | 
             
                    s.on(:altsvc) { |f| frame = f }
         | 
| 180 181 |  | 
| 181 | 
            -
                    @client << set_stream_id(f.generate( | 
| 182 | 
            +
                    @client << set_stream_id(f.generate(altsvc_frame), s.id)
         | 
| 182 183 |  | 
| 183 184 | 
             
                    expect(frame).to be_nil
         | 
| 184 185 | 
             
                  end
         | 
    
        data/spec/compressor_spec.rb
    CHANGED
    
    | @@ -236,6 +236,12 @@ RSpec.describe HTTP2::Header do | |
| 236 236 | 
             
                    expect(cc.table.size).to be 1
         | 
| 237 237 | 
             
                    expect(cc.table.first[0]).to eq 'test2'
         | 
| 238 238 | 
             
                  end
         | 
| 239 | 
            +
             | 
| 240 | 
            +
                  it 'should reject table size update if exceed limit' do
         | 
| 241 | 
            +
                    cc = EncodingContext.new(table_size: 4096)
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                    expect { cc.process(type: :changetablesize, value: 150_000_000) }.to raise_error(CompressionError)
         | 
| 244 | 
            +
                  end
         | 
| 239 245 | 
             
                end
         | 
| 240 246 |  | 
| 241 247 | 
             
                context 'encode' do
         | 
    
        data/spec/connection_spec.rb
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            require 'helper'
         | 
| 2 2 |  | 
| 3 3 | 
             
            RSpec.describe HTTP2::Connection do
         | 
| 4 | 
            +
              include FrameHelpers
         | 
| 4 5 | 
             
              before(:each) do
         | 
| 5 6 | 
             
                @conn = Client.new
         | 
| 6 7 | 
             
              end
         | 
| @@ -8,21 +9,21 @@ RSpec.describe HTTP2::Connection do | |
| 8 9 | 
             
              let(:f) { Framer.new }
         | 
| 9 10 |  | 
| 10 11 | 
             
              context 'initialization and settings' do
         | 
| 11 | 
            -
                 | 
| 12 | 
            -
                   | 
| 12 | 
            +
                it 'should raise error if first frame is not settings' do
         | 
| 13 | 
            +
                  (frame_types - [settings_frame]).each do |frame|
         | 
| 13 14 | 
             
                    expect { @conn << frame }.to raise_error(ProtocolError)
         | 
| 14 15 | 
             
                    expect(@conn).to be_closed
         | 
| 15 16 | 
             
                  end
         | 
| 16 17 | 
             
                end
         | 
| 17 18 |  | 
| 18 19 | 
             
                it 'should not raise error if first frame is SETTINGS' do
         | 
| 19 | 
            -
                  expect { @conn << f.generate( | 
| 20 | 
            +
                  expect { @conn << f.generate(settings_frame) }.to_not raise_error
         | 
| 20 21 | 
             
                  expect(@conn.state).to eq :connected
         | 
| 21 22 | 
             
                  expect(@conn).to_not be_closed
         | 
| 22 23 | 
             
                end
         | 
| 23 24 |  | 
| 24 25 | 
             
                it 'should raise error if SETTINGS stream != 0' do
         | 
| 25 | 
            -
                  frame = set_stream_id(f.generate( | 
| 26 | 
            +
                  frame = set_stream_id(f.generate(settings_frame), 0x1)
         | 
| 26 27 | 
             
                  expect { @conn << frame }.to raise_error(ProtocolError)
         | 
| 27 28 | 
             
                end
         | 
| 28 29 | 
             
              end
         | 
| @@ -41,7 +42,7 @@ RSpec.describe HTTP2::Connection do | |
| 41 42 |  | 
| 42 43 | 
             
                it 'should reflect incoming settings when SETTINGS is received' do
         | 
| 43 44 | 
             
                  expect(@conn.remote_settings[:settings_header_table_size]).to eq 4096
         | 
| 44 | 
            -
                  settings =  | 
| 45 | 
            +
                  settings = settings_frame
         | 
| 45 46 | 
             
                  settings[:payload] = [[:settings_header_table_size, 256]]
         | 
| 46 47 |  | 
| 47 48 | 
             
                  @conn << f.generate(settings)
         | 
| @@ -50,7 +51,7 @@ RSpec.describe HTTP2::Connection do | |
| 50 51 | 
             
                end
         | 
| 51 52 |  | 
| 52 53 | 
             
                it 'should send SETTINGS ACK when SETTINGS is received' do
         | 
| 53 | 
            -
                  settings =  | 
| 54 | 
            +
                  settings = settings_frame
         | 
| 54 55 | 
             
                  settings[:payload] = [[:settings_header_table_size, 256]]
         | 
| 55 56 |  | 
| 56 57 | 
             
                  # We should expect two frames here (append .twice) - one for the connection setup, and one for the settings ack.
         | 
| @@ -75,34 +76,34 @@ RSpec.describe HTTP2::Connection do | |
| 75 76 | 
             
                end
         | 
| 76 77 |  | 
| 77 78 | 
             
                it 'should change stream limit to received SETTINGS value' do
         | 
| 78 | 
            -
                  @conn << f.generate( | 
| 79 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 79 80 | 
             
                  expect(@conn.remote_settings[:settings_max_concurrent_streams]).to eq 10
         | 
| 80 81 | 
             
                end
         | 
| 81 82 |  | 
| 82 83 | 
             
                it 'should count open streams against stream limit' do
         | 
| 83 84 | 
             
                  s = @conn.new_stream
         | 
| 84 85 | 
             
                  expect(@conn.active_stream_count).to eq 0
         | 
| 85 | 
            -
                  s.receive  | 
| 86 | 
            +
                  s.receive headers_frame
         | 
| 86 87 | 
             
                  expect(@conn.active_stream_count).to eq 1
         | 
| 87 88 | 
             
                end
         | 
| 88 89 |  | 
| 89 90 | 
             
                it 'should not count reserved streams against stream limit' do
         | 
| 90 91 | 
             
                  s1 = @conn.new_stream
         | 
| 91 | 
            -
                  s1.receive  | 
| 92 | 
            +
                  s1.receive push_promise_frame
         | 
| 92 93 | 
             
                  expect(@conn.active_stream_count).to eq 0
         | 
| 93 94 |  | 
| 94 95 | 
             
                  s2 = @conn.new_stream
         | 
| 95 | 
            -
                  s2.send  | 
| 96 | 
            +
                  s2.send push_promise_frame
         | 
| 96 97 | 
             
                  expect(@conn.active_stream_count).to eq 0
         | 
| 97 98 |  | 
| 98 99 | 
             
                  # transition to half closed
         | 
| 99 | 
            -
                  s1.receive  | 
| 100 | 
            -
                  s2.send  | 
| 100 | 
            +
                  s1.receive headers_frame
         | 
| 101 | 
            +
                  s2.send headers_frame
         | 
| 101 102 | 
             
                  expect(@conn.active_stream_count).to eq 2
         | 
| 102 103 |  | 
| 103 104 | 
             
                  # transition to closed
         | 
| 104 | 
            -
                  s1.receive  | 
| 105 | 
            -
                  s2.send  | 
| 105 | 
            +
                  s1.receive data_frame
         | 
| 106 | 
            +
                  s2.send data_frame
         | 
| 106 107 | 
             
                  expect(@conn.active_stream_count).to eq 0
         | 
| 107 108 |  | 
| 108 109 | 
             
                  expect(s1).to be_closed
         | 
| @@ -110,12 +111,12 @@ RSpec.describe HTTP2::Connection do | |
| 110 111 | 
             
                end
         | 
| 111 112 |  | 
| 112 113 | 
             
                it 'should not exceed stream limit set by peer' do
         | 
| 113 | 
            -
                  @conn << f.generate( | 
| 114 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 114 115 |  | 
| 115 116 | 
             
                  expect do
         | 
| 116 117 | 
             
                    10.times do
         | 
| 117 118 | 
             
                      s = @conn.new_stream
         | 
| 118 | 
            -
                      s.send  | 
| 119 | 
            +
                      s.send headers_frame
         | 
| 119 120 | 
             
                    end
         | 
| 120 121 | 
             
                  end.to_not raise_error
         | 
| 121 122 |  | 
| @@ -123,9 +124,9 @@ RSpec.describe HTTP2::Connection do | |
| 123 124 | 
             
                end
         | 
| 124 125 |  | 
| 125 126 | 
             
                it 'should initialize stream with HEADERS priority value' do
         | 
| 126 | 
            -
                  @conn << f.generate( | 
| 127 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 127 128 |  | 
| 128 | 
            -
                  stream, headers = nil,  | 
| 129 | 
            +
                  stream, headers = nil, headers_frame
         | 
| 129 130 | 
             
                  headers[:weight] = 20
         | 
| 130 131 | 
             
                  headers[:stream_dependency] = 0
         | 
| 131 132 | 
             
                  headers[:exclusive] = false
         | 
| @@ -137,16 +138,33 @@ RSpec.describe HTTP2::Connection do | |
| 137 138 | 
             
                end
         | 
| 138 139 |  | 
| 139 140 | 
             
                it 'should initialize idle stream on PRIORITY frame' do
         | 
| 140 | 
            -
                  @conn << f.generate( | 
| 141 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 141 142 |  | 
| 142 143 | 
             
                  stream = nil
         | 
| 143 144 | 
             
                  @conn.on(:stream) { |s| stream = s }
         | 
| 144 | 
            -
                  @conn << f.generate( | 
| 145 | 
            +
                  @conn << f.generate(priority_frame)
         | 
| 145 146 |  | 
| 146 147 | 
             
                  expect(stream.state).to eq :idle
         | 
| 147 148 | 
             
                end
         | 
| 148 149 | 
             
              end
         | 
| 149 150 |  | 
| 151 | 
            +
              context 'cleanup_recently_closed' do
         | 
| 152 | 
            +
                it 'should cleanup old connections' do
         | 
| 153 | 
            +
                  now_ts = Time.now.to_i
         | 
| 154 | 
            +
                  stream_ids = Array.new(4) { @conn.new_stream.id }
         | 
| 155 | 
            +
                  expect(@conn.instance_variable_get('@streams').size).to eq(4)
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                  # Assume that the first 3 streams were closed in different time
         | 
| 158 | 
            +
                  recently_closed = stream_ids[0, 3].zip([now_ts - 100, now_ts - 50, now_ts - 5]).to_h
         | 
| 159 | 
            +
                  @conn.instance_variable_set('@streams_recently_closed', recently_closed)
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                  # Cleanup should delete streams that were closed earlier than 15s ago
         | 
| 162 | 
            +
                  @conn.__send__(:cleanup_recently_closed)
         | 
| 163 | 
            +
                  expect(@conn.instance_variable_get('@streams').size).to eq(2)
         | 
| 164 | 
            +
                  expect(@conn.instance_variable_get('@streams_recently_closed')).to eq(stream_ids[2] => now_ts - 5)
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
              end
         | 
| 167 | 
            +
             | 
| 150 168 | 
             
              context 'Headers pre/post processing' do
         | 
| 151 169 | 
             
                it 'should not concatenate multiple occurences of a header field with the same name' do
         | 
| 152 170 | 
             
                  input = [
         | 
| @@ -173,16 +191,14 @@ RSpec.describe HTTP2::Connection do | |
| 173 191 | 
             
                end
         | 
| 174 192 |  | 
| 175 193 | 
             
                it 'should not split zero-concatenated header field values' do
         | 
| 176 | 
            -
                  input = [
         | 
| 177 | 
            -
             | 
| 178 | 
            -
             | 
| 179 | 
            -
             | 
| 180 | 
            -
                   | 
| 181 | 
            -
             | 
| 182 | 
            -
             | 
| 183 | 
            -
             | 
| 184 | 
            -
                    ['cookie', "a=b\0c=d; e=f"],
         | 
| 185 | 
            -
                  ]
         | 
| 194 | 
            +
                  input = [*RESPONSE_HEADERS,
         | 
| 195 | 
            +
                           ['cache-control', "max-age=60, private\0must-revalidate"],
         | 
| 196 | 
            +
                           ['content-type', 'text/html'],
         | 
| 197 | 
            +
                           ['cookie', "a=b\0c=d; e=f"]]
         | 
| 198 | 
            +
                  expected = [*RESPONSE_HEADERS,
         | 
| 199 | 
            +
                              ['cache-control', "max-age=60, private\0must-revalidate"],
         | 
| 200 | 
            +
                              ['content-type', 'text/html'],
         | 
| 201 | 
            +
                              ['cookie', "a=b\0c=d; e=f"]]
         | 
| 186 202 |  | 
| 187 203 | 
             
                  result = nil
         | 
| 188 204 | 
             
                  @conn.on(:stream) do |stream|
         | 
| @@ -204,13 +220,13 @@ RSpec.describe HTTP2::Connection do | |
| 204 220 | 
             
                end
         | 
| 205 221 |  | 
| 206 222 | 
             
                it 'should update connection and stream windows on SETTINGS' do
         | 
| 207 | 
            -
                  settings, data =  | 
| 223 | 
            +
                  settings, data = settings_frame, data_frame
         | 
| 208 224 | 
             
                  settings[:payload] = [[:settings_initial_window_size, 1024]]
         | 
| 209 225 | 
             
                  data[:payload] = 'x' * 2048
         | 
| 210 226 |  | 
| 211 227 | 
             
                  stream = @conn.new_stream
         | 
| 212 228 |  | 
| 213 | 
            -
                  stream.send  | 
| 229 | 
            +
                  stream.send headers_frame
         | 
| 214 230 | 
             
                  stream.send data
         | 
| 215 231 | 
             
                  expect(stream.remote_window).to eq(DEFAULT_FLOW_WINDOW - 2048)
         | 
| 216 232 | 
             
                  expect(@conn.remote_window).to eq(DEFAULT_FLOW_WINDOW - 2048)
         | 
| @@ -221,7 +237,7 @@ RSpec.describe HTTP2::Connection do | |
| 221 237 | 
             
                end
         | 
| 222 238 |  | 
| 223 239 | 
             
                it 'should initialize streams with window specified by peer' do
         | 
| 224 | 
            -
                  settings =  | 
| 240 | 
            +
                  settings = settings_frame
         | 
| 225 241 | 
             
                  settings[:payload] = [[:settings_initial_window_size, 1024]]
         | 
| 226 242 |  | 
| 227 243 | 
             
                  @conn << f.generate(settings)
         | 
| @@ -229,37 +245,37 @@ RSpec.describe HTTP2::Connection do | |
| 229 245 | 
             
                end
         | 
| 230 246 |  | 
| 231 247 | 
             
                it 'should observe connection flow control' do
         | 
| 232 | 
            -
                  settings, data =  | 
| 248 | 
            +
                  settings, data = settings_frame, data_frame
         | 
| 233 249 | 
             
                  settings[:payload] = [[:settings_initial_window_size, 1000]]
         | 
| 234 250 |  | 
| 235 251 | 
             
                  @conn << f.generate(settings)
         | 
| 236 252 | 
             
                  s1 = @conn.new_stream
         | 
| 237 253 | 
             
                  s2 = @conn.new_stream
         | 
| 238 254 |  | 
| 239 | 
            -
                  s1.send  | 
| 255 | 
            +
                  s1.send headers_frame
         | 
| 240 256 | 
             
                  s1.send data.merge(payload: 'x' * 900)
         | 
| 241 257 | 
             
                  expect(@conn.remote_window).to eq 100
         | 
| 242 258 |  | 
| 243 | 
            -
                  s2.send  | 
| 259 | 
            +
                  s2.send headers_frame
         | 
| 244 260 | 
             
                  s2.send data.merge(payload: 'x' * 200)
         | 
| 245 261 | 
             
                  expect(@conn.remote_window).to eq 0
         | 
| 246 262 | 
             
                  expect(@conn.buffered_amount).to eq 100
         | 
| 247 263 |  | 
| 248 | 
            -
                  @conn << f.generate( | 
| 264 | 
            +
                  @conn << f.generate(window_update_frame.merge(stream: 0, increment: 1000))
         | 
| 249 265 | 
             
                  expect(@conn.buffered_amount).to eq 0
         | 
| 250 266 | 
             
                  expect(@conn.remote_window).to eq 900
         | 
| 251 267 | 
             
                end
         | 
| 252 268 |  | 
| 253 269 | 
             
                it 'should update window when data received is over half of the maximum local window size' do
         | 
| 254 | 
            -
                  settings, data =  | 
| 270 | 
            +
                  settings, data = settings_frame, data_frame
         | 
| 255 271 | 
             
                  conn = Client.new(settings_initial_window_size: 500)
         | 
| 256 272 |  | 
| 257 273 | 
             
                  conn.receive f.generate(settings)
         | 
| 258 274 | 
             
                  s1 = conn.new_stream
         | 
| 259 275 | 
             
                  s2 = conn.new_stream
         | 
| 260 276 |  | 
| 261 | 
            -
                  s1.send  | 
| 262 | 
            -
                  s2.send  | 
| 277 | 
            +
                  s1.send headers_frame
         | 
| 278 | 
            +
                  s2.send headers_frame
         | 
| 263 279 | 
             
                  expect(conn).to receive(:send) do |frame|
         | 
| 264 280 | 
             
                    expect(frame[:type]).to eq :window_update
         | 
| 265 281 | 
             
                    expect(frame[:stream]).to eq 0
         | 
| @@ -275,11 +291,11 @@ RSpec.describe HTTP2::Connection do | |
| 275 291 |  | 
| 276 292 | 
             
              context 'framing' do
         | 
| 277 293 | 
             
                it 'should buffer incomplete frames' do
         | 
| 278 | 
            -
                  settings =  | 
| 294 | 
            +
                  settings = settings_frame
         | 
| 279 295 | 
             
                  settings[:payload] = [[:settings_initial_window_size, 1000]]
         | 
| 280 296 | 
             
                  @conn << f.generate(settings)
         | 
| 281 297 |  | 
| 282 | 
            -
                  frame = f.generate( | 
| 298 | 
            +
                  frame = f.generate(window_update_frame.merge(stream: 0, increment: 1000))
         | 
| 283 299 | 
             
                  @conn << frame
         | 
| 284 300 | 
             
                  expect(@conn.remote_window).to eq 2000
         | 
| 285 301 |  | 
| @@ -295,10 +311,10 @@ RSpec.describe HTTP2::Connection do | |
| 295 311 | 
             
                  ]
         | 
| 296 312 |  | 
| 297 313 | 
             
                  cc = Compressor.new
         | 
| 298 | 
            -
                  headers =  | 
| 314 | 
            +
                  headers = headers_frame
         | 
| 299 315 | 
             
                  headers[:payload] = cc.encode(req_headers)
         | 
| 300 316 |  | 
| 301 | 
            -
                  @conn << f.generate( | 
| 317 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 302 318 | 
             
                  @conn.on(:stream) do |stream|
         | 
| 303 319 | 
             
                    expect(stream).to receive(:<<) do |frame|
         | 
| 304 320 | 
             
                      expect(frame[:payload]).to eq req_headers
         | 
| @@ -315,7 +331,7 @@ RSpec.describe HTTP2::Connection do | |
| 315 331 | 
             
                  ]
         | 
| 316 332 |  | 
| 317 333 | 
             
                  cc = Compressor.new
         | 
| 318 | 
            -
                  h1, h2 =  | 
| 334 | 
            +
                  h1, h2 = headers_frame, continuation_frame
         | 
| 319 335 |  | 
| 320 336 | 
             
                  # Header block fragment might not complete for decompression
         | 
| 321 337 | 
             
                  payload = cc.encode(req_headers)
         | 
| @@ -326,7 +342,7 @@ RSpec.describe HTTP2::Connection do | |
| 326 342 | 
             
                  h2[:payload] = payload # the remaining
         | 
| 327 343 | 
             
                  h2[:stream] = 5
         | 
| 328 344 |  | 
| 329 | 
            -
                  @conn << f.generate( | 
| 345 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 330 346 | 
             
                  @conn.on(:stream) do |stream|
         | 
| 331 347 | 
             
                    expect(stream).to receive(:<<) do |frame|
         | 
| 332 348 | 
             
                      expect(frame[:payload]).to eq req_headers
         | 
| @@ -338,18 +354,18 @@ RSpec.describe HTTP2::Connection do | |
| 338 354 | 
             
                end
         | 
| 339 355 |  | 
| 340 356 | 
             
                it 'should require that split header blocks are a contiguous sequence' do
         | 
| 341 | 
            -
                  headers =  | 
| 357 | 
            +
                  headers = headers_frame
         | 
| 342 358 | 
             
                  headers[:flags] = []
         | 
| 343 359 |  | 
| 344 | 
            -
                  @conn << f.generate( | 
| 360 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 345 361 | 
             
                  @conn << f.generate(headers)
         | 
| 346 | 
            -
                  ( | 
| 362 | 
            +
                  (frame_types - [continuation_frame]).each do |frame|
         | 
| 347 363 | 
             
                    expect { @conn << f.generate(frame.deep_dup) }.to raise_error(ProtocolError)
         | 
| 348 364 | 
             
                  end
         | 
| 349 365 | 
             
                end
         | 
| 350 366 |  | 
| 351 367 | 
             
                it 'should raise compression error on encode of invalid frame' do
         | 
| 352 | 
            -
                  @conn << f.generate( | 
| 368 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 353 369 | 
             
                  stream = @conn.new_stream
         | 
| 354 370 |  | 
| 355 371 | 
             
                  expect do
         | 
| @@ -358,8 +374,8 @@ RSpec.describe HTTP2::Connection do | |
| 358 374 | 
             
                end
         | 
| 359 375 |  | 
| 360 376 | 
             
                it 'should raise connection error on decode of invalid frame' do
         | 
| 361 | 
            -
                  @conn << f.generate( | 
| 362 | 
            -
                  frame = f.generate( | 
| 377 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 378 | 
            +
                  frame = f.generate(data_frame) # Receiving DATA on unopened stream 1 is an error.
         | 
| 363 379 | 
             
                  # Connection errors emit protocol error frames
         | 
| 364 380 | 
             
                  expect { @conn << frame }.to raise_error(ProtocolError)
         | 
| 365 381 | 
             
                end
         | 
| @@ -370,7 +386,7 @@ RSpec.describe HTTP2::Connection do | |
| 370 386 | 
             
                  @conn.settings(settings_max_concurrent_streams: 10,
         | 
| 371 387 | 
             
                                 settings_initial_window_size: 0x7fffffff)
         | 
| 372 388 |  | 
| 373 | 
            -
                  expect(bytes).to eq f.generate( | 
| 389 | 
            +
                  expect(bytes).to eq f.generate(settings_frame)
         | 
| 374 390 | 
             
                end
         | 
| 375 391 |  | 
| 376 392 | 
             
                it 'should compress stream headers' do
         | 
| @@ -482,44 +498,44 @@ RSpec.describe HTTP2::Connection do | |
| 482 498 | 
             
              context 'connection management' do
         | 
| 483 499 | 
             
                it 'should raise error on invalid connection header' do
         | 
| 484 500 | 
             
                  srv = Server.new
         | 
| 485 | 
            -
                  expect { srv << f.generate( | 
| 501 | 
            +
                  expect { srv << f.generate(settings_frame) }.to raise_error(HandshakeError)
         | 
| 486 502 |  | 
| 487 503 | 
             
                  srv = Server.new
         | 
| 488 504 | 
             
                  expect do
         | 
| 489 505 | 
             
                    srv << CONNECTION_PREFACE_MAGIC
         | 
| 490 | 
            -
                    srv << f.generate( | 
| 506 | 
            +
                    srv << f.generate(settings_frame)
         | 
| 491 507 | 
             
                  end.to_not raise_error
         | 
| 492 508 | 
             
                end
         | 
| 493 509 |  | 
| 494 510 | 
             
                it 'should respond to PING frames' do
         | 
| 495 | 
            -
                  @conn << f.generate( | 
| 511 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 496 512 | 
             
                  expect(@conn).to receive(:send) do |frame|
         | 
| 497 513 | 
             
                    expect(frame[:type]).to eq :ping
         | 
| 498 514 | 
             
                    expect(frame[:flags]).to eq [:ack]
         | 
| 499 515 | 
             
                    expect(frame[:payload]).to eq '12345678'
         | 
| 500 516 | 
             
                  end
         | 
| 501 517 |  | 
| 502 | 
            -
                  @conn << f.generate( | 
| 518 | 
            +
                  @conn << f.generate(ping_frame)
         | 
| 503 519 | 
             
                end
         | 
| 504 520 |  | 
| 505 521 | 
             
                it 'should fire callback on PONG' do
         | 
| 506 | 
            -
                  @conn << f.generate( | 
| 522 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 507 523 |  | 
| 508 524 | 
             
                  pong = nil
         | 
| 509 525 | 
             
                  @conn.ping('12345678') { |d| pong = d }
         | 
| 510 | 
            -
                  @conn << f.generate( | 
| 526 | 
            +
                  @conn << f.generate(pong_frame)
         | 
| 511 527 | 
             
                  expect(pong).to eq '12345678'
         | 
| 512 528 | 
             
                end
         | 
| 513 529 |  | 
| 514 530 | 
             
                it 'should fire callback on receipt of GOAWAY' do
         | 
| 515 531 | 
             
                  last_stream, payload, error = nil
         | 
| 516 | 
            -
                  @conn << f.generate( | 
| 532 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 517 533 | 
             
                  @conn.on(:goaway) do |s, e, p|
         | 
| 518 534 | 
             
                    last_stream = s
         | 
| 519 535 | 
             
                    error = e
         | 
| 520 536 | 
             
                    payload = p
         | 
| 521 537 | 
             
                  end
         | 
| 522 | 
            -
                  @conn << f.generate( | 
| 538 | 
            +
                  @conn << f.generate(goaway_frame.merge(last_stream: 17, payload: 'test'))
         | 
| 523 539 |  | 
| 524 540 | 
             
                  expect(last_stream).to eq 17
         | 
| 525 541 | 
             
                  expect(error).to eq :no_error
         | 
| @@ -536,8 +552,8 @@ RSpec.describe HTTP2::Connection do | |
| 536 552 | 
             
                end
         | 
| 537 553 |  | 
| 538 554 | 
             
                it 'should raise error when opening new stream after receiving GOAWAY' do
         | 
| 539 | 
            -
                  @conn << f.generate( | 
| 540 | 
            -
                  @conn << f.generate( | 
| 555 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 556 | 
            +
                  @conn << f.generate(goaway_frame)
         | 
| 541 557 | 
             
                  expect { @conn.new_stream }.to raise_error(ConnectionClosed)
         | 
| 542 558 | 
             
                end
         | 
| 543 559 |  | 
| @@ -545,26 +561,26 @@ RSpec.describe HTTP2::Connection do | |
| 545 561 | 
             
                  @conn.goaway
         | 
| 546 562 | 
             
                  expect(@conn).to be_closed
         | 
| 547 563 |  | 
| 548 | 
            -
                  expect { @conn << f.generate( | 
| 549 | 
            -
                  expect { @conn << f.generate( | 
| 550 | 
            -
                  expect { @conn << f.generate( | 
| 564 | 
            +
                  expect { @conn << f.generate(settings_frame) }.not_to raise_error(ProtocolError)
         | 
| 565 | 
            +
                  expect { @conn << f.generate(ping_frame) }.not_to raise_error(ProtocolError)
         | 
| 566 | 
            +
                  expect { @conn << f.generate(goaway_frame) }.not_to raise_error(ProtocolError)
         | 
| 551 567 | 
             
                end
         | 
| 552 568 |  | 
| 553 569 | 
             
                it 'should process connection management frames after GOAWAY' do
         | 
| 554 | 
            -
                  @conn << f.generate( | 
| 555 | 
            -
                  @conn << f.generate( | 
| 556 | 
            -
                  @conn << f.generate( | 
| 557 | 
            -
                  @conn << f.generate( | 
| 558 | 
            -
                  @conn << f.generate( | 
| 570 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 571 | 
            +
                  @conn << f.generate(headers_frame)
         | 
| 572 | 
            +
                  @conn << f.generate(goaway_frame)
         | 
| 573 | 
            +
                  @conn << f.generate(headers_frame.merge(stream: 7))
         | 
| 574 | 
            +
                  @conn << f.generate(push_promise_frame)
         | 
| 559 575 |  | 
| 560 576 | 
             
                  expect(@conn.active_stream_count).to eq 1
         | 
| 561 577 | 
             
                end
         | 
| 562 578 |  | 
| 563 579 | 
             
                it 'should raise error on frame for invalid stream ID' do
         | 
| 564 | 
            -
                  @conn << f.generate( | 
| 580 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 565 581 |  | 
| 566 582 | 
             
                  expect do
         | 
| 567 | 
            -
                    @conn << f.generate( | 
| 583 | 
            +
                    @conn << f.generate(data_frame.merge(stream: 31))
         | 
| 568 584 | 
             
                  end.to raise_error(ProtocolError)
         | 
| 569 585 | 
             
                end
         | 
| 570 586 |  | 
| @@ -573,12 +589,12 @@ RSpec.describe HTTP2::Connection do | |
| 573 589 | 
             
                  srv << CONNECTION_PREFACE_MAGIC
         | 
| 574 590 |  | 
| 575 591 | 
             
                  stream = srv.new_stream
         | 
| 576 | 
            -
                  stream.send  | 
| 577 | 
            -
                  stream.send  | 
| 592 | 
            +
                  stream.send headers_frame
         | 
| 593 | 
            +
                  stream.send data_frame
         | 
| 578 594 | 
             
                  stream.close
         | 
| 579 595 |  | 
| 580 596 | 
             
                  expect do
         | 
| 581 | 
            -
                    srv << f.generate( | 
| 597 | 
            +
                    srv << f.generate(rst_stream_frame.merge(stream: stream.id))
         | 
| 582 598 | 
             
                  end.to_not raise_error
         | 
| 583 599 | 
             
                end
         | 
| 584 600 |  | 
| @@ -596,7 +612,7 @@ RSpec.describe HTTP2::Connection do | |
| 596 612 | 
             
                    [frame]
         | 
| 597 613 | 
             
                  end
         | 
| 598 614 |  | 
| 599 | 
            -
                  expect { @conn << f.generate( | 
| 615 | 
            +
                  expect { @conn << f.generate(data_frame) }.to raise_error(ProtocolError)
         | 
| 600 616 | 
             
                end
         | 
| 601 617 | 
             
              end
         | 
| 602 618 |  | 
| @@ -625,8 +641,8 @@ RSpec.describe HTTP2::Connection do | |
| 625 641 | 
             
                end
         | 
| 626 642 |  | 
| 627 643 | 
             
                it '.goaway should generate GOAWAY frame with last processed stream ID' do
         | 
| 628 | 
            -
                  @conn << f.generate( | 
| 629 | 
            -
                  @conn << f.generate( | 
| 644 | 
            +
                  @conn << f.generate(settings_frame)
         | 
| 645 | 
            +
                  @conn << f.generate(headers_frame.merge(stream: 17))
         | 
| 630 646 |  | 
| 631 647 | 
             
                  expect(@conn).to receive(:send) do |frame|
         | 
| 632 648 | 
             
                    expect(frame[:type]).to eq :goaway
         |