Skip to content

Commit

Permalink
improve #encode_headers
Browse files Browse the repository at this point in the history
early exit for the common case of single header frame, shared empty array for intermediate continuation frames
  • Loading branch information
HoneyryderChuck committed Jun 25, 2024
1 parent aeec322 commit b6e926d
Showing 1 changed file with 23 additions and 17 deletions.
40 changes: 23 additions & 17 deletions lib/http/2/connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ module HTTP2
REQUEST_MANDATORY_HEADERS = %w[:scheme :method :authority :path].freeze
RESPONSE_MANDATORY_HEADERS = %w[:status].freeze

EMPTY = [].freeze

# Connection encapsulates all of the connection, stream, flow-control,
# error management, and other processing logic required for a well-behaved
# HTTP 2.0 endpoint.
Expand Down Expand Up @@ -695,31 +697,35 @@ def decode_headers(frame)
# @return [Array of Frame]
def encode_headers(frame)
payload = frame[:payload]
payload = @compressor.encode(payload) unless payload.is_a?(String)
begin
payload = frame[:payload] = @compressor.encode(payload) unless payload.is_a?(String)
rescue StandardError => e
connection_error(:compression_error, e: e)
end

# if single frame, return immediately
return [frame] if payload.bytesize <= @remote_settings[:settings_max_frame_size]

frames = []

begin
while payload && payload.bytesize > 0
cont = frame.dup
until payload.nil? || payload.empty?
cont = frame.dup

# first frame remains HEADERS
unless frames.empty?
cont[:type] = :continuation
cont[:flags] = []
cont[:payload] = payload.byteslice(0, @remote_settings[:settings_max_frame_size])
payload = payload.byteslice(@remote_settings[:settings_max_frame_size]..-1)
frames << cont
cont[:flags] = EMPTY
end

if frames.empty?
frames << frame
else
frames.first[:type] = frame[:type]
frames.first[:flags] = frame[:flags] - [:end_headers]
frames.last[:flags] << :end_headers
end
rescue StandardError => e
connection_error(:compression_error, e: e)
cont[:payload] = payload.byteslice(0, @remote_settings[:settings_max_frame_size])
payload = payload.byteslice(@remote_settings[:settings_max_frame_size]..-1)

frames << cont
end

frames.first[:flags].delete(:end_headers)
frames.last[:flags] = [:end_headers]

frames
end

Expand Down

0 comments on commit b6e926d

Please sign in to comment.