diff --git a/lib/http/2/connection.rb b/lib/http/2/connection.rb index 420b98f6..23c6702a 100644 --- a/lib/http/2/connection.rb +++ b/lib/http/2/connection.rb @@ -670,11 +670,14 @@ def connection_settings(frame) # Setting header table size might cause some headers evicted case side when :local - @compressor.table_size = v + @decompressor.table_size_limit = v when :remote - @decompressor.table_size = v + @compressor.table_size = v end + when :settings_max_header_list_size + @decompressor.max_header_list_size = v if side == :local + when :settings_enable_push # nothing to do diff --git a/lib/http/2/header/decompressor.rb b/lib/http/2/header/decompressor.rb index b58ce41f..85e67ee5 100644 --- a/lib/http/2/header/decompressor.rb +++ b/lib/http/2/header/decompressor.rb @@ -10,9 +10,11 @@ class Decompressor include BufferUtils FORBIDDEN_HEADERS = %w[connection te].freeze + attr_writer :max_header_list_size def initialize(settings = Settings.new) @cc = EncodingContext.new(settings) + @max_header_list_size = settings.settings_max_header_list_size end # Set dynamic table +size+ in EncodingContext @@ -20,6 +22,10 @@ def table_size=(size) @cc.table_size = size end + def table_size_limit=(size) + @cc.table_size_limit = size + end + # Decodes integer value from provided +buffer+. def integer(buf, n) limit = (1 << n) - 1 @@ -97,6 +103,7 @@ def header(buf) # Decodes and processes header commands within provided +buf+. def decode(buf, frame = nil) list = [] #: Array[header_pair] + header_list_size = 0 decoding_pseudo_headers = true @cc.listen_on_table do until buf.empty? @@ -111,6 +118,9 @@ def decode(buf, frame = nil) decoding_pseudo_headers = is_pseudo_header raise ProtocolError, "invalid header received: #{field}" if FORBIDDEN_HEADERS.include?(field) + header_list_size += field.bytesize + value.bytesize + 32 + raise ProtocolError, "header list exceeds configured maximum" if header_list_size > @max_header_list_size + if frame case field when ":status" diff --git a/lib/http/2/header/encoding_context.rb b/lib/http/2/header/encoding_context.rb index 9e13cd08..5e0ac4a3 100644 --- a/lib/http/2/header/encoding_context.rb +++ b/lib/http/2/header/encoding_context.rb @@ -107,6 +107,7 @@ def initialize(settings = Settings.new) @unshifts = 0 @settings = settings @limit = settings.table_size + @table_size_limit = settings.table_size @_table_updated = false @current_table_size = 0 end @@ -118,11 +119,13 @@ def dup tbf = @table_by_field.transform_values(&:dup) unshifts = @unshifts l = @limit + table_size_limit = @table_size_limit other.instance_eval do @table = t.dup # shallow copy @table_by_field = tbf @unshifts = unshifts @limit = l + @table_size_limit = table_size_limit end other end @@ -156,11 +159,9 @@ def process(cmd) when :changetablesize raise CompressionError, "tried to change table size after adding elements to table" if @_table_updated - # we can receive multiple table size change commands inside a header frame. However, - # we should blow up if we receive another frame where the new table size is bigger. - table_size_updated = @limit != @settings.table_size - - raise CompressionError, "dynamic table size update exceed limit" if !table_size_updated && value > @limit + # Multiple updates may begin a header block, but none may exceed + # the most recently advertised maximum. + raise CompressionError, "dynamic table size update exceeds limit" if value > @table_size_limit self.table_size = value @@ -276,6 +277,11 @@ def table_size=(size) resize_table(0) end + def table_size_limit=(size) + @table_size_limit = size + self.table_size = size if @limit > size + end + def listen_on_table yield ensure diff --git a/sig/header/decompressor.rbs b/sig/header/decompressor.rbs index 88e9874e..02ac7719 100644 --- a/sig/header/decompressor.rbs +++ b/sig/header/decompressor.rbs @@ -6,8 +6,11 @@ module HTTP2 FORBIDDEN_HEADERS: Array[String] @cc: EncodingContext + @max_header_list_size: Integer def table_size=: (Integer) -> void + def table_size_limit=: (Integer) -> void + def max_header_list_size=: (Integer) -> void def integer: (String, Integer) -> Integer diff --git a/sig/header/encoding_context.rbs b/sig/header/encoding_context.rbs index 05359dc5..f47a40c3 100644 --- a/sig/header/encoding_context.rbs +++ b/sig/header/encoding_context.rbs @@ -25,6 +25,7 @@ module HTTP2 @unshifts: Integer @limit: Integer + @table_size_limit: Integer @_table_updated: bool @@ -39,6 +40,7 @@ module HTTP2 def addcmd: (String name, String value) -> header_command def table_size=: (Integer) -> void + def table_size_limit=: (Integer) -> void def listen_on_table: { () -> void } -> void