Enforce limits on sending ECN.

QuicConnection only permits ECN via a state variable accessible only by QuicConnectionPeer.

The send limits will be relaxed as new code meets the requirements.

There is no code that allows sending anything other than NOT_ECT, so this is not flag-protected.

PiperOrigin-RevId: 523154100
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index 27cf706..f0fa10b 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -17465,6 +17465,57 @@
   EXPECT_EQ(kSelfAddress, connection_.self_address());
 }
 
+TEST_P(QuicConnectionTest, EcnCodepointsRejected) {
+  TestPerPacketOptions per_packet_options;
+  connection_.set_per_packet_options(&per_packet_options);
+  for (QuicEcnCodepoint ecn : {ECN_NOT_ECT, ECN_ECT0, ECN_ECT1, ECN_CE}) {
+    per_packet_options.ecn_codepoint = ecn;
+    if (ecn == ECN_ECT0) {
+      EXPECT_CALL(*send_algorithm_, SupportsECT0()).WillOnce(Return(false));
+    } else if (ecn == ECN_ECT1) {
+      EXPECT_CALL(*send_algorithm_, SupportsECT1()).WillOnce(Return(false));
+    }
+    EXPECT_CALL(connection_, OnSerializedPacket(_));
+    SendPing();
+    EXPECT_EQ(per_packet_options.ecn_codepoint, ECN_NOT_ECT);
+    EXPECT_EQ(writer_->last_ecn_sent(), ECN_NOT_ECT);
+  }
+}
+
+TEST_P(QuicConnectionTest, EcnCodepointsAccepted) {
+  TestPerPacketOptions per_packet_options;
+  connection_.set_per_packet_options(&per_packet_options);
+  for (QuicEcnCodepoint ecn : {ECN_NOT_ECT, ECN_ECT0, ECN_ECT1, ECN_CE}) {
+    per_packet_options.ecn_codepoint = ecn;
+    if (ecn == ECN_ECT0) {
+      EXPECT_CALL(*send_algorithm_, SupportsECT0()).WillOnce(Return(true));
+    } else if (ecn == ECN_ECT1) {
+      EXPECT_CALL(*send_algorithm_, SupportsECT1()).WillOnce(Return(true));
+    }
+    EXPECT_CALL(connection_, OnSerializedPacket(_));
+    SendPing();
+    QuicEcnCodepoint expected_codepoint = ecn;
+    if (ecn == ECN_CE) {
+      expected_codepoint = ECN_NOT_ECT;
+    }
+    EXPECT_EQ(per_packet_options.ecn_codepoint, expected_codepoint);
+    EXPECT_EQ(writer_->last_ecn_sent(), expected_codepoint);
+  }
+}
+
+TEST_P(QuicConnectionTest, EcnValidationDisabled) {
+  TestPerPacketOptions per_packet_options;
+  connection_.set_per_packet_options(&per_packet_options);
+  QuicConnectionPeer::DisableEcnCodepointValidation(&connection_);
+  for (QuicEcnCodepoint ecn : {ECN_NOT_ECT, ECN_ECT0, ECN_ECT1, ECN_CE}) {
+    per_packet_options.ecn_codepoint = ecn;
+    EXPECT_CALL(connection_, OnSerializedPacket(_));
+    SendPing();
+    EXPECT_EQ(per_packet_options.ecn_codepoint, ecn);
+    EXPECT_EQ(writer_->last_ecn_sent(), ecn);
+  }
+}
+
 }  // namespace
 }  // namespace test
 }  // namespace quic