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_; }