1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
|
# frozen_string_literal: true
require "helper"
require "shared_examples/connection"
RSpec.describe HTTP2::Server do
include FrameHelpers
it_behaves_like "a connection" do
let(:conn) do
srv = Server.new
srv << CONNECTION_PREFACE_MAGIC
srv
end
let(:connected_conn) do
srv = Server.new
srv << CONNECTION_PREFACE_MAGIC
srv << f.generate(settings_frame)
srv
end
end
let(:srv) { Server.new }
let(:f) { Framer.new }
context "initialization and settings" do
it "should return even stream IDs" do
expect(srv.new_stream.id).to be_even
end
it "should emit SETTINGS on new connection" do
frames = []
srv.on(:frame) { |recv| frames << recv }
srv << CONNECTION_PREFACE_MAGIC
expect(f.parse(frames[0])[:type]).to eq :settings
end
it "should initialize client with custom connection settings" do
frames = []
srv = Server.new(settings_max_concurrent_streams: 200,
settings_initial_window_size: 2**10)
srv.on(:frame) { |recv| frames << recv }
srv << CONNECTION_PREFACE_MAGIC
frame = f.parse(frames[0])
expect(frame[:type]).to eq :settings
expect(frame[:payload]).to include([:settings_max_concurrent_streams, 200])
expect(frame[:payload]).to include([:settings_initial_window_size, 2**10])
end
end
it "should allow server push" do
client = Client.new
client.on(:frame) { |bytes| srv << bytes }
srv.on(:stream) do |stream|
expect do
stream.promise({ ":method" => "GET" }) {}
end.to_not raise_error
end
client.new_stream
client.send headers_frame
end
context "should allow upgrade" do
let(:settings) { Client.settings_header(settings_frame[:payload]) }
it "for bodyless responses" do
expect(srv.active_stream_count).to eq(0)
srv.upgrade(settings, RESPONSE_HEADERS, "")
expect(srv.active_stream_count).to eq(1)
end
it "for responses with body" do
expect(srv.active_stream_count).to eq(0)
srv.upgrade(settings, RESPONSE_HEADERS + [[:content_length, 4]], "bang")
expect(srv.active_stream_count).to eq(1)
end
end
it "should allow to send supported origins" do
srv.origin_set = %w[https://www.youtube.com]
origins = []
client = Client.new
client.on(:frame) { |bytes| srv << bytes }
client.on(:origin) { |origin| origins << origin }
srv.on(:frame) { |bytes| client << bytes }
client.new_stream
client.send headers_frame
expect(origins).to eq(%w[https://www.youtube.com])
end
context "connection management" do
it "should raise error on invalid connection header" do
srv = Server.new
expect { srv << f.generate(settings_frame) }.to raise_error(HandshakeError)
srv = Server.new
expect do
srv << CONNECTION_PREFACE_MAGIC
srv << f.generate(settings_frame)
end.to_not raise_error
end
it "should not raise an error on frame for a closed stream ID" do
srv = Server.new
srv << CONNECTION_PREFACE_MAGIC
stream = srv.new_stream
stream.send headers_frame
stream.send data_frame
stream.close
# WINDOW_UPDATE or RST_STREAM frames can be received in this state
# for a short period
expect do
srv << f.generate(rst_stream_frame.merge(stream: stream.id))
end.to_not raise_error
expect do
srv << f.generate(window_update_frame.merge(stream: stream.id))
end.to_not raise_error
# PRIORITY frames can be sent on closed streams to prioritize
# streams that are dependent on the closed stream.
expect do
srv << f.generate(priority_frame.merge(stream: stream.id))
end.to_not raise_error
end
end
context "stream management" do
it "should initialize stream with HEADERS priority value" do
srv << CONNECTION_PREFACE_MAGIC
srv << f.generate(settings_frame)
stream = nil
headers = headers_frame
headers[:weight] = 20
headers[:dependency] = 0
headers[:exclusive] = false
srv.on(:stream) { |s| stream = s }
srv << f.generate(headers)
expect(stream.weight).to eq 20
end
it "should process connection management frames after GOAWAY" do
srv << CONNECTION_PREFACE_MAGIC
srv << f.generate(settings_frame)
srv << f.generate(headers_frame)
srv << f.generate(goaway_frame)
srv << f.generate(headers_frame.merge(stream: 3))
expect(srv.active_stream_count).to eq 1
end
end
context "API" do
it ".goaway should generate GOAWAY frame with last processed stream ID" do
srv << CONNECTION_PREFACE_MAGIC
srv << f.generate(settings_frame)
srv << f.generate(headers_frame)
expect(srv).to receive(:send) do |frame|
expect(frame[:type]).to eq :goaway
expect(frame[:last_stream]).to eq 1
expect(frame[:error]).to eq :internal_error
expect(frame[:payload]).to eq "payload"
end
srv.goaway(:internal_error, "payload")
end
end
end
|