Skip to content

Commit

Permalink
Improvements to tests and implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
ioquatix committed Dec 1, 2024
1 parent 11fab9c commit fbe89eb
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 76 deletions.
16 changes: 13 additions & 3 deletions lib/protocol/http2/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,25 @@ def send_connection_preface(settings = [])
@framer.write_connection_preface

# We don't support RFC7540 priorities:
settings = settings.to_a
settings << [Settings::NO_RFC7540_PRIORITIES, 1]
if settings.is_a?(Hash)
settings = settings.dup
else
settings = settings.to_h
end

unless settings.key?(Settings::NO_RFC7540_PRIORITIES)
settings = settings.dup
settings[Settings::NO_RFC7540_PRIORITIES] = 1
end

send_settings(settings)

yield if block_given?

read_frame do |frame|
raise ProtocolError, "First frame must be #{SettingsFrame}, but got #{frame.class}" unless frame.is_a? SettingsFrame
unless frame.is_a? SettingsFrame
raise ProtocolError, "First frame must be #{SettingsFrame}, but got #{frame.class}"
end
end
else
raise ProtocolError, "Cannot send connection preface in state #{@state}"
Expand Down
12 changes: 10 additions & 2 deletions lib/protocol/http2/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,16 @@ def read_connection_preface(settings = [])
@framer.read_connection_preface

# We don't support RFC7540 priorities:
settings = settings.to_a
settings << [Settings::NO_RFC7540_PRIORITIES, 1]
if settings.is_a?(Hash)
settings = settings.dup
else
settings = settings.to_h
end

unless settings.key?(Settings::NO_RFC7540_PRIORITIES)
settings = settings.dup
settings[Settings::NO_RFC7540_PRIORITIES] = 1
end

send_settings(settings)

Expand Down
70 changes: 26 additions & 44 deletions lib/protocol/http2/settings_frame.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,22 @@ class Settings
:maximum_header_list_size=,
nil,
:enable_connect_protocol=,
:no_rfc7540_priorities=,
]

def initialize
# These limits are taken from the RFC:
# https://tools.ietf.org/html/rfc7540#section-6.5.2
@header_table_size = 4096
@enable_push = 1
@maximum_concurrent_streams = 0xFFFFFFFF
@initial_window_size = 0xFFFF # 2**16 - 1
@maximum_frame_size = 0x4000 # 2**14
@maximum_header_list_size = 0xFFFFFFFF
@enable_connect_protocol = 0
@no_rfc7540_priorities = 0
end

# Allows the sender to inform the remote endpoint of the maximum size of the header compression table used to decode header blocks, in octets.
attr_accessor :header_table_size

Expand Down Expand Up @@ -91,16 +105,18 @@ def enable_connect_protocol?
@enable_connect_protocol == 1
end

def initialize
# These limits are taken from the RFC:
# https://tools.ietf.org/html/rfc7540#section-6.5.2
@header_table_size = 4096
@enable_push = 1
@maximum_concurrent_streams = 0xFFFFFFFF
@initial_window_size = 0xFFFF # 2**16 - 1
@maximum_frame_size = 0x4000 # 2**14
@maximum_header_list_size = 0xFFFFFFFF
@enable_connect_protocol = 0
attr :no_rfc7540_priorities

def no_rfc7540_priorities= value
if value == 0 or value == 1
@no_rfc7540_priorities = value
else
raise ProtocolError, "Invalid value for no_rfc7540_priorities: #{value}"
end
end

def no_rfc7540_priorities?
@no_rfc7540_priorities == 1
end

def update(changes)
Expand All @@ -110,40 +126,6 @@ def update(changes)
end
end
end

def difference(other)
changes = []

if @header_table_size != other.header_table_size
changes << [HEADER_TABLE_SIZE, @header_table_size]
end

if @enable_push != other.enable_push
changes << [ENABLE_PUSH, @enable_push]
end

if @maximum_concurrent_streams != other.maximum_concurrent_streams
changes << [MAXIMUM_CONCURRENT_STREAMS, @maximum_concurrent_streams]
end

if @initial_window_size != other.initial_window_size
changes << [INITIAL_WINDOW_SIZE, @initial_window_size]
end

if @maximum_frame_size != other.maximum_frame_size
changes << [MAXIMUM_FRAME_SIZE, @maximum_frame_size]
end

if @maximum_header_list_size != other.maximum_header_list_size
changes << [MAXIMUM_HEADER_LIST_SIZE, @maximum_header_list_size]
end

if @enable_connect_protocol != other.enable_connect_protocol
changes << [ENABLE_CONNECT_PROTOCOL, @enable_connect_protocol]
end

return changes
end
end

class PendingSettings
Expand Down
23 changes: 22 additions & 1 deletion test/protocol/http2/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

client_settings_frame = framer.read_frame
expect(client_settings_frame).to be_a Protocol::HTTP2::SettingsFrame
expect(client_settings_frame.unpack).to be == settings
expect(client_settings_frame.unpack).to be == settings + [[Protocol::HTTP2::Settings::NO_RFC7540_PRIORITIES, 1]]

# Fake (empty) server settings:
server_settings_frame = Protocol::HTTP2::SettingsFrame.new
Expand All @@ -52,6 +52,27 @@
expect(client.local_settings.header_table_size).to be == 1024
end

it "should fail if the server does not reply with settings frame" do
data_frame = Protocol::HTTP2::DataFrame.new
data_frame.pack("Hello, World!")

expect do
client.send_connection_preface(settings) do
framer.write_frame(data_frame)
end
end.to raise_exception(Protocol::HTTP2::ProtocolError, message: be =~ /First frame must be Protocol::HTTP2::SettingsFrame/)
end

it "should send connection preface with no RFC7540 priorities" do
server_settings_frame = client.send_connection_preface({}) do
client_settings_frame = server.read_connection_preface({})

expect(client_settings_frame.unpack).to be == [[Protocol::HTTP2::Settings::NO_RFC7540_PRIORITIES, 1]]
end

expect(server_settings_frame.unpack).to be == [[Protocol::HTTP2::Settings::NO_RFC7540_PRIORITIES, 1]]
end

it "can generate a stream id" do
id = client.next_stream_id
expect(id).to be == 1
Expand Down
2 changes: 1 addition & 1 deletion test/protocol/http2/priority_update_frame.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ def before
expect(server_stream.priority).to be == ["u=1", "i"]
end
end
end
end
2 changes: 1 addition & 1 deletion test/protocol/http2/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@
# The server immediately sends its own settings frame...
frame = framer.read_frame
expect(frame).to be_a Protocol::HTTP2::SettingsFrame
expect(frame.unpack).to be == server_settings
expect(frame.unpack).to be == server_settings + [[Protocol::HTTP2::Settings::NO_RFC7540_PRIORITIES, 1]]

# And then it acknowledges the client settings:
frame = framer.read_frame
Expand Down
43 changes: 19 additions & 24 deletions test/protocol/http2/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,23 @@
end
end

with "#no_rfc7540_priorities" do
it "is disabled by default" do
expect(settings.no_rfc7540_priorities).to be == 0
expect(settings).not.to be(:no_rfc7540_priorities?)
end

it "can be enabled" do
settings.no_rfc7540_priorities = 1
expect(settings.no_rfc7540_priorities).to be == 1
expect(settings).to be(:no_rfc7540_priorities?)
end

it "only accepts valid values" do
expect{settings.no_rfc7540_priorities = 2}.to raise_exception(Protocol::HTTP2::ProtocolError)
end
end

it "has the expected defaults" do
expect(settings).to have_attributes(
header_table_size: be == 4096,
Expand All @@ -81,7 +98,8 @@
initial_window_size: be == 0xFFFF,
maximum_frame_size: be == 0x4000,
maximum_header_list_size: be == 0xFFFFFFFF,
enable_connect_protocol: be == 0
enable_connect_protocol: be == 0,
no_rfc7540_priorities: be == 0,
)
end

Expand All @@ -106,29 +124,6 @@
enable_connect_protocol: be == 1
)
end

it "can compute the difference between two settings" do
other = subject.new
other.update({
Protocol::HTTP2::Settings::HEADER_TABLE_SIZE => 100,
Protocol::HTTP2::Settings::ENABLE_PUSH => 0,
Protocol::HTTP2::Settings::MAXIMUM_CONCURRENT_STREAMS => 10,
Protocol::HTTP2::Settings::INITIAL_WINDOW_SIZE => 1000,
Protocol::HTTP2::Settings::MAXIMUM_FRAME_SIZE => 20000,
Protocol::HTTP2::Settings::MAXIMUM_HEADER_LIST_SIZE => 100000,
Protocol::HTTP2::Settings::ENABLE_CONNECT_PROTOCOL => 1
})

expect(other.difference(settings)).to be == [
[Protocol::HTTP2::Settings::HEADER_TABLE_SIZE, 100],
[Protocol::HTTP2::Settings::ENABLE_PUSH, 0],
[Protocol::HTTP2::Settings::MAXIMUM_CONCURRENT_STREAMS, 10],
[Protocol::HTTP2::Settings::INITIAL_WINDOW_SIZE, 1000],
[Protocol::HTTP2::Settings::MAXIMUM_FRAME_SIZE, 20000],
[Protocol::HTTP2::Settings::MAXIMUM_HEADER_LIST_SIZE, 100000],
[Protocol::HTTP2::Settings::ENABLE_CONNECT_PROTOCOL, 1]
]
end
end

describe Protocol::HTTP2::PendingSettings do
Expand Down

0 comments on commit fbe89eb

Please sign in to comment.