Handle error as specified in https://tools.ietf.org/html/draft-ietf-quic-http-29#section-7.2.4.2 : If a server accepts 0-RTT but then sends a SETTINGS frame that omits a setting value that the client understands (apart from reserved setting identifiers) that was previously specified to have a non-default value, this MUST be treated as a connection error of type H3_SETTINGS_ERROR.
Protected by quic_enable_zero_rtt_for_tls
PiperOrigin-RevId: 316781779
Change-Id: If4e174766bbd65be116f14a04449611963e03c2a
diff --git a/quic/core/http/quic_spdy_client_session_base.cc b/quic/core/http/quic_spdy_client_session_base.cc
index dfef4db..eaa022e 100644
--- a/quic/core/http/quic_spdy_client_session_base.cc
+++ b/quic/core/http/quic_spdy_client_session_base.cc
@@ -234,6 +234,38 @@
}
bool QuicSpdyClientSessionBase::OnSettingsFrame(const SettingsFrame& frame) {
+ if (!was_zero_rtt_rejected()) {
+ if (max_outbound_header_list_size() != std::numeric_limits<size_t>::max() &&
+ frame.values.find(SETTINGS_MAX_HEADER_LIST_SIZE) ==
+ frame.values.end()) {
+ CloseConnectionWithDetails(
+ QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH,
+ "Server accepted 0-RTT but omitted non-default "
+ "SETTINGS_MAX_HEADER_LIST_SIZE");
+ return false;
+ }
+
+ if (qpack_encoder()->maximum_blocked_streams() != 0 &&
+ frame.values.find(SETTINGS_QPACK_BLOCKED_STREAMS) ==
+ frame.values.end()) {
+ CloseConnectionWithDetails(
+ QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH,
+ "Server accepted 0-RTT but omitted non-default "
+ "SETTINGS_QPACK_BLOCKED_STREAMS");
+ return false;
+ }
+
+ if (qpack_encoder()->MaximumDynamicTableCapacity() != 0 &&
+ frame.values.find(SETTINGS_QPACK_MAX_TABLE_CAPACITY) ==
+ frame.values.end()) {
+ CloseConnectionWithDetails(
+ QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH,
+ "Server accepted 0-RTT but omitted non-default "
+ "SETTINGS_QPACK_MAX_TABLE_CAPACITY");
+ return false;
+ }
+ }
+
if (!QuicSpdySession::OnSettingsFrame(frame)) {
return false;
}
diff --git a/quic/core/http/quic_spdy_client_session_test.cc b/quic/core/http/quic_spdy_client_session_test.cc
index 824ff22..26b40f9 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -16,6 +16,7 @@
#include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_stream.h"
#include "net/third_party/quiche/src/quic/core/http/spdy_server_push_utils.h"
#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_error_codes.h"
#include "net/third_party/quiche/src/quic/core/quic_utils.h"
#include "net/third_party/quiche/src/quic/core/quic_versions.h"
#include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h"
@@ -1383,6 +1384,30 @@
session_->OnSettingsFrame(settings);
}
+TEST_P(QuicSpdyClientSessionTest, ServerAcceptsZeroRttButOmitSetting) {
+ if (!session_->version().UsesHttp3()) {
+ return;
+ }
+
+ CompleteFirstConnection();
+
+ CreateConnection();
+ CompleteCryptoHandshake();
+ EXPECT_TRUE(session_->GetMutableCryptoStream()->EarlyDataAccepted());
+
+ EXPECT_CALL(*connection_,
+ CloseConnection(QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH, _, _))
+ .WillOnce(testing::Invoke(connection_,
+ &MockQuicConnection::ReallyCloseConnection));
+ // Let the session receive a different SETTINGS frame.
+ SettingsFrame settings;
+ settings.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 1;
+ // Intentionally omit SETTINGS_MAX_HEADER_LIST_SIZE which was previously sent
+ // with a non-zero value.
+ settings.values[256] = 4; // unknown setting
+ session_->OnSettingsFrame(settings);
+}
+
} // namespace
} // namespace test
} // namespace quic
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 836404f..ddc9f74 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -888,9 +888,6 @@
}
bool QuicSpdySession::OnSetting(uint64_t id, uint64_t value) {
- // TODO(b/158614287): If cached SETTINGS has SETTINGS_QPACK_MAX_TABLE_CAPACITY
- // and SETTINGS_MAX_HEADER_LIST_SIZE, and the server accepts 0-RTT connection,
- // make sure the fresh SETTINGS contains the same values.
if (VersionUsesHttp3(transport_version())) {
// SETTINGS frame received on the control stream.
switch (id) {
diff --git a/quic/core/qpack/qpack_encoder.h b/quic/core/qpack/qpack_encoder.h
index 8b75097..202fc6d 100644
--- a/quic/core/qpack/qpack_encoder.h
+++ b/quic/core/qpack/qpack_encoder.h
@@ -99,6 +99,12 @@
return header_table_.dynamic_table_entry_referenced();
}
+ uint64_t maximum_blocked_streams() const { return maximum_blocked_streams_; }
+
+ uint64_t MaximumDynamicTableCapacity() const {
+ return header_table_.maximum_dynamic_table_capacity();
+ }
+
private:
friend class test::QpackEncoderPeer;
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index 4b2c737..4887525 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -203,6 +203,7 @@
RETURN_STRING_LITERAL(QUIC_HTTP_DUPLICATE_SETTING_IDENTIFIER);
RETURN_STRING_LITERAL(QUIC_HTTP_INVALID_MAX_PUSH_ID);
RETURN_STRING_LITERAL(QUIC_HTTP_STREAM_LIMIT_TOO_LOW);
+ RETURN_STRING_LITERAL(QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH);
RETURN_STRING_LITERAL(QUIC_HPACK_INDEX_VARINT_ERROR);
RETURN_STRING_LITERAL(QUIC_HPACK_NAME_LENGTH_VARINT_ERROR);
RETURN_STRING_LITERAL(QUIC_HPACK_VALUE_LENGTH_VARINT_ERROR);
@@ -570,6 +571,8 @@
case QUIC_HTTP_STREAM_LIMIT_TOO_LOW:
return {false, static_cast<uint64_t>(
QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR)};
+ case QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH:
+ return {false, static_cast<uint64_t>(QuicHttp3ErrorCode::SETTINGS_ERROR)};
case QUIC_HPACK_INDEX_VARINT_ERROR:
return {true, static_cast<uint64_t>(INTERNAL_ERROR)};
case QUIC_HPACK_NAME_LENGTH_VARINT_ERROR:
diff --git a/quic/core/quic_error_codes.h b/quic/core/quic_error_codes.h
index f5c5007..60443ce 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -431,6 +431,8 @@
QUIC_HTTP_INVALID_MAX_PUSH_ID = 159,
// Received unidirectional stream limit is lower than required by HTTP/3.
QUIC_HTTP_STREAM_LIMIT_TOO_LOW = 160,
+ // Received mismatched SETTINGS frame from HTTP/3 0-RTT connection.
+ QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH = 164,
// HPACK header block decoding errors.
// Index varint beyond implementation limit.
@@ -479,7 +481,7 @@
QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED = 163,
// No error. Used as bound while iterating.
- QUIC_LAST_ERROR = 164,
+ QUIC_LAST_ERROR = 165,
};
// QuicErrorCodes is encoded as four octets on-the-wire when doing Google QUIC,
// or a varint62 when doing IETF QUIC. Ensure that its value does not exceed
diff --git a/quic/core/quic_session.h b/quic/core/quic_session.h
index 8328782..e5e794f 100644
--- a/quic/core/quic_session.h
+++ b/quic/core/quic_session.h
@@ -604,6 +604,8 @@
size_t num_static_streams() const { return num_static_streams_; }
+ bool was_zero_rtt_rejected() const { return was_zero_rtt_rejected_; }
+
size_t num_outgoing_draining_streams() const {
return num_outgoing_draining_streams_;
}