diff --git a/proxy/http2/HPACK.cc b/proxy/http2/HPACK.cc index 23509fe1cef..2f90f1686a3 100644 --- a/proxy/http2/HPACK.cc +++ b/proxy/http2/HPACK.cc @@ -295,6 +295,12 @@ HpackIndexingTable::add_header_field(const MIMEField *field) _dynamic_table->add_header_field(field); } +uint32_t +HpackIndexingTable::maximum_size() const +{ + return _dynamic_table->maximum_size(); +} + uint32_t HpackIndexingTable::size() const { @@ -351,6 +357,12 @@ HpackDynamicTable::add_header_field(const MIMEField *field) } } +uint32_t +HpackDynamicTable::maximum_size() const +{ + return _maximum_size; +} + uint32_t HpackDynamicTable::size() const { @@ -619,6 +631,18 @@ encode_literal_header_field_with_new_name(uint8_t *buf_start, const uint8_t *buf return p - buf_start; } +int64_t +encode_dynamic_table_size_update(uint8_t *buf_start, const uint8_t *buf_end, uint32_t size) +{ + const int64_t len = encode_integer(buf_start, buf_end, size, 5); + if (len == -1) { + return -1; + } + buf_start[0] |= 0x20; + + return len; +} + // // [RFC 7541] 5.1. Integer representation // @@ -928,7 +952,8 @@ hpack_decode_header_block(HpackIndexingTable &indexing_table, HTTPHdr *hdr, cons } int64_t -hpack_encode_header_block(HpackIndexingTable &indexing_table, uint8_t *out_buf, const size_t out_buf_len, HTTPHdr *hdr) +hpack_encode_header_block(HpackIndexingTable &indexing_table, uint8_t *out_buf, const size_t out_buf_len, HTTPHdr *hdr, + int32_t maximum_table_size) { uint8_t *cursor = out_buf; const uint8_t *const out_buf_end = out_buf + out_buf_len; @@ -936,6 +961,16 @@ hpack_encode_header_block(HpackIndexingTable &indexing_table, uint8_t *out_buf, ink_assert(http_hdr_type_get(hdr->m_http) != HTTP_TYPE_UNKNOWN); + // Update dynamic table size + if (maximum_table_size >= 0) { + indexing_table.update_maximum_size(maximum_table_size); + written = encode_dynamic_table_size_update(cursor, out_buf_end, maximum_table_size); + if (written == HPACK_ERROR_COMPRESSION_ERROR) { + return HPACK_ERROR_COMPRESSION_ERROR; + } + cursor += written; + } + MIMEFieldIter field_iter; for (MIMEField *field = hdr->iter_get_first(&field_iter); field != nullptr; field = hdr->iter_get_next(&field_iter)) { HpackField field_type; @@ -977,3 +1012,9 @@ hpack_encode_header_block(HpackIndexingTable &indexing_table, uint8_t *out_buf, } return cursor - out_buf; } + +int32_t +hpack_get_maximum_table_size(HpackIndexingTable &indexing_table) +{ + return indexing_table.maximum_size(); +} diff --git a/proxy/http2/HPACK.h b/proxy/http2/HPACK.h index 0c1110725f6..8e344390bbd 100644 --- a/proxy/http2/HPACK.h +++ b/proxy/http2/HPACK.h @@ -122,6 +122,7 @@ class HpackDynamicTable const MIMEField *get_header_field(uint32_t index) const; void add_header_field(const MIMEField *field); + uint32_t maximum_size() const; uint32_t size() const; bool update_maximum_size(uint32_t new_size); @@ -146,6 +147,7 @@ class HpackIndexingTable int get_header_field(uint32_t index, MIMEFieldWrapper &header_field) const; void add_header_field(const MIMEField *field); + uint32_t maximum_size() const; uint32_t size() const; bool update_maximum_size(uint32_t new_size); @@ -173,6 +175,8 @@ int64_t update_dynamic_table_size(const uint8_t *buf_start, const uint8_t *buf_e typedef HpackIndexingTable HpackHandle; int64_t hpack_decode_header_block(HpackHandle &handle, HTTPHdr *hdr, const uint8_t *in_buf, const size_t in_buf_len, uint32_t max_header_size); -int64_t hpack_encode_header_block(HpackHandle &handle, uint8_t *out_buf, const size_t out_buf_len, HTTPHdr *hdr); +int64_t hpack_encode_header_block(HpackHandle &handle, uint8_t *out_buf, const size_t out_buf_len, HTTPHdr *hdr, + int32_t maximum_table_size = -1); +int32_t hpack_get_maximum_table_size(HpackHandle &handle); #endif /* __HPACK_H__ */ diff --git a/proxy/http2/HTTP2.cc b/proxy/http2/HTTP2.cc index 1769950ef30..833e80c556f 100644 --- a/proxy/http2/HTTP2.cc +++ b/proxy/http2/HTTP2.cc @@ -43,7 +43,8 @@ const unsigned HTTP2_LEN_AUTHORITY = countof(":authority") - 1; const unsigned HTTP2_LEN_PATH = countof(":path") - 1; const unsigned HTTP2_LEN_STATUS = countof(":status") - 1; -static size_t HTTP2_LEN_STATUS_VALUE_STR = 3; +static size_t HTTP2_LEN_STATUS_VALUE_STR = 3; +static const int HTTP2_MAX_TABLE_SIZE_LIMIT = 64 * 1024; // Statistics RecRawStatBlock *http2_rsb; @@ -597,10 +598,17 @@ http2_generate_h2_header_from_1_1(HTTPHdr *headers, HTTPHdr *h2_headers) } Http2ErrorCode -http2_encode_header_blocks(HTTPHdr *in, uint8_t *out, uint32_t out_len, uint32_t *len_written, HpackHandle &handle) -{ +http2_encode_header_blocks(HTTPHdr *in, uint8_t *out, uint32_t out_len, uint32_t *len_written, HpackHandle &handle, + int32_t maximum_table_size) +{ + // Limit the maximum table size to 64kB, which is the size advertised by major clients + maximum_table_size = min(maximum_table_size, HTTP2_MAX_TABLE_SIZE_LIMIT); + // Set maximum table size only if it is different from current maximum size + if (maximum_table_size == hpack_get_maximum_table_size(handle)) { + maximum_table_size = -1; + } // TODO: It would be better to split Cookie header value - int64_t result = hpack_encode_header_block(handle, out, out_len, in); + int64_t result = hpack_encode_header_block(handle, out, out_len, in, maximum_table_size); if (result < 0) { return Http2ErrorCode::HTTP2_ERROR_COMPRESSION_ERROR; } diff --git a/proxy/http2/HTTP2.h b/proxy/http2/HTTP2.h index 68a7729df2b..96a8f38ab62 100644 --- a/proxy/http2/HTTP2.h +++ b/proxy/http2/HTTP2.h @@ -356,7 +356,7 @@ bool http2_parse_window_update(IOVec, uint32_t &); Http2ErrorCode http2_decode_header_blocks(HTTPHdr *, const uint8_t *, const uint32_t, uint32_t *, HpackHandle &, bool &); -Http2ErrorCode http2_encode_header_blocks(HTTPHdr *, uint8_t *, uint32_t, uint32_t *, HpackHandle &); +Http2ErrorCode http2_encode_header_blocks(HTTPHdr *, uint8_t *, uint32_t, uint32_t *, HpackHandle &, int32_t); ParseResult http2_convert_header_from_2_to_1_1(HTTPHdr *); void http2_generate_h2_header_from_1_1(HTTPHdr *headers, HTTPHdr *h2_headers); diff --git a/proxy/http2/Http2ConnectionState.cc b/proxy/http2/Http2ConnectionState.cc index 7964a49b027..fcbf61a621b 100644 --- a/proxy/http2/Http2ConnectionState.cc +++ b/proxy/http2/Http2ConnectionState.cc @@ -1324,7 +1324,8 @@ Http2ConnectionState::send_headers_frame(Http2Stream *stream) h2_hdr.destroy(); return; } - Http2ErrorCode result = http2_encode_header_blocks(&h2_hdr, buf, buf_len, &header_blocks_size, *(this->remote_hpack_handle)); + Http2ErrorCode result = http2_encode_header_blocks(&h2_hdr, buf, buf_len, &header_blocks_size, *(this->remote_hpack_handle), + client_settings.get(HTTP2_SETTINGS_HEADER_TABLE_SIZE)); if (result != Http2ErrorCode::HTTP2_ERROR_NO_ERROR) { h2_hdr.destroy(); ats_free(buf); @@ -1413,7 +1414,8 @@ Http2ConnectionState::send_push_promise_frame(Http2Stream *stream, URL &url) h2_hdr.destroy(); return; } - Http2ErrorCode result = http2_encode_header_blocks(&h2_hdr, buf, buf_len, &header_blocks_size, *(this->remote_hpack_handle)); + Http2ErrorCode result = http2_encode_header_blocks(&h2_hdr, buf, buf_len, &header_blocks_size, *(this->remote_hpack_handle), + client_settings.get(HTTP2_SETTINGS_HEADER_TABLE_SIZE)); if (result != Http2ErrorCode::HTTP2_ERROR_NO_ERROR) { h2_hdr.destroy(); ats_free(buf);