Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ jobs:
RUBY_ENGINE=`ruby -e 'puts RUBY_ENGINE'`
if [[ "$RUBY_ENGINE" = "ruby" ]] && [[ ${RUBY_VERSION:0:1} = "3" ]] && [[ ! $RUBYOPT =~ "jit" ]]; then
echo "running runtime type checking..."
bundle exec rbs collection install
export RUBYOPT="-rbundler/setup -rrbs/test/setup"
export RBS_TEST_RAISE="true"
export RBS_TEST_LOGLEVEL="error"
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ group :development do
gem "rubocop"
gem "rubocop-performance"
end
gem "irb" if RUBY_VERSION >= "4.0.0"
end

group :docs do
Expand Down
1 change: 1 addition & 0 deletions lib/http/2.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ module HTTP2
require "http/2/flow_buffer"
require "http/2/header"
require "http/2/framer"
require "http/2/settings"
require "http/2/connection"
require "http/2/client"
require "http/2/server"
Expand Down
2 changes: 1 addition & 1 deletion lib/http/2/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def send_connection_preface
@state = :connected
emit(:frame, CONNECTION_PREFACE_MAGIC)

payload = @local_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
payload = @local_settings.each_pair.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
settings(payload)
end

Expand Down
39 changes: 10 additions & 29 deletions lib/http/2/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,6 @@ module HTTP2
# Default stream_limit
DEFAULT_MAX_CONCURRENT_STREAMS = 100

# Default values for SETTINGS frame, as defined by the spec.
SPEC_DEFAULT_CONNECTION_SETTINGS = {
settings_header_table_size: 4096,
settings_enable_push: 1, # enabled for servers
settings_max_concurrent_streams: Framer::MAX_STREAM_ID, # unlimited
settings_initial_window_size: 65_535,
settings_max_frame_size: 16_384,
settings_max_header_list_size: (2 << 30) - 1 # unlimited
}.freeze

DEFAULT_CONNECTION_SETTINGS = {
settings_header_table_size: 4096,
settings_enable_push: 1, # enabled for servers
settings_max_concurrent_streams: 100,
settings_initial_window_size: 65_535,
settings_max_frame_size: 16_384,
settings_max_header_list_size: (2 << 30) - 1 # unlimited
}.freeze

# Default stream priority (lower values are higher priority).
DEFAULT_WEIGHT = 16

Expand Down Expand Up @@ -80,8 +61,8 @@ class Connection
# Initializes new connection object.
#
def initialize(settings = {})
@local_settings = DEFAULT_CONNECTION_SETTINGS.merge(settings)
@remote_settings = SPEC_DEFAULT_CONNECTION_SETTINGS.dup
@local_settings = Settings.new(**settings)
@remote_settings = Settings.new(settings_max_concurrent_streams: Framer::MAX_STREAM_ID)

@compressor = Header::Compressor.new(settings)
@decompressor = Header::Decompressor.new(settings)
Expand All @@ -93,11 +74,11 @@ def initialize(settings = {})
@oldest_stream_recently_closed = nil
@pending_settings = []

@framer = Framer.new(@local_settings[:settings_max_frame_size])
@framer = Framer.new(@local_settings.settings_max_frame_size)

@local_window_limit = @local_settings[:settings_initial_window_size]
@local_window_limit = @local_settings.settings_initial_window_size
@local_window = @local_window_limit
@remote_window_limit = @remote_settings[:settings_initial_window_size]
@remote_window_limit = @remote_settings.settings_initial_window_size
@remote_window = @remote_window_limit

@recv_buffer = "".b
Expand Down Expand Up @@ -137,7 +118,7 @@ def new_stream(**args)

stream = activate_stream(
id: @stream_id,
max_concurrent_streams: @remote_settings[:settings_max_concurrent_streams],
max_concurrent_streams: @remote_settings.settings_max_concurrent_streams,
**args
)
@last_stream_id = stream.id
Expand Down Expand Up @@ -213,7 +194,7 @@ def receive(data)
elsif read_str(@recv_buffer, 24) == CONNECTION_PREFACE_MAGIC
# MAGIC is OK. Send our settings
@state = :waiting_connection_preface
payload = @local_settings.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
payload = @local_settings.each_pair.reject { |k, v| v == SPEC_DEFAULT_CONNECTION_SETTINGS[k] }
settings(payload)
else
raise HandshakeError
Expand Down Expand Up @@ -254,7 +235,7 @@ def receive(data)
# prevent HTTP/2 CONTINUATION FLOOD
# same heuristic as the one from HAProxy: https://www.haproxy.com/blog/haproxy-is-resilient-to-the-http-2-continuation-flood
# different mitigation (connection closed, instead of 400 response)
unless @continuation_size < @local_settings[:settings_max_frame_size]
unless @continuation_size < @local_settings.settings_max_frame_size
connection_error(:protocol_error,
msg: "too many continuations received")
end
Expand Down Expand Up @@ -752,7 +733,7 @@ def encode_headers(headers_frame)
#: @type var payload: String
headers_frame[:payload] = payload

max_frame_size = @remote_settings[:settings_max_frame_size]
max_frame_size = @remote_settings.settings_max_frame_size

# if single frame, return immediately
if payload.bytesize <= max_frame_size
Expand Down Expand Up @@ -796,7 +777,7 @@ def encode_headers(headers_frame)
# @param priority [Integer]
# @param window [Integer]
# @param parent [Stream]
def activate_stream(id:, max_concurrent_streams: @local_settings[:settings_max_concurrent_streams], **args)
def activate_stream(id:, max_concurrent_streams: @local_settings.settings_max_concurrent_streams, **args)
connection_error(msg: "Stream ID already exists") if @streams.key?(id)

# SETTINGS_MAX_CONCURRENT_STREAMS limits the number of concurrent streams that the sender
Expand Down
2 changes: 1 addition & 1 deletion lib/http/2/header/decompressor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ def header(buf)
# @param frame [HTTP2::Frame, nil]
# @return [Array] +[[name, value], ...]
def decode(buf, frame = nil)
list = []
list = [] #: Array[header_pair]
decoding_pseudo_headers = true
@cc.listen_on_table do
until buf.empty?
Expand Down
5 changes: 3 additions & 2 deletions lib/http/2/header/encoding_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,11 +217,12 @@ def process(cmd)
emit = [name, value]

# add to table
if type == :incremental && size_check?(name.bytesize + value.bytesize + 32)
cmdsize = name.bytesize + value.bytesize + 32
if type == :incremental && size_check?(cmdsize)
@table.unshift(emit)
@unshifts += 1
@table_by_field[name].unshift([value, @unshifts])
@current_table_size += name.bytesize + value.bytesize + 32
@current_table_size += cmdsize
@_table_updated = true
end

Expand Down
2 changes: 1 addition & 1 deletion lib/http/2/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def upgrade(settings, headers, body)
dependency: 0,
exclusive: false,
payload: headers
}
} #: headers_frame

if body.empty?
headers_frame[:flags] |= END_STREAM
Expand Down
36 changes: 36 additions & 0 deletions lib/http/2/settings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

module HTTP2
MAX_HEADER_LIST_SIZE = (2 << 30) - 1

# Default values for SETTINGS frame, as defined by the spec.
SPEC_DEFAULT_CONNECTION_SETTINGS = {
settings_header_table_size: 4096,
settings_enable_push: 1, # enabled for servers
settings_max_concurrent_streams: Framer::MAX_STREAM_ID, # unlimited
settings_initial_window_size: 65_535,
settings_max_frame_size: 16_384,
settings_max_header_list_size: MAX_HEADER_LIST_SIZE # unlimited
}.freeze

Settings = Struct.new(
:settings_header_table_size,
:settings_enable_push,
:settings_max_concurrent_streams,
:settings_initial_window_size,
:settings_max_frame_size,
:settings_max_header_list_size,
keyword_init: true
) do
def initialize(
settings_header_table_size: 4096,
settings_enable_push: 1,
settings_max_concurrent_streams: 100,
settings_initial_window_size: 65_535,
settings_max_frame_size: 16_384,
settings_max_header_list_size: MAX_HEADER_LIST_SIZE
)
super
end
end
end
21 changes: 21 additions & 0 deletions rbs_collection.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Download sources
sources:
- type: git
name: ruby/gem_rbs_collection
remote: https://github.com/ruby/gem_rbs_collection.git
revision: main
repo_dir: gems

# You can specify local directories as sources also.
# - type: local
# path: path/to/your/local/repository

# A directory to install the downloaded RBSs
path: .gem_rbs_collection

gems:
# If you want to avoid installing rbs files for gems, you can specify them here.
- name: rbs
ignore: true
- name: steep
ignore: true
19 changes: 6 additions & 13 deletions sig/2.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,10 @@ module HTTP2

type connection_opts = Hash[Symbol, untyped]

type settings_hash = {
settings_header_table_size: Integer,
settings_enable_push: Integer,
settings_max_concurrent_streams: Integer,
settings_initial_window_size: Integer,
settings_max_frame_size: Integer,
settings_max_header_list_size: Integer
}

type settings_ary = Array[settings_enum]

type settings_enum = Enumerable[[Symbol, Integer]]

SPEC_DEFAULT_CONNECTION_SETTINGS: settings_hash

DEFAULT_CONNECTION_SETTINGS: settings_hash

DEFAULT_WEIGHT: Integer

CONNECTION_PREFACE_MAGIC: String
Expand All @@ -36,6 +23,12 @@ module HTTP2

RESPONSE_MANDATORY_HEADERS: Array[String]

CONNECTION_FRAME_TYPES: Array[Symbol]

STREAM_OPEN_STATES: Array[Symbol]

HEADERS_FRAME_TYPES: Array[Symbol]

# Frame flags
END_STREAM: Integer
ACK: Integer
Expand Down
16 changes: 3 additions & 13 deletions sig/connection.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,15 @@ module HTTP2
include Emitter
include BufferUtils

REQUEST_MANDATORY_HEADERS: Array[String]

RESPONSE_MANDATORY_HEADERS: Array[String]

CONNECTION_FRAME_TYPES: Array[Symbol]

HEADERS_FRAME_TYPES: Array[Symbol]

STREAM_OPEN_STATES: Array[Symbol]

attr_reader state: Symbol

attr_reader local_window: Integer
attr_reader remote_window: Integer

alias window local_window

attr_reader remote_settings: settings_hash
attr_reader local_settings: settings_hash
attr_reader remote_settings: Settings
attr_reader local_settings: Settings
attr_reader pending_settings: settings_ary

attr_accessor active_stream_count: Integer
Expand Down Expand Up @@ -87,7 +77,7 @@ module HTTP2

def connection_frame?: (frame) -> bool

def connection_management: (connection_frame frame) -> void
def connection_management: (frame frame) -> void

def ping_management: (frame) -> void

Expand Down
2 changes: 2 additions & 0 deletions sig/server.rbs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module HTTP2
class Server < Connection
@origin_set: Array[String]
@origins_sent: bool

def upgrade: (String settings, Enumerable[header_pair] headers, String body) -> void

Expand Down
26 changes: 26 additions & 0 deletions sig/settings.rbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module HTTP2
MAX_HEADER_LIST_SIZE: Integer

SPEC_DEFAULT_CONNECTION_SETTINGS: Hash[Symbol, Integer]

class Settings # this is actually a Struct, but RBS does not support it yet
attr_reader settings_header_table_size: Integer
attr_reader settings_enable_push: Integer
attr_reader settings_max_concurrent_streams: Integer
attr_reader settings_initial_window_size: Integer
attr_reader settings_max_frame_size: Integer
attr_reader settings_max_header_list_size: Integer

def initalize: (
?settings_header_table_size: Integer,
?settings_enable_push: Integer,
?settings_max_concurrent_streams: Integer,
?settings_initial_window_size: Integer,
?settings_max_frame_size: Integer,
?settings_max_header_list_size: Integer
) -> void

def each_pair: () { (Symbol, Integer) -> void } -> self
| () -> Enumerable[[Symbol, Integer]]
end
end
Loading