Use more detailed error logging when 0-RTT SETTINGS are mismatched.

This CL also fixed a bug on SETTINGS_MAX_HEADER_LIST_SIZE, where we check on increase of the setting but instead should check on decrease.

Protected by disabled quic_enable_zero_rtt_for_tls

PiperOrigin-RevId: 317401398
Change-Id: Id464d788ff01f3cd76733717bc5a20cf15d2016a
diff --git a/quic/core/http/quic_spdy_client_session_base.cc b/quic/core/http/quic_spdy_client_session_base.cc
index eaa022e..b433c56 100644
--- a/quic/core/http/quic_spdy_client_session_base.cc
+++ b/quic/core/http/quic_spdy_client_session_base.cc
@@ -239,7 +239,7 @@
         frame.values.find(SETTINGS_MAX_HEADER_LIST_SIZE) ==
             frame.values.end()) {
       CloseConnectionWithDetails(
-          QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH,
+          QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
           "Server accepted 0-RTT but omitted non-default "
           "SETTINGS_MAX_HEADER_LIST_SIZE");
       return false;
@@ -249,7 +249,7 @@
         frame.values.find(SETTINGS_QPACK_BLOCKED_STREAMS) ==
             frame.values.end()) {
       CloseConnectionWithDetails(
-          QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH,
+          QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
           "Server accepted 0-RTT but omitted non-default "
           "SETTINGS_QPACK_BLOCKED_STREAMS");
       return false;
@@ -259,7 +259,7 @@
         frame.values.find(SETTINGS_QPACK_MAX_TABLE_CAPACITY) ==
             frame.values.end()) {
       CloseConnectionWithDetails(
-          QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH,
+          QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
           "Server accepted 0-RTT but omitted non-default "
           "SETTINGS_QPACK_MAX_TABLE_CAPACITY");
       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 d5481b5..3464a7e 100644
--- a/quic/core/http/quic_spdy_client_session_test.cc
+++ b/quic/core/http/quic_spdy_client_session_test.cc
@@ -1369,7 +1369,7 @@
   EXPECT_TRUE(session_->GetCryptoStream()->IsResumption());
 }
 
-TEST_P(QuicSpdyClientSessionTest, BadSettingsInZeroRtt) {
+TEST_P(QuicSpdyClientSessionTest, BadSettingsInZeroRttResumption) {
   if (!session_->version().UsesHttp3()) {
     return;
   }
@@ -1380,7 +1380,9 @@
   CompleteCryptoHandshake();
   EXPECT_TRUE(session_->GetCryptoStream()->EarlyDataAccepted());
 
-  EXPECT_CALL(*connection_, CloseConnection(QUIC_INTERNAL_ERROR, _, _))
+  EXPECT_CALL(
+      *connection_,
+      CloseConnection(QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, _, _))
       .WillOnce(testing::Invoke(connection_,
                                 &MockQuicConnection::ReallyCloseConnection));
   // Let the session receive a different SETTINGS frame.
@@ -1391,6 +1393,37 @@
   session_->OnSettingsFrame(settings);
 }
 
+TEST_P(QuicSpdyClientSessionTest, BadSettingsInZeroRttRejection) {
+  if (!session_->version().UsesHttp3()) {
+    return;
+  }
+
+  CompleteFirstConnection();
+
+  CreateConnection();
+  SSL_CTX_set_early_data_enabled(server_crypto_config_->ssl_ctx(), false);
+  session_->CryptoConnect();
+  EXPECT_TRUE(session_->IsEncryptionEstablished());
+  QuicConfig config = DefaultQuicConfig();
+  crypto_test_utils::HandshakeWithFakeServer(
+      &config, server_crypto_config_.get(), &helper_, &alarm_factory_,
+      connection_, crypto_stream_, AlpnForVersion(connection_->version()));
+  EXPECT_FALSE(session_->GetCryptoStream()->EarlyDataAccepted());
+
+  EXPECT_CALL(
+      *connection_,
+      CloseConnection(QUIC_HTTP_ZERO_RTT_REJECTION_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] = 2;
+  // setting on SETTINGS_MAX_HEADER_LIST_SIZE is reduced.
+  settings.values[SETTINGS_MAX_HEADER_LIST_SIZE] = 4;
+  settings.values[256] = 4;  // unknown setting
+  session_->OnSettingsFrame(settings);
+}
+
 TEST_P(QuicSpdyClientSessionTest, ServerAcceptsZeroRttButOmitSetting) {
   if (!session_->version().UsesHttp3()) {
     return;
@@ -1402,8 +1435,9 @@
   CompleteCryptoHandshake();
   EXPECT_TRUE(session_->GetMutableCryptoStream()->EarlyDataAccepted());
 
-  EXPECT_CALL(*connection_,
-              CloseConnection(QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH, _, _))
+  EXPECT_CALL(
+      *connection_,
+      CloseConnection(QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH, _, _))
       .WillOnce(testing::Invoke(connection_,
                                 &MockQuicConnection::ReallyCloseConnection));
   // Let the session receive a different SETTINGS frame.
diff --git a/quic/core/http/quic_spdy_session.cc b/quic/core/http/quic_spdy_session.cc
index 08f82c8..d6b5a0e 100644
--- a/quic/core/http/quic_spdy_session.cc
+++ b/quic/core/http/quic_spdy_session.cc
@@ -910,11 +910,17 @@
         // Required Insert Count.
         bool success = qpack_encoder_->SetMaximumDynamicTableCapacity(value);
         if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) && !success) {
-          // TODO(b/153726130): Use different error code for the case of 0-RTT
-          // rejection.
           CloseConnectionWithDetails(
-              QUIC_INTERNAL_ERROR,
-              "Server sent an invalid SETTINGS_QPACK_MAX_TABLE_CAPACITY.");
+              was_zero_rtt_rejected()
+                  ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH
+                  : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
+              quiche::QuicheStrCat(
+                  was_zero_rtt_rejected()
+                      ? "Server rejected 0-RTT, aborting because "
+                      : "",
+                  "Server sent an SETTINGS_QPACK_MAX_TABLE_CAPACITY: ", value,
+                  "while current value is: ",
+                  qpack_encoder_->MaximumDynamicTableCapacity()));
           return false;
         }
         // However, limit the dynamic table capacity to
@@ -928,12 +934,20 @@
                       << "SETTINGS_MAX_HEADER_LIST_SIZE received with value "
                       << value;
         if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) &&
-            max_outbound_header_list_size_ < value) {
-          // TODO(b/153726130): Use different error code for the case of 0-RTT
-          // rejection.
+            max_outbound_header_list_size_ !=
+                std::numeric_limits<size_t>::max() &&
+            max_outbound_header_list_size_ > value) {
           CloseConnectionWithDetails(
-              QUIC_INTERNAL_ERROR,
-              "Server sent an invalid SETTINGS_MAX_HEADER_LIST_SIZE.");
+              was_zero_rtt_rejected()
+                  ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH
+                  : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
+              quiche::QuicheStrCat(
+                  was_zero_rtt_rejected()
+                      ? "Server rejected 0-RTT, aborting because "
+                      : "",
+                  "Server sent an SETTINGS_MAX_HEADER_LIST_SIZE: ", value,
+                  "which reduces current value: ",
+                  max_outbound_header_list_size_));
           return false;
         }
         max_outbound_header_list_size_ = value;
@@ -943,12 +957,18 @@
                       << "SETTINGS_QPACK_BLOCKED_STREAMS received with value "
                       << value;
         bool success = qpack_encoder_->SetMaximumBlockedStreams(value);
-        // TODO(b/153726130): Use different error code for the case of 0-RTT
-        // rejection.
         if (GetQuicReloadableFlag(quic_enable_zero_rtt_for_tls) && !success) {
           CloseConnectionWithDetails(
-              QUIC_INTERNAL_ERROR,
-              "Server sent an invalid SETTINGS_QPACK_BLOCKED_STREAMS.");
+              was_zero_rtt_rejected()
+                  ? QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH
+                  : QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH,
+              quiche::QuicheStrCat(
+                  was_zero_rtt_rejected()
+                      ? "Server rejected 0-RTT, aborting because "
+                      : "",
+                  "Server sent an SETTINGS_QPACK_BLOCKED_STREAMS: ", value,
+                  "which reduces current value: ",
+                  qpack_encoder_->maximum_blocked_streams()));
           return false;
         }
         break;
diff --git a/quic/core/quic_error_codes.cc b/quic/core/quic_error_codes.cc
index 4887525..1c0984a 100644
--- a/quic/core/quic_error_codes.cc
+++ b/quic/core/quic_error_codes.cc
@@ -203,7 +203,8 @@
     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_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH);
+    RETURN_STRING_LITERAL(QUIC_HTTP_ZERO_RTT_REJECTION_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);
@@ -571,8 +572,10 @@
     case QUIC_HTTP_STREAM_LIMIT_TOO_LOW:
       return {false, static_cast<uint64_t>(
                          QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR)};
-    case QUIC_HTTP_ZERO_RTT_SETTINGS_MISMATCH:
+    case QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH:
       return {false, static_cast<uint64_t>(QuicHttp3ErrorCode::SETTINGS_ERROR)};
+    case QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH:
+      return {true, static_cast<uint64_t>(INTERNAL_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 60443ce..4c13f2c 100644
--- a/quic/core/quic_error_codes.h
+++ b/quic/core/quic_error_codes.h
@@ -431,8 +431,12 @@
   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,
+  // Received mismatched SETTINGS frame from HTTP/3 connection where early data
+  // is accepted. Server violated the HTTP/3 spec.
+  QUIC_HTTP_ZERO_RTT_RESUMPTION_SETTINGS_MISMATCH = 164,
+  // Received mismatched SETTINGS frame from HTTP/3 connection where early data
+  // is rejected. Our implementation currently doesn't support it.
+  QUIC_HTTP_ZERO_RTT_REJECTION_SETTINGS_MISMATCH = 165,
 
   // HPACK header block decoding errors.
   // Index varint beyond implementation limit.
@@ -481,7 +485,7 @@
   QUIC_ZERO_RTT_RESUMPTION_LIMIT_REDUCED = 163,
 
   // No error. Used as bound while iterating.
-  QUIC_LAST_ERROR = 165,
+  QUIC_LAST_ERROR = 166,
 };
 // 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