Adds support for sending and receiving the SETTINGS_HEADER_TABLE_SIZE setting in oghttp2.
There is some danger in HPACK limit skew between endpoints, depending on when new limits are applied. For that reason, this change uses the following strategy.
* When sending the setting, the library applies the new limit upon receipt of an acknowledgement of the setting from the peer.
* When receiving the setting, the library applies the limit when sending an acknowledgement to the peer.
PiperOrigin-RevId: 413999148
diff --git a/http2/adapter/http2_protocol.cc b/http2/adapter/http2_protocol.cc
index 8b80718..0f3c0d7 100644
--- a/http2/adapter/http2_protocol.cc
+++ b/http2/adapter/http2_protocol.cc
@@ -25,6 +25,10 @@
}
}
+bool operator==(const Http2Setting& a, const Http2Setting& b) {
+ return a.id == b.id && a.value == b.value;
+}
+
absl::string_view Http2SettingsIdToString(uint16_t id) {
switch (id) {
case Http2KnownSettingsId::HEADER_TABLE_SIZE:
diff --git a/http2/adapter/http2_protocol.h b/http2/adapter/http2_protocol.h
index ce5a282..0529bd6 100644
--- a/http2/adapter/http2_protocol.h
+++ b/http2/adapter/http2_protocol.h
@@ -39,6 +39,8 @@
uint32_t value;
};
+bool operator==(const Http2Setting& a, const Http2Setting& b);
+
// The maximum possible stream ID.
const Http2StreamId kMaxStreamId = 0x7FFFFFFF;
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index 3f25f2b..ef5ec8f 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -557,6 +557,50 @@
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
}
+TEST(NgHttp2AdapterTest, ClientHandlesHpackHeaderTableSetting) {
+ DataSavingVisitor visitor;
+ auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 = ToHeaders({
+ {":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"},
+ {"x-i-do-not-like", "green eggs and ham"},
+ {"x-i-will-not-eat-them", "here or there, in a box, with a fox"},
+ {"x-like-them-in-a-house", "no"},
+ {"x-like-them-with-a-mouse", "no"},
+ });
+
+ const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr);
+ ASSERT_GT(stream_id1, 0);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ visitor.Clear();
+
+ EXPECT_GT(adapter->GetHpackEncoderDynamicTableSize(), 100);
+
+ const std::string stream_frames =
+ TestFrameSequence().Settings({{HEADER_TABLE_SIZE, 100u}}).Serialize();
+ // Server preface (SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSetting(Http2Setting{HEADER_TABLE_SIZE, 100u}));
+
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ const int64_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), stream_result);
+
+ EXPECT_LE(adapter->GetHpackEncoderDynamicTableSize(), 100);
+}
+
TEST(NgHttp2AdapterTest, ClientHandlesInvalidTrailers) {
DataSavingVisitor visitor;
auto adapter = NgHttp2Adapter::CreateClientAdapter(visitor);
diff --git a/http2/adapter/oghttp2_adapter.cc b/http2/adapter/oghttp2_adapter.cc
index c4c2e7b..3847ef8 100644
--- a/http2/adapter/oghttp2_adapter.cc
+++ b/http2/adapter/oghttp2_adapter.cc
@@ -103,10 +103,18 @@
return session_->GetHpackEncoderDynamicTableSize();
}
+int OgHttp2Adapter::GetHpackEncoderDynamicTableCapacity() const {
+ return session_->GetHpackEncoderDynamicTableCapacity();
+}
+
int OgHttp2Adapter::GetHpackDecoderDynamicTableSize() const {
return session_->GetHpackDecoderDynamicTableSize();
}
+int OgHttp2Adapter::GetHpackDecoderSizeLimit() const {
+ return session_->GetHpackDecoderSizeLimit();
+}
+
Http2StreamId OgHttp2Adapter::GetHighestReceivedStreamId() const {
return session_->GetHighestReceivedStreamId();
}
diff --git a/http2/adapter/oghttp2_adapter.h b/http2/adapter/oghttp2_adapter.h
index 0aae5ab..d30ed19 100644
--- a/http2/adapter/oghttp2_adapter.h
+++ b/http2/adapter/oghttp2_adapter.h
@@ -46,7 +46,9 @@
int GetStreamReceiveWindowSize(Http2StreamId stream_id) const override;
int GetReceiveWindowSize() const override;
int GetHpackEncoderDynamicTableSize() const override;
+ int GetHpackEncoderDynamicTableCapacity() const;
int GetHpackDecoderDynamicTableSize() const override;
+ int GetHpackDecoderSizeLimit() const;
Http2StreamId GetHighestReceivedStreamId() const override;
void MarkDataConsumedForStream(Http2StreamId stream_id,
size_t num_bytes) override;
diff --git a/http2/adapter/oghttp2_adapter_test.cc b/http2/adapter/oghttp2_adapter_test.cc
index 86d5f72..88ebbed 100644
--- a/http2/adapter/oghttp2_adapter_test.cc
+++ b/http2/adapter/oghttp2_adapter_test.cc
@@ -870,6 +870,230 @@
EXPECT_THAT(visitor.data(), EqualsFrames({spdy::SpdyFrameType::SETTINGS}));
}
+TEST(OgHttp2AdapterClientTest, ClientHandlesSmallerHpackHeaderTableSetting) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 = ToHeaders({
+ {":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"},
+ {"x-i-do-not-like", "green eggs and ham"},
+ {"x-i-will-not-eat-them", "here or there, in a box, with a fox"},
+ {"x-like-them-in-a-house", "no"},
+ {"x-like-them-with-a-mouse", "no"},
+ });
+
+ const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr);
+ ASSERT_GT(stream_id1, 0);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ visitor.Clear();
+
+ EXPECT_GT(adapter->GetHpackEncoderDynamicTableSize(), 100);
+
+ const std::string stream_frames =
+ TestFrameSequence().Settings({{HEADER_TABLE_SIZE, 100u}}).Serialize();
+ // Server preface (SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSetting(Http2Setting{HEADER_TABLE_SIZE, 100u}));
+ // Duplicate setting callback due to the way extensions work.
+ EXPECT_CALL(visitor, OnSetting(Http2Setting{HEADER_TABLE_SIZE, 100u}));
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ const int64_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result));
+
+ EXPECT_EQ(adapter->GetHpackEncoderDynamicTableCapacity(), 100);
+ EXPECT_LE(adapter->GetHpackEncoderDynamicTableSize(), 100);
+}
+
+TEST(OgHttp2AdapterClientTest, ClientHandlesLargerHpackHeaderTableSetting) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ testing::InSequence s;
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ visitor.Clear();
+
+ EXPECT_EQ(adapter->GetHpackEncoderDynamicTableCapacity(), 4096);
+
+ const std::string stream_frames =
+ TestFrameSequence().Settings({{HEADER_TABLE_SIZE, 40960u}}).Serialize();
+ // Server preface (SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 6, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSetting(Http2Setting{HEADER_TABLE_SIZE, 40960u}));
+ // Duplicate setting callback due to the way extensions work.
+ EXPECT_CALL(visitor, OnSetting(Http2Setting{HEADER_TABLE_SIZE, 40960u}));
+ EXPECT_CALL(visitor, OnSettingsEnd());
+
+ const int64_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result));
+
+ // The increased capacity will not be applied until a SETTINGS ack is
+ // serialized.
+ EXPECT_EQ(adapter->GetHpackEncoderDynamicTableCapacity(), 4096);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ visitor.Clear();
+
+ EXPECT_EQ(adapter->GetHpackEncoderDynamicTableCapacity(), 40960);
+}
+
+TEST(OgHttp2AdapterClientTest, ClientSendsHpackHeaderTableSetting) {
+ DataSavingVisitor visitor;
+ OgHttp2Adapter::Options options{.perspective = Perspective::kClient};
+ auto adapter = OgHttp2Adapter::Create(visitor, options);
+
+ testing::InSequence s;
+
+ const std::vector<const Header> headers1 = ToHeaders({
+ {":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/one"},
+ });
+
+ const int32_t stream_id1 = adapter->SubmitRequest(headers1, nullptr, nullptr);
+ ASSERT_GT(stream_id1, 0);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x0, 0));
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id1, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id1, _, 0x5, 0));
+
+ int result = adapter->Send();
+ EXPECT_EQ(0, result);
+ visitor.Clear();
+
+ const std::string stream_frames =
+ TestFrameSequence()
+ .ServerPreface()
+ .SettingsAck()
+ .Headers(
+ 1,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"},
+ {"x-i-do-not-like", "green eggs and ham"},
+ {"x-i-will-not-eat-them", "here or there, in a box, with a fox"},
+ {"x-like-them-in-a-house", "no"},
+ {"x-like-them-with-a-mouse", "no"}},
+ /*fin=*/true)
+ .Serialize();
+ // Server preface (empty SETTINGS)
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 0));
+ EXPECT_CALL(visitor, OnSettingsStart());
+ EXPECT_CALL(visitor, OnSettingsEnd());
+ // Server acks client's initial SETTINGS.
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 1));
+ EXPECT_CALL(visitor, OnSettingsAck());
+
+ EXPECT_CALL(visitor, OnFrameHeader(1, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(1));
+ EXPECT_CALL(visitor, OnHeaderForStream(1, _, _)).Times(7);
+ EXPECT_CALL(visitor, OnEndHeadersForStream(1));
+ EXPECT_CALL(visitor, OnEndStream(1));
+ EXPECT_CALL(visitor, OnCloseStream(1, Http2ErrorCode::HTTP2_NO_ERROR));
+
+ const int64_t stream_result = adapter->ProcessBytes(stream_frames);
+ EXPECT_EQ(stream_frames.size(), static_cast<size_t>(stream_result));
+
+ EXPECT_GT(adapter->GetHpackDecoderSizeLimit(), 100);
+
+ // Submit settings, check decoder table size.
+ adapter->SubmitSettings({{HEADER_TABLE_SIZE, 100u}});
+ EXPECT_GT(adapter->GetHpackDecoderSizeLimit(), 100);
+
+ // Server preface SETTINGS ack
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, _, 0x1));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, _, 0x1, 0));
+ // SETTINGS with the new header table size value
+ EXPECT_CALL(visitor, OnBeforeFrameSent(SETTINGS, 0, 6, 0x0));
+ EXPECT_CALL(visitor, OnFrameSent(SETTINGS, 0, 6, 0x0, 0));
+
+ // Because the client has not yet seen an ack from the server for the SETTINGS
+ // with header table size, it has not applied the new value.
+ EXPECT_GT(adapter->GetHpackDecoderSizeLimit(), 100);
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ visitor.Clear();
+
+ const std::vector<const Header> headers2 = ToHeaders({
+ {":method", "GET"},
+ {":scheme", "http"},
+ {":authority", "example.com"},
+ {":path", "/this/is/request/two"},
+ });
+
+ const int32_t stream_id2 = adapter->SubmitRequest(headers2, nullptr, nullptr);
+ ASSERT_GT(stream_id2, stream_id1);
+
+ EXPECT_CALL(visitor, OnBeforeFrameSent(HEADERS, stream_id2, _, 0x5));
+ EXPECT_CALL(visitor, OnFrameSent(HEADERS, stream_id2, _, 0x5, 0));
+
+ result = adapter->Send();
+ EXPECT_EQ(0, result);
+ visitor.Clear();
+
+ const std::string response_frames =
+ TestFrameSequence()
+ .Headers(stream_id2,
+ {{":status", "200"},
+ {"server", "my-fake-server"},
+ {"date", "Tue, 6 Apr 2021 12:54:01 GMT"}},
+ /*fin=*/true)
+ .Serialize();
+
+ EXPECT_CALL(visitor, OnFrameHeader(stream_id2, _, HEADERS, 5));
+ EXPECT_CALL(visitor, OnBeginHeadersForStream(stream_id2));
+ EXPECT_CALL(visitor, OnHeaderForStream(stream_id2, _, _)).Times(3);
+ EXPECT_CALL(visitor, OnEndHeadersForStream(stream_id2));
+ EXPECT_CALL(visitor, OnEndStream(stream_id2));
+ EXPECT_CALL(visitor,
+ OnCloseStream(stream_id2, Http2ErrorCode::HTTP2_NO_ERROR));
+
+ const int64_t response_result = adapter->ProcessBytes(response_frames);
+ EXPECT_EQ(response_frames.size(), static_cast<size_t>(response_result));
+
+ // Still no ack for the outbound settings.
+ EXPECT_GT(adapter->GetHpackDecoderSizeLimit(), 100);
+
+ const std::string settings_ack =
+ TestFrameSequence().SettingsAck().Serialize();
+
+ EXPECT_CALL(visitor, OnFrameHeader(0, 0, SETTINGS, 1));
+ EXPECT_CALL(visitor, OnSettingsAck());
+
+ const int64_t ack_result = adapter->ProcessBytes(settings_ack);
+ EXPECT_EQ(settings_ack.size(), static_cast<size_t>(ack_result));
+ // Ack has finally arrived.
+ EXPECT_EQ(adapter->GetHpackDecoderSizeLimit(), 100);
+}
+
// TODO(birenroy): Validate headers and re-enable this test. The library should
// invoke OnErrorDebug() with an error message for the invalid header. The
// library should also invoke OnInvalidFrame() for the invalid HEADERS frame.
diff --git a/http2/adapter/oghttp2_session.cc b/http2/adapter/oghttp2_session.cc
index fb37bdd..c54d335 100644
--- a/http2/adapter/oghttp2_session.cc
+++ b/http2/adapter/oghttp2_session.cc
@@ -30,6 +30,8 @@
#endif
const uint32_t kMaxAllowedMetadataFrameSize = 65536u;
+const uint32_t kDefaultHpackTableCapacity = 4096u;
+const uint32_t kMaximumHpackTableCapacity = 65536u;
// TODO(birenroy): Consider incorporating spdy::FlagsSerializionVisitor here.
class FrameAttributeCollector : public spdy::SpdyFrameVisitor {
@@ -195,6 +197,13 @@
return status.size() == 3 && status[0] == '1';
}
+// Returns the upper bound on HPACK encoder table capacity. If not specified in
+// the Options, a reasonable default upper bound is used.
+uint32_t HpackCapacityBound(const OgHttp2Session::Options& o) {
+ return o.max_hpack_encoding_table_capacity.value_or(
+ kMaximumHpackTableCapacity);
+}
+
} // namespace
void OgHttp2Session::PassthroughHeadersHandler::OnHeaderBlockStart() {
@@ -334,11 +343,22 @@
return encoder == nullptr ? 0 : encoder->GetDynamicTableSize();
}
+int OgHttp2Session::GetHpackEncoderDynamicTableCapacity() const {
+ const spdy::HpackEncoder* encoder = framer_.GetHpackEncoder();
+ return encoder == nullptr ? kDefaultHpackTableCapacity
+ : encoder->CurrentHeaderTableSizeSetting();
+}
+
int OgHttp2Session::GetHpackDecoderDynamicTableSize() const {
const spdy::HpackDecoderAdapter* decoder = decoder_.GetHpackDecoder();
return decoder == nullptr ? 0 : decoder->GetDynamicTableSize();
}
+int OgHttp2Session::GetHpackDecoderSizeLimit() const {
+ const spdy::HpackDecoderAdapter* decoder = decoder_.GetHpackDecoder();
+ return decoder == nullptr ? 0 : decoder->GetCurrentHeaderTableSizeSetting();
+}
+
int64_t OgHttp2Session::ProcessBytes(absl::string_view bytes) {
QUICHE_VLOG(2) << TracePerspectiveAsString(options_.perspective)
<< " processing [" << absl::CEscape(bytes) << "]";
@@ -528,6 +548,14 @@
visitor_.OnFrameSent(frame_type, stream_id, payload_length, flags,
error_code);
if (stream_id == 0) {
+ const bool is_settings_ack =
+ static_cast<FrameType>(frame_type) == FrameType::SETTINGS &&
+ (flags & 0x01);
+ if (is_settings_ack && encoder_header_table_capacity_when_acking_) {
+ framer_.UpdateHeaderEncoderTableSize(
+ encoder_header_table_capacity_when_acking_.value());
+ encoder_header_table_capacity_when_acking_ = absl::nullopt;
+ }
return;
}
auto iter = queued_frames_.find(stream_id);
@@ -906,6 +934,9 @@
void OgHttp2Session::OnSettings() {
visitor_.OnSettingsStart();
+ auto settings = absl::make_unique<SpdySettingsIR>();
+ settings->set_is_ack(true);
+ EnqueueFrame(std::move(settings));
}
void OgHttp2Session::OnSetting(spdy::SpdySettingsId id, uint32_t value) {
@@ -916,14 +947,25 @@
max_frame_payload_ = value;
} else if (id == MAX_CONCURRENT_STREAMS) {
max_outbound_concurrent_streams_ = value;
+ } else if (id == HEADER_TABLE_SIZE) {
+ value = std::min(value, HpackCapacityBound(options_));
+ if (value < framer_.GetHpackEncoder()->CurrentHeaderTableSizeSetting()) {
+ // Safe to apply a smaller table capacity immediately.
+ QUICHE_VLOG(2) << TracePerspectiveAsString(options_.perspective)
+ << " applying encoder table capacity " << value;
+ framer_.GetHpackEncoder()->ApplyHeaderTableSizeSetting(value);
+ } else {
+ QUICHE_VLOG(2)
+ << TracePerspectiveAsString(options_.perspective)
+ << " NOT applying encoder table capacity until writing ack: "
+ << value;
+ encoder_header_table_capacity_when_acking_ = value;
+ }
}
}
void OgHttp2Session::OnSettingsEnd() {
visitor_.OnSettingsEnd();
- auto settings = absl::make_unique<SpdySettingsIR>();
- settings->set_is_ack(true);
- EnqueueFrame(std::move(settings));
}
void OgHttp2Session::OnSettingsAck() {
@@ -1169,6 +1211,9 @@
for (const auto id_and_value : settings_map) {
if (id_and_value.first == spdy::SETTINGS_MAX_CONCURRENT_STREAMS) {
max_inbound_concurrent_streams_ = id_and_value.second;
+ } else if (id_and_value.first == spdy::SETTINGS_HEADER_TABLE_SIZE) {
+ decoder_.GetHpackDecoder()->ApplyHeaderTableSizeSetting(
+ id_and_value.second);
}
}
});
diff --git a/http2/adapter/oghttp2_session.h b/http2/adapter/oghttp2_session.h
index 58f34ec..9f00613 100644
--- a/http2/adapter/oghttp2_session.h
+++ b/http2/adapter/oghttp2_session.h
@@ -8,6 +8,7 @@
#include <vector>
#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
#include "http2/adapter/data_source.h"
#include "http2/adapter/event_forwarder.h"
#include "http2/adapter/header_validator.h"
@@ -37,6 +38,8 @@
public:
struct QUICHE_EXPORT_PRIVATE Options {
Perspective perspective = Perspective::kClient;
+ // The maximum HPACK table size to use.
+ absl::optional<size_t> max_hpack_encoding_table_capacity = absl::nullopt;
// Whether to automatically send PING acks when receiving a PING.
bool auto_ping_ack = true;
// Whether (as server) to send a RST_STREAM NO_ERROR when sending a fin on
@@ -102,10 +105,16 @@
// per-entry overhead from the specification.
int GetHpackEncoderDynamicTableSize() const;
+ // Returns the maximum capacity of the HPACK encoder's dynamic table.
+ int GetHpackEncoderDynamicTableCapacity() const;
+
// Returns the size of the HPACK decoder's dynamic table, including the
// per-entry overhead from the specification.
int GetHpackDecoderDynamicTableSize() const;
+ // Returns the size of the HPACK decoder's most recently applied size limit.
+ int GetHpackDecoderSizeLimit() const;
+
// From Http2Session.
int64_t ProcessBytes(absl::string_view bytes) override;
int Consume(Http2StreamId stream_id, size_t num_bytes) override;
@@ -406,6 +415,13 @@
uint32_t max_inbound_concurrent_streams_ =
std::numeric_limits<uint32_t>::max();
Options options_;
+
+ // The HPACK encoder header table capacity that will be applied when
+ // acking SETTINGS from the peer. Only contains a value if the peer advertises
+ // a larger table capacity than currently used; a smaller value can safely be
+ // applied immediately upon receipt.
+ absl::optional<uint32_t> encoder_header_table_capacity_when_acking_;
+
bool received_goaway_ = false;
bool queued_preface_ = false;
bool peer_supports_metadata_ = false;
diff --git a/spdy/core/hpack/hpack_encoder.h b/spdy/core/hpack/hpack_encoder.h
index 0529297..dbdbb6b 100644
--- a/spdy/core/hpack/hpack_encoder.h
+++ b/spdy/core/hpack/hpack_encoder.h
@@ -79,6 +79,7 @@
// SETTINGS_HEADER_TABLE_SIZE update from the remote decoding endpoint.
void ApplyHeaderTableSizeSetting(size_t size_setting);
+ // TODO(birenroy): Rename this GetDynamicTableCapacity().
size_t CurrentHeaderTableSizeSetting() const {
return header_table_.settings_size_bound();
}