diff options
Diffstat (limited to 'chromium/net/quic')
59 files changed, 3024 insertions, 1274 deletions
diff --git a/chromium/net/quic/OWNERS b/chromium/net/quic/OWNERS index 9db36fa2026..4d12d7e8fff 100644 --- a/chromium/net/quic/OWNERS +++ b/chromium/net/quic/OWNERS @@ -1,6 +1,6 @@ dschinazi@chromium.org nharper@chromium.org -rch@chromium.org +vasilvv@chromium.org zhongyi@chromium.org bnc@chromium.org diff --git a/chromium/net/quic/bidirectional_stream_quic_impl_unittest.cc b/chromium/net/quic/bidirectional_stream_quic_impl_unittest.cc index 6cfc33e5862..e5410eaa44d 100644 --- a/chromium/net/quic/bidirectional_stream_quic_impl_unittest.cc +++ b/chromium/net/quic/bidirectional_stream_quic_impl_unittest.cc @@ -463,6 +463,7 @@ class BidirectionalStreamQuicImplTest printer_(version_), destination_(kDefaultServerHostName, kDefaultServerPort) { quic::QuicEnableVersion(version_); + FLAGS_quic_enable_http3_grease_randomness = false; IPAddress ip(192, 0, 2, 33); peer_addr_ = IPEndPoint(ip, 443); self_addr_ = IPEndPoint(ip, 8435); @@ -857,18 +858,14 @@ INSTANTIATE_TEST_SUITE_P(Version, ::testing::PrintToStringParamName()); TEST_P(BidirectionalStreamQuicImplTest, GetRequest) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("GET", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); if (VersionUsesHttp3(version_.transport_version)) AddWrite(ConstructInitialSettingsPacket()); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructClientAckPacket(3, 1, 2)); @@ -966,22 +963,18 @@ TEST_P(BidirectionalStreamQuicImplTest, GetRequest) { } TEST_P(BidirectionalStreamQuicImplTest, LoadTimingTwoRequests) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("GET", "/", DEFAULT_PRIORITY); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); if (VersionUsesHttp3(version_.transport_version)) AddWrite(ConstructInitialSettingsPacket()); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), kFin, DEFAULT_PRIORITY, - nullptr)); + GetNthClientInitiatedBidirectionalStreamId(0), kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, nullptr)); // SetRequest() again for second request as |request_headers_| was moved. SetRequest("GET", "/", DEFAULT_PRIORITY); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(1), kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(1), kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, GetNthClientInitiatedBidirectionalStreamId(0), nullptr)); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructClientAckPacket(3, 1, 2)); @@ -1045,10 +1038,6 @@ TEST_P(BidirectionalStreamQuicImplTest, LoadTimingTwoRequests) { // Tests that when request headers are not delayed, only data buffers are // coalesced. TEST_P(BidirectionalStreamQuicImplTest, CoalesceDataBuffersNotHeadersFrame) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -1061,7 +1050,8 @@ TEST_P(BidirectionalStreamQuicImplTest, CoalesceDataBuffersNotHeadersFrame) { std::string header2 = ConstructDataHeader(kBody2.length()); std::vector<std::string> two_writes = {kBody1, kBody2}; AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), !kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), !kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); if (!version_.UsesHttp3()) { AddWrite( @@ -1191,11 +1181,6 @@ TEST_P(BidirectionalStreamQuicImplTest, CoalesceDataBuffersNotHeadersFrame) { // request headers with data buffers. TEST_P(BidirectionalStreamQuicImplTest, SendDataCoalesceDataBufferAndHeaderFrame) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -1206,8 +1191,7 @@ TEST_P(BidirectionalStreamQuicImplTest, std::string header = ConstructDataHeader(strlen(kBody1)); if (version_.UsesHttp3()) { AddWrite(ConstructRequestHeadersAndMultipleDataFramesPacket( - !kFin, DEFAULT_PRIORITY, &spdy_request_headers_frame_length, - {header, kBody1})); + !kFin, MEDIUM, &spdy_request_headers_frame_length, {header, kBody1})); } else { AddWrite(ConstructRequestHeadersAndMultipleDataFramesPacket( !kFin, DEFAULT_PRIORITY, &spdy_request_headers_frame_length, {kBody1})); @@ -1312,11 +1296,6 @@ TEST_P(BidirectionalStreamQuicImplTest, // request headers with data buffers. TEST_P(BidirectionalStreamQuicImplTest, SendvDataCoalesceDataBuffersAndHeaderFrame) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -1330,7 +1309,7 @@ TEST_P(BidirectionalStreamQuicImplTest, if (version_.UsesHttp3()) { AddWrite(ConstructRequestHeadersAndMultipleDataFramesPacket( - !kFin, DEFAULT_PRIORITY, &spdy_request_headers_frame_length, + !kFin, MEDIUM, &spdy_request_headers_frame_length, {header + kBody1 + header2 + kBody2})); } else { AddWrite(ConstructRequestHeadersAndMultipleDataFramesPacket( @@ -1453,11 +1432,6 @@ TEST_P(BidirectionalStreamQuicImplTest, // headers to be sent, if that write fails the stream does not crash. TEST_P(BidirectionalStreamQuicImplTest, SendDataWriteErrorCoalesceDataBufferAndHeaderFrame) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); if (VersionUsesHttp3(version_.transport_version)) AddWrite(ConstructInitialSettingsPacket()); @@ -1495,10 +1469,6 @@ TEST_P(BidirectionalStreamQuicImplTest, // headers to be sent, if that write fails the stream does not crash. TEST_P(BidirectionalStreamQuicImplTest, SendvDataWriteErrorCoalesceDataBufferAndHeaderFrame) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); if (VersionUsesHttp3(version_.transport_version)) AddWrite(ConstructInitialSettingsPacket()); @@ -1537,11 +1507,6 @@ TEST_P(BidirectionalStreamQuicImplTest, } TEST_P(BidirectionalStreamQuicImplTest, PostRequest) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -1549,7 +1514,8 @@ TEST_P(BidirectionalStreamQuicImplTest, PostRequest) { AddWrite(ConstructInitialSettingsPacket()); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), !kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), !kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); std::string header = ConstructDataHeader(strlen(kUploadData)); if (version_.UsesHttp3()) { @@ -1637,18 +1603,14 @@ TEST_P(BidirectionalStreamQuicImplTest, PostRequest) { } TEST_P(BidirectionalStreamQuicImplTest, EarlyDataOverrideRequest) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("PUT", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); if (VersionUsesHttp3(version_.transport_version)) AddWrite(ConstructInitialSettingsPacket()); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructClientAckPacket(3, 1, 2)); @@ -1747,11 +1709,6 @@ TEST_P(BidirectionalStreamQuicImplTest, EarlyDataOverrideRequest) { } TEST_P(BidirectionalStreamQuicImplTest, InterleaveReadDataAndSendData) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -1759,7 +1716,8 @@ TEST_P(BidirectionalStreamQuicImplTest, InterleaveReadDataAndSendData) { AddWrite(ConstructInitialSettingsPacket()); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), !kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), !kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); std::string header = ConstructDataHeader(strlen(kUploadData)); @@ -1862,18 +1820,14 @@ TEST_P(BidirectionalStreamQuicImplTest, InterleaveReadDataAndSendData) { } TEST_P(BidirectionalStreamQuicImplTest, ServerSendsRstAfterHeaders) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("GET", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); if (VersionUsesHttp3(version_.transport_version)) AddWrite(ConstructInitialSettingsPacket()); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); Initialize(); @@ -1913,18 +1867,14 @@ TEST_P(BidirectionalStreamQuicImplTest, ServerSendsRstAfterHeaders) { } TEST_P(BidirectionalStreamQuicImplTest, ServerSendsRstAfterReadData) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("GET", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); if (VersionUsesHttp3(version_.transport_version)) AddWrite(ConstructInitialSettingsPacket()); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); // Why does QUIC ack Rst? Is this expected? @@ -1982,11 +1932,6 @@ TEST_P(BidirectionalStreamQuicImplTest, ServerSendsRstAfterReadData) { } TEST_P(BidirectionalStreamQuicImplTest, SessionClosedBeforeReadData) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -1994,7 +1939,8 @@ TEST_P(BidirectionalStreamQuicImplTest, SessionClosedBeforeReadData) { AddWrite(ConstructInitialSettingsPacket()); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), !kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), !kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); Initialize(); @@ -2099,11 +2045,6 @@ TEST_P(BidirectionalStreamQuicImplTest, SessionClosedBeforeStartNotConfirmed) { } TEST_P(BidirectionalStreamQuicImplTest, SessionCloseDuringOnStreamReady) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); if (VersionUsesHttp3(version_.transport_version)) @@ -2132,11 +2073,6 @@ TEST_P(BidirectionalStreamQuicImplTest, SessionCloseDuringOnStreamReady) { } TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnStreamReady) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -2144,7 +2080,8 @@ TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnStreamReady) { AddWrite(ConstructInitialSettingsPacket()); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), !kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), !kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); AddWrite(ConstructClientEarlyRstStreamPacket()); @@ -2171,11 +2108,6 @@ TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnStreamReady) { } TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamAfterReadData) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -2183,7 +2115,8 @@ TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamAfterReadData) { AddWrite(ConstructInitialSettingsPacket()); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), !kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), !kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); AddWrite(ConstructClientAckAndRstStreamPacket(2, 1, 2)); @@ -2234,11 +2167,6 @@ TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamAfterReadData) { } TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnHeadersReceived) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -2246,7 +2174,8 @@ TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnHeadersReceived) { AddWrite(ConstructInitialSettingsPacket()); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), !kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), !kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); AddWrite(ConstructClientAckAndRstStreamPacket(2, 1, 2)); @@ -2289,11 +2218,6 @@ TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnHeadersReceived) { } TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnDataRead) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -2301,7 +2225,8 @@ TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnDataRead) { AddWrite(ConstructInitialSettingsPacket()); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), !kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), !kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); AddWrite(ConstructClientAckPacket(3, 1, 2)); AddWrite(ConstructClientRstStreamPacket()); @@ -2355,11 +2280,6 @@ TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnDataRead) { } TEST_P(BidirectionalStreamQuicImplTest, AsyncFinRead) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - const char kBody[] = "here is some data"; SetRequest("POST", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; @@ -2368,7 +2288,8 @@ TEST_P(BidirectionalStreamQuicImplTest, AsyncFinRead) { AddWrite(ConstructInitialSettingsPacket()); client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); AddWrite(ConstructRequestHeadersPacketInner( - GetNthClientInitiatedBidirectionalStreamId(0), !kFin, DEFAULT_PRIORITY, + GetNthClientInitiatedBidirectionalStreamId(0), !kFin, + version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, &spdy_request_headers_frame_length)); std::string header = ConstructDataHeader(strlen(kBody)); if (version_.UsesHttp3()) { @@ -2437,18 +2358,15 @@ TEST_P(BidirectionalStreamQuicImplTest, AsyncFinRead) { } TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnTrailersReceived) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - SetRequest("GET", "/", DEFAULT_PRIORITY); size_t spdy_request_headers_frame_length; client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); if (VersionUsesHttp3(version_.transport_version)) AddWrite(ConstructInitialSettingsPacket()); - AddWrite(ConstructRequestHeadersPacket(kFin, DEFAULT_PRIORITY, - &spdy_request_headers_frame_length)); + client_maker_.SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); + AddWrite(ConstructRequestHeadersPacket( + kFin, version_.UsesHttp3() ? MEDIUM : DEFAULT_PRIORITY, + &spdy_request_headers_frame_length)); AddWrite(ConstructClientAckPacket(3, 1, 2)); // Ack the data packet AddWrite(ConstructClientAckAndRstStreamPacket(4, 4, 2)); @@ -2467,6 +2385,7 @@ TEST_P(BidirectionalStreamQuicImplTest, DeleteStreamDuringOnTrailersReceived) { DeleteStreamDelegate::ON_TRAILERS_RECEIVED)); delegate->Start(&request, net_log().bound(), session()->CreateHandle(destination_)); + ConfirmHandshake(); delegate->WaitUntilNextCallback(kOnStreamReady); // Server acks the request. diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.cc b/chromium/net/quic/crypto/proof_verifier_chromium.cc index ee05dce91ca..d49b1db3218 100644 --- a/chromium/net/quic/crypto/proof_verifier_chromium.cc +++ b/chromium/net/quic/crypto/proof_verifier_chromium.cc @@ -16,6 +16,7 @@ #include "crypto/signature_verifier.h" #include "net/base/host_port_pair.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" #include "net/cert/cert_status_flags.h" #include "net/cert/cert_verifier.h" #include "net/cert/ct_policy_enforcer.h" @@ -459,7 +460,8 @@ int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) { cert_verify_result.verified_cert.get(), cert_.get(), verify_details_->ct_verify_result.scts, TransportSecurityState::ENABLE_EXPECT_CT_REPORTS, - verify_details_->ct_verify_result.policy_compliance); + verify_details_->ct_verify_result.policy_compliance, + proof_verifier_->network_isolation_key_); if (ct_requirement_status != TransportSecurityState::CT_NOT_REQUIRED) { verify_details_->ct_verify_result.policy_compliance_required = true; if (verify_details_->cert_verify_result.is_issued_by_known_root) { @@ -594,12 +596,14 @@ ProofVerifierChromium::ProofVerifierChromium( CTPolicyEnforcer* ct_policy_enforcer, TransportSecurityState* transport_security_state, CTVerifier* cert_transparency_verifier, - std::set<std::string> hostnames_to_allow_unknown_roots) + std::set<std::string> hostnames_to_allow_unknown_roots, + const NetworkIsolationKey& network_isolation_key) : cert_verifier_(cert_verifier), ct_policy_enforcer_(ct_policy_enforcer), transport_security_state_(transport_security_state), cert_transparency_verifier_(cert_transparency_verifier), - hostnames_to_allow_unknown_roots_(hostnames_to_allow_unknown_roots) { + hostnames_to_allow_unknown_roots_(hostnames_to_allow_unknown_roots), + network_isolation_key_(network_isolation_key) { DCHECK(cert_verifier_); DCHECK(ct_policy_enforcer_); DCHECK(transport_security_state_); diff --git a/chromium/net/quic/crypto/proof_verifier_chromium.h b/chromium/net/quic/crypto/proof_verifier_chromium.h index beacd3d0d63..f05da2eb259 100644 --- a/chromium/net/quic/crypto/proof_verifier_chromium.h +++ b/chromium/net/quic/crypto/proof_verifier_chromium.h @@ -13,6 +13,7 @@ #include "base/compiler_specific.h" #include "base/macros.h" #include "net/base/net_export.h" +#include "net/base/network_isolation_key.h" #include "net/cert/cert_verify_result.h" #include "net/cert/ct_verify_result.h" #include "net/cert/x509_certificate.h" @@ -74,7 +75,8 @@ class NET_EXPORT_PRIVATE ProofVerifierChromium : public quic::ProofVerifier { CTPolicyEnforcer* ct_policy_enforcer, TransportSecurityState* transport_security_state, CTVerifier* cert_transparency_verifier, - std::set<std::string> hostnames_to_allow_unknown_roots); + std::set<std::string> hostnames_to_allow_unknown_roots, + const NetworkIsolationKey& network_isolation_key); ~ProofVerifierChromium() override; // quic::ProofVerifier interface @@ -120,6 +122,8 @@ class NET_EXPORT_PRIVATE ProofVerifierChromium : public quic::ProofVerifier { std::set<std::string> hostnames_to_allow_unknown_roots_; + const NetworkIsolationKey network_isolation_key_; + DISALLOW_COPY_AND_ASSIGN(ProofVerifierChromium); }; diff --git a/chromium/net/quic/crypto/proof_verifier_chromium_test.cc b/chromium/net/quic/crypto/proof_verifier_chromium_test.cc index 62504d05827..e04f01a8566 100644 --- a/chromium/net/quic/crypto/proof_verifier_chromium_test.cc +++ b/chromium/net/quic/crypto/proof_verifier_chromium_test.cc @@ -10,6 +10,7 @@ #include "base/test/metrics/histogram_tester.h" #include "net/base/completion_once_callback.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" #include "net/cert/cert_status_flags.h" #include "net/cert/cert_verifier.h" #include "net/cert/ct_log_verifier.h" @@ -113,6 +114,11 @@ const char kTestEmptySignature[] = ""; const char kLogDescription[] = "somelog"; +// This test exercises code that does not depend on the QUIC version in use +// but that still requires a version so we just use the first one. +const quic::QuicTransportVersion kTestTransportVersion = + quic::AllSupportedVersions().front().transport_version; + } // namespace class ProofVerifierChromiumTest : public ::testing::Test { @@ -152,7 +158,7 @@ class ProofVerifierChromiumTest : public ::testing::Test { base::FilePath()); std::string signature; source.GetProof(quic::QuicSocketAddress(), quic::QuicSocketAddress(), - kTestHostname, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestConfig, kTestTransportVersion, kTestChloHash, std::make_unique<SignatureSaver>(&signature)); return signature; @@ -204,14 +210,14 @@ TEST_F(ProofVerifierChromiumTest, VerifyProof) { MockCertVerifier dummy_verifier; dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, OK); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -238,14 +244,14 @@ TEST_F(ProofVerifierChromiumTest, VerifyProof) { // verification fails. TEST_F(ProofVerifierChromiumTest, FailsIfCertFails) { MockCertVerifier dummy_verifier; - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -264,14 +270,14 @@ TEST_F(ProofVerifierChromiumTest, ValidSCTList) { MockCertVerifier cert_verifier; - ProofVerifierChromium proof_verifier(&cert_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &cert_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, ct::GetSCTListForTesting(), kTestEmptySignature, verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -292,14 +298,14 @@ TEST_F(ProofVerifierChromiumTest, InvalidSCTList) { ASSERT_NO_FATAL_FAILURE(GetSCTTestCertificates(&certs_)); MockCertVerifier cert_verifier; - ProofVerifierChromium proof_verifier(&cert_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &cert_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, ct::GetSCTListWithInvalidSCT(), kTestEmptySignature, verify_context_.get(), &error_details_, &details_, std::move(callback)); @@ -319,14 +325,14 @@ TEST_F(ProofVerifierChromiumTest, InvalidSCTList) { // signature fails. TEST_F(ProofVerifierChromiumTest, FailsIfSignatureFails) { FailsTestCertVerifier cert_verifier; - ProofVerifierChromium proof_verifier(&cert_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &cert_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, kTestEmptySignature, verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -344,14 +350,14 @@ TEST_F(ProofVerifierChromiumTest, PreservesEVIfAllowed) { .WillRepeatedly( Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS)); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -387,14 +393,14 @@ TEST_F(ProofVerifierChromiumTest, StripsEVIfNotAllowed) { .WillRepeatedly( Return(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS)); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -436,14 +442,14 @@ TEST_F(ProofVerifierChromiumTest, CTEVHistogramNonCompliant) { .WillRepeatedly( Return(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS)); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -492,14 +498,14 @@ TEST_F(ProofVerifierChromiumTest, CTEVHistogramCompliant) { .WillRepeatedly( Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS)); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -544,14 +550,14 @@ TEST_F(ProofVerifierChromiumTest, IsFatalErrorNotSetForNonFatalError) { dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, ERR_CERT_DATE_INVALID); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -581,14 +587,14 @@ TEST_F(ProofVerifierChromiumTest, IsFatalErrorSetForFatalError) { base::Time::Now() + base::TimeDelta::FromSeconds(1000); transport_security_state_.AddHSTS(kTestHostname, expiry, true); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -616,14 +622,14 @@ TEST_F(ProofVerifierChromiumTest, PKPEnforced) { transport_security_state_.EnableStaticPinsForTesting(); ScopedTransportSecurityStateSource scoped_security_state_source; - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kCTAndPKPHost, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kCTAndPKPHost, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -662,14 +668,14 @@ TEST_F(ProofVerifierChromiumTest, PKPBypassFlagSet) { transport_security_state_.EnableStaticPinsForTesting(); ScopedTransportSecurityStateSource scoped_security_state_source; - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {kCTAndPKPHost}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {kCTAndPKPHost}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kCTAndPKPHost, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kCTAndPKPHost, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -712,14 +718,14 @@ TEST_F(ProofVerifierChromiumTest, CTIsRequired) { .WillRepeatedly( Return(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS)); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -769,14 +775,14 @@ TEST_F(ProofVerifierChromiumTest, CTIsRequiredHistogramNonCompliant) { .WillRepeatedly( Return(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS)); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -824,14 +830,14 @@ TEST_F(ProofVerifierChromiumTest, CTIsRequiredHistogramCompliant) { { MockCertVerifier dummy_verifier; dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, OK); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {kTestHostname}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {kTestHostname}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -849,14 +855,14 @@ TEST_F(ProofVerifierChromiumTest, CTIsRequiredHistogramCompliant) { dummy_result_.is_issued_by_known_root = true; MockCertVerifier dummy_verifier; dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, OK); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -892,14 +898,14 @@ TEST_F(ProofVerifierChromiumTest, CTIsNotRequiredHistogram) { MockCertVerifier dummy_verifier; dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, OK); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {kTestHostname}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {kTestHostname}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -938,14 +944,14 @@ TEST_F(ProofVerifierChromiumTest, PKPAndCTBothTested) { .WillRepeatedly( Return(ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS)); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kCTAndPKPHost, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kCTAndPKPHost, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -989,14 +995,14 @@ TEST_F(ProofVerifierChromiumTest, CTComplianceStatusHistogram) { { MockCertVerifier dummy_verifier; dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, OK); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {kTestHostname}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {kTestHostname}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -1016,14 +1022,14 @@ TEST_F(ProofVerifierChromiumTest, CTComplianceStatusHistogram) { dummy_result_.is_issued_by_known_root = true; MockCertVerifier dummy_verifier; dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, OK); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -1065,15 +1071,15 @@ TEST_F(ProofVerifierChromiumTest, CTRequirementsFlagNotMet) { .WillRepeatedly( Return(ct::CTPolicyCompliance::CT_POLICY_NOT_DIVERSE_SCTS)); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); { std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); @@ -1118,15 +1124,15 @@ TEST_F(ProofVerifierChromiumTest, CTRequirementsFlagMet) { .WillRepeatedly( Return(ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS)); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); { std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); @@ -1160,14 +1166,14 @@ TEST_F(ProofVerifierChromiumTest, UnknownRootRejected) { MockCertVerifier dummy_verifier; dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, OK); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_FAILURE, status); @@ -1191,14 +1197,14 @@ TEST_F(ProofVerifierChromiumTest, UnknownRootAcceptedWithOverride) { MockCertVerifier dummy_verifier; dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, OK); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {kTestHostname}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {kTestHostname}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); @@ -1227,14 +1233,14 @@ TEST_F(ProofVerifierChromiumTest, UnknownRootAcceptedWithWildcardOverride) { MockCertVerifier dummy_verifier; dummy_verifier.AddResultForCert(test_cert_.get(), dummy_result_, OK); - ProofVerifierChromium proof_verifier(&dummy_verifier, &ct_policy_enforcer_, - &transport_security_state_, - ct_verifier_.get(), {""}); + ProofVerifierChromium proof_verifier( + &dummy_verifier, &ct_policy_enforcer_, &transport_security_state_, + ct_verifier_.get(), {""}, NetworkIsolationKey()); std::unique_ptr<DummyProofVerifierCallback> callback( new DummyProofVerifierCallback); quic::QuicAsyncStatus status = proof_verifier.VerifyProof( - kTestHostname, kTestPort, kTestConfig, quic::QUIC_VERSION_43, + kTestHostname, kTestPort, kTestConfig, kTestTransportVersion, kTestChloHash, certs_, kTestEmptySCT, GetTestSignature(), verify_context_.get(), &error_details_, &details_, std::move(callback)); ASSERT_EQ(quic::QUIC_SUCCESS, status); diff --git a/chromium/net/quic/crypto_test_utils_chromium.cc b/chromium/net/quic/crypto_test_utils_chromium.cc index f284f7c4cdb..256dc0e28f5 100644 --- a/chromium/net/quic/crypto_test_utils_chromium.cc +++ b/chromium/net/quic/crypto_test_utils_chromium.cc @@ -11,6 +11,7 @@ #include "base/stl_util.h" #include "base/strings/stringprintf.h" #include "net/base/net_errors.h" +#include "net/base/network_isolation_key.h" #include "net/base/test_completion_callback.h" #include "net/cert/cert_status_flags.h" #include "net/cert/cert_verifier.h" @@ -53,7 +54,8 @@ class TestProofVerifierChromium : public ProofVerifierChromium { ct_policy_enforcer.get(), transport_security_state.get(), cert_transparency_verifier.get(), - {"test.example.com"}), + {"test.example.com"}, + NetworkIsolationKey()), cert_verifier_(std::move(cert_verifier)), transport_security_state_(std::move(transport_security_state)), cert_transparency_verifier_(std::move(cert_transparency_verifier)), diff --git a/chromium/net/quic/mock_crypto_client_stream.cc b/chromium/net/quic/mock_crypto_client_stream.cc index 9a8d2b55e39..3eb98fdef89 100644 --- a/chromium/net/quic/mock_crypto_client_stream.cc +++ b/chromium/net/quic/mock_crypto_client_stream.cc @@ -11,6 +11,7 @@ #include "net/third_party/quiche/src/quic/core/crypto/quic_decrypter.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_encrypter.h" #include "net/third_party/quiche/src/quic/core/http/quic_spdy_client_session_base.h" +#include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h" #include "net/third_party/quiche/src/quic/test_tools/quic_config_peer.h" #include "testing/gtest/include/gtest/gtest.h" @@ -32,7 +33,6 @@ using quic::NullEncrypter; using quic::PACKET_8BYTE_CONNECTION_ID; using quic::Perspective; using quic::ProofVerifyContext; -using quic::PROTOCOL_TLS1_3; using quic::QUIC_CRYPTO_MESSAGE_AFTER_HANDSHAKE_COMPLETE; using quic::QUIC_NO_ERROR; using quic::QUIC_PROOF_INVALID; @@ -49,6 +49,19 @@ using quic::TransportParameters; using quiche::QuicheStringPiece; using std::string; +namespace { + +// TODO(crbug.com/1085541): Consider moving this method into QuicTestUtils. +quic::QuicConnectionId TestConnectionId(uint64_t connection_number) { + const uint64_t connection_id64_net = + quiche::QuicheEndian::HostToNet64(connection_number); + return quic::QuicConnectionId( + reinterpret_cast<const char*>(&connection_id64_net), + sizeof(connection_id64_net)); +} + +} // namespace + namespace net { MockCryptoClientStream::MockCryptoClientStream( @@ -138,13 +151,17 @@ bool MockCryptoClientStream::CryptoConnect() { ENCRYPTION_ZERO_RTT, std::make_unique<NullDecrypter>(Perspective::IS_CLIENT)); } - session()->connection()->SetEncrypter( + if (session()->version().UsesHttp3()) { + SetConfigNegotiated(); + } + session()->OnNewEncryptionKeyAvailable( ENCRYPTION_ZERO_RTT, std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); } - if (session()->connection()->version().handshake_protocol == - quic::PROTOCOL_QUIC_CRYPTO) { + if (session()->version().UsesQuicCrypto()) { session()->SetDefaultEncryptionLevel(ENCRYPTION_ZERO_RTT); + } else { + session()->DiscardOldEncryptionKey(ENCRYPTION_INITIAL); } break; } @@ -186,8 +203,7 @@ bool MockCryptoClientStream::CryptoConnect() { ENCRYPTION_FORWARD_SECURE, std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); } - if (session()->connection()->version().handshake_protocol == - quic::PROTOCOL_TLS1_3) { + if (session()->version().UsesTls()) { session()->OnOneRttKeysAvailable(); } else { session()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); @@ -238,44 +254,48 @@ void MockCryptoClientStream::OnOneRttPacketAcknowledged() {} void MockCryptoClientStream::NotifySessionOneRttKeyAvailable() { encryption_established_ = true; - handshake_confirmed_ = true; + handshake_confirmed_ = true; + if (session()->version().UsesQuicCrypto()) SetConfigNegotiated(); - if (use_mock_crypter_) { - if (session()->connection()->version().KnowsWhichDecrypterToUse()) { - session()->connection()->InstallDecrypter( - ENCRYPTION_FORWARD_SECURE, - std::make_unique<MockDecrypter>(Perspective::IS_CLIENT)); - } else { - session()->connection()->SetDecrypter( - ENCRYPTION_FORWARD_SECURE, - std::make_unique<MockDecrypter>(Perspective::IS_CLIENT)); - } - session()->connection()->SetEncrypter( + if (use_mock_crypter_) { + if (session()->connection()->version().KnowsWhichDecrypterToUse()) { + session()->connection()->InstallDecrypter( ENCRYPTION_FORWARD_SECURE, - std::make_unique<MockEncrypter>(Perspective::IS_CLIENT)); + std::make_unique<MockDecrypter>(Perspective::IS_CLIENT)); } else { - if (session()->connection()->version().KnowsWhichDecrypterToUse()) { - session()->connection()->InstallDecrypter( - ENCRYPTION_FORWARD_SECURE, - std::make_unique<NullDecrypter>(Perspective::IS_CLIENT)); - } else { - session()->connection()->SetDecrypter( - ENCRYPTION_FORWARD_SECURE, - std::make_unique<NullDecrypter>(Perspective::IS_CLIENT)); - } - session()->connection()->SetEncrypter(ENCRYPTION_INITIAL, nullptr); - session()->OnNewEncryptionKeyAvailable( + session()->connection()->SetDecrypter( ENCRYPTION_FORWARD_SECURE, - std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); + std::make_unique<MockDecrypter>(Perspective::IS_CLIENT)); } - if (session()->connection()->version().handshake_protocol == - quic::PROTOCOL_TLS1_3) { - session()->OnOneRttKeysAvailable(); + session()->connection()->SetEncrypter( + ENCRYPTION_FORWARD_SECURE, + std::make_unique<MockEncrypter>(Perspective::IS_CLIENT)); + } else { + if (session()->connection()->version().KnowsWhichDecrypterToUse()) { + session()->connection()->InstallDecrypter( + ENCRYPTION_FORWARD_SECURE, + std::make_unique<NullDecrypter>(Perspective::IS_CLIENT)); } else { - session()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + session()->connection()->SetDecrypter( + ENCRYPTION_FORWARD_SECURE, + std::make_unique<NullDecrypter>(Perspective::IS_CLIENT)); } - session()->DiscardOldEncryptionKey(ENCRYPTION_INITIAL); - session()->NeuterHandshakeData(); + session()->connection()->SetEncrypter(ENCRYPTION_INITIAL, nullptr); + session()->OnNewEncryptionKeyAvailable( + ENCRYPTION_FORWARD_SECURE, + std::make_unique<NullEncrypter>(Perspective::IS_CLIENT)); + } + if (session()->version().UsesTls()) { + SetConfigNegotiated(); + session()->OnOneRttKeysAvailable(); + } else { + session()->SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + } + session()->DiscardOldEncryptionKey(ENCRYPTION_INITIAL); + if (session()->version().UsesTls()) { + session()->DiscardOldEncryptionKey(ENCRYPTION_ZERO_RTT); + } + session()->NeuterHandshakeData(); } // static @@ -286,7 +306,8 @@ CryptoHandshakeMessage MockCryptoClientStream::GetDummyCHLOMessage() { } void MockCryptoClientStream::SetConfigNegotiated() { - ASSERT_FALSE(session()->config()->negotiated()); + if (!session()->version().UsesHttp3()) + ASSERT_FALSE(session()->config()->negotiated()); QuicTagVector cgst; // TODO(rtenneti): Enable the following code after BBR code is checked in. #if 0 @@ -304,10 +325,27 @@ void MockCryptoClientStream::SetConfigNegotiated() { config.SetInitialMaxStreamDataBytesUnidirectionalToSend( quic::kMinimumFlowControlSendWindow); + if (quic::VersionHasIetfInvariantHeader( + session()->connection()->transport_version())) { + auto connection_id = TestConnectionId(0x1337); + config.SetStatelessResetTokenToSend( + quic::QuicUtils::GenerateStatelessResetToken(connection_id)); + } + if (session()->version().AuthenticatesHandshakeConnectionIds()) { + if (session()->perspective() == Perspective::IS_CLIENT) { + config.SetOriginalConnectionIdToSend( + session()->connection()->connection_id()); + config.SetInitialSourceConnectionIdToSend( + session()->connection()->connection_id()); + } else { + config.SetInitialSourceConnectionIdToSend( + session()->connection()->client_connection_id()); + } + } + QuicErrorCode error; std::string error_details; - if (session()->connection()->version().handshake_protocol == - PROTOCOL_TLS1_3) { + if (session()->version().UsesTls()) { TransportParameters params; ASSERT_TRUE(config.FillTransportParameters(¶ms)); error = session()->config()->ProcessTransportParameters( @@ -324,8 +362,7 @@ void MockCryptoClientStream::SetConfigNegotiated() { } void MockCryptoClientStream::FillCryptoParams() { - if (session()->connection()->version().handshake_protocol == - quic::PROTOCOL_QUIC_CRYPTO) { + if (session()->version().UsesQuicCrypto()) { crypto_negotiated_params_->key_exchange = kC255; crypto_negotiated_params_->aead = kAESG; return; diff --git a/chromium/net/quic/mock_quic_data.cc b/chromium/net/quic/mock_quic_data.cc index 9c71eff9c95..d1b5cbc41cb 100644 --- a/chromium/net/quic/mock_quic_data.cc +++ b/chromium/net/quic/mock_quic_data.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "net/quic/mock_quic_data.h" +#include "net/base/hex_utils.h" namespace net { namespace test { diff --git a/chromium/net/quic/network_connection.cc b/chromium/net/quic/network_connection.cc index 6cf3d116f65..9c36d366445 100644 --- a/chromium/net/quic/network_connection.cc +++ b/chromium/net/quic/network_connection.cc @@ -4,6 +4,7 @@ #include "net/quic/network_connection.h" +#include "base/logging.h" #include "net/base/network_interfaces.h" namespace net { diff --git a/chromium/net/quic/platform/impl/quic_default_proof_providers_impl.cc b/chromium/net/quic/platform/impl/quic_default_proof_providers_impl.cc index 28ec2766c6a..bbe4a7069e7 100644 --- a/chromium/net/quic/platform/impl/quic_default_proof_providers_impl.cc +++ b/chromium/net/quic/platform/impl/quic_default_proof_providers_impl.cc @@ -9,6 +9,7 @@ #include "base/files/file_path.h" #include "base/strings/utf_string_conversions.h" #include "build/build_config.h" +#include "net/base/network_isolation_key.h" #include "net/cert/cert_verifier.h" #include "net/cert/ct_log_verifier.h" #include "net/cert/ct_policy_enforcer.h" @@ -64,7 +65,10 @@ class ProofVerifierChromiumWithOwnership : public net::ProofVerifierChromium { &ct_policy_enforcer_, &transport_security_state_, &ct_verifier_, - UnknownRootAllowlistForHost(host)), + UnknownRootAllowlistForHost(host), + // Fine to use an empty NetworkIsolationKey + // here, since this isn't used in Chromium. + net::NetworkIsolationKey()), cert_verifier_(std::move(cert_verifier)) {} private: diff --git a/chromium/net/quic/platform/impl/quic_socket_utils.cc b/chromium/net/quic/platform/impl/quic_socket_utils.cc index bb062039fe5..9fe56a19040 100644 --- a/chromium/net/quic/platform/impl/quic_socket_utils.cc +++ b/chromium/net/quic/platform/impl/quic_socket_utils.cc @@ -14,6 +14,7 @@ #include <unistd.h> #include <string> +#include "base/notreached.h" #include "net/third_party/quiche/src/common/platform/api/quiche_arraysize.h" #include "net/third_party/quiche/src/quic/core/quic_packets.h" #include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h" diff --git a/chromium/net/quic/platform/impl/quic_test_impl.h b/chromium/net/quic/platform/impl/quic_test_impl.h index 98bc7b27c29..1bcda9fde67 100644 --- a/chromium/net/quic/platform/impl/quic_test_impl.h +++ b/chromium/net/quic/platform/impl/quic_test_impl.h @@ -5,7 +5,7 @@ #ifndef NET_QUIC_PLATFORM_IMPL_QUIC_TEST_IMPL_H_ #define NET_QUIC_PLATFORM_IMPL_QUIC_TEST_IMPL_H_ -#include "base/logging.h" +#include "base/check_op.h" #include "net/test/test_with_task_environment.h" #include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" diff --git a/chromium/net/quic/quic_chromium_client_session.cc b/chromium/net/quic/quic_chromium_client_session.cc index 780b8507c71..da5ac629afc 100644 --- a/chromium/net/quic/quic_chromium_client_session.cc +++ b/chromium/net/quic/quic_chromium_client_session.cc @@ -136,7 +136,7 @@ void RecordConnectionCloseErrorCode(const quic::QuicConnectionCloseFrame& frame, quic::ConnectionCloseSource source, const std::string& hostname, bool handshake_confirmed) { - bool is_google_host = HasGoogleHost(GURL("https://" + hostname)); + bool is_google_host = IsGoogleHost(hostname); std::string histogram = "Net.QuicSession.ConnectionCloseErrorCode"; if (source == quic::ConnectionCloseSource::FROM_SELF) { @@ -161,10 +161,20 @@ void RecordConnectionCloseErrorCode(const quic::QuicConnectionCloseFrame& frame, histogram += "IetfTransport"; RecordConnectionCloseErrorCodeImpl(histogram, frame.wire_error_code, is_google_host, handshake_confirmed); + if (frame.quic_error_code == quic::QUIC_IETF_GQUIC_ERROR_MISSING) { + histogram += "GQuicErrorMissing"; + RecordConnectionCloseErrorCodeImpl(histogram, frame.wire_error_code, + is_google_host, handshake_confirmed); + } } else if (frame.close_type == quic::IETF_QUIC_APPLICATION_CONNECTION_CLOSE) { histogram += "IetfApplication"; RecordConnectionCloseErrorCodeImpl(histogram, frame.wire_error_code, is_google_host, handshake_confirmed); + if (frame.quic_error_code == quic::QUIC_IETF_GQUIC_ERROR_MISSING) { + histogram += "GQuicErrorMissing"; + RecordConnectionCloseErrorCodeImpl(histogram, frame.wire_error_code, + is_google_host, handshake_confirmed); + } } } @@ -826,8 +836,7 @@ QuicChromiumClientSession::QuicChromiumClientSession( wait_for_new_network_(false), ignore_read_error_(false), headers_include_h2_stream_dependency_( - headers_include_h2_stream_dependency && - this->connection()->transport_version() >= quic::QUIC_VERSION_43), + headers_include_h2_stream_dependency), max_allowed_push_id_(max_allowed_push_id) { // Make sure connection migration and goaway on path degrading are not turned // on at the same time. @@ -880,6 +889,9 @@ QuicChromiumClientSession::QuicChromiumClientSession( QuicChromiumClientSession::~QuicChromiumClientSession() { DCHECK(callback_.is_null()); + for (auto& observer : connectivity_observer_list_) + observer.OnSessionRemoved(this); + net_log_.EndEvent(NetLogEventType::QUIC_SESSION); DCHECK(waiting_for_confirmation_callbacks_.empty()); if (HasActiveRequestStreams()) @@ -995,6 +1007,13 @@ void QuicChromiumClientSession::Initialize() { quic::QuicSpdyClientSessionBase::SetMaxPushId(max_allowed_push_id_); } set_max_inbound_header_list_size(kQuicMaxHeaderListSize); + if (config()->HasClientRequestedIndependentOption( + quic::kQLVE, quic::Perspective::IS_CLIENT)) { + connection()->EnableLegacyVersionEncapsulation(session_key_.host()); + // Legacy Version Encapsulation needs CHLO padding to be disabled. + // TODO(dschinazi) remove this line once we deprecate quic_dont_pad_chlo. + crypto_config_->GetConfig()->set_disable_chlo_padding(true); + } quic::QuicSpdyClientSessionBase::Initialize(); SetHpackEncoderDebugVisitor(std::make_unique<HpackEncoderDebugVisitor>()); SetHpackDecoderDebugVisitor(std::make_unique<HpackDecoderDebugVisitor>()); @@ -1078,6 +1097,16 @@ void QuicChromiumClientSession::RemoveHandle(Handle* handle) { handles_.erase(handle); } +void QuicChromiumClientSession::AddConnectivityObserver( + ConnectivityObserver* observer) { + connectivity_observer_list_.AddObserver(observer); +} + +void QuicChromiumClientSession::RemoveConnectivityObserver( + ConnectivityObserver* observer) { + connectivity_observer_list_.RemoveObserver(observer); +} + // TODO(zhongyi): replace migration_session_* booleans with // ConnectionMigrationMode. ConnectionMigrationMode QuicChromiumClientSession::connection_migration_mode() @@ -1363,7 +1392,7 @@ bool QuicChromiumClientSession::CanPool( return SpdySession::CanPool(transport_security_state_, ssl_info, *ssl_config_service_, session_key_.host(), - hostname); + hostname, network_isolation_key); } bool QuicChromiumClientSession::ShouldCreateIncomingStream( @@ -1383,7 +1412,7 @@ bool QuicChromiumClientSession::ShouldCreateIncomingStream( if (quic::QuicUtils::IsClientInitiatedStreamId( connection()->transport_version(), id) || (connection()->version().HasIetfQuicFrames() && - quic::QuicUtils::IsBidirectionalStreamId(id))) { + quic::QuicUtils::IsBidirectionalStreamId(id, connection()->version()))) { LOG(WARNING) << "Received invalid push stream id " << id; connection()->CloseConnection( quic::QUIC_INVALID_STREAM_ID, @@ -1537,36 +1566,26 @@ void QuicChromiumClientSession::OnConfigNegotiated() { IPEndPoint old_address; GetDefaultSocket()->GetPeerAddress(&old_address); - // Migrate only if address families match, or if new address family is v6, - // since a v4 address should be reachable over a v6 network (using a - // v4-mapped v6 address). + // Migrate only if address families match. IPEndPoint new_address; if (old_address.GetFamily() == ADDRESS_FAMILY_IPV6) { - if (config()->HasReceivedIPv6AlternateServerAddress()) { + if (!config()->HasReceivedIPv6AlternateServerAddress()) { + return; + } new_address = ToIPEndPoint(config()->ReceivedIPv6AlternateServerAddress()); - } else { - new_address = - ToIPEndPoint(config()->ReceivedIPv4AlternateServerAddress()); - // Use a v4-mapped v6 address. - new_address = - IPEndPoint(ConvertIPv4ToIPv4MappedIPv6(new_address.address()), - new_address.port()); - } } else if (old_address.GetFamily() == ADDRESS_FAMILY_IPV4) { - if (config()->HasReceivedIPv4AlternateServerAddress()) { - new_address = - ToIPEndPoint(config()->ReceivedIPv4AlternateServerAddress()); - } else { + if (!config()->HasReceivedIPv4AlternateServerAddress()) { return; } + new_address = ToIPEndPoint(config()->ReceivedIPv4AlternateServerAddress()); } DCHECK_EQ(new_address.GetFamily(), old_address.GetFamily()); // Specifying kInvalidNetworkHandle for the |network| parameter // causes the session to use the default network for the new socket. Migrate(NetworkChangeNotifier::kInvalidNetworkHandle, new_address, - /*close_session_on_error=*/true, net_log_); + /*close_session_on_error=*/true); } void QuicChromiumClientSession::SetDefaultEncryptionLevel( @@ -1694,6 +1713,10 @@ void QuicChromiumClientSession::OnConnectionClosed( UMA_HISTOGRAM_COUNTS_1000( "Net.QuicSession.ClosedByRtoAtClient.SentPacketCount", connection()->GetStats().packets_sent); + UMA_HISTOGRAM_COUNTS_100( + "Net.QuicSession." + "MaxConsecutiveRtoWithForwardProgressAndBlackholeDetected", + connection()->GetStats().max_consecutive_rto_with_forward_progress); } } @@ -1740,6 +1763,9 @@ void QuicChromiumClientSession::OnConnectionClosed( UMA_HISTOGRAM_COUNTS_100( "Net.QuicSession.CryptoRetransmitCount.HandshakeConfirmed", connection()->GetStats().crypto_retransmit_count); + UMA_HISTOGRAM_COUNTS_100( + "Net.QuicSession.MaxConsecutiveRtoWithForwardProgress", + connection()->GetStats().max_consecutive_rto_with_forward_progress); } else { if (error == quic::QUIC_PUBLIC_RESET) { RecordHandshakeFailureReason(HANDSHAKE_FAILURE_PUBLIC_RESET); @@ -1812,8 +1838,7 @@ int QuicChromiumClientSession::HandleWriteError( return error_code; } - NetworkChangeNotifier::NetworkHandle current_network = - GetDefaultSocket()->GetBoundNetwork(); + NetworkChangeNotifier::NetworkHandle current_network = GetCurrentNetwork(); net_log_.AddEventWithInt64Params( NetLogEventType::QUIC_CONNECTION_MIGRATION_ON_WRITE_ERROR, "network", @@ -1867,8 +1892,7 @@ void QuicChromiumClientSession::MigrateSessionOnWriteError( if (migrate_idle_session_ && CheckIdleTimeExceedsIdleMigrationPeriod()) return; - if (!migrate_idle_session_ && GetNumActiveStreams() == 0 && - GetNumDrainingStreams() == 0) { + if (!migrate_idle_session_ && !HasActiveRequestStreams()) { // connection close packet to be sent since socket may be borked. connection()->CloseConnection(quic::QUIC_PACKET_WRITE_ERROR, "Write error for non-migratable session", @@ -1878,9 +1902,9 @@ void QuicChromiumClientSession::MigrateSessionOnWriteError( // Do not migrate if connection migration is disabled. if (config()->DisableConnectionMigration()) { - HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_DISABLED_BY_CONFIG, connection_id(), - "Migration disabled by config"); + HistogramAndLogMigrationFailure(MIGRATION_STATUS_DISABLED_BY_CONFIG, + connection_id(), + "Migration disabled by config"); // Close the connection since migration was disabled. Do not cause a // connection close packet to be sent since socket may be borked. connection()->CloseConnection(quic::QUIC_PACKET_WRITE_ERROR, @@ -1890,22 +1914,21 @@ void QuicChromiumClientSession::MigrateSessionOnWriteError( } NetworkChangeNotifier::NetworkHandle new_network = - stream_factory_->FindAlternateNetwork( - GetDefaultSocket()->GetBoundNetwork()); + stream_factory_->FindAlternateNetwork(GetCurrentNetwork()); if (new_network == NetworkChangeNotifier::kInvalidNetworkHandle) { // No alternate network found. - HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_NO_ALTERNATE_NETWORK, connection_id(), - "No alternate network found"); + HistogramAndLogMigrationFailure(MIGRATION_STATUS_NO_ALTERNATE_NETWORK, + connection_id(), + "No alternate network found"); OnNoNewNetwork(); return; } - if (GetDefaultSocket()->GetBoundNetwork() == default_network_ && + if (GetCurrentNetwork() == default_network_ && current_migrations_to_non_default_network_on_write_error_ >= max_migrations_to_non_default_network_on_write_error_) { HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_ON_WRITE_ERROR_DISABLED, connection_id(), + MIGRATION_STATUS_ON_WRITE_ERROR_DISABLED, connection_id(), "Exceeds maximum number of migrations on write error"); connection()->CloseConnection( quic::QUIC_PACKET_WRITE_ERROR, @@ -1915,16 +1938,13 @@ void QuicChromiumClientSession::MigrateSessionOnWriteError( } current_migrations_to_non_default_network_on_write_error_++; - const NetLogWithSource migration_net_log = NetLogWithSource::Make( - net_log_.net_log(), NetLogSourceType::QUIC_CONNECTION_MIGRATION); - migration_net_log.BeginEventWithStringParams( + net_log_.BeginEventWithStringParams( NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED, "trigger", "WriteError"); MigrationResult result = Migrate(new_network, ToIPEndPoint(connection()->peer_address()), - /*close_session_on_error=*/false, migration_net_log); - migration_net_log.EndEvent( - NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED); + /*close_session_on_error=*/false); + net_log_.EndEvent(NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED); if (result == MigrationResult::FAILURE) { // Close the connection if migration failed. Do not cause a @@ -2019,8 +2039,7 @@ void QuicChromiumClientSession::OnProbeSucceeded( // Close streams that are not migratable to the probed |network|. ResetNonMigratableStreams(); - if (!migrate_idle_session_ && GetNumActiveStreams() == 0 && - GetNumDrainingStreams() == 0) { + if (!migrate_idle_session_ && !HasActiveRequestStreams()) { // If idle sessions won't be migrated, close the connection. CloseSessionOnErrorLater( ERR_NETWORK_CHANGED, @@ -2041,10 +2060,14 @@ void QuicChromiumClientSession::OnProbeSucceeded( return; } + // Notify the connection that migration succeeds after probing. + if (connection()->IsPathDegrading()) + connection()->OnSuccessfulMigrationAfterProbing(); + net_log_.AddEventWithInt64Params( NetLogEventType::QUIC_CONNECTION_MIGRATION_SUCCESS_AFTER_PROBING, "migrate_to_network", network); - HistogramAndLogMigrationSuccess(net_log_, connection_id()); + HistogramAndLogMigrationSuccess(connection_id()); if (network == default_network_) { DVLOG(1) << "Client successfully migrated to default network: " << default_network_; @@ -2080,7 +2103,7 @@ void QuicChromiumClientSession::OnProbeFailed( DVLOG(1) << "Connectivity probing failed on <network: " << network << ", peer_address: " << peer_address.ToString() << ">."; DVLOG_IF(1, network == default_network_ && - GetDefaultSocket()->GetBoundNetwork() != default_network_) + GetCurrentNetwork() != default_network_) << "Client probing failed on the default network, still using " "non-default network."; } @@ -2093,9 +2116,17 @@ bool QuicChromiumClientSession::OnSendConnectivityProbingPacket( } void QuicChromiumClientSession::OnNetworkConnected( - NetworkChangeNotifier::NetworkHandle network, - const NetLogWithSource& net_log) { - DCHECK(migrate_session_on_network_change_v2_); + NetworkChangeNotifier::NetworkHandle network) { + if (connection()->IsPathDegrading()) { + base::TimeDelta duration = + tick_clock_->NowTicks() - most_recent_path_degrading_timestamp_; + UMA_HISTOGRAM_CUSTOM_TIMES("Net.QuicNetworkDegradingDurationTillConnected", + duration, base::TimeDelta::FromMilliseconds(1), + base::TimeDelta::FromMinutes(10), 50); + } + if (!migrate_session_on_network_change_v2_) + return; + net_log_.AddEventWithInt64Params( NetLogEventType::QUIC_CONNECTION_MIGRATION_ON_NETWORK_CONNECTED, "connected_network", network); @@ -2104,15 +2135,8 @@ void QuicChromiumClientSession::OnNetworkConnected( if (!wait_for_new_network_ && !connection()->IsPathDegrading()) return; - if (connection()->IsPathDegrading()) { - base::TimeDelta duration = - tick_clock_->NowTicks() - most_recent_path_degrading_timestamp_; - UMA_HISTOGRAM_CUSTOM_TIMES("Net.QuicNetworkDegradingDurationTillConnected", - duration, base::TimeDelta::FromMilliseconds(1), - base::TimeDelta::FromMinutes(10), 50); - + if (connection()->IsPathDegrading()) current_migration_cause_ = NEW_NETWORK_CONNECTED_POST_PATH_DEGRADING; - } if (wait_for_new_network_) { wait_for_new_network_ = false; @@ -2124,18 +2148,18 @@ void QuicChromiumClientSession::OnNetworkConnected( } else { // The connection is path degrading. DCHECK(connection()->IsPathDegrading()); - OnPathDegrading(); + MaybeMigrateToAlternateNetworkOnPathDegrading(); } } void QuicChromiumClientSession::OnNetworkDisconnectedV2( - NetworkChangeNotifier::NetworkHandle disconnected_network, - const NetLogWithSource& migration_net_log) { - DCHECK(migrate_session_on_network_change_v2_); + NetworkChangeNotifier::NetworkHandle disconnected_network) { + LogMetricsOnNetworkDisconnected(); + if (!migrate_session_on_network_change_v2_) + return; net_log_.AddEventWithInt64Params( NetLogEventType::QUIC_CONNECTION_MIGRATION_ON_NETWORK_DISCONNECTED, "disconnected_network", disconnected_network); - LogMetricsOnNetworkDisconnected(); // Stop probing the disconnected network if there is one. probing_manager_.CancelProbing(disconnected_network, peer_address()); @@ -2146,7 +2170,7 @@ void QuicChromiumClientSession::OnNetworkDisconnectedV2( } // Ignore the signal if the current active network is not affected. - if (GetDefaultSocket()->GetBoundNetwork() != disconnected_network) { + if (GetCurrentNetwork() != disconnected_network) { DVLOG(1) << "Client's current default network is not affected by the " << "disconnected one."; return; @@ -2179,29 +2203,31 @@ void QuicChromiumClientSession::OnNetworkDisconnectedV2( } void QuicChromiumClientSession::OnNetworkMadeDefault( - NetworkChangeNotifier::NetworkHandle new_network, - const NetLogWithSource& migration_net_log) { - DCHECK(migrate_session_on_network_change_v2_); + NetworkChangeNotifier::NetworkHandle new_network) { + LogMetricsOnNetworkMadeDefault(); + + if (!migrate_session_on_network_change_v2_) + return; + + DCHECK_NE(NetworkChangeNotifier::kInvalidNetworkHandle, new_network); net_log_.AddEventWithInt64Params( NetLogEventType::QUIC_CONNECTION_MIGRATION_ON_NETWORK_MADE_DEFAULT, "new_default_network", new_network); - LogMetricsOnNetworkMadeDefault(); + default_network_ = new_network; - DCHECK_NE(NetworkChangeNotifier::kInvalidNetworkHandle, new_network); DVLOG(1) << "Network: " << new_network << " becomes default, old default: " << default_network_; - default_network_ = new_network; current_migration_cause_ = ON_NETWORK_MADE_DEFAULT; current_migrations_to_non_default_network_on_write_error_ = 0; current_migrations_to_non_default_network_on_path_degrading_ = 0; // Simply cancel the timer to migrate back to the default network if session // is already on the default network. - if (GetDefaultSocket()->GetBoundNetwork() == new_network) { + if (GetCurrentNetwork() == new_network) { CancelMigrateBackToDefaultNetworkTimer(); - HistogramAndLogMigrationFailure( - migration_net_log, MIGRATION_STATUS_ALREADY_MIGRATED, connection_id(), - "Already migrated on the new network"); + HistogramAndLogMigrationFailure(MIGRATION_STATUS_ALREADY_MIGRATED, + connection_id(), + "Already migrated on the new network"); return; } @@ -2221,10 +2247,8 @@ void QuicChromiumClientSession::MigrateNetworkImmediately( // - otherwise, it's brought to default network, cancel the running timer to // migrate back. - if (!migrate_idle_session_ && GetNumActiveStreams() == 0 && - GetNumDrainingStreams() == 0) { - HistogramAndLogMigrationFailure(net_log_, - MIGRATION_STATUS_NO_MIGRATABLE_STREAMS, + if (!migrate_idle_session_ && !HasActiveRequestStreams()) { + HistogramAndLogMigrationFailure(MIGRATION_STATUS_NO_MIGRATABLE_STREAMS, connection_id(), "No active streams"); CloseSessionOnErrorLater( ERR_NETWORK_CHANGED, @@ -2238,17 +2262,17 @@ void QuicChromiumClientSession::MigrateNetworkImmediately( // Do not migrate if connection migration is disabled. if (config()->DisableConnectionMigration()) { - HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_DISABLED_BY_CONFIG, connection_id(), - "Migration disabled by config"); + HistogramAndLogMigrationFailure(MIGRATION_STATUS_DISABLED_BY_CONFIG, + connection_id(), + "Migration disabled by config"); CloseSessionOnErrorLater(ERR_NETWORK_CHANGED, quic::QUIC_CONNECTION_MIGRATION_DISABLED_BY_CONFIG, quic::ConnectionCloseBehavior::SILENT_CLOSE); return; } - if (network == GetDefaultSocket()->GetBoundNetwork()) { - HistogramAndLogMigrationFailure(net_log_, MIGRATION_STATUS_ALREADY_MIGRATED, + if (network == GetCurrentNetwork()) { + HistogramAndLogMigrationFailure(MIGRATION_STATUS_ALREADY_MIGRATED, connection_id(), "Already bound to new network"); return; @@ -2259,7 +2283,7 @@ void QuicChromiumClientSession::MigrateNetworkImmediately( MigrationResult result = Migrate(network, ToIPEndPoint(connection()->peer_address()), - /*close_session_on_error=*/true, net_log_); + /*close_session_on_error=*/true); if (result == MigrationResult::FAILURE) return; @@ -2308,6 +2332,9 @@ void QuicChromiumClientSession::OnWriteUnblocked() { } void QuicChromiumClientSession::OnPathDegrading() { + if (most_recent_path_degrading_timestamp_ == base::TimeTicks()) + most_recent_path_degrading_timestamp_ = tick_clock_->NowTicks(); + if (go_away_on_path_degrading_ && OneRttKeysAvailable()) { net_log_.AddEvent( NetLogEventType::QUIC_SESSION_CLIENT_GOAWAY_ON_PATH_DEGRADING); @@ -2317,14 +2344,15 @@ void QuicChromiumClientSession::OnPathDegrading() { GetNumActiveStreams()); UMA_HISTOGRAM_COUNTS_1M( "Net.QuicSession.DrainingStreamsOnGoAwayAfterPathDegrading", - GetNumDrainingStreams()); + num_outgoing_draining_streams()); return; } - net_log_.AddEvent( - NetLogEventType::QUIC_CONNECTION_MIGRATION_ON_PATH_DEGRADING); - if (most_recent_path_degrading_timestamp_ == base::TimeTicks()) - most_recent_path_degrading_timestamp_ = tick_clock_->NowTicks(); + if (!go_away_on_path_degrading_) { + NetworkChangeNotifier::NetworkHandle current_network = GetCurrentNetwork(); + for (auto& observer : connectivity_observer_list_) + observer.OnSessionPathDegrading(this, current_network); + } if (!stream_factory_) return; @@ -2335,21 +2363,16 @@ void QuicChromiumClientSession::OnPathDegrading() { return; } - current_migration_cause_ = CHANGE_NETWORK_ON_PATH_DEGRADING; + MaybeMigrateToAlternateNetworkOnPathDegrading(); +} - if (migrate_session_early_v2_) { - MaybeMigrateToAlternateNetworkOnPathDegrading(); +void QuicChromiumClientSession::OnForwardProgressMadeAfterPathDegrading() { + if (go_away_on_path_degrading_) return; - } - - HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_PATH_DEGRADING_NOT_ENABLED, connection_id(), - "Migration on path degrading not enabled"); -} -bool QuicChromiumClientSession::ShouldKeepConnectionAlive() const { - return quic::QuicSpdySession::ShouldKeepConnectionAlive() || - GetNumDrainingOutgoingStreams() > 0; + NetworkChangeNotifier::NetworkHandle current_network = GetCurrentNetwork(); + for (auto& observer : connectivity_observer_list_) + observer.OnSessionResumedPostPathDegrading(this, current_network); } void QuicChromiumClientSession::OnProofValid( @@ -2488,43 +2511,50 @@ void QuicChromiumClientSession::MaybeMigrateToDifferentPortOnPathDegrading() { // Migration before handshake is not allowed. if (!OneRttKeysAvailable()) { HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_PATH_DEGRADING_BEFORE_HANDSHAKE_CONFIRMED, + MIGRATION_STATUS_PATH_DEGRADING_BEFORE_HANDSHAKE_CONFIRMED, connection_id(), "Path degrading before handshake confirmed"); return; } - const NetLogWithSource migration_net_log = NetLogWithSource::Make( - net_log_.net_log(), NetLogSourceType::QUIC_PORT_MIGRATION); - migration_net_log.BeginEvent(NetLogEventType::QUIC_PORT_MIGRATION_TRIGGERED); + net_log_.BeginEvent(NetLogEventType::QUIC_PORT_MIGRATION_TRIGGERED); if (!stream_factory_) return; // Probe a different port, session will migrate to the probed port on success. - StartProbing(default_network_, peer_address(), migration_net_log); - migration_net_log.EndEvent(NetLogEventType::QUIC_PORT_MIGRATION_TRIGGERED); + StartProbing(default_network_, peer_address()); + net_log_.EndEvent(NetLogEventType::QUIC_PORT_MIGRATION_TRIGGERED); } void QuicChromiumClientSession:: MaybeMigrateToAlternateNetworkOnPathDegrading() { - DCHECK(migrate_session_early_v2_); + net_log_.AddEvent( + NetLogEventType::QUIC_CONNECTION_MIGRATION_ON_PATH_DEGRADING); + + current_migration_cause_ = CHANGE_NETWORK_ON_PATH_DEGRADING; - if (GetDefaultSocket()->GetBoundNetwork() == default_network_ && + if (!migrate_session_early_v2_) { + HistogramAndLogMigrationFailure(MIGRATION_STATUS_PATH_DEGRADING_NOT_ENABLED, + connection_id(), + "Migration on path degrading not enabled"); + return; + } + + if (GetCurrentNetwork() == default_network_ && current_migrations_to_non_default_network_on_path_degrading_ >= max_migrations_to_non_default_network_on_path_degrading_) { HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_ON_PATH_DEGRADING_DISABLED, connection_id(), + MIGRATION_STATUS_ON_PATH_DEGRADING_DISABLED, connection_id(), "Exceeds maximum number of migrations on path degrading"); return; } NetworkChangeNotifier::NetworkHandle alternate_network = - stream_factory_->FindAlternateNetwork( - GetDefaultSocket()->GetBoundNetwork()); + stream_factory_->FindAlternateNetwork(GetCurrentNetwork()); if (alternate_network == NetworkChangeNotifier::kInvalidNetworkHandle) { - HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_NO_ALTERNATE_NETWORK, connection_id(), - "No alternative network on path degrading"); + HistogramAndLogMigrationFailure(MIGRATION_STATUS_NO_ALTERNATE_NETWORK, + connection_id(), + "No alternative network on path degrading"); return; } @@ -2532,37 +2562,31 @@ void QuicChromiumClientSession:: if (!OneRttKeysAvailable()) { HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_PATH_DEGRADING_BEFORE_HANDSHAKE_CONFIRMED, + MIGRATION_STATUS_PATH_DEGRADING_BEFORE_HANDSHAKE_CONFIRMED, connection_id(), "Path degrading before handshake confirmed"); return; } - const NetLogWithSource migration_net_log = NetLogWithSource::Make( - net_log_.net_log(), NetLogSourceType::QUIC_CONNECTION_MIGRATION); - migration_net_log.BeginEventWithStringParams( + net_log_.BeginEventWithStringParams( NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED, "trigger", "PathDegrading"); // Probe the alternative network, session will migrate to the probed // network and decide whether it wants to migrate back to the default // network on success. - MaybeStartProbing(alternate_network, peer_address(), migration_net_log); - migration_net_log.EndEvent( - NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED); + MaybeStartProbing(alternate_network, peer_address()); + net_log_.EndEvent(NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED); } ProbingResult QuicChromiumClientSession::MaybeStartProbing( NetworkChangeNotifier::NetworkHandle network, - const quic::QuicSocketAddress& peer_address, - const NetLogWithSource& migration_net_log) { + const quic::QuicSocketAddress& peer_address) { if (!stream_factory_) return ProbingResult::FAILURE; CHECK_NE(NetworkChangeNotifier::kInvalidNetworkHandle, network); - if (!migrate_idle_session_ && GetNumActiveStreams() == 0 && - GetNumDrainingStreams() == 0) { - HistogramAndLogMigrationFailure(migration_net_log, - MIGRATION_STATUS_NO_MIGRATABLE_STREAMS, + if (!migrate_idle_session_ && !HasActiveRequestStreams()) { + HistogramAndLogMigrationFailure(MIGRATION_STATUS_NO_MIGRATABLE_STREAMS, connection_id(), "No active streams"); CloseSessionOnErrorLater( ERR_NETWORK_CHANGED, @@ -2578,19 +2602,18 @@ ProbingResult QuicChromiumClientSession::MaybeStartProbing( if (config()->DisableConnectionMigration()) { DVLOG(1) << "Client disables probing network with connection migration " << "disabled by config"; - HistogramAndLogMigrationFailure( - migration_net_log, MIGRATION_STATUS_DISABLED_BY_CONFIG, connection_id(), - "Migration disabled by config"); + HistogramAndLogMigrationFailure(MIGRATION_STATUS_DISABLED_BY_CONFIG, + connection_id(), + "Migration disabled by config"); return ProbingResult::DISABLED_BY_CONFIG; } - return StartProbing(network, peer_address, migration_net_log); + return StartProbing(network, peer_address); } ProbingResult QuicChromiumClientSession::StartProbing( NetworkChangeNotifier::NetworkHandle network, - const quic::QuicSocketAddress& peer_address, - const NetLogWithSource& migration_net_log) { + const quic::QuicSocketAddress& peer_address) { // Check if probing manager is probing the same path. if (probing_manager_.IsUnderProbing(network, peer_address)) return ProbingResult::PENDING; @@ -2601,9 +2624,9 @@ ProbingResult QuicChromiumClientSession::StartProbing( if (stream_factory_->ConfigureSocket(probing_socket.get(), ToIPEndPoint(peer_address), network, session_key_.socket_tag()) != OK) { - HistogramAndLogMigrationFailure( - migration_net_log, MIGRATION_STATUS_INTERNAL_ERROR, connection_id(), - "Socket configuration failed"); + HistogramAndLogMigrationFailure(MIGRATION_STATUS_INTERNAL_ERROR, + connection_id(), + "Socket configuration failed"); return ProbingResult::INTERNAL_ERROR; } @@ -2664,8 +2687,7 @@ void QuicChromiumClientSession::TryMigrateBackToDefaultNetwork( // the same network, this will be a no-op. Otherwise, previous probe // will be cancelled and manager starts to probe |default_network_| // immediately. - ProbingResult result = - MaybeStartProbing(default_network_, peer_address(), net_log_); + ProbingResult result = MaybeStartProbing(default_network_, peer_address()); if (result == ProbingResult::DISABLED_WITH_IDLE_SESSION) return; @@ -2689,7 +2711,7 @@ void QuicChromiumClientSession::TryMigrateBackToDefaultNetwork( void QuicChromiumClientSession::MaybeRetryMigrateBackToDefaultNetwork() { base::TimeDelta retry_migrate_back_timeout = base::TimeDelta::FromSeconds(UINT64_C(1) << retry_migrate_back_count_); - if (default_network_ == GetDefaultSocket()->GetBoundNetwork()) { + if (default_network_ == GetCurrentNetwork()) { // If session has been back on the default already by other direct // migration attempt, cancel migrate back now. CancelMigrateBackToDefaultNetworkTimer(); @@ -2707,7 +2729,7 @@ bool QuicChromiumClientSession::CheckIdleTimeExceedsIdleMigrationPeriod() { if (!migrate_idle_session_) return false; - if (GetNumActiveStreams() != 0 || GetNumDrainingStreams() != 0) { + if (HasActiveRequestStreams()) { return false; } @@ -2718,9 +2740,9 @@ bool QuicChromiumClientSession::CheckIdleTimeExceedsIdleMigrationPeriod() { return false; } - HistogramAndLogMigrationFailure( - net_log_, MIGRATION_STATUS_IDLE_MIGRATION_TIMEOUT, connection_id(), - "Ilde migration period exceeded"); + HistogramAndLogMigrationFailure(MIGRATION_STATUS_IDLE_MIGRATION_TIMEOUT, + connection_id(), + "Ilde migration period exceeded"); CloseSessionOnErrorLater(ERR_NETWORK_CHANGED, quic::QUIC_NETWORK_IDLE_TIMEOUT, quic::ConnectionCloseBehavior::SILENT_CLOSE); return true; @@ -2840,7 +2862,6 @@ void QuicChromiumClientSession::LogHandshakeStatusOnMigrationSignal() const { } void QuicChromiumClientSession::HistogramAndLogMigrationFailure( - const NetLogWithSource& net_log, QuicConnectionMigrationStatus status, quic::QuicConnectionId connection_id, const char* reason) { @@ -2849,7 +2870,7 @@ void QuicChromiumClientSession::HistogramAndLogMigrationFailure( ? NetLogEventType::QUIC_PORT_MIGRATION_FAILURE : NetLogEventType::QUIC_CONNECTION_MIGRATION_FAILURE; - net_log.AddEvent(event_type, [&] { + net_log_.AddEvent(event_type, [&] { return NetLogQuicMigrationFailureParams(connection_id, reason); }); @@ -2858,14 +2879,13 @@ void QuicChromiumClientSession::HistogramAndLogMigrationFailure( } void QuicChromiumClientSession::HistogramAndLogMigrationSuccess( - const NetLogWithSource& net_log, quic::QuicConnectionId connection_id) { NetLogEventType event_type = current_migration_cause_ == CHANGE_PORT_ON_PATH_DEGRADING ? NetLogEventType::QUIC_PORT_MIGRATION_SUCCESS : NetLogEventType::QUIC_CONNECTION_MIGRATION_SUCCESS; - net_log.AddEvent(event_type, [&] { + net_log_.AddEvent(event_type, [&] { return NetLogQuicMigrationSuccessParams(connection_id); }); @@ -3030,7 +3050,7 @@ void QuicChromiumClientSession::OnCryptoHandshakeComplete() { // confirmed if the session is not created on the default network. if (migrate_session_on_network_change_v2_ && default_network_ != NetworkChangeNotifier::kInvalidNetworkHandle && - GetDefaultSocket()->GetBoundNetwork() != default_network_) { + GetCurrentNetwork() != default_network_) { current_migration_cause_ = ON_MIGRATE_BACK_TO_DEFAULT_NETWORK; StartMigrateBackToDefaultNetworkTimer( base::TimeDelta::FromSeconds(kMinRetryTimeForDefaultNetworkSecs)); @@ -3040,16 +3060,14 @@ void QuicChromiumClientSession::OnCryptoHandshakeComplete() { MigrationResult QuicChromiumClientSession::Migrate( NetworkChangeNotifier::NetworkHandle network, IPEndPoint peer_address, - bool close_session_on_error, - const NetLogWithSource& migration_net_log) { + bool close_session_on_error) { if (!stream_factory_) return MigrationResult::FAILURE; if (network != NetworkChangeNotifier::kInvalidNetworkHandle) { // This is a migration attempt from connection migration. ResetNonMigratableStreams(); - if (!migrate_idle_session_ && GetNumActiveStreams() == 0 && - GetNumDrainingStreams() == 0) { + if (!migrate_idle_session_ && !HasActiveRequestStreams()) { // If idle sessions can not be migrated, close the session if needed. if (close_session_on_error) { CloseSessionOnErrorLater( @@ -3066,9 +3084,9 @@ MigrationResult QuicChromiumClientSession::Migrate( stream_factory_->CreateSocket(net_log_.net_log(), net_log_.source())); if (stream_factory_->ConfigureSocket(socket.get(), peer_address, network, session_key_.socket_tag()) != OK) { - HistogramAndLogMigrationFailure( - migration_net_log, MIGRATION_STATUS_INTERNAL_ERROR, connection_id(), - "Socket configuration failed"); + HistogramAndLogMigrationFailure(MIGRATION_STATUS_INTERNAL_ERROR, + connection_id(), + "Socket configuration failed"); if (close_session_on_error) { if (migrate_session_on_network_change_v2_) { CloseSessionOnErrorLater(ERR_NETWORK_CHANGED, @@ -3099,8 +3117,7 @@ MigrationResult QuicChromiumClientSession::Migrate( // Migrate to the new socket. if (!MigrateToSocket(std::move(socket), std::move(new_reader), std::move(new_writer))) { - HistogramAndLogMigrationFailure(migration_net_log, - MIGRATION_STATUS_TOO_MANY_CHANGES, + HistogramAndLogMigrationFailure(MIGRATION_STATUS_TOO_MANY_CHANGES, connection_id(), "Too many changes"); if (close_session_on_error) { if (migrate_session_on_network_change_v2_) { @@ -3116,7 +3133,7 @@ MigrationResult QuicChromiumClientSession::Migrate( } return MigrationResult::FAILURE; } - HistogramAndLogMigrationSuccess(migration_net_log, connection_id()); + HistogramAndLogMigrationSuccess(connection_id()); return MigrationResult::SUCCESS; } @@ -3165,6 +3182,16 @@ const DatagramClientSocket* QuicChromiumClientSession::GetDefaultSocket() return sockets_.back().get(); } +NetworkChangeNotifier::NetworkHandle +QuicChromiumClientSession::GetCurrentNetwork() const { + // If connection migration is enabled, alternate network interface may be + // used to send packet, it is identified as the bound network of the default + // socket. Otherwise, always use |default_network_|. + return migrate_session_on_network_change_v2_ + ? GetDefaultSocket()->GetBoundNetwork() + : default_network_; +} + bool QuicChromiumClientSession::IsAuthorized(const std::string& hostname) { bool result = CanPool( hostname, session_key_.privacy_mode(), session_key_.socket_tag(), @@ -3268,4 +3295,17 @@ size_t QuicChromiumClientSession::EstimateMemoryUsage() const { return base::trace_event::EstimateMemoryUsage(packet_readers_); } +bool QuicChromiumClientSession::ValidateStatelessReset( + const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address) { + if (probing_manager_.ValidateStatelessReset(self_address, peer_address)) { + // The stateless reset is received from probing path. We shouldn't close the + // connection, but should disable further port migration attempt. + if (allow_port_migration_) + allow_port_migration_ = false; + return false; + } + return true; +} + } // namespace net diff --git a/chromium/net/quic/quic_chromium_client_session.h b/chromium/net/quic/quic_chromium_client_session.h index 34ea4fda6eb..3b1c2fda0ac 100644 --- a/chromium/net/quic/quic_chromium_client_session.h +++ b/chromium/net/quic/quic_chromium_client_session.h @@ -20,6 +20,7 @@ #include "base/containers/mru_cache.h" #include "base/macros.h" +#include "base/observer_list_types.h" #include "base/time/time.h" #include "base/timer/timer.h" #include "net/base/completion_once_callback.h" @@ -139,6 +140,25 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession public: class StreamRequest; + // An interface that when implemented and added via + // AddConnectivityObserver(), provides notifications when connectivity + // quality changes. + class NET_EXPORT_PRIVATE ConnectivityObserver : public base::CheckedObserver { + public: + // Called when path degrading is detected on |network|. + virtual void OnSessionPathDegrading( + QuicChromiumClientSession* session, + NetworkChangeNotifier::NetworkHandle network) = 0; + + // Called when forward progress is made after path degrading on |network|. + virtual void OnSessionResumedPostPathDegrading( + QuicChromiumClientSession* session, + NetworkChangeNotifier::NetworkHandle network) = 0; + + // Called when |session| is removed. + virtual void OnSessionRemoved(QuicChromiumClientSession* session) = 0; + }; + // Wrapper for interacting with the session in a restricted fashion which // hides the details of the underlying session's lifetime. All methods of // the Handle are safe to use even after the underlying session is destroyed. @@ -422,6 +442,9 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession void AddHandle(Handle* handle); void RemoveHandle(Handle* handle); + void AddConnectivityObserver(ConnectivityObserver* observer); + void RemoveConnectivityObserver(ConnectivityObserver* observer); + // Returns the session's connection migration mode. ConnectionMigrationMode connection_migration_mode() const; @@ -503,6 +526,9 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession void OnGoAway(const quic::QuicGoAwayFrame& frame) override; void OnRstStream(const quic::QuicRstStreamFrame& frame) override; void OnCanCreateNewOutgoingStream(bool unidirectional) override; + bool ValidateStatelessReset( + const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address) override; // QuicClientSessionBase methods: void OnConfigNegotiated() override; @@ -520,7 +546,7 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession const quic::QuicSocketAddress& peer_address, bool is_connectivity_probe) override; void OnPathDegrading() override; - bool ShouldKeepConnectionAlive() const override; + void OnForwardProgressMadeAfterPathDegrading() override; // QuicChromiumPacketReader::Visitor methods: void OnReadError(int result, const DatagramClientSocket* socket) override; @@ -599,8 +625,7 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession // closed. MigrationResult Migrate(NetworkChangeNotifier::NetworkHandle network, IPEndPoint peer_address, - bool close_session_on_error, - const NetLogWithSource& migration_net_log); + bool close_session_on_error); // Migrates session onto new socket, i.e., sets |writer| to be the new // default writer and post a task to write to |socket|. |reader| *must* @@ -617,19 +642,16 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession // Called when NetworkChangeNotifier notifies observers of a newly // connected network. Migrates this session to the newly connected // network if the session has a pending migration. - void OnNetworkConnected(NetworkChangeNotifier::NetworkHandle network, - const NetLogWithSource& net_log); + void OnNetworkConnected(NetworkChangeNotifier::NetworkHandle network); // Called when NetworkChangeNotifier broadcasts to observers of // |disconnected_network|. void OnNetworkDisconnectedV2( - NetworkChangeNotifier::NetworkHandle disconnected_network, - const NetLogWithSource& migration_net_log); + NetworkChangeNotifier::NetworkHandle disconnected_network); // Called when NetworkChangeNotifier broadcats to observers of a new default // network. Migrates this session to |new_network| if appropriate. - void OnNetworkMadeDefault(NetworkChangeNotifier::NetworkHandle new_network, - const NetLogWithSource& migration_net_log); + void OnNetworkMadeDefault(NetworkChangeNotifier::NetworkHandle new_network); // Schedules a migration alarm to wait for a new network. void OnNoNewNetwork(); @@ -646,6 +668,11 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession // returned socket. const DatagramClientSocket* GetDefaultSocket() const; + // Returns the network interface that is currently used to send packets. + // If NetworkHandle is not supported, always return + // NetworkChangeNotifier::kInvalidNetworkHandle. + NetworkChangeNotifier::NetworkHandle GetCurrentNetwork() const; + bool IsAuthorized(const std::string& hostname) override; bool HandlePromised(quic::QuicStreamId associated_id, @@ -710,17 +737,17 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession // If <network, peer_addres> is identical to the current path, the probe // is sent on a different port. ProbingResult StartProbing(NetworkChangeNotifier::NetworkHandle network, - const quic::QuicSocketAddress& peer_address, - const NetLogWithSource& migration_net_log); + const quic::QuicSocketAddress& peer_address); // Perform a few checks before StartProbing. If any of those checks fails, // StartProbing will be skipped. ProbingResult MaybeStartProbing(NetworkChangeNotifier::NetworkHandle network, - const quic::QuicSocketAddress& peer_address, - const NetLogWithSource& migration_net_log); + const quic::QuicSocketAddress& peer_address); // Helper method to perform a few checks and initiate connection migration // attempt when path degrading is detected. + // Called when path is degrading and there is an alternate network or a new + // network is connected after path degrading. void MaybeMigrateToAlternateNetworkOnPathDegrading(); // Helper method to initiate a port migration on path degrading is detected. @@ -754,12 +781,10 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession void LogMetricsOnNetworkMadeDefault(); void LogMigrationResultToHistogram(QuicConnectionMigrationStatus status); void LogHandshakeStatusOnMigrationSignal() const; - void HistogramAndLogMigrationFailure(const NetLogWithSource& net_log, - QuicConnectionMigrationStatus status, + void HistogramAndLogMigrationFailure(QuicConnectionMigrationStatus status, quic::QuicConnectionId connection_id, const char* reason); - void HistogramAndLogMigrationSuccess(const NetLogWithSource& net_log, - quic::QuicConnectionId connection_id); + void HistogramAndLogMigrationSuccess(quic::QuicConnectionId connection_id); // Notifies the factory that this session is going away and no more streams // should be created from it. This needs to be called before closing any @@ -810,6 +835,7 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession std::unique_ptr<quic::QuicCryptoClientStream> crypto_stream_; QuicStreamFactory* stream_factory_; + base::ObserverList<ConnectivityObserver> connectivity_observer_list_; std::vector<std::unique_ptr<DatagramClientSocket>> sockets_; TransportSecurityState* transport_security_state_; SSLConfigService* ssl_config_service_; @@ -847,7 +873,8 @@ class NET_EXPORT_PRIVATE QuicChromiumClientSession // written to an alternate socket when the migration completes and the // alternate socket is unblocked. scoped_refptr<QuicChromiumPacketWriter::ReusableIOBuffer> packet_; - // Stores the latest default network platform marks. + // Stores the latest default network platform marks if migration is enabled. + // Otherwise, stores the network interface that is used by the connection. NetworkChangeNotifier::NetworkHandle default_network_; QuicConnectivityProbingManager probing_manager_; int retry_migrate_back_count_; diff --git a/chromium/net/quic/quic_chromium_client_session_peer.cc b/chromium/net/quic/quic_chromium_client_session_peer.cc index 2c460a8c34f..8d4747f8ac5 100644 --- a/chromium/net/quic/quic_chromium_client_session_peer.cc +++ b/chromium/net/quic/quic_chromium_client_session_peer.cc @@ -48,5 +48,10 @@ bool QuicChromiumClientSessionPeer::GetSessionGoingAway( return session->going_away_; } +bool QuicChromiumClientSessionPeer::DoesSessionAllowPortMigration( + QuicChromiumClientSession* session) { + return session->allow_port_migration_; +} + } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_chromium_client_session_peer.h b/chromium/net/quic/quic_chromium_client_session_peer.h index 8b057727eb4..4832017dc31 100644 --- a/chromium/net/quic/quic_chromium_client_session_peer.h +++ b/chromium/net/quic/quic_chromium_client_session_peer.h @@ -34,6 +34,8 @@ class QuicChromiumClientSessionPeer { static bool GetSessionGoingAway(QuicChromiumClientSession* session); + static bool DoesSessionAllowPortMigration(QuicChromiumClientSession* session); + private: DISALLOW_COPY_AND_ASSIGN(QuicChromiumClientSessionPeer); }; diff --git a/chromium/net/quic/quic_chromium_client_session_test.cc b/chromium/net/quic/quic_chromium_client_session_test.cc index 404d815f3af..075e6111ac9 100644 --- a/chromium/net/quic/quic_chromium_client_session_test.cc +++ b/chromium/net/quic/quic_chromium_client_session_test.cc @@ -31,6 +31,7 @@ #include "net/quic/quic_chromium_connection_helper.h" #include "net/quic/quic_chromium_packet_reader.h" #include "net/quic/quic_chromium_packet_writer.h" +#include "net/quic/quic_connectivity_monitor.h" #include "net/quic/quic_crypto_client_config_handle.h" #include "net/quic/quic_crypto_client_stream_factory.h" #include "net/quic/quic_http_utils.h" @@ -77,6 +78,9 @@ const char kServerHostname[] = "test.example.com"; const uint16_t kServerPort = 443; const size_t kMaxReadersPerQuicSession = 5; +const NetworkChangeNotifier::NetworkHandle kDefaultNetworkForTests = 1; +const NetworkChangeNotifier::NetworkHandle kNewNetworkForTests = 2; + struct TestParams { quic::ParsedQuicVersion version; bool client_headers_include_h2_stream_dependency; @@ -128,6 +132,7 @@ class QuicChromiumClientSessionTest base::span<MockWrite>())), random_(0), helper_(&clock_, &random_), + transport_security_state_(std::make_unique<TransportSecurityState>()), session_key_(kServerHostname, kServerPort, PRIVACY_MODE_DISABLED, @@ -135,6 +140,7 @@ class QuicChromiumClientSessionTest NetworkIsolationKey(), false /* disable_secure_dns */), destination_(kServerHostname, kServerPort), + default_network_(NetworkChangeNotifier::kInvalidNetworkHandle), client_maker_(version_, quic::QuicUtils::CreateRandomConnectionId(&random_), &clock_, @@ -180,12 +186,11 @@ class QuicChromiumClientSessionTest session_.reset(new TestingQuicChromiumClientSession( connection, std::move(socket), /*stream_factory=*/nullptr, &crypto_client_stream_factory_, &clock_, - &transport_security_state_, /*ssl_config_service=*/nullptr, + transport_security_state_.get(), /*ssl_config_service=*/nullptr, base::WrapUnique(static_cast<QuicServerInfo*>(nullptr)), session_key_, /*require_confirmation=*/false, /*max_allowed_push_id=*/0, migrate_session_early_v2_, - /*migrate_session_on_network_change_v2=*/false, - /*defaulet_network=*/NetworkChangeNotifier::kInvalidNetworkHandle, + /*migrate_session_on_network_change_v2=*/false, default_network_, quic::QuicTime::Delta::FromMilliseconds( kDefaultRetransmittableOnWireTimeout.InMilliseconds()), /*migrate_idle_session=*/false, /*allow_port_migration=*/false, @@ -204,6 +209,10 @@ class QuicChromiumClientSessionTest base::DefaultTickClock::GetInstance(), base::ThreadTaskRunnerHandle::Get().get(), /*socket_performance_watcher=*/nullptr, &net_log_)); + if (connectivity_monitor_) { + connectivity_monitor_->SetInitialDefaultNetwork(default_network_); + session_->AddConnectivityObserver(connectivity_monitor_.get()); + } scoped_refptr<X509Certificate> cert( ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem")); @@ -220,10 +229,13 @@ class QuicChromiumClientSessionTest } void TearDown() override { - if (session_) + if (session_) { + if (connectivity_monitor_) + session_->RemoveConnectivityObserver(connectivity_monitor_.get()); session_->CloseSessionOnError( ERR_ABORTED, quic::QUIC_INTERNAL_ERROR, quic::ConnectionCloseBehavior::SILENT_CLOSE); + } } void CompleteCryptoHandshake() { @@ -277,12 +289,14 @@ class QuicChromiumClientSessionTest quic::test::MockRandom random_; QuicChromiumConnectionHelper helper_; quic::test::MockAlarmFactory alarm_factory_; - TransportSecurityState transport_security_state_; + std::unique_ptr<TransportSecurityState> transport_security_state_; MockCryptoClientStreamFactory crypto_client_stream_factory_; quic::QuicClientPushPromiseIndex push_promise_index_; QuicSessionKey session_key_; HostPortPair destination_; std::unique_ptr<TestingQuicChromiumClientSession> session_; + NetworkChangeNotifier::NetworkHandle default_network_; + std::unique_ptr<QuicConnectivityMonitor> connectivity_monitor_; TestServerPushDelegate test_push_delegate_; quic::QuicConnectionVisitorInterface* visitor_; TestCompletionCallback callback_; @@ -299,8 +313,88 @@ INSTANTIATE_TEST_SUITE_P(VersionIncludeStreamDependencySequence, ::testing::ValuesIn(GetTestParams()), ::testing::PrintToStringParamName()); -// TODO(950069): Add testing for frame_origin in NetworkIsolationKey using -// kAppendInitiatingFrameOriginToNetworkIsolationKey. +// TODO(crbug.com/950069): Add testing for frame_origin in NetworkIsolationKey +// using kAppendInitiatingFrameOriginToNetworkIsolationKey. + +// Basic test of ProofVerifyDetailsChromium is converted to SSLInfo retrieved +// through QuicChromiumClientSession::GetSSLInfo(). Doesn't test some of the +// more complicated fields. +TEST_P(QuicChromiumClientSessionTest, GetSSLInfo1) { + MockQuicData quic_data(version_); + if (VersionUsesHttp3(version_.transport_version)) + quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1)); + quic_data.AddRead(ASYNC, ERR_IO_PENDING); + quic_data.AddRead(ASYNC, OK); // EOF + quic_data.AddSocketDataToFactory(&socket_factory_); + + Initialize(); + + ProofVerifyDetailsChromium details; + details.is_fatal_cert_error = false; + details.cert_verify_result.verified_cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + details.cert_verify_result.is_issued_by_known_root = true; + details.ct_verify_result.policy_compliance = + ct::CTPolicyCompliance::CT_POLICY_COMPLIES_VIA_SCTS; + details.ct_verify_result.policy_compliance_required = true; + + CompleteCryptoHandshake(); + session_->OnProofVerifyDetailsAvailable(details); + + SSLInfo ssl_info; + ASSERT_TRUE(session_->GetSSLInfo(&ssl_info)); + EXPECT_TRUE(ssl_info.is_valid()); + + EXPECT_EQ(details.is_fatal_cert_error, ssl_info.is_fatal_cert_error); + EXPECT_TRUE(ssl_info.cert->EqualsIncludingChain( + details.cert_verify_result.verified_cert.get())); + EXPECT_EQ(details.cert_verify_result.cert_status, ssl_info.cert_status); + EXPECT_EQ(details.cert_verify_result.is_issued_by_known_root, + ssl_info.is_issued_by_known_root); + EXPECT_EQ(details.ct_verify_result.policy_compliance, + ssl_info.ct_policy_compliance); + EXPECT_EQ(details.ct_verify_result.policy_compliance_required, + ssl_info.ct_policy_compliance_required); +} + +// Just like GetSSLInfo1, but uses different values. +TEST_P(QuicChromiumClientSessionTest, GetSSLInfo2) { + MockQuicData quic_data(version_); + if (VersionUsesHttp3(version_.transport_version)) + quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1)); + quic_data.AddRead(ASYNC, ERR_IO_PENDING); + quic_data.AddRead(ASYNC, OK); // EOF + quic_data.AddSocketDataToFactory(&socket_factory_); + + Initialize(); + + ProofVerifyDetailsChromium details; + details.is_fatal_cert_error = false; + details.cert_verify_result.verified_cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + details.cert_verify_result.is_issued_by_known_root = false; + details.ct_verify_result.policy_compliance = + ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS; + details.ct_verify_result.policy_compliance_required = false; + + CompleteCryptoHandshake(); + session_->OnProofVerifyDetailsAvailable(details); + + SSLInfo ssl_info; + ASSERT_TRUE(session_->GetSSLInfo(&ssl_info)); + EXPECT_TRUE(ssl_info.is_valid()); + + EXPECT_EQ(details.is_fatal_cert_error, ssl_info.is_fatal_cert_error); + EXPECT_TRUE(ssl_info.cert->EqualsIncludingChain( + details.cert_verify_result.verified_cert.get())); + EXPECT_EQ(details.cert_verify_result.cert_status, ssl_info.cert_status); + EXPECT_EQ(details.cert_verify_result.is_issued_by_known_root, + ssl_info.is_issued_by_known_root); + EXPECT_EQ(details.ct_verify_result.policy_compliance, + ssl_info.ct_policy_compliance); + EXPECT_EQ(details.ct_verify_result.policy_compliance_required, + ssl_info.ct_policy_compliance_required); +} TEST_P(QuicChromiumClientSessionTest, IsFatalErrorNotSetForNonFatalError) { MockQuicData quic_data(version_); @@ -872,7 +966,7 @@ TEST_P(QuicChromiumClientSessionTest, ConnectionCloseBeforeStreamRequest) { } TEST_P(QuicChromiumClientSessionTest, ConnectionCloseBeforeHandshakeConfirmed) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { // TODO(nharper, b/112643533): Figure out why this test fails when TLS is // enabled and fix it. return; @@ -1539,6 +1633,81 @@ TEST_P(QuicChromiumClientSessionTest, CanPool) { } } +TEST_P(QuicChromiumClientSessionTest, CanPoolExpectCT) { + base::test::ScopedFeatureList feature_list; + feature_list.InitWithFeatures( + /* enabled_features */ + {TransportSecurityState::kDynamicExpectCTFeature, + features::kPartitionExpectCTStateByNetworkIsolationKey}, + /* disabled_features */ + {}); + + NetworkIsolationKey network_isolation_key = + NetworkIsolationKey::CreateTransient(); + // Need to create a session key after setting + // kPartitionExpectCTStateByNetworkIsolationKey, otherwise, it will ignore the + // NetworkIsolationKey value. + session_key_ = QuicSessionKey( + kServerHostname, kServerPort, PRIVACY_MODE_DISABLED, SocketTag(), + network_isolation_key, false /* disable_secure_dns */); + + // Need to create this after enabling + // kPartitionExpectCTStateByNetworkIsolationKey. + transport_security_state_ = std::make_unique<TransportSecurityState>(); + + MockQuicData quic_data(version_); + if (VersionUsesHttp3(version_.transport_version)) + quic_data.AddWrite(SYNCHRONOUS, client_maker_.MakeInitialSettingsPacket(1)); + quic_data.AddRead(ASYNC, ERR_IO_PENDING); + quic_data.AddRead(ASYNC, OK); // EOF + quic_data.AddSocketDataToFactory(&socket_factory_); + Initialize(); + + // Load a cert that is valid for: + // www.example.org + // mail.example.org + // www.example.com + + // Details with a CT error. + ProofVerifyDetailsChromium details; + details.cert_verify_result.verified_cert = + ImportCertFromFile(GetTestCertsDirectory(), "spdy_pooling.pem"); + ASSERT_TRUE(details.cert_verify_result.verified_cert.get()); + details.cert_verify_result.is_issued_by_known_root = true; + details.ct_verify_result.policy_compliance = + ct::CTPolicyCompliance::CT_POLICY_NOT_ENOUGH_SCTS; + + CompleteCryptoHandshake(); + session_->OnProofVerifyDetailsAvailable(details); + + // Pooling succeeds if CT isn't required. + EXPECT_TRUE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED, + SocketTag(), network_isolation_key, + false /* disable_secure_dns */)); + + // Adding Expect-CT data for different NetworkIsolationKeys should have no + // effect. + base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1); + transport_security_state_->AddExpectCT( + "www.example.org", expiry, true /* enforce */, GURL() /* report_url */, + NetworkIsolationKey::CreateTransient()); + transport_security_state_->AddExpectCT( + "www.example.org", expiry, true /* enforce */, GURL() /* report_url */, + NetworkIsolationKey()); + EXPECT_TRUE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED, + SocketTag(), network_isolation_key, + false /* disable_secure_dns */)); + + // Adding Expect-CT data for the same NetworkIsolationKey should prevent + // pooling. + transport_security_state_->AddExpectCT( + "www.example.org", expiry, true /* enforce */, GURL() /* report_url */, + network_isolation_key); + EXPECT_FALSE(session_->CanPool("www.example.org", PRIVACY_MODE_DISABLED, + SocketTag(), network_isolation_key, + false /* disable_secure_dns */)); +} + // Much as above, but uses a non-empty NetworkIsolationKey. TEST_P(QuicChromiumClientSessionTest, CanPoolWithNetworkIsolationKey) { base::test::ScopedFeatureList feature_list; @@ -1629,7 +1798,7 @@ TEST_P(QuicChromiumClientSessionTest, ConnectionNotPooledWithDifferentPin) { quic_data.AddSocketDataToFactory(&socket_factory_); Initialize(); - transport_security_state_.EnableStaticPinsForTesting(); + transport_security_state_->EnableStaticPinsForTesting(); ProofVerifyDetailsChromium details; details.cert_verify_result.verified_cert = @@ -1661,7 +1830,7 @@ TEST_P(QuicChromiumClientSessionTest, ConnectionPooledWithMatchingPin) { quic_data.AddSocketDataToFactory(&socket_factory_); Initialize(); - transport_security_state_.EnableStaticPinsForTesting(); + transport_security_state_->EnableStaticPinsForTesting(); ProofVerifyDetailsChromium details; details.cert_verify_result.verified_cert = @@ -1752,7 +1921,7 @@ TEST_P(QuicChromiumClientSessionTest, MigrateToSocket) { quic::test::QuicStreamPeer::SendBuffer(stream).SaveStreamData(iov, 1, 0, 4); quic::test::QuicStreamPeer::SetStreamBytesWritten(4, stream); session_->WritevData(stream->id(), 4, 0, quic::NO_FIN, - quic::NOT_RETRANSMISSION, QuicheNullOpt); + quic::NOT_RETRANSMISSION, QUICHE_NULLOPT); EXPECT_TRUE(socket_data.AllReadDataConsumed()); EXPECT_TRUE(socket_data.AllWriteDataConsumed()); @@ -2033,6 +2202,174 @@ TEST_P(QuicChromiumClientSessionTest, ResetOnEmptyResponseHeaders) { EXPECT_TRUE(quic_data.AllWriteDataConsumed()); } +// This test verifies that when NetworkHandle is not supported and there is no +// network change, session reports to the connectivity monitor correctly on path +// degrading detection and recovery. +TEST_P(QuicChromiumClientSessionTest, + DegradingWithoutNetworkChange_NoNetworkHandle) { + // Add a connectivity monitor for testing. + default_network_ = NetworkChangeNotifier::kInvalidNetworkHandle; + connectivity_monitor_ = + std::make_unique<QuicConnectivityMonitor>(default_network_); + + Initialize(); + + // Fire path degrading detection. + session_->ReallyOnPathDegrading(); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + session_->OnForwardProgressMadeAfterPathDegrading(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + // Fire again. + session_->ReallyOnPathDegrading(); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + // Close the session but keep the session around, the connectivity monitor + // will not remove the tracking immediately. + session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR, + quic::ConnectionCloseBehavior::SILENT_CLOSE); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + // Delete the session will remove the degrading count in connectivity + // monitor. + session_.reset(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); +} + +// This test verifies that when the NetworkHandle is not supported, and there +// are speculated network change reported via OnIPAddressChange, session +// still reports to the connectivity monitor correctly on path degrading +// detection and recovery. +TEST_P(QuicChromiumClientSessionTest, DegradingWithIPAddressChange) { + // Default network is always set to kInvalidNetworkHandle. + default_network_ = NetworkChangeNotifier::kInvalidNetworkHandle; + connectivity_monitor_ = + std::make_unique<QuicConnectivityMonitor>(default_network_); + + Initialize(); + + session_->ReallyOnPathDegrading(); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + session_->OnForwardProgressMadeAfterPathDegrading(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + session_->ReallyOnPathDegrading(); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + // When NetworkHandle is not supported, network change is notified via + // IP address change. + connectivity_monitor_->OnIPAddressChanged(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + // When NetworkHandle is not supported and IP address changes, + // session either goes away or gets closed. When it goes away, + // reporting to connectivity monitor is disabled. + connectivity_monitor_->OnSessionGoingAwayOnIPAddressChange(session_.get()); + + // Even if session detects recovery or degradation, this session is no longer + // on the default network and connectivity monitor will not update. + session_->OnForwardProgressMadeAfterPathDegrading(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + session_->ReallyOnPathDegrading(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR, + quic::ConnectionCloseBehavior::SILENT_CLOSE); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + session_.reset(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); +} + +// This test verifies that when NetworkHandle is supported but migration is +// not supported and there's no network change, session reports to +// connectivity monitor correctly on path degrading detection or recovery. +// Default network change is currently reported with valid NetworkHandles +// while session's current network interface is tracked by |default_network_|. +TEST_P(QuicChromiumClientSessionTest, + DegradingOnDeafultNetwork_WithoutMigration) { + default_network_ = kDefaultNetworkForTests; + connectivity_monitor_ = + std::make_unique<QuicConnectivityMonitor>(default_network_); + + Initialize(); + + session_->ReallyOnPathDegrading(); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + session_->OnForwardProgressMadeAfterPathDegrading(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + session_->ReallyOnPathDegrading(); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + // Close the session but keep the session around, the connectivity monitor + // should not remove the count immediately. + session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR, + quic::ConnectionCloseBehavior::SILENT_CLOSE); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + // Delete the session will remove the degrading count in connectivity + // monitor. + session_.reset(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); +} + +// This test verifies that when NetworkHandle is supported but migrations is not +// supported and there is network changes, session reports to the connectivity +// monitor correctly on path degrading detection or recovery. +TEST_P(QuicChromiumClientSessionTest, + DegradingWithDeafultNetworkChange_WithoutMigration) { + default_network_ = kDefaultNetworkForTests; + connectivity_monitor_ = + std::make_unique<QuicConnectivityMonitor>(default_network_); + + Initialize(); + + session_->ReallyOnPathDegrading(); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + session_->OnForwardProgressMadeAfterPathDegrading(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + session_->ReallyOnPathDegrading(); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + // Simulate the default network change. + connectivity_monitor_->OnDefaultNetworkUpdated(kNewNetworkForTests); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + session_->OnNetworkMadeDefault(kNewNetworkForTests); + + // Session stays on the old default network, and recovers. + session_->OnForwardProgressMadeAfterPathDegrading(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + // Session degrades again on the old default. + session_->ReallyOnPathDegrading(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + // Simulate that default network switches back to the old default. + connectivity_monitor_->OnDefaultNetworkUpdated(kDefaultNetworkForTests); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + session_->OnNetworkMadeDefault(kDefaultNetworkForTests); + + // Session recovers again on the (old) default. + session_->OnForwardProgressMadeAfterPathDegrading(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); + + // Session degrades again on the (old) default. + session_->ReallyOnPathDegrading(); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + session_->CloseSessionOnError(ERR_ABORTED, quic::QUIC_INTERNAL_ERROR, + quic::ConnectionCloseBehavior::SILENT_CLOSE); + EXPECT_EQ(1u, connectivity_monitor_->GetNumDegradingSessions()); + + session_.reset(); + EXPECT_EQ(0u, connectivity_monitor_->GetNumDegradingSessions()); +} + } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_chromium_client_stream.h b/chromium/net/quic/quic_chromium_client_stream.h index 71372a16f52..6399b92a5e3 100644 --- a/chromium/net/quic/quic_chromium_client_stream.h +++ b/chromium/net/quic/quic_chromium_client_stream.h @@ -9,10 +9,10 @@ #include <stddef.h> +#include <memory> #include <vector> #include "base/callback_forward.h" -#include "base/containers/circular_deque.h" #include "base/macros.h" #include "net/base/completion_once_callback.h" #include "net/base/ip_endpoint.h" diff --git a/chromium/net/quic/quic_chromium_packet_writer.cc b/chromium/net/quic/quic_chromium_packet_writer.cc index 2c5d194df73..e45613b9511 100644 --- a/chromium/net/quic/quic_chromium_packet_writer.cc +++ b/chromium/net/quic/quic_chromium_packet_writer.cc @@ -263,10 +263,10 @@ bool QuicChromiumPacketWriter::IsBatchMode() const { return false; } -char* QuicChromiumPacketWriter::GetNextWriteLocation( +quic::QuicPacketBuffer QuicChromiumPacketWriter::GetNextWriteLocation( const quic::QuicIpAddress& self_address, const quic::QuicSocketAddress& peer_address) { - return nullptr; + return {nullptr, nullptr}; } quic::WriteResult QuicChromiumPacketWriter::Flush() { diff --git a/chromium/net/quic/quic_chromium_packet_writer.h b/chromium/net/quic/quic_chromium_packet_writer.h index 82acb30f367..2f03d3914dc 100644 --- a/chromium/net/quic/quic_chromium_packet_writer.h +++ b/chromium/net/quic/quic_chromium_packet_writer.h @@ -94,7 +94,7 @@ class NET_EXPORT_PRIVATE QuicChromiumPacketWriter const quic::QuicSocketAddress& peer_address) const override; bool SupportsReleaseTime() const override; bool IsBatchMode() const override; - char* GetNextWriteLocation( + quic::QuicPacketBuffer GetNextWriteLocation( const quic::QuicIpAddress& self_address, const quic::QuicSocketAddress& peer_address) override; quic::WriteResult Flush() override; diff --git a/chromium/net/quic/quic_client_session_cache.cc b/chromium/net/quic/quic_client_session_cache.cc index 7bc5945225a..64e8d962993 100644 --- a/chromium/net/quic/quic_client_session_cache.cc +++ b/chromium/net/quic/quic_client_session_cache.cc @@ -46,9 +46,9 @@ QuicClientSessionCache::QuicClientSessionCache() QuicClientSessionCache::QuicClientSessionCache(size_t max_entries) : clock_(base::DefaultClock::GetInstance()), cache_(max_entries) { - memory_pressure_listener_.reset( - new base::MemoryPressureListener(base::BindRepeating( - &QuicClientSessionCache::OnMemoryPressure, base::Unretained(this)))); + memory_pressure_listener_ = std::make_unique<base::MemoryPressureListener>( + FROM_HERE, base::BindRepeating(&QuicClientSessionCache::OnMemoryPressure, + base::Unretained(this))); } QuicClientSessionCache::~QuicClientSessionCache() { @@ -103,6 +103,18 @@ std::unique_ptr<quic::QuicResumptionState> QuicClientSessionCache::Lookup( return state; } +void QuicClientSessionCache::ClearEarlyData( + const quic::QuicServerId& server_id) { + auto iter = cache_.Get(server_id); + if (iter == cache_.end()) + return; + for (auto& session : iter->second.sessions) { + if (session) { + session.reset(SSL_SESSION_copy_without_early_data(session.get())); + } + } +} + void QuicClientSessionCache::FlushInvalidEntries() { time_t now = clock_->Now().ToTimeT(); auto iter = cache_.begin(); diff --git a/chromium/net/quic/quic_client_session_cache.h b/chromium/net/quic/quic_client_session_cache.h index 8d7506ee4ec..c59c28a2937 100644 --- a/chromium/net/quic/quic_client_session_cache.h +++ b/chromium/net/quic/quic_client_session_cache.h @@ -39,6 +39,8 @@ class NET_EXPORT_PRIVATE QuicClientSessionCache : public quic::SessionCache { const quic::QuicServerId& server_id, const SSL_CTX* ctx) override; + void ClearEarlyData(const quic::QuicServerId& server_id) override; + void SetClockForTesting(base::Clock* clock) { clock_ = clock; } size_t size() const { return cache_.size(); } diff --git a/chromium/net/quic/quic_client_session_cache_unittests.cc b/chromium/net/quic/quic_client_session_cache_unittests.cc index 0fac11c6840..c1e241df403 100644 --- a/chromium/net/quic/quic_client_session_cache_unittests.cc +++ b/chromium/net/quic/quic_client_session_cache_unittests.cc @@ -8,6 +8,8 @@ #include "base/test/simple_test_clock.h" #include "base/test/task_environment.h" #include "base/time/time.h" +#include "build/build_config.h" +#include "net/third_party/quiche/src/common/platform/api/quiche_text_utils.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/boringssl/src/include/openssl/ssl.h" @@ -15,6 +17,7 @@ namespace net { namespace { +const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(1000); const quic::QuicVersionLabel kFakeVersionLabel = 0x01234567; const quic::QuicVersionLabel kFakeVersionLabel2 = 0x89ABCDEF; const uint64_t kFakeIdleTimeoutMilliseconds = 12012; @@ -53,37 +56,96 @@ std::unique_ptr<quic::TransportParameters> MakeFakeTransportParams() { params->version = kFakeVersionLabel; params->supported_versions.push_back(kFakeVersionLabel); params->supported_versions.push_back(kFakeVersionLabel2); - params->idle_timeout_milliseconds.set_value(kFakeIdleTimeoutMilliseconds); + params->max_idle_timeout_ms.set_value(kFakeIdleTimeoutMilliseconds); params->stateless_reset_token = CreateFakeStatelessResetToken(); - params->max_packet_size.set_value(kFakeMaxPacketSize); + params->max_udp_payload_size.set_value(kFakeMaxPacketSize); params->initial_max_data.set_value(kFakeInitialMaxData); - params->disable_migration = kFakeDisableMigration; + params->disable_active_migration = kFakeDisableMigration; params->custom_parameters[kCustomParameter1] = kCustomParameter1Value; params->custom_parameters[kCustomParameter2] = kCustomParameter2Value; return params; } +namespace { + +// Generated by running TlsClientHandshakerTest.ZeroRttResumption and in +// TlsClientHandshaker::InsertSession calling SSL_SESSION_to_bytes to serialize +// the received 0-RTT capable ticket. +static const char kCachedSession[] = + "3082068702010102020304040213010420b9c2a657e565db0babd09e192a9fc4d768fbd706" + "9f03f9278a4a0be62392e55b0420d87ed2ab8cafc986fd2e288bd2d654cd57c3a2bed1d532" + "20726e55fed39d021ea10602045ed16771a205020302a300a382025f3082025b30820143a0" + "03020102020104300d06092a864886f70d01010b0500302c3110300e060355040a13074163" + "6d6520436f311830160603550403130f496e7465726d656469617465204341301e170d3133" + "303130313130303030305a170d3233313233313130303030305a302d3110300e060355040a" + "130741636d6520436f3119301706035504031310746573742e6578616d706c652e636f6d30" + "59301306072a8648ce3d020106082a8648ce3d030107034200040526220e77278300d06bc0" + "86aff4f999a828a2ed5cc75adc2972794befe885aa3a9b843de321b36b0a795289cebff1a5" + "428bad5e34665ce5e36daad08fb3ffd8a3523050300e0603551d0f0101ff04040302078030" + "130603551d25040c300a06082b06010505070301300c0603551d130101ff04023000301b06" + "03551d11041430128210746573742e6578616d706c652e636f6d300d06092a864886f70d01" + "010b050003820101008c1f1e380831b6437a8b9284d28d4ead38d9503a9fc936db89048aa2" + "edd6ec2fb830d962ef7a4f384e679504f4d5520f3272e0b9e702b110aff31711578fa5aeb1" + "11e9d184c994b0f97e7b17d1995f3f477f25bc1258398ec0ec729caed55d594a009f48093a" + "17f33a7f3bb6e420cc3499838398a421d93c7132efa8bee5ed2645cbc55179c400da006feb" + "761badd356cac3bd7a0e6b22a511106a355ec62a4c0ac2541d2996adb4a918c866d10c3e31" + "62039a91d4ce600b276740d833380b37f66866d261bf6efa8855e7ae6c7d12a8a864cd9a1f" + "4663e07714b0204e51bbc189a2d04c2a5043202379ff1c8cbf30cbb44fde4ee9a1c0c976dc" + "4943df2c132ca4020400aa7f047d494e534543555245003072020101020203040402130104" + "000420d87ed2ab8cafc986fd2e288bd2d654cd57c3a2bed1d53220726e55fed39d021ea106" + "02045ed16771a205020302a300a4020400b20302011db5060404bd909308b807020500ffff" + "ffffb9050203093a80ba07040568332d3238bb030101ffbc03040100b20302011db3820307" + "30820303308201eba003020102020102300d06092a864886f70d01010b050030243110300e" + "060355040a130741636d6520436f3110300e06035504031307526f6f74204341301e170d31" + "33303130313130303030305a170d3233313233313130303030305a302c3110300e06035504" + "0a130741636d6520436f311830160603550403130f496e7465726d65646961746520434130" + "820122300d06092a864886f70d01010105000382010f003082010a0282010100cd3550e70a" + "6880e52bf0012b93110c50f723e1d8d2ed489aea3b649f82fae4ad2396a8a19b31d1d64ab2" + "79f1c18003184154a5303a82bd57109cfd5d34fd19d3211bcb06e76640e1278998822dd72e" + "0d5c059a740d45de325e784e81b4c86097f08b2a8ce057f6b9db5a53641d27e09347d993ee" + "acf67be7d297b1a6853775ffaaf78fae924e300b5654fd32f99d3cd82e95f56417ff26d265" + "e2b1786c835d67a4d8ae896b6eb34b35a5b1033c209779ed0bf8de25a13a507040ae9e0475" + "a26a2f15845b08c3e0554e47dbbc7925b02e580dbcaaa6f2eecde6b8028c5b00b33d44d0a6" + "bfb3e72e9d4670de45d1bd79bdc0f2470b71286091c29873152db4b1f30203010001a33830" + "36300e0603551d0f0101ff04040302020430130603551d25040c300a06082b060105050703" + "01300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003820101" + "00bc4f8234860558dd404a626403819bfc759029d625a002143e75ebdb2898d1befdd326c3" + "4b14dc3507d732bb29af7e6af31552db53052a2be0d950efee5e0f699304231611ed8bf73a" + "6f216a904c6c2f1a2186d1ed08a8005a7914394d71e7d4b643c808f86365c5fecad8b52934" + "2d3b3f03447126d278d75b1dab3ed53f23e36e9b3d695f28727916e5ee56ce22d387c81f05" + "919b2a37bd4981eb67d9f57b7072285dbbb61f48b6b14768c069a092aad5a094cf295dafd2" + "3ca008f89a5f5ab37a56e5f68df45091c7cb85574677127087a2887ba3baa6d4fc436c6e40" + "40885e81621d38974f0c7f0d792418c5adebb10e92a165f8d79b169617ff575c0d4a85b506" + "0404bd909308b603010100b70402020403b807020500ffffffffb9050203093a80ba070405" + "68332d3238bb030101ff"; + +} // namespace + class QuicClientSessionCacheTest : public testing::Test { public: - QuicClientSessionCacheTest() : ssl_ctx_(SSL_CTX_new(TLS_method())) {} + QuicClientSessionCacheTest() + : ssl_ctx_(SSL_CTX_new(TLS_method())), clock_(MakeTestClock()) {} protected: bssl::UniquePtr<SSL_SESSION> NewSSLSession() { - SSL_SESSION* session = SSL_SESSION_new(ssl_ctx_.get()); - if (!SSL_SESSION_set_protocol_version(session, TLS1_3_VERSION)) - return nullptr; + std::string cached_session = + quiche::QuicheTextUtils::HexDecode(kCachedSession); + SSL_SESSION* session = SSL_SESSION_from_bytes( + reinterpret_cast<const uint8_t*>(cached_session.data()), + cached_session.size(), ssl_ctx_.get()); return bssl::UniquePtr<SSL_SESSION>(session); } - bssl::UniquePtr<SSL_SESSION> MakeTestSession(base::Time now, - base::TimeDelta timeout) { + bssl::UniquePtr<SSL_SESSION> MakeTestSession( + base::TimeDelta timeout = kTimeout) { bssl::UniquePtr<SSL_SESSION> session = NewSSLSession(); - SSL_SESSION_set_time(session.get(), now.ToTimeT()); + SSL_SESSION_set_time(session.get(), clock_->Now().ToTimeT()); SSL_SESSION_set_timeout(session.get(), timeout.InSeconds()); return session; } bssl::UniquePtr<SSL_CTX> ssl_ctx_; + std::unique_ptr<base::SimpleTestClock> clock_; }; } // namespace @@ -91,13 +153,14 @@ class QuicClientSessionCacheTest : public testing::Test { // Tests that simple insertion and lookup work correctly. TEST_F(QuicClientSessionCacheTest, SingleSession) { QuicClientSessionCache cache; + cache.SetClockForTesting(clock_.get()); auto params = MakeFakeTransportParams(); - auto session = NewSSLSession(); + auto session = MakeTestSession(); quic::QuicServerId id1("a.com", 443); auto params2 = MakeFakeTransportParams(); - auto session2 = NewSSLSession(); + auto session2 = MakeTestSession(); SSL_SESSION* unowned2 = session2.get(); quic::QuicServerId id2("b.com", 443); @@ -115,7 +178,7 @@ TEST_F(QuicClientSessionCacheTest, SingleSession) { // Lookup() will trigger a deletion of invalid entry. EXPECT_EQ(0u, cache.size()); - auto session3 = NewSSLSession(); + auto session3 = MakeTestSession(); SSL_SESSION* unowned3 = session3.get(); quic::QuicServerId id3("c.com", 443); cache.Insert(id3, std::move(session3), *params, nullptr); @@ -133,13 +196,14 @@ TEST_F(QuicClientSessionCacheTest, SingleSession) { TEST_F(QuicClientSessionCacheTest, MultipleSessions) { QuicClientSessionCache cache; + cache.SetClockForTesting(clock_.get()); auto params = MakeFakeTransportParams(); - auto session = NewSSLSession(); + auto session = MakeTestSession(); quic::QuicServerId id1("a.com", 443); - auto session2 = NewSSLSession(); + auto session2 = MakeTestSession(); SSL_SESSION* unowned2 = session2.get(); - auto session3 = NewSSLSession(); + auto session3 = MakeTestSession(); SSL_SESSION* unowned3 = session3.get(); cache.Insert(id1, std::move(session), *params, nullptr); @@ -156,12 +220,13 @@ TEST_F(QuicClientSessionCacheTest, MultipleSessions) { // the same server id, the existing entry is removed. TEST_F(QuicClientSessionCacheTest, DifferentTransportParams) { QuicClientSessionCache cache; + cache.SetClockForTesting(clock_.get()); auto params = MakeFakeTransportParams(); - auto session = NewSSLSession(); + auto session = MakeTestSession(); quic::QuicServerId id1("a.com", 443); - auto session2 = NewSSLSession(); - auto session3 = NewSSLSession(); + auto session2 = MakeTestSession(); + auto session3 = MakeTestSession(); SSL_SESSION* unowned3 = session3.get(); cache.Insert(id1, std::move(session), *params, nullptr); @@ -177,12 +242,13 @@ TEST_F(QuicClientSessionCacheTest, DifferentTransportParams) { TEST_F(QuicClientSessionCacheTest, DifferentApplicationState) { QuicClientSessionCache cache; + cache.SetClockForTesting(clock_.get()); auto params = MakeFakeTransportParams(); - auto session = NewSSLSession(); + auto session = MakeTestSession(); quic::QuicServerId id1("a.com", 443); - auto session2 = NewSSLSession(); - auto session3 = NewSSLSession(); + auto session2 = MakeTestSession(); + auto session3 = MakeTestSession(); SSL_SESSION* unowned3 = session3.get(); quic::ApplicationState state; state.push_back('a'); @@ -198,12 +264,13 @@ TEST_F(QuicClientSessionCacheTest, DifferentApplicationState) { TEST_F(QuicClientSessionCacheTest, BothStatesDifferent) { QuicClientSessionCache cache; + cache.SetClockForTesting(clock_.get()); auto params = MakeFakeTransportParams(); - auto session = NewSSLSession(); + auto session = MakeTestSession(); quic::QuicServerId id1("a.com", 443); - auto session2 = NewSSLSession(); - auto session3 = NewSSLSession(); + auto session2 = MakeTestSession(); + auto session3 = MakeTestSession(); SSL_SESSION* unowned3 = session3.get(); quic::ApplicationState state; state.push_back('a'); @@ -222,16 +289,17 @@ TEST_F(QuicClientSessionCacheTest, BothStatesDifferent) { // When the size limit is exceeded, the oldest entry should be erased. TEST_F(QuicClientSessionCacheTest, SizeLimit) { QuicClientSessionCache cache(2); + cache.SetClockForTesting(clock_.get()); auto params = MakeFakeTransportParams(); - auto session = NewSSLSession(); + auto session = MakeTestSession(); quic::QuicServerId id1("a.com", 443); - auto session2 = NewSSLSession(); + auto session2 = MakeTestSession(); SSL_SESSION* unowned2 = session2.get(); quic::QuicServerId id2("b.com", 443); - auto session3 = NewSSLSession(); + auto session3 = MakeTestSession(); SSL_SESSION* unowned3 = session3.get(); quic::QuicServerId id3("c.com", 443); @@ -245,19 +313,43 @@ TEST_F(QuicClientSessionCacheTest, SizeLimit) { EXPECT_EQ(nullptr, cache.Lookup(id1, ssl_ctx_.get())); } +TEST_F(QuicClientSessionCacheTest, ClearEarlyData) { + QuicClientSessionCache cache; + cache.SetClockForTesting(clock_.get()); + SSL_CTX_set_early_data_enabled(ssl_ctx_.get(), 1); + auto params = MakeFakeTransportParams(); + auto session = MakeTestSession(); + quic::QuicServerId id1("a.com", 443); + auto session2 = MakeTestSession(); + + EXPECT_TRUE(SSL_SESSION_early_data_capable(session.get())); + EXPECT_TRUE(SSL_SESSION_early_data_capable(session2.get())); + + cache.Insert(id1, std::move(session), *params, nullptr); + cache.Insert(id1, std::move(session2), *params, nullptr); + + cache.ClearEarlyData(id1); + + auto resumption_state = cache.Lookup(id1, ssl_ctx_.get()); + EXPECT_FALSE( + SSL_SESSION_early_data_capable(resumption_state->tls_session.get())); + resumption_state = cache.Lookup(id1, ssl_ctx_.get()); + EXPECT_FALSE( + SSL_SESSION_early_data_capable(resumption_state->tls_session.get())); + EXPECT_EQ(nullptr, cache.Lookup(id1, ssl_ctx_.get())); +} + // Expired session isn't considered valid and nullptr will be returned upon // Lookup. TEST_F(QuicClientSessionCacheTest, Expiration) { - const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(1000); QuicClientSessionCache cache; - std::unique_ptr<base::SimpleTestClock> clock = MakeTestClock(); - cache.SetClockForTesting(clock.get()); + cache.SetClockForTesting(clock_.get()); auto params = MakeFakeTransportParams(); - auto session = MakeTestSession(clock->Now(), kTimeout); + auto session = MakeTestSession(); quic::QuicServerId id1("a.com", 443); - auto session2 = MakeTestSession(clock->Now(), 3 * kTimeout); + auto session2 = MakeTestSession(3 * kTimeout); SSL_SESSION* unowned2 = session2.get(); quic::QuicServerId id2("b.com", 443); @@ -266,7 +358,7 @@ TEST_F(QuicClientSessionCacheTest, Expiration) { EXPECT_EQ(2u, cache.size()); // Expire the session. - clock->Advance(kTimeout * 2); + clock_->Advance(kTimeout * 2); // The entry has not been removed yet. EXPECT_EQ(2u, cache.size()); @@ -278,16 +370,14 @@ TEST_F(QuicClientSessionCacheTest, Expiration) { TEST_F(QuicClientSessionCacheTest, FlushOnMemoryNotifications) { base::test::TaskEnvironment task_environment; - const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(1000); QuicClientSessionCache cache; - std::unique_ptr<base::SimpleTestClock> clock = MakeTestClock(); - cache.SetClockForTesting(clock.get()); + cache.SetClockForTesting(clock_.get()); auto params = MakeFakeTransportParams(); - auto session = MakeTestSession(clock->Now(), kTimeout); + auto session = MakeTestSession(); quic::QuicServerId id1("a.com", 443); - auto session2 = MakeTestSession(clock->Now(), 3 * kTimeout); + auto session2 = MakeTestSession(3 * kTimeout); quic::QuicServerId id2("b.com", 443); cache.Insert(id1, std::move(session), *params, nullptr); @@ -295,7 +385,7 @@ TEST_F(QuicClientSessionCacheTest, FlushOnMemoryNotifications) { EXPECT_EQ(2u, cache.size()); // Expire the session. - clock->Advance(kTimeout * 2); + clock_->Advance(kTimeout * 2); // The entry has not been removed yet. EXPECT_EQ(2u, cache.size()); diff --git a/chromium/net/quic/quic_connection_logger.cc b/chromium/net/quic/quic_connection_logger.cc index 1293fac42ac..71803309b69 100644 --- a/chromium/net/quic/quic_connection_logger.cc +++ b/chromium/net/quic/quic_connection_logger.cc @@ -227,6 +227,14 @@ base::Value NetLogQuicCryptoHandshakeMessageParams( return dict; } +base::Value NetLogQuicTransportParametersParams( + const quic::TransportParameters& transport_parameters) { + base::Value dict(base::Value::Type::DICTIONARY); + dict.SetStringKey("quic_transport_parameters", + transport_parameters.ToString()); + return dict; +} + base::Value NetLogQuicOnConnectionClosedParams( quic::QuicErrorCode error, string error_details, @@ -877,8 +885,6 @@ void QuicConnectionLogger::OnIncomingAck( return; net_log_.AddEvent(NetLogEventType::QUIC_SESSION_ACK_FRAME_RECEIVED, [&] { return NetLogQuicAckFrameParams(&frame); }); - - // TODO(rch, rtenneti) sort out histograms for QUIC_VERSION_34 and above. } void QuicConnectionLogger::OnStopWaitingFrame( @@ -1142,6 +1148,26 @@ void QuicConnectionLogger::OnRttChanged(quic::QuicTime::Delta rtt) const { } } +void QuicConnectionLogger::OnTransportParametersSent( + const quic::TransportParameters& transport_parameters) { + if (!net_log_.IsCapturing()) + return; + net_log_.AddEvent( + NetLogEventType::QUIC_SESSION_TRANSPORT_PARAMETERS_SENT, [&] { + return NetLogQuicTransportParametersParams(transport_parameters); + }); +} + +void QuicConnectionLogger::OnTransportParametersReceived( + const quic::TransportParameters& transport_parameters) { + if (!net_log_.IsCapturing()) + return; + net_log_.AddEvent( + NetLogEventType::QUIC_SESSION_TRANSPORT_PARAMETERS_RECEIVED, [&] { + return NetLogQuicTransportParametersParams(transport_parameters); + }); +} + void QuicConnectionLogger::RecordAggregatePacketLossRate() const { // For short connections under 22 packets in length, we'll rely on the // Net.QuicSession.21CumulativePacketsReceived_* histogram to indicate packet diff --git a/chromium/net/quic/quic_connection_logger.h b/chromium/net/quic/quic_connection_logger.h index 9c45bf983d9..879ebf1c986 100644 --- a/chromium/net/quic/quic_connection_logger.h +++ b/chromium/net/quic/quic_connection_logger.h @@ -109,6 +109,10 @@ class NET_EXPORT_PRIVATE QuicConnectionLogger void OnSuccessfulVersionNegotiation( const quic::ParsedQuicVersion& version) override; void OnRttChanged(quic::QuicTime::Delta rtt) const override; + void OnTransportParametersSent( + const quic::TransportParameters& transport_parameters) override; + void OnTransportParametersReceived( + const quic::TransportParameters& transport_parameters) override; void OnCryptoHandshakeMessageReceived( const quic::CryptoHandshakeMessage& message); diff --git a/chromium/net/quic/quic_connectivity_monitor.cc b/chromium/net/quic/quic_connectivity_monitor.cc new file mode 100644 index 00000000000..00d0a774465 --- /dev/null +++ b/chromium/net/quic/quic_connectivity_monitor.cc @@ -0,0 +1,71 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_connectivity_monitor.h" + +#include "base/metrics/histogram_macros.h" + +namespace net { + +QuicConnectivityMonitor::QuicConnectivityMonitor( + NetworkChangeNotifier::NetworkHandle default_network) + : default_network_(default_network) {} + +QuicConnectivityMonitor::~QuicConnectivityMonitor() = default; + +size_t QuicConnectivityMonitor::GetNumDegradingSessions() const { + return degrading_sessions_.size(); +} + +void QuicConnectivityMonitor::SetInitialDefaultNetwork( + NetworkChangeNotifier::NetworkHandle default_network) { + default_network_ = default_network; +} + +void QuicConnectivityMonitor::OnSessionPathDegrading( + QuicChromiumClientSession* session, + NetworkChangeNotifier::NetworkHandle network) { + if (network == default_network_) + degrading_sessions_.insert(session); +} + +void QuicConnectivityMonitor::OnSessionResumedPostPathDegrading( + QuicChromiumClientSession* session, + NetworkChangeNotifier::NetworkHandle network) { + if (network == default_network_) + degrading_sessions_.erase(session); +} + +void QuicConnectivityMonitor::OnSessionRemoved( + QuicChromiumClientSession* session) { + degrading_sessions_.erase(session); +} + +void QuicConnectivityMonitor::OnDefaultNetworkUpdated( + NetworkChangeNotifier::NetworkHandle default_network) { + default_network_ = default_network; + degrading_sessions_.clear(); +} + +void QuicConnectivityMonitor::OnIPAddressChanged() { + // If NetworkHandle is supported, connectivity monitor will receive + // notifications via OnDefaultNetworkUpdated. + if (NetworkChangeNotifier::AreNetworkHandlesSupported()) + return; + + DCHECK_EQ(default_network_, NetworkChangeNotifier::kInvalidNetworkHandle); + degrading_sessions_.clear(); +} + +void QuicConnectivityMonitor::OnSessionGoingAwayOnIPAddressChange( + QuicChromiumClientSession* session) { + // This should only be called after ConnectivityMonitor gets notified via + // OnIPAddressChanged(). + DCHECK(degrading_sessions_.empty()); + // |session| that encounters IP address change will lose track which network + // it is bound to. Future connectivity monitoring may be misleading. + session->RemoveConnectivityObserver(this); +} + +} // namespace net diff --git a/chromium/net/quic/quic_connectivity_monitor.h b/chromium/net/quic/quic_connectivity_monitor.h new file mode 100644 index 00000000000..b8b7274a8d8 --- /dev/null +++ b/chromium/net/quic/quic_connectivity_monitor.h @@ -0,0 +1,74 @@ +// Copyright (c) 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_QUIC_CONNECTIVITY_MONITOR_H_ +#define NET_QUIC_QUIC_CONNECTIVITY_MONITOR_H_ + +#include "net/base/network_change_notifier.h" +#include "net/quic/quic_chromium_client_session.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_containers.h" + +namespace net { + +// Responsible for monitoring path degrading detection/recovery events on the +// default network interface. +// Reset all raw observations (reported by sessions) when the default network +// is changed, which happens either: +// - via OnDefaultNetworkUpdated if NetworkHandle is supported on the platform; +// - via OnIPAddressChanged otherwise. +class NET_EXPORT_PRIVATE QuicConnectivityMonitor + : public QuicChromiumClientSession::ConnectivityObserver { + public: + explicit QuicConnectivityMonitor( + NetworkChangeNotifier::NetworkHandle default_network); + + ~QuicConnectivityMonitor() override; + + // Returns the number of sessions that are currently degrading on the default + // network interface. + size_t GetNumDegradingSessions() const; + + // Called to set up the initial default network, which happens when the + // default network tracking is lost upon |this| creation. + void SetInitialDefaultNetwork( + NetworkChangeNotifier::NetworkHandle default_network); + + // Called when NetworkHandle is supported and the default network interface + // used by the platform is updated. + void OnDefaultNetworkUpdated( + NetworkChangeNotifier::NetworkHandle default_network); + + // Called when NetworkHandle is NOT supported and the IP address of the + // primary interface changes. This includes when the primary interface itself + // changes. + void OnIPAddressChanged(); + + // Called when |session| is marked as going away due to IP address change. + void OnSessionGoingAwayOnIPAddressChange(QuicChromiumClientSession* session); + + // QuicChromiumClientSession::ConnectivityObserver implementation. + void OnSessionPathDegrading( + QuicChromiumClientSession* session, + NetworkChangeNotifier::NetworkHandle network) override; + + void OnSessionResumedPostPathDegrading( + QuicChromiumClientSession* session, + NetworkChangeNotifier::NetworkHandle network) override; + + void OnSessionRemoved(QuicChromiumClientSession* session) override; + + private: + // If NetworkHandle is not supported, always set to + // NetworkChangeNotifier::kInvalidNetworkHandle. + NetworkChangeNotifier::NetworkHandle default_network_; + // Sessions that are currently degrading on the |default_network_|. + quic::QuicHashSet<QuicChromiumClientSession*> degrading_sessions_; + + base::WeakPtrFactory<QuicConnectivityMonitor> weak_factory_{this}; + DISALLOW_COPY_AND_ASSIGN(QuicConnectivityMonitor); +}; + +} // namespace net + +#endif // NET_QUIC_QUIC_CONNECTIVITY_MONITOR_H_ diff --git a/chromium/net/quic/quic_connectivity_probing_manager.cc b/chromium/net/quic/quic_connectivity_probing_manager.cc index 9f0d16fbee4..dd240252bf6 100644 --- a/chromium/net/quic/quic_connectivity_probing_manager.cc +++ b/chromium/net/quic/quic_connectivity_probing_manager.cc @@ -60,7 +60,9 @@ QuicConnectivityProbingManager::QuicConnectivityProbingManager( network_(NetworkChangeNotifier::kInvalidNetworkHandle), retry_count_(0), probe_start_time_(base::TimeTicks()), - task_runner_(task_runner) { + task_runner_(task_runner), + last_self_address_(IPEndPoint()), + stateless_reset_received_(false) { retransmit_timer_.SetTaskRunner(task_runner_); } @@ -98,14 +100,19 @@ void QuicConnectivityProbingManager::CancelProbing( void QuicConnectivityProbingManager::CancelProbingIfAny() { if (is_running_) { + UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.StatelessResetDuringProbing", + stateless_reset_received_); net_log_.AddEvent( NetLogEventType::QUIC_CONNECTIVITY_PROBING_MANAGER_CANCEL_PROBING, [&] { return NetLogProbingDestinationParams(network_, &peer_address_); }); } is_running_ = false; + if (socket_ != nullptr) { + socket_->GetLocalAddress(&last_self_address_); + } network_ = NetworkChangeNotifier::kInvalidNetworkHandle; - peer_address_ = quic::QuicSocketAddress(); + stateless_reset_received_ = false; socket_.reset(); writer_.reset(); reader_.reset(); @@ -198,6 +205,43 @@ void QuicConnectivityProbingManager::OnPacketReceived( CancelProbingIfAny(); } +bool QuicConnectivityProbingManager::ValidateStatelessReset( + const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address) { + IPEndPoint local_address; + if (!socket_) { + local_address = last_self_address_; + } else { + socket_->GetLocalAddress(&local_address); + } + + if (local_address != ToIPEndPoint(self_address) || + peer_address_ != peer_address) { + DVLOG(1) << "Probing lives at different path:"; + DVLOG(1) << " peer_address: " << local_address.ToString(); + DVLOG(1) << " self_address: " << self_address.ToString(); + return false; + } + + if (is_running_) { + stateless_reset_received_ = true; + } else { + UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.StatelessResetAfterProbingCancelled", + true); + } + + net_log_.AddEvent( + NetLogEventType:: + QUIC_CONNECTIVITY_PROBING_MANAGER_STATELESS_RESET_RECEIVED, + [&] { + return NetLogProbeReceivedParams(network_, &local_address, + &peer_address_); + }); + + NotifyDelegateProbeFailed(); + return true; +} + void QuicConnectivityProbingManager::SendConnectivityProbingPacket( base::TimeDelta timeout) { net_log_.AddEventWithInt64Params( diff --git a/chromium/net/quic/quic_connectivity_probing_manager.h b/chromium/net/quic/quic_connectivity_probing_manager.h index 9d4de8e25bb..d7e0a9c5067 100644 --- a/chromium/net/quic/quic_connectivity_probing_manager.h +++ b/chromium/net/quic/quic_connectivity_probing_manager.h @@ -7,6 +7,7 @@ #include "base/time/time.h" #include "base/timer/timer.h" +#include "net/base/ip_endpoint.h" #include "net/base/net_export.h" #include "net/log/net_log_with_source.h" #include "net/quic/quic_chromium_packet_reader.h" @@ -95,6 +96,11 @@ class NET_EXPORT_PRIVATE QuicConnectivityProbingManager peer_address == peer_address_); } + // Returns true if both |self_address| and |peer_address| + // match with the probing manager's socket address. Returns false otherwise. + bool ValidateStatelessReset(const quic::QuicSocketAddress& self_address, + const quic::QuicSocketAddress& peer_address); + private: // Cancels undergoing probing. void CancelProbingIfAny(); @@ -116,6 +122,8 @@ class NET_EXPORT_PRIVATE QuicConnectivityProbingManager // if |is_running_| is true. bool is_running_; NetworkChangeNotifier::NetworkHandle network_; + // If |is_running| is false, |peer_address_| caches the peer address of the + // last probing path. quic::QuicSocketAddress peer_address_; std::unique_ptr<DatagramClientSocket> socket_; @@ -129,6 +137,11 @@ class NET_EXPORT_PRIVATE QuicConnectivityProbingManager base::SequencedTaskRunner* task_runner_; + // The cached local address set when probing is cancelled. + IPEndPoint last_self_address_; + + bool stateless_reset_received_; + base::WeakPtrFactory<QuicConnectivityProbingManager> weak_factory_{this}; DISALLOW_COPY_AND_ASSIGN(QuicConnectivityProbingManager); }; diff --git a/chromium/net/quic/quic_connectivity_probing_manager_test.cc b/chromium/net/quic/quic_connectivity_probing_manager_test.cc index 15bd236a20a..abce3d51857 100644 --- a/chromium/net/quic/quic_connectivity_probing_manager_test.cc +++ b/chromium/net/quic/quic_connectivity_probing_manager_test.cc @@ -378,6 +378,89 @@ TEST_F(QuicConnectivityProbingManagerTest, RetryProbingWithExponentailBackoff) { EXPECT_EQ(0u, test_task_runner_->GetPendingTaskCount()); } +TEST_F(QuicConnectivityProbingManagerTest, ProbingReceivedStatelessReset) { + int initial_timeout_ms = 100; + EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress)) + .WillOnce(Return(true)); + probing_manager_.StartProbing( + testNetworkHandle, testPeerAddress, std::move(socket_), + std::move(writer_), std::move(reader_), + base::TimeDelta::FromMilliseconds(initial_timeout_ms), + bound_test_net_log_.bound()); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + + // Fast forward initial_timeout_ms, timeout the first connectivity probing + // packet, cause another probing packet to be sent with timeout set to + // 2 * initial_timeout_ms. + EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress)) + .WillOnce(Return(true)); + test_task_runner_->FastForwardBy( + base::TimeDelta::FromMilliseconds(initial_timeout_ms)); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + + // Fast forward initial_timeout_ms, should be no-op. + EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress)) + .Times(0); + test_task_runner_->FastForwardBy( + base::TimeDelta::FromMilliseconds(initial_timeout_ms)); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + + EXPECT_CALL(session_, OnProbeFailed(testNetworkHandle, testPeerAddress)) + .Times(1); + EXPECT_TRUE( + probing_manager_.ValidateStatelessReset(self_address_, testPeerAddress)); + EXPECT_FALSE(session_.is_successfully_probed()); + EXPECT_FALSE( + probing_manager_.IsUnderProbing(testNetworkHandle, testPeerAddress)); + test_task_runner_->RunUntilIdle(); +} + +TEST_F(QuicConnectivityProbingManagerTest, + StatelessResetReceivedAfterProbingCancelled) { + int initial_timeout_ms = 100; + + EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress)) + .WillOnce(Return(true)); + probing_manager_.StartProbing( + testNetworkHandle, testPeerAddress, std::move(socket_), + std::move(writer_), std::move(reader_), + base::TimeDelta::FromMilliseconds(initial_timeout_ms), + bound_test_net_log_.bound()); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + + // Fast forward initial_timeout_ms, timeout the first connectivity probing + // packet, cause another probing packet to be sent with timeout set to + // 2 * initial_timeout_ms. + EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress)) + .WillOnce(Return(true)); + test_task_runner_->FastForwardBy( + base::TimeDelta::FromMilliseconds(initial_timeout_ms)); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + + // Fast forward initial_timeout_ms, should be no-op. + EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, testPeerAddress)) + .Times(0); + test_task_runner_->FastForwardBy( + base::TimeDelta::FromMilliseconds(initial_timeout_ms)); + EXPECT_EQ(1u, test_task_runner_->GetPendingTaskCount()); + + // Request cancel probing, manager will no longer send connectivity probes. + EXPECT_CALL(session_, OnSendConnectivityProbingPacket(_, _)).Times(0); + EXPECT_CALL(session_, OnProbeFailed(_, _)).Times(0); + probing_manager_.CancelProbing(testNetworkHandle, testPeerAddress); + EXPECT_FALSE( + probing_manager_.IsUnderProbing(testNetworkHandle, testPeerAddress)); + + // Verify that the probing manager is still able to verify STATELESS_RESET + // received on the previous probing path. + EXPECT_TRUE( + probing_manager_.ValidateStatelessReset(self_address_, testPeerAddress)); + EXPECT_FALSE(session_.is_successfully_probed()); + EXPECT_FALSE( + probing_manager_.IsUnderProbing(testNetworkHandle, testPeerAddress)); + test_task_runner_->RunUntilIdle(); +} + TEST_F(QuicConnectivityProbingManagerTest, CancelProbing) { int initial_timeout_ms = 100; diff --git a/chromium/net/quic/quic_context.h b/chromium/net/quic/quic_context.h index 1f3ece3c0c7..e27af55433a 100644 --- a/chromium/net/quic/quic_context.h +++ b/chromium/net/quic/quic_context.h @@ -13,8 +13,8 @@ namespace net { // Default QUIC version used in absence of any external configuration. -constexpr quic::ParsedQuicVersion kDefaultSupportedQuicVersion{ - quic::PROTOCOL_QUIC_CRYPTO, quic::QUIC_VERSION_46}; +constexpr quic::ParsedQuicVersion kDefaultSupportedQuicVersion = + quic::ParsedQuicVersion::Q050(); // Returns a list containing only the current default version. inline NET_EXPORT_PRIVATE quic::ParsedQuicVersionVector @@ -165,6 +165,8 @@ struct NET_EXPORT QuicParams { // The initial rtt that will be used in crypto handshake if no cached // smoothed rtt is present. base::TimeDelta initial_rtt_for_handshake; + // If true, QUIC with TLS will not try 0-RTT connection. + bool disable_tls_zero_rtt = false; }; // QuicContext contains QUIC-related variables that are shared across all of the @@ -187,6 +189,11 @@ class NET_EXPORT_PRIVATE QuicContext { return params_.supported_versions; } + void SetHelperForTesting( + std::unique_ptr<quic::QuicConnectionHelperInterface> helper) { + helper_ = std::move(helper); + } + private: std::unique_ptr<quic::QuicConnectionHelperInterface> helper_; diff --git a/chromium/net/quic/quic_flags_list.h b/chromium/net/quic/quic_flags_list.h index e90d9ee34b2..8275a075e38 100644 --- a/chromium/net/quic/quic_flags_list.h +++ b/chromium/net/quic/quic_flags_list.h @@ -166,9 +166,6 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, false) -// If true, will negotiate the ACK delay time. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_negotiate_ack_delay_time, true) - // If true, QuicFramer::WriteClientVersionNegotiationProbePacket uses // length-prefixed connection IDs. QUIC_FLAG(bool, FLAGS_quic_prober_uses_length_prefixed_connection_ids, false) @@ -212,7 +209,7 @@ QUIC_FLAG(int32_t, FLAGS_quic_bbr2_default_probe_rtt_period_ms, 10000) // The default loss threshold for QUIC BBRv2, should be a value // between 0 and 1. -QUIC_FLAG(double, FLAGS_quic_bbr2_default_loss_threshold, 0.3) +QUIC_FLAG(double, FLAGS_quic_bbr2_default_loss_threshold, 0.02) // The default minimum number of loss marking events to exit STARTUP. QUIC_FLAG(int32_t, FLAGS_quic_bbr2_default_startup_full_loss_count, 8) @@ -228,20 +225,26 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_q043, false) QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_q046, false) // If true, disable QUIC version Q048. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_q048, false) +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_q048, true) // If true, disable QUIC version Q049. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_q049, false) +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_q049, true) // If true, disable QUIC version Q050. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_q050, false) -// If true, enable QUIC version T050. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_t050_v2, true) - // A testonly reloadable flag that will always default to false. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_testonly_default_false, false) +// A testonly reloadable flag that will always default to true. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_testonly_default_true, true) + +// A testonly restart flag that will always default to false. +QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_testonly_default_false, false) + +// A testonly restart flag that will always default to true. +QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_testonly_default_true, true) + // In BBR, slow pacing rate if it is likely causing overshoot. QUIC_FLAG( bool, @@ -263,20 +266,11 @@ QUIC_FLAG(bool, // If true, use predictable grease settings identifiers and values. QUIC_FLAG(bool, FLAGS_quic_enable_http3_grease_randomness, true) -// If true, enable QUIC version h3-25. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_enable_version_draft_25_v3, - true) +// If true, disable QUIC version h3-25. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_draft_25, false) -// If true, enable QUIC version h3-27. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_draft_27, true) - -// If true, fix QUIC bandwidth sampler to avoid over estimating bandwidth in -// the presence of ack aggregation. -QUIC_FLAG( - bool, - FLAGS_quic_reloadable_flag_quic_avoid_overestimate_bandwidth_with_aggregation, - true) +// If true, disable QUIC version h3-27. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_draft_27, false) // If true, QUIC BBRv2 to take ack height into account when calculating // queuing_threshold in PROBE_UP. @@ -285,38 +279,13 @@ QUIC_FLAG( FLAGS_quic_reloadable_flag_quic_bbr2_add_ack_height_to_queueing_threshold, true) -// If true, quic::BandwidthSampler will start in application limited phase. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_bw_sampler_app_limited_starting_value, - true) - // If true, use idle network detector to detect handshake timeout and idle // network timeout. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_use_idle_network_detector, - false) - -// If true, QUIC will enable connection options LRTT+BBQ2 by default. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_bbr_default_exit_startup_on_loss, - false) +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_use_idle_network_detector, true) // If true, server push will be allowed in QUIC versions using HTTP/3. QUIC_FLAG(bool, FLAGS_quic_enable_http3_server_push, false) -// If true, disable QuicDispatcher workaround that replies to invalid QUIC -// packets from the Android Conformance Test. -QUIC_FLAG( - bool, - FLAGS_quic_reloadable_flag_quic_remove_android_conformance_test_workaround, - true) - -// If true, lower the CWND gain in BBRv2 STARTUP to 2 when BBQ2 is in connection -// options. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_bbr2_lower_startup_cwnd_gain, - true) - // The divisor that controls how often MAX_STREAMS frames are sent. QUIC_FLAG(int32_t, FLAGS_quic_max_streams_window_divisor, 2) @@ -333,25 +302,10 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr2_fewer_startup_round_trips, false) -// If true, remove draining_streams_ from QuicSession. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_deprecate_draining_streams, - true) - -// If true, break session/stream close loop. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_break_session_stream_close_loop, - true) - // Replace the usage of ConnectionData::encryption_level in // quic_time_wait_list_manager with a new TimeWaitAction. QUIC_FLAG(bool, FLAGS_quic_restart_flag_quic_replace_time_wait_list_encryption_level, - false) - -// If true, move Goolge QUIC stream accounting to LegacyQuicStreamIdManager. -QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_stream_id_manager_handles_accounting, true) // If true, enables support for TLS resumption in QUIC. @@ -360,58 +314,153 @@ QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_tls_resumption, false) // When true, QUIC's BBRv2 ignores inflight_lo in PROBE_BW. QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr2_ignore_inflight_lo, false) -// If true, returns min_rtt in rtt_stats_ if it is available. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr_use_available_min_rtt, true) +// If true, do not change ACK in PostProcessAckFrame if an ACK has been queued. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_donot_change_queued_ack, true) + +// If true, reject IETF QUIC connections with invalid SNI. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_tls_enforce_valid_sni, true) + +// If true, support for IETF QUIC 0-rtt is enabled. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_zero_rtt_for_tls, true) + +// If true, default on PTO which unifies TLP + RTO loss recovery. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_default_on_pto, false) -// If true, notify handshakers when connection closes. +// When true, QUIC+TLS will not send nor parse the old-format Google-specific +// transport parameters. QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_notify_handshaker_on_connection_close, + FLAGS_quic_restart_flag_quic_google_transport_param_omit_old, + false) + +// When true, QUIC+TLS will send and parse the new-format Google-specific +// transport parameters. +QUIC_FLAG(bool, + FLAGS_quic_restart_flag_quic_google_transport_param_send_new, true) -// If true, for QUIC + TLS, change default encryption level when new encryption -// key is available. +// If true, check ShouldGeneratePacket for every crypto packet. QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_change_default_encryption_level, + FLAGS_quic_reloadable_flag_quic_fix_checking_should_generate_packet, true) -// If true, do not change ACK in PostProcessAckFrame if an ACK has been queued. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_donot_change_queued_ack, false) +// If true, notify stream ID manager even connection disconnects. +QUIC_FLAG( + bool, + FLAGS_quic_reloadable_flag_quic_notify_stream_id_manager_when_disconnected, + true) -// If true, reject IETF QUIC connections with invalid SNI. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_tls_enforce_valid_sni, false) +// If true, return from QuicCryptoStream::WritePendingCryptoRetransmission after +// partial writes. +QUIC_FLAG( + bool, + FLAGS_quic_reloadable_flag_quic_fix_write_pending_crypto_retransmission, + true) -// If true, update ack timeout upon receiving an retransmittable frame. +// If true, clear last_inflight_packets_sent_time_ of a packet number space when +// there is no bytes in flight. QUIC_FLAG(bool, - FLAGS_quic_reloadable_flag_quic_advance_ack_timeout_update, - false) + FLAGS_quic_reloadable_flag_quic_fix_last_inflight_packets_sent_time, + true) -// If true, only extend idle time on decryptable packets. +// If true, QUIC will free writer-allocated packet buffer if writer->WritePacket +// is not called. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_avoid_leak_writer_buffer, false) + +// If true, QuicConnection::SendAllPendingAcks will Update instead of Set the +// ack alarm. QUIC_FLAG( bool, - FLAGS_quic_reloadable_flag_quic_extend_idle_time_on_decryptable_packets, - false) + FLAGS_quic_reloadable_flag_quic_update_ack_alarm_in_send_all_pending_acks, + true) -// If true, support for IETF QUIC 0-rtt is enabled. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_zero_rtt_for_tls, false) +// If true, the B2HI connection option limits reduction of inflight_hi to +// (1-Beta)*CWND. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_bbr2_limit_inflight_hi, false) -// If true, default on PTO which unifies TLP + RTO loss recovery. -QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_default_on_pto, false) +// When true, always check the amplification limit before writing, not just for +// handshake packets. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_move_amplification_limit, true) -// When true, QUIC+TLS will not send nor parse the old-format Google-specific -// transport parameters. +// If true, SendAllPendingAcks always send the earliest ACK. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_always_send_earliest_ack, true) + +// If true, check connection level flow control for send control stream and +// qpack streams in QuicSession::WillingAndAbleToWrite. QUIC_FLAG(bool, - FLAGS_quic_restart_flag_quic_google_transport_param_omit_old, + FLAGS_quic_reloadable_flag_quic_fix_willing_and_able_to_write, + true) + +// If true, disable QUIC version h3-T050. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_disable_version_t050, false) + +// If true, do not arm PTO on half RTT packets if they are the only ones in +// flight. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_server_pto_timeout, true) + +// If true, default-enable 5RTO blachole detection. +QUIC_FLAG( + bool, + FLAGS_quic_reloadable_flag_quic_default_enable_5rto_blackhole_detection2, + true) + +// If true, session does not send duplicate MAX_STREAMS. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_stop_sending_duplicate_max_streams, + true) + +// If true, enable QUIC version h3-29. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_enable_version_draft_29, true) + +// If true, support HANDSHAKE_DONE frame in T050 +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_support_handshake_done_in_t050, + true) + +// If true, save user agent into in QuicSession. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_save_user_agent_in_quic_session, false) -// When true, QUIC+TLS will send and parse the new-format Google-specific -// transport parameters. +// When true, QUIC_CRYPTO versions of QUIC will not send the max ACK delay +// unless it is configured to a non-default value. QUIC_FLAG(bool, - FLAGS_quic_restart_flag_quic_google_transport_param_send_new, + FLAGS_quic_reloadable_flag_quic_dont_send_max_ack_delay_if_default, true) -// If true, if a buffered MTU packet causes a write to return MSG_TOO_BIG, this -// error will be ignored. +// If true, remove the head of line blocking caused by an unprocessable packet +// in the undecryptable packets list. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_undecryptable_packets, true) + +// If true, QUIC client only tries to retransmit data when 1-RTT key is +// available. QUIC_FLAG( bool, - FLAGS_quic_reloadable_flag_quic_ignore_msg_too_big_from_buffered_packets, + FLAGS_quic_reloadable_flag_quic_do_not_retransmit_immediately_on_zero_rtt_reject, true) + +// If true, try to bundle INITIAL data when trying to send INITIAL ACK. +QUIC_FLAG(bool, + FLAGS_quic_reloadable_flag_quic_bundle_crypto_data_with_initial_ack, + true) + +// If true, do not use QuicUtil::IsBidirectionalStreamId() to determine gQUIC +// stream type. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_gquic_stream_type, true) + +// When true, do not pad the QUIC_CRYPTO CHLO message itself. Note that the +// packet containing the CHLO will still be padded. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_dont_pad_chlo, false) + +// If true, include MinPlaintextPacketSize when determine whether removing soft +// limit for crypto frames. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_fix_min_crypto_frame_size, true) + +// When true, QuicDispatcher supports decapsulation of Legacy Version +// Encapsulation packets. +QUIC_FLAG( + bool, + FLAGS_quic_reloadable_flag_quic_dispatcher_legacy_version_encapsulation, + false) + +// If true, update packet size when the first frame gets queued. +QUIC_FLAG(bool, FLAGS_quic_reloadable_flag_quic_update_packet_size, false) diff --git a/chromium/net/quic/quic_http3_logger.cc b/chromium/net/quic/quic_http3_logger.cc index c190156ff45..e93ff2987ea 100644 --- a/chromium/net/quic/quic_http3_logger.cc +++ b/chromium/net/quic/quic_http3_logger.cc @@ -9,10 +9,13 @@ #include <vector> #include "base/metrics/histogram_macros.h" +#include "base/strings/strcat.h" #include "base/strings/string_number_conversions.h" +#include "net/http/http_log_util.h" #include "net/log/net_log_capture_mode.h" #include "net/log/net_log_event_type.h" #include "net/log/net_log_values.h" +#include "net/spdy/spdy_log_util.h" namespace net { @@ -64,20 +67,19 @@ base::Value NetLogThreeIntParams(base::StringPiece name1, return dict; } -base::Value NetLogHeadersToDict(const quic::QuicHeaderList& headers) { - base::Value dict(base::Value::Type::DICTIONARY); - for (auto header : headers) { - dict.SetStringKey(header.first, header.second); - } - return dict; -} - -base::Value NetLogHeadersToDict(const spdy::SpdyHeaderBlock& headers) { - base::Value dict(base::Value::Type::DICTIONARY); - for (auto header : headers) { - dict.SetStringKey(header.first, header.second); +base::ListValue ElideQuicHeaderListForNetLog( + const quic::QuicHeaderList& headers, + NetLogCaptureMode capture_mode) { + base::ListValue headers_list; + for (const auto& header : headers) { + base::StringPiece key = header.first; + base::StringPiece value = header.second; + headers_list.Append(NetLogStringValue( + base::StrCat({key, ": ", + ElideHeaderValueForNetLog(capture_mode, key.as_string(), + value.as_string())}))); } - return dict; + return headers_list; } } // namespace @@ -248,11 +250,13 @@ void QuicHttp3Logger::OnHeadersDecoded(quic::QuicStreamId stream_id, return; } net_log_.AddEvent( - NetLogEventType::HTTP3_HEADERS_DECODED, [stream_id, &headers] { + NetLogEventType::HTTP3_HEADERS_DECODED, + [stream_id, &headers](NetLogCaptureMode capture_mode) { base::Value dict(base::Value::Type::DICTIONARY); dict.SetKey("stream_id", NetLogNumberValue(static_cast<uint64_t>(stream_id))); - dict.SetKey("headers", NetLogHeadersToDict(headers)); + dict.SetKey("headers", + ElideQuicHeaderListForNetLog(headers, capture_mode)); return dict; }); } @@ -279,16 +283,18 @@ void QuicHttp3Logger::OnPushPromiseDecoded(quic::QuicStreamId stream_id, if (!net_log_.IsCapturing()) { return; } - net_log_.AddEvent(NetLogEventType::HTTP3_PUSH_PROMISE_DECODED, [stream_id, - push_id, - &headers] { - base::Value dict(base::Value::Type::DICTIONARY); - dict.SetKey("stream_id", - NetLogNumberValue(static_cast<uint64_t>(stream_id))); - dict.SetKey("push_id", NetLogNumberValue(static_cast<uint64_t>(push_id))); - dict.SetKey("headers", NetLogHeadersToDict(headers)); - return dict; - }); + net_log_.AddEvent( + NetLogEventType::HTTP3_PUSH_PROMISE_DECODED, + [stream_id, push_id, &headers](NetLogCaptureMode capture_mode) { + base::Value dict(base::Value::Type::DICTIONARY); + dict.SetKey("stream_id", + NetLogNumberValue(static_cast<uint64_t>(stream_id))); + dict.SetKey("push_id", + NetLogNumberValue(static_cast<uint64_t>(push_id))); + dict.SetKey("headers", + ElideQuicHeaderListForNetLog(headers, capture_mode)); + return dict; + }); } void QuicHttp3Logger::OnUnknownFrameReceived( @@ -357,11 +363,13 @@ void QuicHttp3Logger::OnHeadersFrameSent( return; } net_log_.AddEvent( - NetLogEventType::HTTP3_HEADERS_SENT, [stream_id, &header_block] { + NetLogEventType::HTTP3_HEADERS_SENT, + [stream_id, &header_block](NetLogCaptureMode capture_mode) { base::Value dict(base::Value::Type::DICTIONARY); dict.SetKey("stream_id", NetLogNumberValue(static_cast<uint64_t>(stream_id))); - dict.SetKey("headers", NetLogHeadersToDict(header_block)); + dict.SetKey("headers", + ElideSpdyHeaderBlockForNetLog(header_block, capture_mode)); return dict; }); } @@ -373,16 +381,18 @@ void QuicHttp3Logger::OnPushPromiseFrameSent( if (!net_log_.IsCapturing()) { return; } - net_log_.AddEvent(NetLogEventType::HTTP3_PUSH_PROMISE_SENT, [stream_id, - push_id, - &header_block] { - base::Value dict(base::Value::Type::DICTIONARY); - dict.SetKey("stream_id", - NetLogNumberValue(static_cast<uint64_t>(stream_id))); - dict.SetKey("push_id", NetLogNumberValue(static_cast<uint64_t>(push_id))); - dict.SetKey("headers", NetLogHeadersToDict(header_block)); - return dict; - }); + net_log_.AddEvent( + NetLogEventType::HTTP3_PUSH_PROMISE_SENT, + [stream_id, push_id, &header_block](NetLogCaptureMode capture_mode) { + base::Value dict(base::Value::Type::DICTIONARY); + dict.SetKey("stream_id", + NetLogNumberValue(static_cast<uint64_t>(stream_id))); + dict.SetKey("push_id", + NetLogNumberValue(static_cast<uint64_t>(push_id))); + dict.SetKey("headers", + ElideSpdyHeaderBlockForNetLog(header_block, capture_mode)); + return dict; + }); } } // namespace net diff --git a/chromium/net/quic/quic_http_stream.cc b/chromium/net/quic/quic_http_stream.cc index 2107b89a840..5b21bb9e067 100644 --- a/chromium/net/quic/quic_http_stream.cc +++ b/chromium/net/quic/quic_http_stream.cc @@ -94,23 +94,26 @@ HttpResponseInfo::ConnectionInfo QuicHttpStream::ConnectionInfoFromQuicVersion( case quic::QUIC_VERSION_46: return HttpResponseInfo::CONNECTION_INFO_QUIC_46; case quic::QUIC_VERSION_48: - return quic_version.handshake_protocol == quic::PROTOCOL_TLS1_3 + return quic_version.UsesTls() ? HttpResponseInfo::CONNECTION_INFO_QUIC_T048 : HttpResponseInfo::CONNECTION_INFO_QUIC_Q048; case quic::QUIC_VERSION_49: - return quic_version.handshake_protocol == quic::PROTOCOL_TLS1_3 + return quic_version.UsesTls() ? HttpResponseInfo::CONNECTION_INFO_QUIC_T049 : HttpResponseInfo::CONNECTION_INFO_QUIC_Q049; case quic::QUIC_VERSION_50: - return quic_version.handshake_protocol == quic::PROTOCOL_TLS1_3 + return quic_version.UsesTls() ? HttpResponseInfo::CONNECTION_INFO_QUIC_T050 : HttpResponseInfo::CONNECTION_INFO_QUIC_Q050; case quic::QUIC_VERSION_IETF_DRAFT_25: - DCHECK(quic_version.handshake_protocol == quic::PROTOCOL_TLS1_3); + DCHECK(quic_version.UsesTls()); return HttpResponseInfo::CONNECTION_INFO_QUIC_DRAFT_25; case quic::QUIC_VERSION_IETF_DRAFT_27: - DCHECK(quic_version.handshake_protocol == quic::PROTOCOL_TLS1_3); + DCHECK(quic_version.UsesTls()); return HttpResponseInfo::CONNECTION_INFO_QUIC_DRAFT_27; + case quic::QUIC_VERSION_IETF_DRAFT_29: + DCHECK(quic_version.UsesTls()); + return HttpResponseInfo::CONNECTION_INFO_QUIC_DRAFT_29; case quic::QUIC_VERSION_RESERVED_FOR_NEGOTIATION: return HttpResponseInfo::CONNECTION_INFO_QUIC_999; } diff --git a/chromium/net/quic/quic_http_stream_test.cc b/chromium/net/quic/quic_http_stream_test.cc index c5638861d8c..7432512b4d8 100644 --- a/chromium/net/quic/quic_http_stream_test.cc +++ b/chromium/net/quic/quic_http_stream_test.cc @@ -1111,7 +1111,7 @@ TEST_P(QuicHttpStreamTest, LogGranularQuicConnectionError) { TEST_P(QuicHttpStreamTest, LogGranularQuicErrorIfHandshakeNotConfirmed) { // TODO(nharper): Figure out why this test does not send packets // when TLS is used. - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { Initialize(); return; @@ -1615,10 +1615,11 @@ TEST_P(QuicHttpStreamTest, SendChunkedPostRequestAbortedByResetStream) { kIncludeVersion, !kFin, DEFAULT_PRIORITY, 0, &spdy_request_headers_frame_length, {header, kUploadData})); AddWrite(ConstructClientAckPacket(packet_number++, 3, 1, 2)); - AddWrite(client_maker_.MakeRstPacket( - packet_number++, - /* include_version = */ true, stream_id_, quic::QUIC_STREAM_NO_ERROR, - /* include_stop_sending_if_v99 = */ false)); + AddWrite(client_maker_.MakeAckAndRstPacket( + packet_number++, + /* include_version = */ true, stream_id_, quic::QUIC_STREAM_NO_ERROR, + 4, 1, 1, + /* include_stop_sending_if_v99 = */ false)); } else { AddWrite(ConstructRequestHeadersAndDataFramesPacket( packet_number++, GetNthClientInitiatedBidirectionalStreamId(0), diff --git a/chromium/net/quic/quic_http_utils.cc b/chromium/net/quic/quic_http_utils.cc index 077bd50335e..1faf58cce2f 100644 --- a/chromium/net/quic/quic_http_utils.cc +++ b/chromium/net/quic/quic_http_utils.cc @@ -64,8 +64,9 @@ quic::ParsedQuicVersionVector FilterSupportedAltSvcVersions( << quic_alt_svc.protocol_id; for (uint32_t quic_version : quic_alt_svc.version) { - for (quic::ParsedQuicVersion supported : supported_versions) { - if (supported.handshake_protocol == quic::PROTOCOL_QUIC_CRYPTO && + for (const quic::ParsedQuicVersion& supported : supported_versions) { + if (supported.UsesQuicCrypto() && + supported.SupportsGoogleAltSvcFormat() && static_cast<uint32_t>(supported.transport_version) == quic_version) { supported_alt_svc_versions.push_back(supported); RecordAltSvcFormat(GOOGLE_FORMAT); diff --git a/chromium/net/quic/quic_http_utils_test.cc b/chromium/net/quic/quic_http_utils_test.cc index d1609675277..34356cf2db1 100644 --- a/chromium/net/quic/quic_http_utils_test.cc +++ b/chromium/net/quic/quic_http_utils_test.cc @@ -11,9 +11,6 @@ #include "net/third_party/quiche/src/spdy/core/spdy_alt_svc_wire_format.h" #include "testing/gtest/include/gtest/gtest.h" -using quic::ParsedQuicVersion; -using quic::PROTOCOL_QUIC_CRYPTO; - namespace net { namespace test { @@ -45,20 +42,18 @@ TEST(QuicHttpUtilsTest, FilterSupportedAltSvcVersions) { // finds the intersection of the two sets ... version C. Note that // as QUIC versions are defined/undefined, the exact version numbers // used may need to change. The actual version numbers are not - // important. + // important. Note that FilterSupportedAltSvcVersions is only used + // for the old Google-specific Alt-Svc format which is now deprecated. quic::ParsedQuicVersionVector supported_versions = { - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, quic::QUIC_VERSION_48), - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, quic::QUIC_VERSION_43), + quic::ParsedQuicVersion::Q050(), + quic::ParsedQuicVersion::Q043(), }; - std::vector<uint32_t> alt_svc_versions_google = {quic::QUIC_VERSION_48, - quic::QUIC_VERSION_46}; - std::vector<uint32_t> alt_svc_versions_ietf = { - QuicVersionToQuicVersionLabel(quic::QUIC_VERSION_48), - QuicVersionToQuicVersionLabel(quic::QUIC_VERSION_46)}; + std::vector<uint32_t> alt_svc_versions_google = { + 33, quic::ParsedQuicVersion::Q043().transport_version}; quic::ParsedQuicVersionVector supported_alt_svc_versions = { - ParsedQuicVersion(PROTOCOL_QUIC_CRYPTO, quic::QUIC_VERSION_48)}; + quic::ParsedQuicVersion::Q043()}; spdy::SpdyAltSvcWireFormat::AlternativeService altsvc; altsvc.protocol_id = "quic"; diff --git a/chromium/net/quic/quic_network_transaction_unittest.cc b/chromium/net/quic/quic_network_transaction_unittest.cc index 5dc3abdbe8c..e1068b88a76 100644 --- a/chromium/net/quic/quic_network_transaction_unittest.cc +++ b/chromium/net/quic/quic_network_transaction_unittest.cc @@ -38,6 +38,7 @@ #include "net/http/http_stream.h" #include "net/http/http_stream_factory.h" #include "net/http/http_transaction_test_util.h" +#include "net/http/test_upload_data_stream_not_allow_http1.h" #include "net/http/transport_security_state.h" #include "net/log/net_log_event_type.h" #include "net/log/test_net_log.h" @@ -449,6 +450,16 @@ class QuicNetworkTransactionTest } std::unique_ptr<quic::QuicEncryptedPacket> + ConstructClientPriorityFramesPacket( + uint64_t packet_number, + bool should_include_version, + const std::vector<QuicTestPacketMaker::Http2StreamDependency>& + priority_frames) { + return client_maker_->MakeMultiplePriorityFramesPacket( + packet_number, should_include_version, priority_frames); + } + + std::unique_ptr<quic::QuicEncryptedPacket> ConstructClientAckAndPriorityFramesPacket( uint64_t packet_number, bool should_include_version, @@ -462,6 +473,20 @@ class QuicNetworkTransactionTest smallest_received, least_unacked, priority_frames); } + std::unique_ptr<quic::QuicReceivedPacket> ConstructClientAckAndPriorityPacket( + uint64_t packet_number, + bool should_include_version, + uint64_t largest_received, + uint64_t smallest_received, + quic::QuicStreamId id, + quic::QuicStreamId parent_stream_id, + RequestPriority request_priority) { + return client_maker_->MakeAckAndPriorityPacket( + packet_number, should_include_version, largest_received, + smallest_received, id, parent_stream_id, + ConvertRequestPriorityToQuicPriority(request_priority)); + } + // Uses default QuicTestPacketMaker. spdy::SpdyHeaderBlock GetRequestHeaders(const std::string& method, const std::string& scheme, @@ -1723,7 +1748,8 @@ TEST_P(QuicNetworkTransactionTest, AlternativeServicesDifferentHost) { } TEST_P(QuicNetworkTransactionTest, DoNotUseQuicForUnsupportedVersion) { - quic::ParsedQuicVersion unsupported_version = quic::UnsupportedQuicVersion(); + quic::ParsedQuicVersion unsupported_version = + quic::ParsedQuicVersion::Unsupported(); // Add support for another QUIC version besides |version_|. Also find an // unsupported version. for (const quic::ParsedQuicVersion& version : quic::AllSupportedVersions()) { @@ -1737,7 +1763,7 @@ TEST_P(QuicNetworkTransactionTest, DoNotUseQuicForUnsupportedVersion) { break; } ASSERT_EQ(2u, supported_versions_.size()); - ASSERT_NE(quic::UnsupportedQuicVersion(), unsupported_version); + ASSERT_NE(quic::ParsedQuicVersion::Unsupported(), unsupported_version); // Set up alternative service to use QUIC with a version that is not // supported. @@ -1816,10 +1842,10 @@ TEST_P(QuicNetworkTransactionTest, DoNotUseQuicForUnsupportedVersion) { alt_svc_info_vector = session_->http_server_properties()->GetAlternativeServiceInfos( server, NetworkIsolationKey()); - // All PROTOCOL_QUIC_CRYPTO versions are sent in a single Alt-Svc entry, - // therefore they aer accumulated in a single AlternativeServiceInfo, whereas - // each PROTOCOL_TLS1_3 version has its own Alt-Svc entry and - // AlternativeServiceInfo entry. Flatten to compare. + // Versions that support the legacy Google-specific Alt-Svc format are sent in + // a single Alt-Svc entry, therefore they are accumulated in a single + // AlternativeServiceInfo, whereas more recent versions all have their own + // Alt-Svc entry and AlternativeServiceInfo entry. Flatten to compare. quic::ParsedQuicVersionVector alt_svc_negotiated_versions; for (const auto& alt_svc_info : alt_svc_info_vector) { EXPECT_EQ(kProtoQUIC, alt_svc_info.alternative_service().protocol); @@ -2177,7 +2203,8 @@ TEST_P(QuicNetworkTransactionTest, UseAlternativeServiceWithVersionForQuic1) { // Add support for another QUIC version besides |version_| on the client side. // Also find a different version advertised by the server. - quic::ParsedQuicVersion advertised_version_2 = quic::UnsupportedQuicVersion(); + quic::ParsedQuicVersion advertised_version_2 = + quic::ParsedQuicVersion::Unsupported(); for (const quic::ParsedQuicVersion& version : quic::AllSupportedVersions()) { if (version == version_) continue; @@ -2189,7 +2216,7 @@ TEST_P(QuicNetworkTransactionTest, UseAlternativeServiceWithVersionForQuic1) { break; } ASSERT_EQ(2u, supported_versions_.size()); - ASSERT_NE(quic::UnsupportedQuicVersion(), advertised_version_2); + ASSERT_NE(quic::ParsedQuicVersion::Unsupported(), advertised_version_2); std::string QuicAltSvcWithVersionHeader = base::StringPrintf("Alt-Svc: %s=\":443\", %s=\":443\"\r\n\r\n", @@ -2251,23 +2278,15 @@ TEST_P(QuicNetworkTransactionTest, // TestPacketMakers and the response. // Find an alternative commonly supported version other than |version_|. - quic::ParsedQuicVersion common_version_2 = quic::UnsupportedQuicVersion(); - if (version_.handshake_protocol == quic::PROTOCOL_QUIC_CRYPTO) { - for (const quic::ParsedQuicVersion& version : - quic::AllSupportedVersions()) { - if (version == version_) - continue; + quic::ParsedQuicVersion common_version_2 = + quic::ParsedQuicVersion::Unsupported(); + for (const quic::ParsedQuicVersion& version : quic::AllSupportedVersions()) { + if (version != version_) { common_version_2 = version; break; } - } else if (version_.transport_version == quic::QUIC_VERSION_IETF_DRAFT_27) { - common_version_2 = quic::ParsedQuicVersion( - quic::PROTOCOL_TLS1_3, quic::QUIC_VERSION_IETF_DRAFT_25); - } else { - common_version_2 = quic::ParsedQuicVersion( - quic::PROTOCOL_TLS1_3, quic::QUIC_VERSION_IETF_DRAFT_27); } - ASSERT_NE(quic::UnsupportedQuicVersion(), common_version_2); + ASSERT_NE(common_version_2, quic::ParsedQuicVersion::Unsupported()); // Setting up client's preference list: {|version_|, |common_version_2|}. supported_versions_.clear(); @@ -2277,12 +2296,13 @@ TEST_P(QuicNetworkTransactionTest, // Setting up server's Alt-Svc header in the following preference order: // |common_version_2|, |version_|. std::string QuicAltSvcWithVersionHeader; - quic::ParsedQuicVersion picked_version = quic::UnsupportedQuicVersion(); - QuicAltSvcWithVersionHeader = - "Alt-Svc: " + quic::AlpnForVersion(common_version_2) + - "=\":443\"; ma=3600, " + quic::AlpnForVersion(version_) + - "=\":443\"; ma=3600\r\n\r\n"; - picked_version = common_version_2; // Use server's preference. + quic::ParsedQuicVersion picked_version = + quic::ParsedQuicVersion::Unsupported(); + QuicAltSvcWithVersionHeader = + "Alt-Svc: " + quic::AlpnForVersion(common_version_2) + + "=\":443\"; ma=3600, " + quic::AlpnForVersion(version_) + + "=\":443\"; ma=3600\r\n\r\n"; + picked_version = common_version_2; // Use server's preference. MockRead http_reads[] = { MockRead("HTTP/1.1 200 OK\r\n"), @@ -2534,10 +2554,10 @@ TEST_P(QuicNetworkTransactionTest, const AlternativeServiceInfoVector alt_svc_info_vector = session_->http_server_properties()->GetAlternativeServiceInfos( https_server, NetworkIsolationKey()); - // However, all PROTOCOL_QUIC_CRYPTO versions are sent in a single Alt-Svc - // entry, therefore they aer accumulated in a single AlternativeServiceInfo, - // whereas each PROTOCOL_TLS1_3 version has its own Alt-Svc entry and - // AlternativeServiceInfo entry. Flatten to compare. + // Versions that support the legacy Google-specific Alt-Svc format are sent in + // a single Alt-Svc entry, therefore they are accumulated in a single + // AlternativeServiceInfo, whereas more recent versions all have their own + // Alt-Svc entry and AlternativeServiceInfo entry. Flatten to compare. quic::ParsedQuicVersionVector alt_svc_negotiated_versions; for (const auto& alt_svc_info : alt_svc_info_vector) { EXPECT_EQ(kProtoQUIC, alt_svc_info.alternative_service().protocol); @@ -2696,7 +2716,7 @@ TEST_P(QuicNetworkTransactionTest, GoAwayWithConnectionMigrationOnPortsOnly) { // alternate network as well, QUIC is marked as broken and the brokenness will // not expire when default network changes. TEST_P(QuicNetworkTransactionTest, QuicFailsOnBothNetworksWhileTCPSucceeds) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 || + if (version_.UsesTls() || GetQuicReloadableFlag(quic_use_idle_network_detector)) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. // TODO(fayang): Add time driven idle network detection test. @@ -2806,7 +2826,7 @@ TEST_P(QuicNetworkTransactionTest, QuicFailsOnBothNetworksWhileTCPSucceeds) { // alternate network, QUIC is marked as broken. The brokenness will expire when // the default network changes. TEST_P(QuicNetworkTransactionTest, RetryOnAlternateNetworkWhileTCPSucceeds) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 || + if (version_.UsesTls() || GetQuicReloadableFlag(quic_use_idle_network_detector)) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. // TODO(fayang): Add time driven idle network detection test. @@ -2928,7 +2948,7 @@ TEST_P(QuicNetworkTransactionTest, RetryOnAlternateNetworkWhileTCPSucceeds) { // Much like above test, but verifies NetworkIsolationKeys are respected. TEST_P(QuicNetworkTransactionTest, RetryOnAlternateNetworkWhileTCPSucceedsWithNetworkIsolationKey) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 || + if (version_.UsesTls() || GetQuicReloadableFlag(quic_use_idle_network_detector)) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. // TODO(fayang): Add time driven idle network detection test. @@ -3076,7 +3096,7 @@ TEST_P(QuicNetworkTransactionTest, // before handshake is confirmed. If TCP doesn't succeed but QUIC on the // alternative network succeeds, QUIC is not marked as broken. TEST_P(QuicNetworkTransactionTest, RetryOnAlternateNetworkWhileTCPHanging) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 || + if (version_.UsesTls() || GetQuicReloadableFlag(quic_use_idle_network_detector)) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. // TODO(fayang): Add time driven idle network detection test. @@ -3201,7 +3221,7 @@ TEST_P(QuicNetworkTransactionTest, RetryOnAlternateNetworkWhileTCPHanging) { // Verify that if a QUIC connection times out, the QuicHttpStream will // return QUIC_PROTOCOL_ERROR. TEST_P(QuicNetworkTransactionTest, TimeoutAfterHandshakeConfirmed) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. return; } @@ -3327,11 +3347,6 @@ TEST_P(QuicNetworkTransactionTest, TimeoutAfterHandshakeConfirmed) { // Verify that if a QUIC protocol error occurs after the handshake is confirmed // the request fails with QUIC_PROTOCOL_ERROR. TEST_P(QuicNetworkTransactionTest, ProtocolErrorAfterHandshakeConfirmed) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - context_.params()->retry_without_alt_svc_on_quic_errors = false; // The request will initially go out over QUIC. MockQuicData quic_data(version_); @@ -3355,10 +3370,13 @@ TEST_P(QuicNetworkTransactionTest, ProtocolErrorAfterHandshakeConfirmed) { 1, false, GetNthClientInitiatedBidirectionalStreamId(47), quic::QUIC_STREAM_LAST_ERROR)); std::string quic_error_details = "Data for nonexistent stream"; - quic_data.AddWrite(SYNCHRONOUS, - ConstructClientAckAndConnectionClosePacket( - packet_num++, 1, 1, 1, quic::QUIC_INVALID_STREAM_ID, - quic_error_details, quic::IETF_RST_STREAM)); + quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientAckAndConnectionClosePacket( + packet_num++, 1, 1, 1, + version_.HasIetfQuicFrames() ? quic::QUIC_HTTP_STREAM_WRONG_DIRECTION + : quic::QUIC_INVALID_STREAM_ID, + quic_error_details, quic::IETF_RST_STREAM)); quic_data.AddSocketDataToFactory(&socket_factory_); // In order for a new QUIC session to be established via alternate-protocol @@ -3402,7 +3420,7 @@ TEST_P(QuicNetworkTransactionTest, ProtocolErrorAfterHandshakeConfirmed) { // connection times out, then QUIC will be marked as broken and the request // retried over TCP. TEST_P(QuicNetworkTransactionTest, TimeoutAfterHandshakeConfirmedThenBroken2) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. return; } @@ -3550,11 +3568,6 @@ TEST_P(QuicNetworkTransactionTest, TimeoutAfterHandshakeConfirmedThenBroken2) { // retried over TCP and the QUIC will be marked as broken. TEST_P(QuicNetworkTransactionTest, ProtocolErrorAfterHandshakeConfirmedThenBroken) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - context_.params()->idle_connection_timeout = base::TimeDelta::FromSeconds(5); // The request will initially go out over QUIC. @@ -3580,10 +3593,13 @@ TEST_P(QuicNetworkTransactionTest, 1, false, GetNthClientInitiatedBidirectionalStreamId(47), quic::QUIC_STREAM_LAST_ERROR)); std::string quic_error_details = "Data for nonexistent stream"; - quic_data.AddWrite(SYNCHRONOUS, - ConstructClientAckAndConnectionClosePacket( - packet_num++, 1, 1, 1, quic::QUIC_INVALID_STREAM_ID, - quic_error_details, quic::IETF_RST_STREAM)); + quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientAckAndConnectionClosePacket( + packet_num++, 1, 1, 1, + version_.HasIetfQuicFrames() ? quic::QUIC_HTTP_STREAM_WRONG_DIRECTION + : quic::QUIC_INVALID_STREAM_ID, + quic_error_details, quic::IETF_RST_STREAM)); quic_data.AddSocketDataToFactory(&socket_factory_); // After that fails, it will be resent via TCP. @@ -3648,7 +3664,7 @@ TEST_P(QuicNetworkTransactionTest, // Much like above test, but verifies that NetworkIsolationKey is respected. TEST_P(QuicNetworkTransactionTest, ProtocolErrorAfterHandshakeConfirmedThenBrokenWithNetworkIsolationKey) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. return; } @@ -3779,11 +3795,6 @@ TEST_P(QuicNetworkTransactionTest, // request is reset from, then QUIC will be marked as broken and the request // retried over TCP. TEST_P(QuicNetworkTransactionTest, ResetAfterHandshakeConfirmedThenBroken) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - // The request will initially go out over QUIC. MockQuicData quic_data(version_); spdy::SpdyPriority priority = @@ -3810,10 +3821,10 @@ TEST_P(QuicNetworkTransactionTest, ResetAfterHandshakeConfirmedThenBroken) { quic::QUIC_HEADERS_TOO_LARGE)); if (VersionUsesHttp3(version_.transport_version)) { - quic_data.AddWrite(SYNCHRONOUS, - ConstructClientDataPacket( - packet_num++, GetQpackDecoderStreamId(), true, false, - StreamCancellationQpackDecoderInstruction(0))); + quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckAndDataPacket( + packet_num++, true, GetQpackDecoderStreamId(), 1, 1, 1, + false, StreamCancellationQpackDecoderInstruction(0))); } quic_data.AddRead(ASYNC, OK); @@ -4079,10 +4090,12 @@ TEST_P(QuicNetworkTransactionTest, quic::QUIC_HEADERS_TOO_LARGE)); if (VersionUsesHttp3(version_.transport_version)) { - mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientDataPacket( - packet_num++, GetQpackDecoderStreamId(), true, false, - StreamCancellationQpackDecoderInstruction(1))); + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientAckAndDataPacket( + packet_num++, /*include_version=*/true, GetQpackDecoderStreamId(), + 3, 2, 1, + /*fin=*/false, StreamCancellationQpackDecoderInstruction(1))); } mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read @@ -4992,7 +5005,7 @@ TEST_P(QuicNetworkTransactionTest, HungAlternativeService) { } TEST_P(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. return; } @@ -5039,7 +5052,7 @@ TEST_P(QuicNetworkTransactionTest, ZeroRTTWithHttpRace) { } TEST_P(QuicNetworkTransactionTest, ZeroRTTWithNoHttpRace) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. return; } @@ -5117,11 +5130,6 @@ TEST_P(QuicNetworkTransactionTest, ZeroRTTWithProxy) { } TEST_P(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - MockQuicData mock_quic_data(version_); int packet_num = 1; client_maker_->SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -5181,11 +5189,6 @@ TEST_P(QuicNetworkTransactionTest, ZeroRTTWithConfirmationRequired) { } TEST_P(QuicNetworkTransactionTest, ZeroRTTWithTooEarlyResponse) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - uint64_t packet_number = 1; MockQuicData mock_quic_data(version_); client_maker_->SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -5282,11 +5285,6 @@ TEST_P(QuicNetworkTransactionTest, ZeroRTTWithTooEarlyResponse) { } TEST_P(QuicNetworkTransactionTest, ZeroRTTWithMultipleTooEarlyResponse) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - uint64_t packet_number = 1; MockQuicData mock_quic_data(version_); client_maker_->SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -5339,7 +5337,7 @@ TEST_P(QuicNetworkTransactionTest, ZeroRTTWithMultipleTooEarlyResponse) { SYNCHRONOUS, ConstructClientAckAndDataPacket( packet_number++, false, GetQpackDecoderStreamId(), 2, 1, 1, false, - StreamCancellationQpackDecoderInstruction(1))); + StreamCancellationQpackDecoderInstruction(1, false))); mock_quic_data.AddWrite(SYNCHRONOUS, client_maker_->MakeRstPacket( packet_number++, false, @@ -5400,11 +5398,6 @@ TEST_P(QuicNetworkTransactionTest, ZeroRTTWithMultipleTooEarlyResponse) { TEST_P(QuicNetworkTransactionTest, LogGranularQuicErrorCodeOnQuicProtocolErrorLocal) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - context_.params()->retry_without_alt_svc_on_quic_errors = false; MockQuicData mock_quic_data(version_); int packet_num = 1; @@ -5462,11 +5455,6 @@ TEST_P(QuicNetworkTransactionTest, TEST_P(QuicNetworkTransactionTest, LogGranularQuicErrorCodeOnQuicProtocolErrorRemote) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - context_.params()->retry_without_alt_svc_on_quic_errors = false; MockQuicData mock_quic_data(version_); int packet_num = 1; @@ -5489,9 +5477,12 @@ TEST_P(QuicNetworkTransactionTest, quic::QUIC_STREAM_LAST_ERROR)); std::string quic_error_details = "Data for nonexistent stream"; mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientAckAndConnectionClosePacket( - packet_num++, 1, 1, 1, quic::QUIC_INVALID_STREAM_ID, - quic_error_details, quic::IETF_RST_STREAM)); + SYNCHRONOUS, + ConstructClientAckAndConnectionClosePacket( + packet_num++, 1, 1, 1, + version_.HasIetfQuicFrames() ? quic::QUIC_HTTP_STREAM_WRONG_DIRECTION + : quic::QUIC_INVALID_STREAM_ID, + quic_error_details, quic::IETF_RST_STREAM)); mock_quic_data.AddSocketDataToFactory(&socket_factory_); // The non-alternate protocol job needs to hang in order to guarantee that @@ -5524,15 +5515,13 @@ TEST_P(QuicNetworkTransactionTest, EXPECT_EQ(quic::QUIC_NO_ERROR, details.quic_connection_error); trans.PopulateNetErrorDetails(&details); - EXPECT_EQ(quic::QUIC_INVALID_STREAM_ID, details.quic_connection_error); + EXPECT_EQ(version_.HasIetfQuicFrames() + ? quic::QUIC_HTTP_STREAM_WRONG_DIRECTION + : quic::QUIC_INVALID_STREAM_ID, + details.quic_connection_error); } TEST_P(QuicNetworkTransactionTest, RstStreamErrorHandling) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - MockQuicData mock_quic_data(version_); int packet_num = 1; client_maker_->SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); @@ -5611,11 +5600,6 @@ TEST_P(QuicNetworkTransactionTest, RstStreamErrorHandling) { } TEST_P(QuicNetworkTransactionTest, RstStreamBeforeHeaders) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - context_.params()->retry_without_alt_svc_on_quic_errors = false; MockQuicData mock_quic_data(version_); int packet_num = 1; @@ -5637,9 +5621,10 @@ TEST_P(QuicNetworkTransactionTest, RstStreamBeforeHeaders) { if (VersionUsesHttp3(version_.transport_version)) { mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientDataPacket( - packet_num++, GetQpackDecoderStreamId(), true, false, - StreamCancellationQpackDecoderInstruction(0))); + SYNCHRONOUS, + ConstructClientAckAndDataPacket( + packet_num++, false, GetQpackDecoderStreamId(), 1, 1, 1, false, + StreamCancellationQpackDecoderInstruction(0))); } mock_quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // No more read data. @@ -5810,7 +5795,7 @@ TEST_P(QuicNetworkTransactionTest, NoBrokenAlternateProtocolIfTcpFails) { } TEST_P(QuicNetworkTransactionTest, DelayTCPOnStartWithQuicSupportOnSameIP) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. return; } @@ -5881,11 +5866,6 @@ TEST_P(QuicNetworkTransactionTest, DelayTCPOnStartWithQuicSupportOnSameIP) { TEST_P(QuicNetworkTransactionTest, DelayTCPOnStartWithQuicSupportOnDifferentIP) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // QUIC with TLS1.3 handshake doesn't support 0-rtt. - return; - } - // Tests that TCP job is delayed and QUIC job requires confirmation if QUIC // was recently supported on a different IP address on start. @@ -6031,7 +6011,7 @@ TEST_P(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocol) { TEST_P(QuicNetworkTransactionTest, FailedZeroRttBrokenAlternateProtocolWithNetworkIsolationKey) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { // QUIC with TLS1.3 handshake doesn't support 0-rtt. return; } @@ -6656,38 +6636,52 @@ TEST_P(QuicNetworkTransactionTest, QuicServerPush) { 1, GetNthClientInitiatedBidirectionalStreamId(0), GetNthServerInitiatedUnidirectionalStreamId(0), false, GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_)); - if ((client_headers_include_h2_stream_dependency_ && - version_.transport_version >= quic::QUIC_VERSION_43 && - !VersionUsesHttp3(version_.transport_version))) { - mock_quic_data.AddWrite( - SYNCHRONOUS, - ConstructClientPriorityPacket( - client_packet_number++, false, - GetNthServerInitiatedUnidirectionalStreamId(0), - GetNthClientInitiatedBidirectionalStreamId(0), DEFAULT_PRIORITY)); + const bool should_send_priority_packet = + client_headers_include_h2_stream_dependency_ && + !VersionUsesHttp3(version_.transport_version); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientAckAndPriorityPacket( + client_packet_number++, false, + /*largest_received=*/1, /*smallest_received=*/1, + GetNthServerInitiatedUnidirectionalStreamId(0), + GetNthClientInitiatedBidirectionalStreamId(0), DEFAULT_PRIORITY)); } mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false, GetResponseHeaders("200 OK"))); - mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 2, 1, 1)); + if (!should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 2, 1, 1)); + } mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 3, GetNthServerInitiatedUnidirectionalStreamId(0), false, false, GetResponseHeaders("200 OK"))); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 3, 1, 1)); + } std::string header = ConstructDataHeader(6); mock_quic_data.AddRead( ASYNC, ConstructServerDataPacket( 4, GetNthClientInitiatedBidirectionalStreamId(0), false, true, header + "hello!")); - mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 4, 3, 1)); + if (!should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 4, 3, 1)); + } std::string header2 = ConstructDataHeader(10); mock_quic_data.AddRead( ASYNC, ConstructServerDataPacket( 5, GetNthServerInitiatedUnidirectionalStreamId(0), false, true, header2 + "and hello!")); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 5, 3, 1)); + } if (!VersionUsesHttp3(version_.transport_version)) { mock_quic_data.AddWrite(SYNCHRONOUS, ConstructClientAckAndRstPacket( @@ -6757,30 +6751,39 @@ TEST_P(QuicNetworkTransactionTest, CancelServerPushAfterConnectionClose) { 1, GetNthClientInitiatedBidirectionalStreamId(0), GetNthServerInitiatedUnidirectionalStreamId(0), false, GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_)); - if ((client_headers_include_h2_stream_dependency_ && - version_.transport_version >= quic::QUIC_VERSION_43 && - !VersionUsesHttp3(version_.transport_version))) { - mock_quic_data.AddWrite( - SYNCHRONOUS, - ConstructClientPriorityPacket( - client_packet_number++, false, - GetNthServerInitiatedUnidirectionalStreamId(0), - GetNthClientInitiatedBidirectionalStreamId(0), DEFAULT_PRIORITY)); + const bool should_send_priority_packet = + client_headers_include_h2_stream_dependency_ && + !VersionUsesHttp3(version_.transport_version); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientAckAndPriorityPacket( + client_packet_number++, false, + /*largest_received=*/1, /*smallest_received=*/1, + GetNthServerInitiatedUnidirectionalStreamId(0), + GetNthClientInitiatedBidirectionalStreamId(0), DEFAULT_PRIORITY)); } // Response headers for first request. mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false, GetResponseHeaders("200 OK"))); - // Client ACKs the response headers. - mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 2, 1, 1)); + if (!should_send_priority_packet) { + // Client ACKs the response headers. + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 2, 1, 1)); + } // Response body for first request. std::string header = ConstructDataHeader(6); mock_quic_data.AddRead( ASYNC, ConstructServerDataPacket( 3, GetNthClientInitiatedBidirectionalStreamId(0), false, true, header + "hello!")); + if (should_send_priority_packet) { + // Client ACKs the response headers. + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 3, 1, 1)); + } // Write error for the third request. mock_quic_data.AddWrite(SYNCHRONOUS, ERR_FAILED); mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read @@ -7027,15 +7030,17 @@ TEST_P(QuicNetworkTransactionTest, RawHeaderSizeSuccessfullPushHeadersFirst) { initial; } - if ((client_headers_include_h2_stream_dependency_ && - version_.transport_version >= quic::QUIC_VERSION_43 && - !VersionUsesHttp3(version_.transport_version))) { - mock_quic_data.AddWrite( - SYNCHRONOUS, - ConstructClientPriorityPacket( - client_packet_number++, false, - GetNthServerInitiatedUnidirectionalStreamId(0), - GetNthClientInitiatedBidirectionalStreamId(0), DEFAULT_PRIORITY)); + const bool should_send_priority_packet = + client_headers_include_h2_stream_dependency_ && + !VersionUsesHttp3(version_.transport_version); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientAckAndPriorityPacket( + client_packet_number++, false, + /*largest_received=*/1, /*smallest_received=*/1, + GetNthServerInitiatedUnidirectionalStreamId(0), + GetNthClientInitiatedBidirectionalStreamId(0), DEFAULT_PRIORITY)); } const quic::QuicStreamOffset initial_offset = server_maker_.stream_offset( @@ -7053,26 +7058,38 @@ TEST_P(QuicNetworkTransactionTest, RawHeaderSizeSuccessfullPushHeadersFirst) { quic::QuicStreamOffset expected_raw_header_response_size = final_offset - initial_offset; - mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 2, 1, 1)); + if (!should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 2, 1, 1)); + } mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 3, GetNthServerInitiatedUnidirectionalStreamId(0), false, false, GetResponseHeaders("200 OK"))); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 3, 1, 1)); + } std::string header = ConstructDataHeader(20); mock_quic_data.AddRead( ASYNC, ConstructServerDataPacket( 4, GetNthServerInitiatedUnidirectionalStreamId(0), false, true, header + "Pushed Resource Data")); - mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 4, 3, 1)); + if (!should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 4, 3, 1)); + } std::string header2 = ConstructDataHeader(18); mock_quic_data.AddRead( ASYNC, ConstructServerDataPacket( 5, GetNthClientInitiatedBidirectionalStreamId(0), false, true, header2 + "Main Resource Data")); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 5, 3, 1)); + } mock_quic_data.AddRead(ASYNC, ConstructServerConnectionClosePacket(6)); @@ -7709,48 +7726,70 @@ TEST_P(QuicNetworkTransactionTest, QuicServerPushMatchesRequestWithBody) { GetNthServerInitiatedUnidirectionalStreamId(0), false, GetRequestHeaders("GET", "https", "/pushed.jpg"), &server_maker_)); - if ((client_headers_include_h2_stream_dependency_ && - version_.transport_version >= quic::QUIC_VERSION_43 && - !VersionUsesHttp3(version_.transport_version))) { - mock_quic_data.AddWrite( - SYNCHRONOUS, - ConstructClientPriorityPacket( - client_packet_number++, false, - GetNthServerInitiatedUnidirectionalStreamId(0), - GetNthClientInitiatedBidirectionalStreamId(0), DEFAULT_PRIORITY)); + const bool should_send_priority_packet = + client_headers_include_h2_stream_dependency_ && + !VersionUsesHttp3(version_.transport_version); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientAckAndPriorityPacket( + client_packet_number++, false, + /*largest_received=*/1, /*smallest_received=*/1, + GetNthServerInitiatedUnidirectionalStreamId(0), + GetNthClientInitiatedBidirectionalStreamId(0), DEFAULT_PRIORITY)); } mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false, GetResponseHeaders("200 OK"))); - mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 2, 1, 1)); + if (!should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 2, 1, 1)); + } mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 3, GetNthServerInitiatedUnidirectionalStreamId(0), false, false, GetResponseHeaders("200 OK"))); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 3, 1, 1)); + } std::string header = ConstructDataHeader(6); mock_quic_data.AddRead( ASYNC, ConstructServerDataPacket( 4, GetNthClientInitiatedBidirectionalStreamId(0), false, true, header + "hello!")); - mock_quic_data.AddWrite( - SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 4, 3, 1)); + if (!should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 4, 3, 1)); + } std::string header2 = ConstructDataHeader(10); mock_quic_data.AddRead( ASYNC, ConstructServerDataPacket( 5, GetNthServerInitiatedUnidirectionalStreamId(0), false, true, header2 + "and hello!")); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(client_packet_number++, 5, 3, 1)); + } // Because the matching request has a body, we will see the push // stream get cancelled, and the matching request go out on the // wire. - mock_quic_data.AddWrite(SYNCHRONOUS, - ConstructClientAckAndRstPacket( - client_packet_number++, - GetNthServerInitiatedUnidirectionalStreamId(0), - quic::QUIC_STREAM_CANCELLED, 5, 5, 1)); + if (should_send_priority_packet) { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientRstPacket(client_packet_number++, + GetNthServerInitiatedUnidirectionalStreamId(0), + quic::QUIC_STREAM_CANCELLED)); + } else { + mock_quic_data.AddWrite(SYNCHRONOUS, + ConstructClientAckAndRstPacket( + client_packet_number++, + GetNthServerInitiatedUnidirectionalStreamId(0), + quic::QUIC_STREAM_CANCELLED, 5, 5, 1)); + } const char kBody[] = "1"; std::string header3 = ConstructDataHeader(1); if (!version_.HasIetfQuicFrames()) { @@ -7839,30 +7878,28 @@ TEST_P(QuicNetworkTransactionTest, QuicServerPushWithEmptyHostname) { 1, GetNthClientInitiatedBidirectionalStreamId(0), GetNthServerInitiatedUnidirectionalStreamId(0), false, std::move(pushed_request_headers), &server_maker_)); - mock_quic_data.AddWrite( - SYNCHRONOUS, - ConstructClientRstPacket(packet_num++, - GetNthServerInitiatedUnidirectionalStreamId(0), - quic::QUIC_INVALID_PROMISE_URL)); + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientAckAndRstPacket( + packet_num++, GetNthServerInitiatedUnidirectionalStreamId(0), + quic::QUIC_INVALID_PROMISE_URL, 1, 1, 1)); mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 2, GetNthClientInitiatedBidirectionalStreamId(0), false, false, GetResponseHeaders("200 OK"))); - mock_quic_data.AddWrite(SYNCHRONOUS, - ConstructClientAckPacket(packet_num++, 2, 1, 1)); mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 3, GetNthServerInitiatedUnidirectionalStreamId(0), false, false, GetResponseHeaders("200 OK"))); + mock_quic_data.AddWrite(SYNCHRONOUS, + ConstructClientAckPacket(packet_num++, 3, 1, 1)); std::string header = ConstructDataHeader(6); mock_quic_data.AddRead( ASYNC, ConstructServerDataPacket( 4, GetNthClientInitiatedBidirectionalStreamId(0), false, true, header + "hello!")); - mock_quic_data.AddWrite(SYNCHRONOUS, - ConstructClientAckPacket(packet_num++, 4, 3, 1)); mock_quic_data.AddRead(ASYNC, 0); mock_quic_data.AddSocketDataToFactory(&socket_factory_); @@ -9120,8 +9157,7 @@ TEST_P(QuicNetworkTransactionTest, QuicServerPushUpdatesPriority) { // Only run this test if HTTP/2 stream dependency info is sent by client (sent // in HEADERS frames for requests and PRIORITY frames). - if (version_.transport_version < quic::QUIC_VERSION_43 || - !client_headers_include_h2_stream_dependency_) { + if (!client_headers_include_h2_stream_dependency_) { return; } @@ -9199,31 +9235,33 @@ TEST_P(QuicNetworkTransactionTest, QuicServerPushUpdatesPriority) { ConstructServerPushPromisePacket( 5, client_stream_0, push_stream_1, false, GetRequestHeaders("GET", "https", "/pushed_1.jpg"), &server_maker_)); - mock_quic_data.AddWrite(SYNCHRONOUS, ConstructClientPriorityPacket( - packet_num++, false, push_stream_1, - push_stream_0, DEFAULT_PRIORITY)); + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckAndPriorityPacket( + packet_num++, false, + /*largest_received=*/5, /*smallest_received=*/4, + push_stream_1, push_stream_0, DEFAULT_PRIORITY)); // Server sends the response headers for the two push promises. mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 6, push_stream_0, false, false, GetResponseHeaders("200 OK"))); - mock_quic_data.AddWrite(SYNCHRONOUS, - ConstructClientAckPacket(packet_num++, 6, 5, 1)); mock_quic_data.AddRead( ASYNC, ConstructServerResponseHeadersPacket( 7, push_stream_1, false, false, GetResponseHeaders("200 OK"))); + mock_quic_data.AddWrite(SYNCHRONOUS, + ConstructClientAckPacket(packet_num++, 7, 5, 1)); // Request for "pushed_0.jpg" matches |push_stream_0|. |push_stream_0|'s // priority updates to match the request's priority. Client sends PRIORITY // frames to inform server of new HTTP/2 stream dependencies. - mock_quic_data.AddWrite( - SYNCHRONOUS, - ConstructClientAckAndPriorityFramesPacket( - packet_num++, false, 7, 7, 1, - {{push_stream_1, client_stream_2, - ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY)}, - {push_stream_0, client_stream_0, - ConvertRequestPriorityToQuicPriority(HIGHEST)}})); + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientPriorityFramesPacket( + packet_num++, false, + {{push_stream_1, client_stream_2, + ConvertRequestPriorityToQuicPriority(DEFAULT_PRIORITY)}, + {push_stream_0, client_stream_0, + ConvertRequestPriorityToQuicPriority(HIGHEST)}})); // Server sends data for the three requests and the two push promises. std::string header = ConstructDataHeader(8); @@ -9730,5 +9768,260 @@ TEST_P(QuicNetworkTransactionTest, NetworkIsolationTunnel) { EXPECT_TRUE(mock_quic_data[1]->AllWriteDataConsumed()); } +TEST_P(QuicNetworkTransactionTest, AllowHTTP1FalseProhibitsH1) { + MockRead http_reads[] = {MockRead(SYNCHRONOUS, ERR_IO_PENDING), + MockRead(ASYNC, OK)}; + StaticSocketDataProvider http_data(http_reads, base::span<MockWrite>()); + socket_factory_.AddSocketDataProvider(&http_data); + AddCertificate(&ssl_data_); + socket_factory_.AddSSLSocketDataProvider(&ssl_data_); + + CreateSession(); + + request_.method = "POST"; + UploadDataStreamNotAllowHTTP1 upload_data(""); + request_.upload_data_stream = &upload_data; + + HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get()); + TestCompletionCallback callback; + int rv = trans.Start(&request_, callback.callback(), net_log_.bound()); + EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); + EXPECT_THAT(callback.WaitForResult(), IsError(ERR_H2_OR_QUIC_REQUIRED)); +} + +// Confirm mock class UploadDataStreamNotAllowHTTP1 can upload content over +// QUIC. +TEST_P(QuicNetworkTransactionTest, AllowHTTP1MockTest) { + context_.params()->origins_to_force_quic_on.insert( + HostPortPair::FromString("mail.example.org:443")); + + MockQuicData mock_quic_data(version_); + int write_packet_index = 1; + if (VersionUsesHttp3(version_.transport_version)) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructInitialSettingsPacket(write_packet_index++)); + } + const std::string upload_content = "foo"; + if (!version_.HasIetfQuicFrames()) { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientRequestHeadersAndDataFramesPacket( + write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0), + true, true, DEFAULT_PRIORITY, + GetRequestHeaders("POST", "https", "/"), 0, nullptr, + {upload_content})); + } else { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientRequestHeadersAndDataFramesPacket( + write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0), + true, true, DEFAULT_PRIORITY, + GetRequestHeaders("POST", "https", "/"), 0, nullptr, + {ConstructDataHeader(upload_content.length()), upload_content})); + } + mock_quic_data.AddRead( + ASYNC, ConstructServerResponseHeadersPacket( + 1, GetNthClientInitiatedBidirectionalStreamId(0), false, false, + GetResponseHeaders("200 OK"))); + + std::string header2 = ConstructDataHeader(6); + mock_quic_data.AddRead( + ASYNC, ConstructServerDataPacket( + 2, GetNthClientInitiatedBidirectionalStreamId(0), false, true, + header2 + "hello!")); + + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(write_packet_index++, 2, 1, 1)); + + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read + mock_quic_data.AddRead(ASYNC, 0); // EOF + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + + // The non-alternate protocol job needs to hang in order to guarantee that + // the alternate-protocol job will "win". + AddHangingNonAlternateProtocolSocketData(); + + CreateSession(); + request_.method = "POST"; + UploadDataStreamNotAllowHTTP1 upload_data(upload_content); + request_.upload_data_stream = &upload_data; + + SendRequestAndExpectQuicResponse("hello!"); +} + +TEST_P(QuicNetworkTransactionTest, AllowHTTP1UploadPauseAndResume) { + context_.params()->origins_to_force_quic_on.insert( + HostPortPair::FromString("mail.example.org:443")); + + MockQuicData mock_quic_data(version_); + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // Hanging read + int write_packet_index = 1; + mock_quic_data.AddWrite( + ASYNC, client_maker_->MakeDummyCHLOPacket(write_packet_index++)); + client_maker_->SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); + if (VersionUsesHttp3(version_.transport_version)) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructInitialSettingsPacket(write_packet_index++)); + } + const std::string upload_content = "foo"; + if (!version_.HasIetfQuicFrames()) { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientRequestHeadersAndDataFramesPacket( + write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0), + true, true, DEFAULT_PRIORITY, + GetRequestHeaders("POST", "https", "/"), 0, nullptr, + {upload_content})); + } else { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientRequestHeadersAndDataFramesPacket( + write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0), + true, true, DEFAULT_PRIORITY, + GetRequestHeaders("POST", "https", "/"), 0, nullptr, + {ConstructDataHeader(upload_content.length()), upload_content})); + } + mock_quic_data.AddRead( + SYNCHRONOUS, ConstructServerResponseHeadersPacket( + 1, GetNthClientInitiatedBidirectionalStreamId(0), false, + false, GetResponseHeaders("200 OK"))); + std::string header2 = ConstructDataHeader(6); + mock_quic_data.AddRead( + SYNCHRONOUS, ConstructServerDataPacket( + 2, GetNthClientInitiatedBidirectionalStreamId(0), false, + true, header2 + "hello!")); + + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(write_packet_index++, 2, 1, 1)); + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read + mock_quic_data.AddRead(ASYNC, 0); // EOF + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + SequencedSocketData* socket_data = mock_quic_data.GetSequencedSocketData(); + + CreateSession(); + + AddQuicAlternateProtocolMapping( + MockCryptoClientStream::COLD_START_WITH_CHLO_SENT); + + // Set up request. + request_.method = "POST"; + UploadDataStreamNotAllowHTTP1 upload_data(upload_content); + request_.upload_data_stream = &upload_data; + + HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get()); + TestCompletionCallback callback; + int rv = trans.Start(&request_, callback.callback(), net_log_.bound()); + EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); + base::RunLoop().RunUntilIdle(); + // Resume QUIC job + crypto_client_stream_factory_.last_stream() + ->NotifySessionOneRttKeyAvailable(); + socket_data->Resume(); + + base::RunLoop().RunUntilIdle(); + CheckResponseData(&trans, "hello!"); +} + +TEST_P(QuicNetworkTransactionTest, AllowHTTP1UploadFailH1AndResumeQuic) { + // This test confirms failed main job should not bother quic job. + MockRead http_reads[] = { + MockRead("HTTP/1.1 200 OK\r\n"), MockRead(kQuicAlternativeServiceHeader), + MockRead("1.1 Body"), + MockRead(SYNCHRONOUS, ERR_TEST_PEER_CLOSE_AFTER_NEXT_MOCK_READ), + MockRead(ASYNC, OK)}; + StaticSocketDataProvider http_data(http_reads, base::span<MockWrite>()); + socket_factory_.AddSocketDataProvider(&http_data); + AddCertificate(&ssl_data_); + socket_factory_.AddSSLSocketDataProvider(&ssl_data_); + + MockQuicData mock_quic_data(version_); + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // Hanging read + int write_packet_index = 1; + mock_quic_data.AddWrite( + ASYNC, client_maker_->MakeDummyCHLOPacket(write_packet_index++)); + client_maker_->SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE); + if (VersionUsesHttp3(version_.transport_version)) { + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructInitialSettingsPacket(write_packet_index++)); + } + const std::string upload_content = "foo"; + if (!version_.HasIetfQuicFrames()) { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientRequestHeadersAndDataFramesPacket( + write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0), + true, true, DEFAULT_PRIORITY, + GetRequestHeaders("POST", "https", "/"), 0, nullptr, + {upload_content})); + } else { + mock_quic_data.AddWrite( + SYNCHRONOUS, + ConstructClientRequestHeadersAndDataFramesPacket( + write_packet_index++, GetNthClientInitiatedBidirectionalStreamId(0), + true, true, DEFAULT_PRIORITY, + GetRequestHeaders("POST", "https", "/"), 0, nullptr, + {ConstructDataHeader(upload_content.length()), upload_content})); + } + mock_quic_data.AddRead( + SYNCHRONOUS, ConstructServerResponseHeadersPacket( + 1, GetNthClientInitiatedBidirectionalStreamId(0), false, + false, GetResponseHeaders("200 OK"))); + std::string header = ConstructDataHeader(6); + mock_quic_data.AddRead( + SYNCHRONOUS, ConstructServerDataPacket( + 2, GetNthClientInitiatedBidirectionalStreamId(0), false, + true, header + "hello!")); + mock_quic_data.AddWrite( + SYNCHRONOUS, ConstructClientAckPacket(write_packet_index++, 2, 1, 1)); + mock_quic_data.AddRead(ASYNC, ERR_IO_PENDING); // No more data to read + mock_quic_data.AddRead(ASYNC, 0); // EOF + mock_quic_data.AddSocketDataToFactory(&socket_factory_); + SequencedSocketData* socket_data = mock_quic_data.GetSequencedSocketData(); + + // This packet won't be read because AllowHTTP1:false doesn't allow H/1 + // connection. + MockRead http_reads2[] = {MockRead("HTTP/1.1 200 OK\r\n")}; + StaticSocketDataProvider http_data2(http_reads2, base::span<MockWrite>()); + socket_factory_.AddSocketDataProvider(&http_data2); + socket_factory_.AddSSLSocketDataProvider(&ssl_data_); + + CreateSession(); + + // Send the first request via TCP and set up alternative service (QUIC) for + // the origin. + SendRequestAndExpectHttpResponse("1.1 Body"); + + // Settings to resume main H/1 job quickly while pausing quic job. + AddQuicAlternateProtocolMapping( + MockCryptoClientStream::COLD_START_WITH_CHLO_SENT); + ServerNetworkStats stats1; + stats1.srtt = base::TimeDelta::FromMicroseconds(10); + http_server_properties_->SetServerNetworkStats( + url::SchemeHostPort(request_.url), NetworkIsolationKey(), stats1); + + // Set up request. + request_.method = "POST"; + UploadDataStreamNotAllowHTTP1 upload_data(upload_content); + request_.upload_data_stream = &upload_data; + + HttpNetworkTransaction trans(DEFAULT_PRIORITY, session_.get()); + TestCompletionCallback callback; + int rv = trans.Start(&request_, callback.callback(), net_log_.bound()); + EXPECT_THAT(rv, IsError(ERR_IO_PENDING)); + // Confirm TCP job was resumed. + // We can not check its failure because HttpStreamFactory::JobController. + // main_job_net_error is not exposed. + while (socket_factory_.mock_data().next_index() < 3u) + base::RunLoop().RunUntilIdle(); + // Resume QUIC job. + crypto_client_stream_factory_.last_stream() + ->NotifySessionOneRttKeyAvailable(); + socket_data->Resume(); + base::RunLoop().RunUntilIdle(); + CheckResponseData(&trans, "hello!"); +} + +// TODO(yoichio): Add the TCP job reuse case. See crrev.com/c/2174099. + } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_proxy_client_socket.cc b/chromium/net/quic/quic_proxy_client_socket.cc index 2d87902522c..dad704704ac 100644 --- a/chromium/net/quic/quic_proxy_client_socket.cc +++ b/chromium/net/quic/quic_proxy_client_socket.cc @@ -458,25 +458,7 @@ int QuicProxyClientSocket::ProcessResponseHeaders( DLOG(WARNING) << "Invalid headers"; return ERR_QUIC_PROTOCOL_ERROR; } - // Populate |connect_timing_| when response headers are received. This - // should take care of 0-RTT where request is sent before handshake is - // confirmed. - connect_timing_ = session_->GetConnectTiming(); return OK; } -bool QuicProxyClientSocket::GetLoadTimingInfo( - LoadTimingInfo* load_timing_info) const { - bool is_first_stream = stream_->IsFirstStream(); - if (stream_) - is_first_stream = stream_->IsFirstStream(); - if (is_first_stream) { - load_timing_info->socket_reused = false; - load_timing_info->connect_timing = connect_timing_; - } else { - load_timing_info->socket_reused = true; - } - return true; -} - } // namespace net diff --git a/chromium/net/quic/quic_proxy_client_socket.h b/chromium/net/quic/quic_proxy_client_socket.h index 723b6670401..a65d0cc4bbb 100644 --- a/chromium/net/quic/quic_proxy_client_socket.h +++ b/chromium/net/quic/quic_proxy_client_socket.h @@ -10,7 +10,6 @@ #include <string> #include "net/base/completion_once_callback.h" -#include "net/base/load_timing_info.h" #include "net/base/proxy_server.h" #include "net/http/proxy_client_socket.h" #include "net/quic/quic_chromium_client_session.h" @@ -109,8 +108,6 @@ class NET_EXPORT_PRIVATE QuicProxyClientSocket : public ProxyClientSocket { int DoReadReply(); int DoReadReplyComplete(int result); - bool GetLoadTimingInfo(LoadTimingInfo* load_timing_info) const; - State next_state_; // Handle to the QUIC Stream that this sits on top of. @@ -148,9 +145,6 @@ class NET_EXPORT_PRIVATE QuicProxyClientSocket : public ProxyClientSocket { std::string user_agent_; - // Session connect timing info. - LoadTimingInfo::ConnectTiming connect_timing_; - const NetLogWithSource net_log_; // The default weak pointer factory. diff --git a/chromium/net/quic/quic_proxy_client_socket_unittest.cc b/chromium/net/quic/quic_proxy_client_socket_unittest.cc index 045cfeec480..0129108e9fa 100644 --- a/chromium/net/quic/quic_proxy_client_socket_unittest.cc +++ b/chromium/net/quic/quic_proxy_client_socket_unittest.cc @@ -187,9 +187,7 @@ class QuicProxyClientSocketTest : public ::testing::TestWithParam<TestParams>, IPAddress ip(192, 0, 2, 33); peer_addr_ = IPEndPoint(ip, 443); clock_.AdvanceTime(quic::QuicTime::Delta::FromMilliseconds(20)); - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - quic::QuicEnableVersion(version_); - } + quic::QuicEnableVersion(version_); } void SetUp() override {} diff --git a/chromium/net/quic/quic_session_key.cc b/chromium/net/quic/quic_session_key.cc index b2393c547a7..ff1a8f524fc 100644 --- a/chromium/net/quic/quic_session_key.cc +++ b/chromium/net/quic/quic_session_key.cc @@ -30,7 +30,12 @@ QuicSessionKey::QuicSessionKey(const std::string& host, const NetworkIsolationKey& network_isolation_key, bool disable_secure_dns) : QuicSessionKey( - quic::QuicServerId(host, port, privacy_mode == PRIVACY_MODE_ENABLED), + // TODO(crbug.com/1103350): Handle non-boolean privacy modes. + quic::QuicServerId( + host, + port, + privacy_mode == PRIVACY_MODE_ENABLED_WITHOUT_CLIENT_CERTS || + privacy_mode == PRIVACY_MODE_ENABLED), socket_tag, network_isolation_key, disable_secure_dns) {} diff --git a/chromium/net/quic/quic_stream_factory.cc b/chromium/net/quic/quic_stream_factory.cc index 3358bd3d4be..6361607eb00 100644 --- a/chromium/net/quic/quic_stream_factory.cc +++ b/chromium/net/quic/quic_stream_factory.cc @@ -45,6 +45,7 @@ #include "net/quic/quic_chromium_connection_helper.h" #include "net/quic/quic_chromium_packet_reader.h" #include "net/quic/quic_chromium_packet_writer.h" +#include "net/quic/quic_client_session_cache.h" #include "net/quic/quic_context.h" #include "net/quic/quic_crypto_client_stream_factory.h" #include "net/quic/quic_http_stream.h" @@ -118,39 +119,31 @@ base::Value NetLogQuicStreamFactoryJobParams( return std::move(dict); } -// Helper class that is used to log a connection migration event. -class ScopedConnectionMigrationEventLog { - public: - ScopedConnectionMigrationEventLog(NetLog* net_log, const char* trigger) - : net_log_(NetLogWithSource::Make( - net_log, - NetLogSourceType::QUIC_CONNECTION_MIGRATION)) { - net_log_.BeginEventWithStringParams( - NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED, "trigger", - trigger); - } - - ~ScopedConnectionMigrationEventLog() { - net_log_.EndEvent(NetLogEventType::QUIC_CONNECTION_MIGRATION_TRIGGERED); +std::string QuicPlatformNotificationToString( + QuicPlatformNotification notification) { + switch (notification) { + case NETWORK_CONNECTED: + return "OnNetworkConnected"; + case NETWORK_MADE_DEFAULT: + return "OnNetworkMadeDefault"; + case NETWORK_DISCONNECTED: + return "OnNetworkDisconnected"; + case NETWORK_SOON_TO_DISCONNECT: + return "OnNetworkSoonToDisconnect"; + case NETWORK_IP_ADDRESS_CHANGED: + return "OnIPAddressChanged"; + default: + QUIC_NOTREACHED(); + break; } - - const NetLogWithSource& net_log() { return net_log_; } - - private: - const NetLogWithSource net_log_; -}; + return "InvalidNotification"; +} void HistogramCreateSessionFailure(enum CreateSessionFailure error) { UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.CreationError", error, CREATION_ERROR_MAX); } -void LogPlatformNotificationInHistogram( - enum QuicPlatformNotification notification) { - UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.PlatformNotification", - notification, NETWORK_NOTIFICATION_MAX); -} - void LogConnectionIpPooling(bool pooled) { UMA_HISTOGRAM_BOOLEAN("Net.QuicSession.ConnectionIpPooled", pooled); } @@ -225,8 +218,9 @@ class QuicStreamFactory::QuicCryptoClientConfigOwner { public: QuicCryptoClientConfigOwner( std::unique_ptr<quic::ProofVerifier> proof_verifier, + std::unique_ptr<QuicClientSessionCache> session_cache, QuicStreamFactory* quic_stream_factory) - : config_(std::move(proof_verifier)), + : config_(std::move(proof_verifier), std::move(session_cache)), quic_stream_factory_(quic_stream_factory) { DCHECK(quic_stream_factory_); } @@ -390,8 +384,11 @@ class QuicStreamFactory::Job { if (session_) { QuicChromiumClientSession* session = session_; session_ = nullptr; + // Use ERR_FAILED instead of ERR_ABORTED out of paranoia - ERR_ABORTED + // should only be used when the next layer up cancels a request, and has + // special semantic meaning for some consumers when they see it. session->CloseSessionOnErrorLater( - ERR_ABORTED, quic::QUIC_STALE_CONNECTION_CANCELLED, + ERR_FAILED, quic::QUIC_STALE_CONNECTION_CANCELLED, quic::ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); } } @@ -769,7 +766,7 @@ int QuicStreamFactory::Job::DoConnect() { NetLogEventType::QUIC_STREAM_FACTORY_JOB_CONNECT, NetLogEventPhase::BEGIN, "require_confirmation", require_confirmation); - DCHECK_NE(quic_version_.transport_version, quic::QUIC_VERSION_UNSUPPORTED); + DCHECK_NE(quic_version_, quic::ParsedQuicVersion::Unsupported()); int rv = factory_->CreateSession( key_, quic_version_, cert_verify_flags_, require_confirmation, resolve_host_request_->GetAddressResults().value(), @@ -956,7 +953,7 @@ int QuicStreamRequest::Request( NetErrorDetails* net_error_details, CompletionOnceCallback failed_on_default_network_callback, CompletionOnceCallback callback) { - DCHECK_NE(quic_version.transport_version, quic::QUIC_VERSION_UNSUPPORTED); + DCHECK_NE(quic_version, quic::ParsedQuicVersion::Unsupported()); DCHECK(net_error_details); DCHECK(callback_.is_null()); DCHECK(host_resolution_callback_.is_null()); @@ -1106,6 +1103,7 @@ QuicStreamFactory::QuicStreamFactory( need_to_check_persisted_supports_quic_(true), prefer_aes_gcm_recorded_(false), num_push_streams_created_(0), + connectivity_monitor_(default_network_), tick_clock_(nullptr), task_runner_(nullptr), ssl_config_service_(ssl_config_service), @@ -1114,6 +1112,8 @@ QuicStreamFactory::QuicStreamFactory( features::kPartitionHttpServerPropertiesByNetworkIsolationKey)) { DCHECK(transport_security_state_); DCHECK(http_server_properties_); + if (params_.disable_tls_zero_rtt) + SetQuicReloadableFlag(quic_enable_zero_rtt_for_tls, false); InitializeMigrationOptions(); } @@ -1174,7 +1174,7 @@ int QuicStreamFactory::Create(const QuicSessionKey& session_key, QuicStreamRequest* request) { if (clock_skew_detector_.ClockSkewDetected(base::TimeTicks::Now(), base::Time::Now())) { - MarkAllActiveSessionsGoingAway(); + MarkAllActiveSessionsGoingAway(kClockSkewDetected); } DCHECK(HostPortPair(session_key.server_id().host(), session_key.server_id().port()) @@ -1308,7 +1308,6 @@ void QuicStreamFactory::OnSessionGoingAway(QuicChromiumClientSession* session) { void QuicStreamFactory::OnSessionClosed(QuicChromiumClientSession* session) { DCHECK_EQ(0u, session->GetNumActiveStreams()); OnSessionGoingAway(session); - for (const auto& iter : active_jobs_) { if (iter.first == session->quic_session_key()) { iter.second->OnSessionClosed(session); @@ -1478,63 +1477,72 @@ std::unique_ptr<DatagramClientSocket> QuicStreamFactory::CreateSocket( } void QuicStreamFactory::OnIPAddressChanged() { - LogPlatformNotificationInHistogram(NETWORK_IP_ADDRESS_CHANGED); + CollectDataOnPlatformNotification( + NETWORK_IP_ADDRESS_CHANGED, NetworkChangeNotifier::kInvalidNetworkHandle); // Do nothing if connection migration is turned on. if (params_.migrate_sessions_on_network_change_v2) return; + connectivity_monitor_.OnIPAddressChanged(); + set_is_quic_known_to_work_on_current_network(false); if (params_.close_sessions_on_ip_change) { CloseAllSessions(ERR_NETWORK_CHANGED, quic::QUIC_IP_ADDRESS_CHANGED); } else { DCHECK(params_.goaway_sessions_on_ip_change); - MarkAllActiveSessionsGoingAway(); + MarkAllActiveSessionsGoingAway(kIPAddressChanged); } } void QuicStreamFactory::OnNetworkConnected(NetworkHandle network) { - LogPlatformNotificationInHistogram(NETWORK_CONNECTED); - if (!params_.migrate_sessions_on_network_change_v2) - return; - - ScopedConnectionMigrationEventLog scoped_event_log(net_log_, - "OnNetworkConnected"); + CollectDataOnPlatformNotification(NETWORK_CONNECTED, network); + if (params_.migrate_sessions_on_network_change_v2) { + NetLogWithSource net_log = NetLogWithSource::Make( + net_log_, NetLogSourceType::QUIC_CONNECTION_MIGRATION); + net_log.AddEventWithStringParams( + NetLogEventType::QUIC_CONNECTION_MIGRATION_PLATFORM_NOTIFICATION, + "signal", "OnNetworkConnected"); + } + // Broadcast network connected to all sessions. + // If migration is not turned on, session will not migrate but collect data. auto it = all_sessions_.begin(); // Sessions may be deleted while iterating through the map. while (it != all_sessions_.end()) { QuicChromiumClientSession* session = it->first; ++it; - session->OnNetworkConnected(network, scoped_event_log.net_log()); + session->OnNetworkConnected(network); } } void QuicStreamFactory::OnNetworkDisconnected(NetworkHandle network) { - LogPlatformNotificationInHistogram(NETWORK_DISCONNECTED); - if (!params_.migrate_sessions_on_network_change_v2) - return; - - ScopedConnectionMigrationEventLog scoped_event_log(net_log_, - "OnNetworkDisconnected"); + CollectDataOnPlatformNotification(NETWORK_DISCONNECTED, network); + if (params_.migrate_sessions_on_network_change_v2) { + NetLogWithSource net_log = NetLogWithSource::Make( + net_log_, NetLogSourceType::QUIC_CONNECTION_MIGRATION); + net_log.AddEventWithStringParams( + NetLogEventType::QUIC_CONNECTION_MIGRATION_PLATFORM_NOTIFICATION, + "signal", "OnNetworkDisconnected"); + } + // Broadcast network disconnected to all sessions. + // If migration is not turned on, session will not migrate but collect data. auto it = all_sessions_.begin(); // Sessions may be deleted while iterating through the map. while (it != all_sessions_.end()) { QuicChromiumClientSession* session = it->first; ++it; - session->OnNetworkDisconnectedV2(/*disconnected_network*/ network, - scoped_event_log.net_log()); + session->OnNetworkDisconnectedV2(/*disconnected_network*/ network); } } // This method is expected to only be called when migrating from Cellular to // WiFi on Android, and should always be preceded by OnNetworkMadeDefault(). void QuicStreamFactory::OnNetworkSoonToDisconnect(NetworkHandle network) { - LogPlatformNotificationInHistogram(NETWORK_SOON_TO_DISCONNECT); + CollectDataOnPlatformNotification(NETWORK_SOON_TO_DISCONNECT, network); } void QuicStreamFactory::OnNetworkMadeDefault(NetworkHandle network) { - LogPlatformNotificationInHistogram(NETWORK_MADE_DEFAULT); - if (!params_.migrate_sessions_on_network_change_v2) - return; + CollectDataOnPlatformNotification(NETWORK_MADE_DEFAULT, network); + connectivity_monitor_.OnDefaultNetworkUpdated(network); // Clear alternative services that were marked as broken until default network // changes. @@ -1546,17 +1554,24 @@ void QuicStreamFactory::OnNetworkMadeDefault(NetworkHandle network) { DCHECK_NE(NetworkChangeNotifier::kInvalidNetworkHandle, network); default_network_ = network; - ScopedConnectionMigrationEventLog scoped_event_log(net_log_, - "OnNetworkMadeDefault"); + + if (params_.migrate_sessions_on_network_change_v2) { + NetLogWithSource net_log = NetLogWithSource::Make( + net_log_, NetLogSourceType::QUIC_CONNECTION_MIGRATION); + net_log.AddEventWithStringParams( + NetLogEventType::QUIC_CONNECTION_MIGRATION_PLATFORM_NOTIFICATION, + "signal", "OnNetworkMadeDefault"); + } auto it = all_sessions_.begin(); // Sessions may be deleted while iterating through the map. while (it != all_sessions_.end()) { QuicChromiumClientSession* session = it->first; ++it; - session->OnNetworkMadeDefault(network, scoped_event_log.net_log()); + session->OnNetworkMadeDefault(network); } - set_is_quic_known_to_work_on_current_network(false); + if (params_.migrate_sessions_on_network_change_v2) + set_is_quic_known_to_work_on_current_network(false); } void QuicStreamFactory::OnCertDBChanged() { @@ -1569,7 +1584,7 @@ void QuicStreamFactory::OnCertDBChanged() { // Since the OnCertDBChanged method doesn't tell us what // kind of change it is, we have to flush the socket // pools to be safe. - MarkAllActiveSessionsGoingAway(); + MarkAllActiveSessionsGoingAway(kCertDBChanged); } void QuicStreamFactory::set_is_quic_known_to_work_on_current_network( @@ -1742,6 +1757,7 @@ int QuicStreamFactory::CreateSession( // creation, update |default_network_| when the first socket is bound // to the default network. default_network_ = *network; + connectivity_monitor_.SetInitialDefaultNetwork(default_network_); } else { UMA_HISTOGRAM_BOOLEAN("Net.QuicStreamFactory.DefaultNetworkMatch", default_network_ == *network); @@ -1782,7 +1798,9 @@ int QuicStreamFactory::CreateSession( quic::QuicConfig config = config_; ConfigureInitialRttEstimate( server_id, key.session_key().network_isolation_key(), &config); - if (quic_version.transport_version <= quic::QUIC_VERSION_43 && + // QUIC versions that use the IETF invariant header all have NSTP + // enabled by default, so we only need to add it for those that don't. + if (!quic_version.HasIetfInvariantHeader() && !config.HasClientSentConnectionOption(quic::kNSTP, quic::Perspective::IS_CLIENT)) { // Enable the no stop waiting frames connection option by default. @@ -1827,6 +1845,7 @@ int QuicStreamFactory::CreateSession( all_sessions_[*session] = key; // owning pointer writer->set_delegate(*session); + (*session)->AddConnectivityObserver(&connectivity_monitor_); (*session)->Initialize(); bool closed_during_initialize = !base::Contains(all_sessions_, *session) || @@ -1855,9 +1874,14 @@ void QuicStreamFactory::ActivateSession(const QuicSessionAliasKey& key, session_peer_ip_[session] = peer_address; } -void QuicStreamFactory::MarkAllActiveSessionsGoingAway() { +void QuicStreamFactory::MarkAllActiveSessionsGoingAway( + AllActiveSessionsGoingAwayReason reason) { while (!active_sessions_.empty()) { QuicChromiumClientSession* session = active_sessions_.begin()->second; + // If IP address change is detected, disable session's connectivity + // monitoring by remove the Delegate. + if (reason == kIPAddressChanged) + connectivity_monitor_.OnSessionGoingAwayOnIPAddressChange(session); OnSessionGoingAway(session); } } @@ -2014,8 +2038,6 @@ void QuicStreamFactory::InitializeCachedStateInCryptoConfig( quic::QuicConnectionId* connection_id) { quic::QuicCryptoClientConfig::CachedState* cached = crypto_config_handle.GetConfig()->LookupOrCreate(server_id); - if (cached->has_server_designated_connection_id()) - *connection_id = cached->GetNextServerDesignatedConnectionId(); if (!cached->IsEmpty()) { return; @@ -2133,8 +2155,9 @@ QuicStreamFactory::CreateCryptoConfigHandle( std::make_unique<ProofVerifierChromium>( cert_verifier_, ct_policy_enforcer_, transport_security_state_, cert_transparency_verifier_, - HostsFromOrigins(params_.origins_to_force_quic_on)), - this); + HostsFromOrigins(params_.origins_to_force_quic_on), + actual_network_isolation_key), + std::make_unique<QuicClientSessionCache>(), this); quic::QuicCryptoClientConfig* crypto_config = crypto_config_owner->config(); crypto_config->set_user_agent_id(params_.user_agent_id); @@ -2165,6 +2188,38 @@ void QuicStreamFactory::OnAllCryptoClientRefReleased( active_crypto_config_map_.erase(map_iterator); } +void QuicStreamFactory::CollectDataOnPlatformNotification( + enum QuicPlatformNotification notification, + NetworkChangeNotifier::NetworkHandle affected_network) const { + UMA_HISTOGRAM_ENUMERATION("Net.QuicSession.PlatformNotification", + notification, NETWORK_NOTIFICATION_MAX); + if (notification == NETWORK_SOON_TO_DISCONNECT || + notification == NETWORK_DISCONNECTED) { + // If the disconnected network is not the default network, ignore + // stats collections. + if (affected_network != default_network_) + return; + } + + // Skip degrading session collection if there are less than two sessions. + if (all_sessions_.size() < 2) + return; + + size_t num_degrading_sessions = + connectivity_monitor_.GetNumDegradingSessions(); + const std::string raw_histogram_name = + "Net.QuicStreamFactory.NumDegradingSessions." + + QuicPlatformNotificationToString(notification); + base::UmaHistogramExactLinear(raw_histogram_name, num_degrading_sessions, + 101); + + int percentage = num_degrading_sessions * 100 / all_sessions_.size(); + const std::string percentage_histogram_name = + "Net.QuicStreamFactory.PercentageDegradingSessions." + + QuicPlatformNotificationToString(notification); + base::UmaHistogramExactLinear(percentage_histogram_name, percentage, 101); +} + std::unique_ptr<QuicCryptoClientConfigHandle> QuicStreamFactory::GetCryptoConfigForTesting( const NetworkIsolationKey& network_isolation_key) { diff --git a/chromium/net/quic/quic_stream_factory.h b/chromium/net/quic/quic_stream_factory.h index 6e33a290557..9dd5bb2ca03 100644 --- a/chromium/net/quic/quic_stream_factory.h +++ b/chromium/net/quic/quic_stream_factory.h @@ -17,7 +17,6 @@ #include "base/containers/mru_cache.h" #include "base/gtest_prod_util.h" -#include "base/logging.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/time/tick_clock.h" @@ -35,6 +34,7 @@ #include "net/quic/network_connection.h" #include "net/quic/quic_chromium_client_session.h" #include "net/quic/quic_clock_skew_detector.h" +#include "net/quic/quic_connectivity_monitor.h" #include "net/quic/quic_context.h" #include "net/quic/quic_crypto_client_config_handle.h" #include "net/quic/quic_session_key.h" @@ -101,6 +101,12 @@ enum QuicPlatformNotification { NETWORK_NOTIFICATION_MAX }; +enum AllActiveSessionsGoingAwayReason { + kClockSkewDetected, + kIPAddressChanged, + kCertDBChanged +}; + // Encapsulates a pending request for a QuicChromiumClientSession. // If the request is still pending when it is destroyed, it will // cancel the request with the factory. @@ -396,7 +402,9 @@ class NET_EXPORT_PRIVATE QuicStreamFactory NetworkChangeNotifier::NetworkHandle* network); void ActivateSession(const QuicSessionAliasKey& key, QuicChromiumClientSession* session); - void MarkAllActiveSessionsGoingAway(); + // Go away all active sessions. May disable session's connectivity monitoring + // based on the |reason|. + void MarkAllActiveSessionsGoingAway(AllActiveSessionsGoingAwayReason reason); void ConfigureInitialRttEstimate( const quic::QuicServerId& server_id, @@ -457,6 +465,14 @@ class NET_EXPORT_PRIVATE QuicStreamFactory void OnAllCryptoClientRefReleased( QuicCryptoClientConfigMap::iterator& map_iterator); + // Called when a network change happens. + // Collect platform notification metrics, and if the change affects the + // original default network interface, collect connectivity degradation + // metrics from |connectivity_monitor_| and add to histograms. + void CollectDataOnPlatformNotification( + enum QuicPlatformNotification notification, + NetworkChangeNotifier::NetworkHandle affected_network) const; + std::unique_ptr<QuicCryptoClientConfigHandle> GetCryptoConfigForTesting( const NetworkIsolationKey& network_isolation_key); @@ -556,6 +572,8 @@ class NET_EXPORT_PRIVATE QuicStreamFactory int num_push_streams_created_; + QuicConnectivityMonitor connectivity_monitor_; + quic::QuicClientPushPromiseIndex push_promise_index_; const base::TickClock* tick_clock_; diff --git a/chromium/net/quic/quic_stream_factory_peer.cc b/chromium/net/quic/quic_stream_factory_peer.cc index ac673b84b67..63b316a5e30 100644 --- a/chromium/net/quic/quic_stream_factory_peer.cc +++ b/chromium/net/quic/quic_stream_factory_peer.cc @@ -185,6 +185,11 @@ int QuicStreamFactoryPeer::GetNumPushStreamsCreated( return factory->num_push_streams_created_; } +size_t QuicStreamFactoryPeer::GetNumDegradingSessions( + QuicStreamFactory* factory) { + return factory->connectivity_monitor_.GetNumDegradingSessions(); +} + void QuicStreamFactoryPeer::SetAlarmFactory( QuicStreamFactory* factory, std::unique_ptr<quic::QuicAlarmFactory> alarm_factory) { diff --git a/chromium/net/quic/quic_stream_factory_peer.h b/chromium/net/quic/quic_stream_factory_peer.h index 507aedda772..7e2a59922fd 100644 --- a/chromium/net/quic/quic_stream_factory_peer.h +++ b/chromium/net/quic/quic_stream_factory_peer.h @@ -103,6 +103,8 @@ class QuicStreamFactoryPeer { static int GetNumPushStreamsCreated(QuicStreamFactory* factory); + static size_t GetNumDegradingSessions(QuicStreamFactory* factory); + static void SetAlarmFactory( QuicStreamFactory* factory, std::unique_ptr<quic::QuicAlarmFactory> alarm_factory); diff --git a/chromium/net/quic/quic_stream_factory_test.cc b/chromium/net/quic/quic_stream_factory_test.cc index a4dd7ce26d3..031cfe38238 100644 --- a/chromium/net/quic/quic_stream_factory_test.cc +++ b/chromium/net/quic/quic_stream_factory_test.cc @@ -42,6 +42,7 @@ #include "net/quic/mock_quic_data.h" #include "net/quic/properties_based_quic_server_info.h" #include "net/quic/quic_chromium_alarm_factory.h" +#include "net/quic/quic_chromium_client_session_peer.h" #include "net/quic/quic_http_stream.h" #include "net/quic/quic_http_utils.h" #include "net/quic/quic_server_info.h" @@ -1002,11 +1003,6 @@ TEST_P(QuicStreamFactoryTest, Create) { } TEST_P(QuicStreamFactoryTest, CreateZeroRtt) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } Initialize(); factory_->set_is_quic_known_to_work_on_current_network(true); ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); @@ -1097,11 +1093,6 @@ TEST_P(QuicStreamFactoryTest, FactoryDestroyedWhenJobPending) { } TEST_P(QuicStreamFactoryTest, RequireConfirmation) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } crypto_client_stream_factory_.set_handshake_mode( MockCryptoClientStream::ZERO_RTT); host_resolver_->set_synchronous_mode(true); @@ -1144,11 +1135,6 @@ TEST_P(QuicStreamFactoryTest, RequireConfirmation) { } TEST_P(QuicStreamFactoryTest, DontRequireConfirmationFromSameIP) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } crypto_client_stream_factory_.set_handshake_mode( MockCryptoClientStream::ZERO_RTT); host_resolver_->set_synchronous_mode(true); @@ -3152,24 +3138,24 @@ void QuicStreamFactoryTestBase::TestOnNetworkMadeDefaultNonMigratableStream( quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read. // A RESET will be sent to the peer to cancel the non-migratable stream. if (VersionUsesHttp3(version_.transport_version)) { - quic_data1.AddWrite(SYNCHRONOUS, - client_maker_.MakeDataAndRstPacket( - packet_num++, true, GetQpackDecoderStreamId(), - StreamCancellationQpackDecoderInstruction(0), - GetNthClientInitiatedBidirectionalStreamId(0), - quic::QUIC_STREAM_CANCELLED)); + quic_data1.AddWrite(SYNCHRONOUS, + client_maker_.MakeDataRstAndAckPacket( + packet_num++, true, GetQpackDecoderStreamId(), + StreamCancellationQpackDecoderInstruction(0), + GetNthClientInitiatedBidirectionalStreamId(0), + quic::QUIC_STREAM_CANCELLED, 1, 1)); } else { - quic_data1.AddWrite(SYNCHRONOUS, - client_maker_.MakeRstPacket( - packet_num++, false, - GetNthClientInitiatedBidirectionalStreamId(0), - quic::QUIC_STREAM_CANCELLED)); + quic_data1.AddWrite(SYNCHRONOUS, + client_maker_.MakeAckAndRstPacket( + packet_num++, false, + GetNthClientInitiatedBidirectionalStreamId(0), + quic::QUIC_STREAM_CANCELLED, 1, 1, 1)); } // Ping packet to send after migration is completed. - quic_data1.AddWrite(SYNCHRONOUS, client_maker_.MakeAckAndPingPacket( - packet_num++, false, 1, 1, 1)); + quic_data1.AddWrite(SYNCHRONOUS, + client_maker_.MakePingPacket(packet_num++, false)); } else { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { if (VersionUsesHttp3(version_.transport_version)) { socket_data.AddWrite( SYNCHRONOUS, @@ -3586,8 +3572,7 @@ void QuicStreamFactoryTestBase::TestOnNetworkMadeDefaultNoOpenStreams( QuicChromiumClientSession* session = GetActiveSession(host_port_pair_); EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); EXPECT_TRUE(HasActiveSession(host_port_pair_)); - EXPECT_EQ(0u, session->GetNumActiveStreams()); - EXPECT_EQ(0u, session->GetNumDrainingStreams()); + EXPECT_FALSE(session->HasActiveRequestStreams()); // Trigger connection migration. scoped_mock_network_change_notifier_->mock_network_change_notifier() @@ -4079,9 +4064,11 @@ TEST_P(QuicStreamFactoryTest, MigrateToProbingSocket) { EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, callback_.callback())); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Cause the connection to report path degrading to the session. // Session will start to probe the alternate network. - session->connection()->OnPathDegradingTimeout(); + session->connection()->OnPathDegradingDetected(); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Next connectivity probe is scheduled to be sent in 2 * // kDefaultRTTMilliSecs. @@ -4111,6 +4098,8 @@ TEST_P(QuicStreamFactoryTest, MigrateToProbingSocket) { EXPECT_EQ(base::TimeDelta(), next_task_delay); task_runner->FastForwardBy(next_task_delay); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Response headers are received over the new network. EXPECT_THAT(callback_.WaitForResult(), IsOk()); EXPECT_EQ(200, response.headers->response_code()); @@ -4136,6 +4125,8 @@ TEST_P(QuicStreamFactoryTest, MigrateToProbingSocket) { scoped_mock_network_change_notifier_->mock_network_change_notifier() ->NotifyNetworkMadeDefault(kNewNetworkForTests); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + task_runner->FastForwardBy(next_task_delay); EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); @@ -4271,9 +4262,11 @@ void QuicStreamFactoryTestBase::TestMigrationOnPathDegrading( if (async_write_before) session->SendPing(); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Cause the connection to report path degrading to the session. // Session will start to probe the alternate network. - session->connection()->OnPathDegradingTimeout(); + session->connection()->OnPathDegradingDetected(); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Next connectivity probe is scheduled to be sent in 2 * // kDefaultRTTMilliSecs. @@ -4303,10 +4296,14 @@ void QuicStreamFactoryTestBase::TestMigrationOnPathDegrading( EXPECT_EQ(base::TimeDelta(), next_task_delay); task_runner->FastForwardBy(next_task_delay); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Response headers are received over the new network. EXPECT_THAT(callback_.WaitForResult(), IsOk()); EXPECT_EQ(200, response.headers->response_code()); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Now there are two pending tasks, the nearest one was to send connectivity // probe and has been cancelled due to successful migration. EXPECT_EQ(2u, task_runner->GetPendingTaskCount()); @@ -4328,6 +4325,8 @@ void QuicStreamFactoryTestBase::TestMigrationOnPathDegrading( scoped_mock_network_change_notifier_->mock_network_change_notifier() ->NotifyNetworkMadeDefault(kNewNetworkForTests); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + task_runner->FastForwardBy(next_task_delay); EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); @@ -4352,6 +4351,143 @@ TEST_P(QuicStreamFactoryTest, MigratePortOnPathDegrading_WithoutNetworkHandle) { TestSimplePortMigrationOnPathDegrading(); } +// Verifies that if a stateless reset is received on port migration probing +// path, the session is still alive and probing is considered failed. +TEST_P(QuicStreamFactoryTest, PortMigrationProbingReceivedStatelessReset) { + if (!VersionHasIetfInvariantHeader(version_.transport_version)) { + // STATELESS_RESET is only supported after QUIC V46. + return; + } + quic_params_->allow_port_migration = true; + socket_factory_.reset(new TestMigrationSocketFactory); + Initialize(); + ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); + crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); + crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); + + // Using a testing task runner so that we can control time. + auto task_runner = base::MakeRefCounted<base::TestMockTimeTaskRunner>(); + QuicStreamFactoryPeer::SetTaskRunner(factory_.get(), task_runner.get()); + + int packet_number = 1; + MockQuicData quic_data1(version_); + quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // hanging read + if (VersionUsesHttp3(version_.transport_version)) { + quic_data1.AddWrite(SYNCHRONOUS, + ConstructInitialSettingsPacket(packet_number++)); + } + quic_data1.AddWrite( + SYNCHRONOUS, + ConstructGetRequestPacket(packet_number++, + GetNthClientInitiatedBidirectionalStreamId(0), + true, true)); + if (VersionUsesHttp3(version_.transport_version)) { + quic_data1.AddWrite( + SYNCHRONOUS, client_maker_.MakeDataPacket( + packet_number + 1, GetQpackDecoderStreamId(), true, + false, StreamCancellationQpackDecoderInstruction(0))); + quic_data1.AddWrite(SYNCHRONOUS, + client_maker_.MakeRstPacket( + packet_number + 2, false, + GetNthClientInitiatedBidirectionalStreamId(0), + quic::QUIC_STREAM_CANCELLED)); + } else { + quic_data1.AddWrite(SYNCHRONOUS, + client_maker_.MakeRstPacket( + packet_number + 1, false, + GetNthClientInitiatedBidirectionalStreamId(0), + quic::QUIC_STREAM_CANCELLED)); + } + quic_data1.AddSocketDataToFactory(socket_factory_.get()); + + // Set up the second socket data provider that is used after migration. + // The response to the earlier request is read on the new socket. + MockQuicData quic_data2(version_); + // Connectivity probe to be sent on the new path. + quic_data2.AddWrite(SYNCHRONOUS, client_maker_.MakeConnectivityProbingPacket( + packet_number, true)); + quic_data2.AddRead(ASYNC, ERR_IO_PENDING); // Pause + // Stateless reset to receive from the server. + quic_data2.AddRead(ASYNC, server_maker_.MakeStatelessResetPacket()); + quic_data2.AddSocketDataToFactory(socket_factory_.get()); + + // Create request and QuicHttpStream. + QuicStreamRequest request(factory_.get()); + EXPECT_EQ( + ERR_IO_PENDING, + request.Request( + host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, + SocketTag(), NetworkIsolationKey(), false /* disable_secure_dns */, + /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_, + failed_on_default_network_callback_, callback_.callback())); + EXPECT_THAT(callback_.WaitForResult(), IsOk()); + std::unique_ptr<HttpStream> stream = CreateStream(&request); + EXPECT_TRUE(stream.get()); + + // Cause QUIC stream to be created. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = url_; + request_info.traffic_annotation = + MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY, + net_log_, CompletionOnceCallback())); + + // Ensure that session is alive and active. + QuicChromiumClientSession* session = GetActiveSession(host_port_pair_); + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); + EXPECT_TRUE(HasActiveSession(host_port_pair_)); + + // Send GET request on stream. + HttpResponseInfo response; + HttpRequestHeaders request_headers; + EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, + callback_.callback())); + + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + + // Cause the connection to report path degrading to the session. + // Session will start to probe a different port. + session->connection()->OnPathDegradingDetected(); + + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + + // Next connectivity probe is scheduled to be sent in 2 * + // kDefaultRTTMilliSecs. + EXPECT_EQ(1u, task_runner->GetPendingTaskCount()); + base::TimeDelta next_task_delay = task_runner->NextPendingTaskDelay(); + EXPECT_EQ(base::TimeDelta::FromMilliseconds(2 * kDefaultRTTMilliSecs), + next_task_delay); + + // The connection should still be alive, and not marked as going away. + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); + EXPECT_TRUE(HasActiveSession(host_port_pair_)); + EXPECT_EQ(1u, session->GetNumActiveStreams()); + EXPECT_EQ(ERR_IO_PENDING, stream->ReadResponseHeaders(callback_.callback())); + + // Resume quic data and a STATELESS_RESET is read from the probing path. + quic_data2.Resume(); + + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + + // Verify that the session is still active, and the request stream is active. + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); + EXPECT_TRUE(HasActiveSession(host_port_pair_)); + EXPECT_EQ(1u, session->GetNumActiveStreams()); + EXPECT_FALSE( + QuicChromiumClientSessionPeer::DoesSessionAllowPortMigration(session)); + + // The task to resend connectivity probe is cancelled due to probe failure. + task_runner->FastForwardBy(next_task_delay); + EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); + + stream.reset(); + EXPECT_TRUE(quic_data1.AllReadDataConsumed()); + EXPECT_TRUE(quic_data1.AllWriteDataConsumed()); + EXPECT_TRUE(quic_data2.AllReadDataConsumed()); + EXPECT_TRUE(quic_data2.AllWriteDataConsumed()); +} + // Verifies that port migration can be attempted on the default network and // succeed when path degrading is detected. NetworkHandle is supported. TEST_P(QuicStreamFactoryTest, MigratePortOnPathDegrading_WithNetworkHandle) { @@ -4488,9 +4624,13 @@ void QuicStreamFactoryTestBase::TestSimplePortMigrationOnPathDegrading() { EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, callback_.callback())); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Cause the connection to report path degrading to the session. // Session will start to probe a different port. - session->connection()->OnPathDegradingTimeout(); + session->connection()->OnPathDegradingDetected(); + + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Next connectivity probe is scheduled to be sent in 2 * // kDefaultRTTMilliSecs. @@ -4512,6 +4652,9 @@ void QuicStreamFactoryTestBase::TestSimplePortMigrationOnPathDegrading() { EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); EXPECT_TRUE(HasActiveSession(host_port_pair_)); EXPECT_EQ(1u, session->GetNumActiveStreams()); + // Successful port migration causes the path no longer degrading on the same + // network. + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // There should be pending tasks, the nearest one will complete // migration to the new port. @@ -4524,6 +4667,8 @@ void QuicStreamFactoryTestBase::TestSimplePortMigrationOnPathDegrading() { EXPECT_THAT(callback_.WaitForResult(), IsOk()); EXPECT_EQ(200, response.headers->response_code()); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Now there is one pending task to send connectivity probe and has been // cancelled due to successful migration. EXPECT_EQ(1u, task_runner->GetPendingTaskCount()); @@ -4660,9 +4805,15 @@ TEST_P(QuicStreamFactoryTest, MultiplePortMigrationsExceedsMaxLimit) { quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // EOF. quic_data2.AddSocketDataToFactory(socket_factory_.get()); + EXPECT_EQ(0u, + QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Cause the connection to report path degrading to the session. // Session will start to probe a different port. - session->connection()->OnPathDegradingTimeout(); + session->connection()->OnPathDegradingDetected(); + + EXPECT_EQ(1u, + QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Next connectivity probe is scheduled to be sent in 2 * // kDefaultRTTMilliSecs. @@ -4784,9 +4935,15 @@ TEST_P(QuicStreamFactoryTest, GoawayOnPathDegrading) { EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, callback_.callback())); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Trigger the connection to report path degrading to the session. // Session will mark itself GOAWAY. - session->connection()->OnPathDegradingTimeout(); + session->connection()->OnPathDegradingDetected(); + + /// Path degrading detection on go_away_on_path_degrading detection is + // disabled. + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // The connection should still be alive, but marked as going away. EXPECT_FALSE(HasActiveSession(host_port_pair_)); @@ -4920,9 +5077,11 @@ TEST_P(QuicStreamFactoryTest, DoNotMigrateToBadSocketOnPathDegrading) { EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, callback_.callback())); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Cause the connection to report path degrading to the session. // Session will start to probe the alternate network. - session->connection()->OnPathDegradingTimeout(); + session->connection()->OnPathDegradingDetected(); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // The connection should still be alive, and not marked as going away. EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); @@ -5063,10 +5222,12 @@ void QuicStreamFactoryTestBase::TestMigrateSessionWithDrainingStream( base::RunLoop().RunUntilIdle(); EXPECT_EQ(0u, session->GetNumActiveStreams()); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Cause the connection to report path degrading to the session. // Session should still start to probe the alternate network. - session->connection()->OnPathDegradingTimeout(); + session->connection()->OnPathDegradingDetected(); EXPECT_TRUE(HasActiveSession(host_port_pair_)); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Next connectivity probe is scheduled to be sent in 2 * // kDefaultRTTMilliSecs. @@ -5085,7 +5246,7 @@ void QuicStreamFactoryTestBase::TestMigrateSessionWithDrainingStream( EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); EXPECT_TRUE(HasActiveSession(host_port_pair_)); EXPECT_EQ(0u, session->GetNumActiveStreams()); - EXPECT_EQ(1u, session->GetNumDrainingStreams()); + EXPECT_TRUE(session->HasActiveRequestStreams()); // There should be three pending tasks, the nearest one will complete // migration to the new network. @@ -5233,12 +5394,18 @@ TEST_P(QuicStreamFactoryTest, MigrateOnNewNetworkConnectAfterPathDegrading) { EXPECT_EQ(OK, stream->SendRequest(request_headers, &response, callback_.callback())); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Cause the connection to report path degrading to the session. // Due to lack of alternate network, session will not mgirate connection. EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); - session->connection()->OnPathDegradingTimeout(); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + session->connection()->OnPathDegradingDetected(); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Deliver a signal that a alternate network is connected now, this should // cause the connection to start early migration on path degrading. scoped_mock_network_change_notifier_->mock_network_change_notifier() @@ -5275,6 +5442,10 @@ TEST_P(QuicStreamFactoryTest, MigrateOnNewNetworkConnectAfterPathDegrading) { EXPECT_EQ(base::TimeDelta(), next_task_delay); task_runner->FastForwardBy(next_task_delay); + // Although the session successfully migrates, it is still considered + // degrading sessions. + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + // Response headers are received over the new network. EXPECT_THAT(callback_.WaitForResult(), IsOk()); EXPECT_EQ(200, response.headers->response_code()); @@ -5523,8 +5694,10 @@ TEST_P(QuicStreamFactoryTest, MigrateOnPathDegradingWithNoNewNetwork) { // Trigger connection migration on path degrading. Since there are no networks // to migrate to, the session will remain on the original network, not marked // as going away. - session->connection()->OnPathDegradingTimeout(); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + session->connection()->OnPathDegradingDetected(); EXPECT_TRUE(session->connection()->IsPathDegrading()); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); EXPECT_TRUE(HasActiveSession(host_port_pair_)); EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); @@ -5586,24 +5759,25 @@ void QuicStreamFactoryTestBase::TestMigrateSessionEarlyNonMigratableStream( quic_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); // Hanging read. // A RESET will be sent to the peer to cancel the non-migratable stream. if (VersionUsesHttp3(version_.transport_version)) { - quic_data1.AddWrite(SYNCHRONOUS, - client_maker_.MakeDataAndRstPacket( - packet_num++, true, GetQpackDecoderStreamId(), - StreamCancellationQpackDecoderInstruction(0), - GetNthClientInitiatedBidirectionalStreamId(0), - quic::QUIC_STREAM_CANCELLED)); + quic_data1.AddWrite(SYNCHRONOUS, + client_maker_.MakeDataRstAndAckPacket( + packet_num++, true, GetQpackDecoderStreamId(), + StreamCancellationQpackDecoderInstruction(0), + GetNthClientInitiatedBidirectionalStreamId(0), + quic::QUIC_STREAM_CANCELLED, 1, 1)); } else { - quic_data1.AddWrite(SYNCHRONOUS, - client_maker_.MakeRstPacket( - packet_num++, false, - GetNthClientInitiatedBidirectionalStreamId(0), - quic::QUIC_STREAM_CANCELLED)); + quic_data1.AddWrite(SYNCHRONOUS, + client_maker_.MakeAckAndRstPacket( + packet_num++, false, + GetNthClientInitiatedBidirectionalStreamId(0), + quic::QUIC_STREAM_CANCELLED, 1, 1, 1)); } // Ping packet to send after migration is completed. - quic_data1.AddWrite(SYNCHRONOUS, client_maker_.MakeAckAndPingPacket( - packet_num++, false, 1, 1, 1)); + quic_data1.AddWrite(SYNCHRONOUS, + client_maker_.MakePingPacket(packet_num++, false)); + } else { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { if (VersionUsesHttp3(version_.transport_version)) { socket_data.AddWrite( SYNCHRONOUS, @@ -6152,8 +6326,10 @@ TEST_P(QuicStreamFactoryTest, // Cause the connection to report path degrading to the session. // Session will ignore the signal as handshake is not completed. - session->connection()->OnPathDegradingTimeout(); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); + session->connection()->OnPathDegradingDetected(); EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); EXPECT_FALSE(HasActiveSession(host_port_pair_)); EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_)); @@ -6212,9 +6388,11 @@ void QuicStreamFactoryTestBase::TestNoAlternateNetworkBeforeHandshake( EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); + EXPECT_EQ(0u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); // Cause the connection to report path degrading to the session. // Session will ignore the signal as handshake is not completed. - session->connection()->OnPathDegradingTimeout(); + session->connection()->OnPathDegradingDetected(); + EXPECT_EQ(1u, QuicStreamFactoryPeer::GetNumDegradingSessions(factory_.get())); EXPECT_EQ(0u, task_runner->GetPendingTaskCount()); EXPECT_FALSE(HasActiveSession(host_port_pair_)); EXPECT_TRUE(HasActiveJob(host_port_pair_, privacy_mode_)); @@ -10135,8 +10313,7 @@ TEST_P(QuicStreamFactoryTest, ServerMigration) { const uint8_t kTestIpAddress[] = {1, 2, 3, 4}; const uint16_t kTestPort = 123; session->Migrate(NetworkChangeNotifier::kInvalidNetworkHandle, - IPEndPoint(IPAddress(kTestIpAddress), kTestPort), true, - net_log_); + IPEndPoint(IPAddress(kTestIpAddress), kTestPort), true); session->GetDefaultSocket()->GetPeerAddress(&ip); DVLOG(1) << "Socket migrated to: " << ip.address().ToString() << " " @@ -10184,7 +10361,10 @@ TEST_P(QuicStreamFactoryTest, ServerMigrationIPv6ToIPv6) { VerifyServerMigration(config, alt_address); } -TEST_P(QuicStreamFactoryTest, ServerMigrationIPv6ToIPv4) { +TEST_P(QuicStreamFactoryTest, ServerMigrationIPv6ToIPv4Fails) { + quic_params_->allow_server_migration = true; + Initialize(); + // Add a resolver rule to make initial connection to an IPv6 address. host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(), "fe80::aebc:32ff:febb:1e33", ""); @@ -10192,9 +10372,75 @@ TEST_P(QuicStreamFactoryTest, ServerMigrationIPv6ToIPv4) { IPEndPoint alt_address = IPEndPoint(IPAddress(1, 2, 3, 4), 123); quic::QuicConfig config; config.SetIPv4AlternateServerAddressToSend(ToQuicSocketAddress(alt_address)); - IPEndPoint expected_address( - ConvertIPv4ToIPv4MappedIPv6(alt_address.address()), alt_address.port()); - VerifyServerMigration(config, expected_address); + + ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); + crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); + crypto_client_stream_factory_.AddProofVerifyDetails(&verify_details); + + crypto_client_stream_factory_.SetConfig(config); + + // Set up only socket data provider. + MockQuicData socket_data1(version_); + socket_data1.AddRead(SYNCHRONOUS, ERR_IO_PENDING); + int packet_num = 1; + if (VersionUsesHttp3(version_.transport_version)) { + socket_data1.AddWrite(SYNCHRONOUS, + ConstructInitialSettingsPacket(packet_num++)); + socket_data1.AddWrite( + SYNCHRONOUS, client_maker_.MakeDataPacket( + packet_num++, GetQpackDecoderStreamId(), true, false, + StreamCancellationQpackDecoderInstruction(0))); + } + socket_data1.AddWrite( + SYNCHRONOUS, + client_maker_.MakeRstPacket(packet_num++, true, + GetNthClientInitiatedBidirectionalStreamId(0), + quic::QUIC_STREAM_CANCELLED)); + socket_data1.AddSocketDataToFactory(socket_factory_.get()); + + // Create request and QuicHttpStream. + QuicStreamRequest request(factory_.get()); + EXPECT_EQ( + ERR_IO_PENDING, + request.Request( + host_port_pair_, version_, privacy_mode_, DEFAULT_PRIORITY, + SocketTag(), NetworkIsolationKey(), false /* disable_secure_dns */, + /*cert_verify_flags=*/0, url_, net_log_, &net_error_details_, + failed_on_default_network_callback_, callback_.callback())); + EXPECT_EQ(OK, callback_.WaitForResult()); + std::unique_ptr<HttpStream> stream = CreateStream(&request); + EXPECT_TRUE(stream.get()); + + // Cause QUIC stream to be created. + HttpRequestInfo request_info; + request_info.method = "GET"; + request_info.url = GURL("https://www.example.org/"); + request_info.traffic_annotation = + MutableNetworkTrafficAnnotationTag(TRAFFIC_ANNOTATION_FOR_TESTS); + EXPECT_EQ(OK, stream->InitializeStream(&request_info, true, DEFAULT_PRIORITY, + net_log_, CompletionOnceCallback())); + + // Ensure that session is alive and active. + QuicChromiumClientSession* session = GetActiveSession(host_port_pair_); + EXPECT_TRUE(QuicStreamFactoryPeer::IsLiveSession(factory_.get(), session)); + EXPECT_TRUE(HasActiveSession(host_port_pair_)); + + IPEndPoint actual_address; + session->GetDefaultSocket()->GetPeerAddress(&actual_address); + // No migration should have happened. + IPEndPoint expected_address = + IPEndPoint(IPAddress(0xfe, 0x80, 0, 0, 0, 0, 0, 0, 0xae, 0xbc, 0x32, 0xff, + 0xfe, 0xbb, 0x1e, 0x33), + kDefaultServerPort); + EXPECT_EQ(actual_address, expected_address); + DVLOG(1) << "Socket connected to: " << actual_address.address().ToString() + << " " << actual_address.port(); + DVLOG(1) << "Expected address: " << expected_address.address().ToString() + << " " << expected_address.port(); + + stream.reset(); + EXPECT_TRUE(socket_data1.AllReadDataConsumed()); + EXPECT_TRUE(socket_data1.AllWriteDataConsumed()); } TEST_P(QuicStreamFactoryTest, ServerMigrationIPv4ToIPv6Fails) { @@ -10431,11 +10677,6 @@ TEST_P(QuicStreamFactoryTest, CryptoConfigWhenProofIsInvalid) { } TEST_P(QuicStreamFactoryTest, EnableNotLoadFromDiskCache) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } Initialize(); factory_->set_is_quic_known_to_work_on_current_network(true); ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); @@ -10581,20 +10822,10 @@ TEST_P(QuicStreamFactoryTest, ReducePingTimeoutOnConnectionTimeOutOpenStreams) { // Verifies that the QUIC stream factory is initialized correctly. TEST_P(QuicStreamFactoryTest, MaybeInitialize) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } VerifyInitialization(false /* vary_network_isolation_key */); } TEST_P(QuicStreamFactoryTest, MaybeInitializeWithNetworkIsolationKey) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } base::test::ScopedFeatureList feature_list; feature_list.InitWithFeatures( // enabled_features @@ -10822,11 +11053,6 @@ TEST_P(QuicStreamFactoryTest, CryptoConfigCacheMRUWithNetworkIsolationKey) { // around, so evictions happen immediately. TEST_P(QuicStreamFactoryTest, CryptoConfigCacheMRUWithRealRequestsAndWithNetworkIsolationKey) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } const int kNumSessionsToMake = kMaxRecentCryptoConfigs + 5; base::test::ScopedFeatureList feature_list; @@ -10968,11 +11194,6 @@ TEST_P(QuicStreamFactoryTest, } TEST_P(QuicStreamFactoryTest, YieldAfterPackets) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } Initialize(); factory_->set_is_quic_known_to_work_on_current_network(true); ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); @@ -11025,11 +11246,6 @@ TEST_P(QuicStreamFactoryTest, YieldAfterPackets) { } TEST_P(QuicStreamFactoryTest, YieldAfterDuration) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } Initialize(); factory_->set_is_quic_known_to_work_on_current_network(true); ProofVerifyDetailsChromium verify_details = DefaultProofVerifyDetails(); @@ -12306,11 +12522,6 @@ TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceHostResolveAsyncStaleMatch) { // async, and then the result matches. TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceHostResolveAsyncConnectAsyncStaleMatch) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } quic_params_->race_stale_dns_on_connection = true; host_resolver_ = std::make_unique<MockCachingHostResolver>(); Initialize(); @@ -12382,11 +12593,6 @@ TEST_P(QuicStreamFactoryTest, // return, then connection finishes and matches with the result. TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceHostResolveAsyncStaleMatchConnectAsync) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } quic_params_->race_stale_dns_on_connection = true; host_resolver_ = std::make_unique<MockCachingHostResolver>(); Initialize(); @@ -12532,10 +12738,6 @@ TEST_P(QuicStreamFactoryTest, // With dns race experiment on, dns resolve async, stale used and connects // async, finishes before dns, but no match TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceStaleAsyncResolveAsyncNoMatch) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // TODO(fayang): 0-rtt is not supported in IETF QUIC yet. Fix it. - return; - } quic_params_->race_stale_dns_on_connection = true; host_resolver_ = std::make_unique<MockCachingHostResolver>(); Initialize(); @@ -12550,7 +12752,7 @@ TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceStaleAsyncResolveAsyncNoMatch) { host_resolver_->rules()->AddIPLiteralRule(host_port_pair_.host(), kNonCachedIPAddress, ""); - // Set up a different address in the stale resolvercache. + // Set up a different address in the stale resolver cache. HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0, HostResolverSource::ANY, NetworkIsolationKey()); HostCache::Entry entry(OK, @@ -12621,10 +12823,6 @@ TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceStaleAsyncResolveAsyncNoMatch) { // With dns race experiment on, dns resolve async, stale used and connects // async, dns finishes first, but no match TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceResolveAsyncStaleAsyncNoMatch) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // TODO(fayang): 0-rtt is not supported in IETF QUIC yet. Fix it. - return; - } quic_params_->race_stale_dns_on_connection = true; host_resolver_ = std::make_unique<MockCachingHostResolver>(); Initialize(); @@ -12988,10 +13186,6 @@ TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceStaleErrorDNSNoMatchError) { // With dns race experiment on, dns resolve async and stale connect async, dns // resolve returns error and then preconnect finishes TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceResolveAsyncErrorStaleAsync) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // TODO(fayang): 0-rtt is not supported in IETF QUIC yet. Fix it. - return; - } quic_params_->race_stale_dns_on_connection = true; host_resolver_ = std::make_unique<MockCachingHostResolver>(); Initialize(); @@ -13002,8 +13196,6 @@ TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceResolveAsyncErrorStaleAsync) { host_resolver_->set_ondemand_mode(true); host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host()); factory_->set_is_quic_known_to_work_on_current_network(false); - crypto_client_stream_factory_.set_handshake_mode( - MockCryptoClientStream::ZERO_RTT); // Set up an address in stale resolver cache. HostCache::Key key(host_port_pair_.host(), DnsQueryType::UNSPECIFIED, 0, @@ -13020,7 +13212,6 @@ TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceResolveAsyncErrorStaleAsync) { // Socket data for stale connection which is supposed to disconnect. MockQuicData quic_data(version_); quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); - client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); int packet_number = 1; if (VersionUsesHttp3(version_.transport_version)) { quic_data.AddWrite(SYNCHRONOUS, @@ -13053,10 +13244,6 @@ TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceResolveAsyncErrorStaleAsync) { // resolve returns error and then preconnect fails. TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceResolveAsyncErrorStaleAsyncError) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { - // TODO(fayang): 0-rtt is not supported in IETF QUIC yet. Fix it. - return; - } quic_params_->race_stale_dns_on_connection = true; host_resolver_ = std::make_unique<MockCachingHostResolver>(); Initialize(); @@ -13066,8 +13253,6 @@ TEST_P(QuicStreamFactoryTest, // Add asynchronous failure to host resolver. host_resolver_->set_ondemand_mode(true); factory_->set_is_quic_known_to_work_on_current_network(false); - crypto_client_stream_factory_.set_handshake_mode( - MockCryptoClientStream::ZERO_RTT); host_resolver_->rules()->AddSimulatedFailure(host_port_pair_.host()); // Set up an address in stale resolver cache. @@ -13084,7 +13269,6 @@ TEST_P(QuicStreamFactoryTest, MockQuicData quic_data(version_); quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING); - client_maker_.SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT); int packet_number = 1; if (VersionUsesHttp3(version_.transport_version)) { quic_data.AddWrite(SYNCHRONOUS, @@ -13163,11 +13347,6 @@ TEST_P(QuicStreamFactoryTest, ResultAfterDNSRaceHostResolveAsync) { // With stale dns and migration before handshake experiment on, migration failed // after handshake confirmed, and then fresh resolve returns. TEST_P(QuicStreamFactoryTest, StaleNetworkFailedAfterHandshake) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } quic_params_->race_stale_dns_on_connection = true; host_resolver_ = std::make_unique<MockCachingHostResolver>(); @@ -13243,11 +13422,6 @@ TEST_P(QuicStreamFactoryTest, StaleNetworkFailedAfterHandshake) { // With stale dns experiment on, the stale session is killed while waiting for // handshake TEST_P(QuicStreamFactoryTest, StaleNetworkFailedBeforeHandshake) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3 && - version_.HasIetfQuicFrames()) { - // 0-rtt is not supported in IETF QUIC yet. - return; - } quic_params_->race_stale_dns_on_connection = true; host_resolver_ = std::make_unique<MockCachingHostResolver>(); InitializeConnectionMigrationV2Test( @@ -13326,7 +13500,7 @@ TEST_P(QuicStreamFactoryTest, StaleNetworkFailedBeforeHandshake) { } TEST_P(QuicStreamFactoryTest, ConfigInitialRttForHandshake) { - if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) { + if (version_.UsesTls()) { // IETF QUIC uses a different handshake timeout management system. return; } diff --git a/chromium/net/quic/quic_test_packet_maker.cc b/chromium/net/quic/quic_test_packet_maker.cc index db63151afa8..7539328b11e 100644 --- a/chromium/net/quic/quic_test_packet_maker.cc +++ b/chromium/net/quic/quic_test_packet_maker.cc @@ -91,6 +91,10 @@ quic::QuicFrames CloneFrames(const quic::QuicFrames& frames) { frame.new_token_frame = new quic::QuicNewTokenFrame(*frame.new_token_frame); break; + case quic::ACK_FREQUENCY_FRAME: + frame.ack_frequency_frame = + new quic::QuicAckFrequencyFrame(*frame.ack_frequency_frame); + break; case quic::NUM_FRAME_TYPES: DCHECK(false) << "Cannot clone frame type: " << frame.type; @@ -99,6 +103,15 @@ quic::QuicFrames CloneFrames(const quic::QuicFrames& frames) { return new_frames; } +// TODO(crbug.com/1085541): Consider moving this method into QuicTestUtils. +quic::QuicConnectionId TestConnectionId(uint64_t connection_number) { + const uint64_t connection_id64_net = + quiche::QuicheEndian::HostToNet64(connection_number); + return quic::QuicConnectionId( + reinterpret_cast<const char*>(&connection_id64_net), + sizeof(connection_id64_net)); +} + } // namespace QuicTestPacketMaker::QuicTestPacketMaker( @@ -120,8 +133,7 @@ QuicTestPacketMaker::QuicTestPacketMaker( encryption_level_(quic::ENCRYPTION_FORWARD_SECURE), long_header_type_(quic::INVALID_PACKET_TYPE), client_headers_include_h2_stream_dependency_( - client_headers_include_h2_stream_dependency && - version.transport_version >= quic::QUIC_VERSION_43), + client_headers_include_h2_stream_dependency), save_packet_frames_(false) { DCHECK(!(perspective_ == quic::Perspective::IS_SERVER && client_headers_include_h2_stream_dependency_)); @@ -246,7 +258,7 @@ std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeRstPacket( InitializeHeader(num, include_version); if (!version_.HasIetfQuicFrames() || - quic::QuicUtils::IsBidirectionalStreamId(stream_id)) { + quic::QuicUtils::IsBidirectionalStreamId(stream_id, version_)) { AddQuicRstStreamFrame(stream_id, error_code); } @@ -299,6 +311,30 @@ QuicTestPacketMaker::MakeDataAndRstPacket( } std::unique_ptr<quic::QuicReceivedPacket> +QuicTestPacketMaker::MakeDataRstAndAckPacket( + uint64_t num, + bool include_version, + quic::QuicStreamId data_stream_id, + quiche::QuicheStringPiece data, + quic::QuicStreamId rst_stream_id, + quic::QuicRstStreamErrorCode rst_error_code, + uint64_t largest_received, + uint64_t smallest_received) { + InitializeHeader(num, include_version); + + AddQuicAckFrame(largest_received, smallest_received); + + AddQuicStreamFrame(data_stream_id, /* fin = */ false, data); + AddQuicRstStreamFrame(rst_stream_id, rst_error_code); + + if (version_.HasIetfQuicFrames()) { + AddQuicStopSendingFrame(rst_stream_id, rst_error_code); + } + + return BuildPacket(); +} + +std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckAndRstPacket( uint64_t num, bool include_version, @@ -327,7 +363,7 @@ QuicTestPacketMaker::MakeAckAndRstPacket( AddQuicAckFrame(largest_received, smallest_received); if (!version_.HasIetfQuicFrames() || - quic::QuicUtils::IsBidirectionalStreamId(stream_id)) { + quic::QuicUtils::IsBidirectionalStreamId(stream_id, version_)) { AddQuicRstStreamFrame(stream_id, error_code); } @@ -351,6 +387,8 @@ QuicTestPacketMaker::MakeRstAckAndConnectionClosePacket( const std::string& quic_error_details) { InitializeHeader(num, include_version); + AddQuicAckFrame(largest_received, smallest_received); + AddQuicRstStreamFrame(stream_id, error_code); if (version_.HasIetfQuicFrames()) { @@ -423,6 +461,8 @@ QuicTestPacketMaker::MakeDataRstAckAndConnectionClosePacket( const std::string& quic_error_details) { InitializeHeader(num, include_version); + AddQuicAckFrame(largest_received, smallest_received); + AddQuicStreamFrame(data_stream_id, /* fin = */ false, data); AddQuicRstStreamFrame(rst_stream_id, error_code); @@ -790,6 +830,44 @@ QuicTestPacketMaker::MakePriorityPacket(uint64_t packet_number, } std::unique_ptr<quic::QuicReceivedPacket> +QuicTestPacketMaker::MakeAckAndPriorityPacket( + uint64_t packet_number, + bool should_include_version, + uint64_t largest_received, + uint64_t smallest_received, + quic::QuicStreamId id, + quic::QuicStreamId parent_stream_id, + spdy::SpdyPriority priority) { + InitializeHeader(packet_number, should_include_version); + + AddQuicAckFrame(largest_received, smallest_received); + + if (!client_headers_include_h2_stream_dependency_) { + parent_stream_id = 0; + } + int weight = spdy::Spdy3PriorityToHttp2Weight(priority); + bool exclusive = client_headers_include_h2_stream_dependency_; + + if (!VersionUsesHttp3(version_.transport_version)) { + spdy::SpdyPriorityIR priority_frame(id, parent_stream_id, weight, + exclusive); + spdy::SpdySerializedFrame spdy_frame( + spdy_request_framer_.SerializeFrame(priority_frame)); + AddQuicStreamFrame( + GetHeadersStreamId(), false, + quiche::QuicheStringPiece(spdy_frame.data(), spdy_frame.size())); + + return BuildPacket(); + } + if (priority != quic::QuicStream::kDefaultUrgency) { + std::string priority_data = GenerateHttp3PriorityData(priority, id); + AddQuicStreamFrame(2, false, priority_data); + } + + return BuildPacket(); +} + +std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckAndPriorityUpdatePacket( uint64_t packet_number, bool should_include_version, @@ -809,6 +887,30 @@ QuicTestPacketMaker::MakeAckAndPriorityUpdatePacket( } std::unique_ptr<quic::QuicReceivedPacket> +QuicTestPacketMaker::MakeMultiplePriorityFramesPacket( + uint64_t packet_number, + bool should_include_version, + const std::vector<Http2StreamDependency>& priority_frames) { + InitializeHeader(packet_number, should_include_version); + + const bool exclusive = client_headers_include_h2_stream_dependency_; + std::string coalesced_data; + for (const Http2StreamDependency& info : priority_frames) { + spdy::SpdyPriorityIR priority_frame( + info.stream_id, info.parent_stream_id, + spdy::Spdy3PriorityToHttp2Weight(info.spdy_priority), exclusive); + auto spdy_frame = spdy_request_framer_.SerializeFrame(priority_frame); + coalesced_data += std::string(spdy_frame.data(), spdy_frame.size()); + } + AddQuicStreamFrame(quic::VersionUsesHttp3(version_.transport_version) + ? GetFirstBidirectionalStreamId() + : GetHeadersStreamId(), + false, coalesced_data); + + return BuildPacket(); +} + +std::unique_ptr<quic::QuicReceivedPacket> QuicTestPacketMaker::MakeAckAndMultiplePriorityFramesPacket( uint64_t packet_number, bool should_include_version, @@ -847,6 +949,14 @@ QuicTestPacketMaker::MakeRetransmissionPacket(uint64_t original_packet_number, saved_frames_[quic::QuicPacketNumber(original_packet_number)], nullptr); } +std::unique_ptr<quic::QuicEncryptedPacket> +QuicTestPacketMaker::MakeStatelessResetPacket() { + auto connection_id = TestConnectionId(0x1337); + return quic::QuicFramer::BuildIetfStatelessResetPacket( + connection_id, + quic::QuicUtils::GenerateStatelessResetToken(connection_id)); +} + void QuicTestPacketMaker::RemoveSavedStreamFrames( quic::QuicStreamId stream_id) { for (auto& kv : saved_frames_) { @@ -939,7 +1049,7 @@ std::string QuicTestPacketMaker::QpackEncodeHeaders( &headers_frame_header); // Possible add a PUSH stream type. - if (!quic::QuicUtils::IsBidirectionalStreamId(stream_id) && + if (!quic::QuicUtils::IsBidirectionalStreamId(stream_id, version_) && stream_offsets_[stream_id] == 0) { // Push stream type header data += "\x01"; @@ -1223,7 +1333,7 @@ spdy::SpdySerializedFrame QuicTestPacketMaker::MakeSpdyHeadersFrame( } bool QuicTestPacketMaker::ShouldIncludeVersion(bool include_version) const { - if (version_.transport_version > quic::QUIC_VERSION_43) { + if (version_.HasIetfInvariantHeader()) { return encryption_level_ < quic::ENCRYPTION_FORWARD_SECURE; } return include_version; @@ -1231,7 +1341,7 @@ bool QuicTestPacketMaker::ShouldIncludeVersion(bool include_version) const { quic::QuicPacketNumberLength QuicTestPacketMaker::GetPacketNumberLength() const { - if (version_.transport_version > quic::QUIC_VERSION_43 && + if (version_.HasIetfInvariantHeader() && encryption_level_ < quic::ENCRYPTION_FORWARD_SECURE && !version_.SendsVariableLengthPacketNumberInLongHeader()) { return quic::PACKET_4BYTE_PACKET_NUMBER; diff --git a/chromium/net/quic/quic_test_packet_maker.h b/chromium/net/quic/quic_test_packet_maker.h index 8df27cfbdb6..d0549ec20b2 100644 --- a/chromium/net/quic/quic_test_packet_maker.h +++ b/chromium/net/quic/quic_test_packet_maker.h @@ -116,6 +116,16 @@ class QuicTestPacketMaker { quic::QuicStreamId rst_stream_id, quic::QuicRstStreamErrorCode rst_error_code); + std::unique_ptr<quic::QuicReceivedPacket> MakeDataRstAndAckPacket( + uint64_t num, + bool include_version, + quic::QuicStreamId data_stream_id, + quiche::QuicheStringPiece data, + quic::QuicStreamId rst_stream_id, + quic::QuicRstStreamErrorCode rst_error_code, + uint64_t largest_received, + uint64_t smallest_received); + std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndRstPacket( uint64_t num, bool include_version, @@ -297,6 +307,20 @@ class QuicTestPacketMaker { quic::QuicStreamId parent_stream_id, spdy::SpdyPriority priority); + std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndPriorityPacket( + uint64_t packet_number, + bool should_include_version, + uint64_t largest_received, + uint64_t smallest_received, + quic::QuicStreamId id, + quic::QuicStreamId parent_stream_id, + spdy::SpdyPriority priority); + + std::unique_ptr<quic::QuicReceivedPacket> MakeMultiplePriorityFramesPacket( + uint64_t packet_number, + bool should_include_version, + const std::vector<Http2StreamDependency>& priority_frames); + std::unique_ptr<quic::QuicReceivedPacket> MakeAckAndMultiplePriorityFramesPacket( uint64_t packet_number, @@ -320,6 +344,8 @@ class QuicTestPacketMaker { quic::QuicStreamId id, spdy::SpdyPriority priority); + std::unique_ptr<quic::QuicEncryptedPacket> MakeStatelessResetPacket(); + // Removes all stream frames associated with |stream_id|. void RemoveSavedStreamFrames(quic::QuicStreamId stream_id); diff --git a/chromium/net/quic/quic_test_packet_printer.cc b/chromium/net/quic/quic_test_packet_printer.cc index 2a5741df791..86edab085bd 100644 --- a/chromium/net/quic/quic_test_packet_printer.cc +++ b/chromium/net/quic/quic_test_packet_printer.cc @@ -180,6 +180,10 @@ class QuicPacketPrinter : public QuicFramerVisitorInterface { *output_ << "OnHandshakeDoneFrame: " << frame; return true; } + bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override { + *output_ << "OnAckFrequencyFrame: " << frame; + return true; + } void OnPacketComplete() override { *output_ << "OnPacketComplete\n"; } bool IsValidStatelessResetToken(QuicUint128 token) const override { *output_ << "IsValidStatelessResetToken\n"; diff --git a/chromium/net/quic/quic_transport_client.cc b/chromium/net/quic/quic_transport_client.cc index 6da53b0ac2e..8b2c4f3ea69 100644 --- a/chromium/net/quic/quic_transport_client.cc +++ b/chromium/net/quic/quic_transport_client.cc @@ -4,6 +4,8 @@ #include "net/quic/quic_transport_client.h" +#include "base/metrics/histogram_functions.h" +#include "base/stl_util.h" #include "base/threading/thread_task_runner_handle.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" #include "net/proxy_resolution/proxy_resolution_request.h" @@ -17,6 +19,10 @@ namespace net { namespace { +// From +// https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints +constexpr int kCustomCertificateMaxValidityDays = 14; + std::set<std::string> HostsFromOrigins(std::set<HostPortPair> origins) { std::set<std::string> hosts; for (const auto& origin : origins) { @@ -24,17 +30,47 @@ std::set<std::string> HostsFromOrigins(std::set<HostPortPair> origins) { } return hosts; } + +std::unique_ptr<quic::ProofVerifier> CreateProofVerifier( + const NetworkIsolationKey& isolation_key, + URLRequestContext* context, + const QuicTransportClient::Parameters& parameters) { + if (parameters.server_certificate_fingerprints.empty()) { + return std::make_unique<ProofVerifierChromium>( + context->cert_verifier(), context->ct_policy_enforcer(), + context->transport_security_state(), + context->cert_transparency_verifier(), + HostsFromOrigins( + context->quic_context()->params()->origins_to_force_quic_on), + isolation_key); + } + + auto verifier = std::make_unique<quic::WebTransportFingerprintProofVerifier>( + context->quic_context()->clock(), kCustomCertificateMaxValidityDays); + for (const quic::CertificateFingerprint& fingerprint : + parameters.server_certificate_fingerprints) { + bool success = verifier->AddFingerprint(fingerprint); + if (!success) { + DLOG(WARNING) << "Failed to add a certificate fingerprint: " + << fingerprint.fingerprint; + } + } + return verifier; +} } // namespace -constexpr quic::ParsedQuicVersion - QuicTransportClient::kQuicVersionForOriginTrial; +QuicTransportClient::Parameters::Parameters() = default; +QuicTransportClient::Parameters::~Parameters() = default; +QuicTransportClient::Parameters::Parameters(const Parameters&) = default; +QuicTransportClient::Parameters::Parameters(Parameters&&) = default; QuicTransportClient::QuicTransportClient( const GURL& url, const url::Origin& origin, Visitor* visitor, const NetworkIsolationKey& isolation_key, - URLRequestContext* context) + URLRequestContext* context, + const Parameters& parameters) : url_(url), origin_(origin), isolation_key_(isolation_key), @@ -53,15 +89,8 @@ QuicTransportClient::QuicTransportClient( // (currently, all certificate verification errors result in "TLS // handshake error" even when more detailed message is available). This // requires implementing ProofHandler::OnProofVerifyDetailsAvailable. - crypto_config_( - std::make_unique<ProofVerifierChromium>( - context->cert_verifier(), - context->ct_policy_enforcer(), - context->transport_security_state(), - context->cert_transparency_verifier(), - std::set<std::string>(HostsFromOrigins( - quic_context_->params()->origins_to_force_quic_on))), - /* session_cache */ nullptr) {} + crypto_config_(CreateProofVerifier(isolation_key_, context, parameters), + /* session_cache */ nullptr) {} QuicTransportClient::~QuicTransportClient() = default; @@ -139,13 +168,13 @@ int QuicTransportClient::DoInit() { // Ensure that for the duration of the origin trial, a fixed QUIC transport // version is available. - supported_versions_.push_back(kQuicVersionForOriginTrial); + supported_versions_ = QuicVersionsForWebTransportOriginTrial(); // Add other supported versions if available. for (quic::ParsedQuicVersion& version : quic_context_->params()->supported_versions) { if (!quic::IsVersionValidForQuicTransport(version)) continue; - if (version == kQuicVersionForOriginTrial) + if (base::Contains(supported_versions_, version)) continue; // Skip as we've already added it above. supported_versions_.push_back(version); } @@ -227,6 +256,20 @@ int QuicTransportClient::DoConnect() { if (rv != OK) return rv; + CreateConnection(); + next_connect_state_ = CONNECT_STATE_CONFIRM_CONNECTION; + return ERR_IO_PENDING; +} + +void QuicTransportClient::CreateConnection() { + // Delete the objects in the same order they would be normally deleted by the + // destructor. + packet_reader_ = nullptr; + session_ = nullptr; + connection_ = nullptr; + + IPEndPoint server_address = + *resolve_host_request_->GetAddressResults()->begin(); quic::QuicConnectionId connection_id = quic::QuicUtils::CreateRandomConnectionId( quic_context_->random_generator()); @@ -251,9 +294,6 @@ int QuicTransportClient::DoConnect() { session_->Initialize(); packet_reader_->StartReading(); session_->CryptoConnect(); - - next_connect_state_ = CONNECT_STATE_CONFIRM_CONNECTION; - return ERR_IO_PENDING; } int QuicTransportClient::DoConfirmConnection() { @@ -292,6 +332,18 @@ void QuicTransportClient::TransitionToState(State next_state) { error_.safe_to_report_details = session_->alpn_received(); } + base::UmaHistogramEnumeration("Net.QuicTransportClient.FailedAtState", + last_state, NUM_STATES); + base::UmaHistogramSparse("Net.QuicTransportClient.Error", + std::abs(error_.net_error)); + if (last_state == CONNECTING) { + base::UmaHistogramEnumeration( + "Net.QuicTransportClient.FailedAtConnectState", next_connect_state_, + CONNECT_STATE_NUM_STATES); + base::UmaHistogramSparse("Net.QuicTransportClient.ConnectionError", + std::abs(error_.net_error)); + } + DCHECK_NE(error_.net_error, OK); if (error_.details.empty()) { error_.details = ErrorToString(error_.net_error); @@ -372,7 +424,38 @@ void QuicTransportClient::OnConnectionClosed( quic::QuicConnectionId /*server_connection_id*/, quic::QuicErrorCode error, const std::string& error_details, - quic::ConnectionCloseSource /*source*/) { + quic::ConnectionCloseSource source) { + if (!retried_with_new_version_ && + session_->error() == quic::QUIC_INVALID_VERSION) { + retried_with_new_version_ = true; + base::EraseIf( + supported_versions_, [this](const quic::ParsedQuicVersion& version) { + return !base::Contains( + session_->connection()->server_supported_versions(), version); + }); + if (!supported_versions_.empty()) { + // Since this is a callback from QuicConnection, we can't replace the + // connection object in this method; do it from the top of the event loop + // instead. + task_runner_->PostTask( + FROM_HERE, base::BindOnce(&QuicTransportClient::CreateConnection, + weak_factory_.GetWeakPtr())); + return; + } + // If there are no supported versions, treat this as a regular error. + } + + std::string histogram_name; + switch (source) { + case quic::ConnectionCloseSource::FROM_SELF: + histogram_name = "Net.QuicTransportClient.ConnectionCloseCodeClient"; + break; + case quic::ConnectionCloseSource::FROM_PEER: + histogram_name = "Net.QuicTransportClient.ConnectionCloseCodeServer"; + break; + } + base::UmaHistogramSparse(histogram_name, error); + if (error == quic::QUIC_NO_ERROR) { TransitionToState(CLOSED); return; @@ -392,17 +475,4 @@ void QuicTransportClient::OnConnectionClosed( TransitionToState(FAILED); } -std::string QuicTransportErrorToString(const QuicTransportError& error) { - std::string message = - ExtendedErrorToString(error.net_error, error.quic_error); - if (error.details == message) - return message; - return quiche::QuicheStrCat(message, " (", error.details, ")"); -} - -std::ostream& operator<<(std::ostream& os, const QuicTransportError& error) { - os << QuicTransportErrorToString(error); - return os; -} - } // namespace net diff --git a/chromium/net/quic/quic_transport_client.h b/chromium/net/quic/quic_transport_client.h index 4f0f58ff8c8..de270225642 100644 --- a/chromium/net/quic/quic_transport_client.h +++ b/chromium/net/quic/quic_transport_client.h @@ -13,11 +13,13 @@ #include "net/quic/quic_chromium_packet_reader.h" #include "net/quic/quic_chromium_packet_writer.h" #include "net/quic/quic_context.h" +#include "net/quic/quic_transport_error.h" #include "net/socket/client_socket_factory.h" #include "net/third_party/quiche/src/quic/core/crypto/quic_crypto_client_config.h" #include "net/third_party/quiche/src/quic/core/quic_config.h" #include "net/third_party/quiche/src/quic/core/quic_versions.h" #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_client_session.h" +#include "net/third_party/quiche/src/quic/quic_transport/web_transport_fingerprint_proof_verifier.h" #include "url/gurl.h" #include "url/origin.h" @@ -27,29 +29,6 @@ class ProxyResolutionRequest; class QuicChromiumAlarmFactory; class URLRequestContext; -struct NET_EXPORT QuicTransportError { - // |net_error| is always set to a meaningful value. - int net_error = OK; - - // |quic_error| is set to a QUIC error, or to quic::QUIC_NO_ERROR if the error - // originates non-QUIC parts of the stack. - quic::QuicErrorCode quic_error = quic::QUIC_NO_ERROR; - - // Human-readable error summary. - std::string details; - - // QuicTransport requires that the connection errors have to be - // undistinguishable until the peer is confirmed to be a QuicTransport - // endpoint. See https://wicg.github.io/web-transport/#protocol-security - bool safe_to_report_details = false; -}; - -NET_EXPORT -std::string QuicTransportErrorToString(const QuicTransportError& error); - -NET_EXPORT -std::ostream& operator<<(std::ostream& os, const QuicTransportError& error); - // QuicTransportClient is the top-level API for QuicTransport in //net. class NET_EXPORT QuicTransportClient : public quic::QuicTransportClientSession::ClientVisitor, @@ -65,6 +44,9 @@ class NET_EXPORT QuicTransportClient // | | // +---> FAILED <---+ // + // These values are logged to UMA. Entries should not be renumbered and + // numeric values should never be reused. Please keep in sync with + // "QuicTransportClientState" in src/tools/metrics/histograms/enums.xml. enum State { // The client object has been created but Connect() has not been called. NEW, @@ -78,6 +60,9 @@ class NET_EXPORT QuicTransportClient CLOSED, // The connection has been closed abruptly. FAILED, + + // Total number of possible states. + NUM_STATES, }; class NET_EXPORT Visitor { @@ -97,17 +82,35 @@ class NET_EXPORT QuicTransportClient virtual void OnCanCreateNewOutgoingUnidirectionalStream() = 0; }; - // QUIC protocol version that is used in the origin trial. - static constexpr quic::ParsedQuicVersion kQuicVersionForOriginTrial = - quic::ParsedQuicVersion(quic::PROTOCOL_TLS1_3, - quic::QUIC_VERSION_IETF_DRAFT_27); + struct NET_EXPORT Parameters { + Parameters(); + ~Parameters(); + Parameters(const Parameters&); + Parameters(Parameters&&); + + // A vector of fingerprints for expected server certificates, as described + // in + // https://wicg.github.io/web-transport/#dom-quictransportconfiguration-server_certificate_fingerprints + // When empty, Web PKI is used. + std::vector<quic::CertificateFingerprint> server_certificate_fingerprints; + }; + + // QUIC protocol versions that are used in the origin trial. + static quic::ParsedQuicVersionVector + QuicVersionsForWebTransportOriginTrial() { + return quic::ParsedQuicVersionVector{ + quic::ParsedQuicVersion::Draft29(), // Enabled in M85. + quic::ParsedQuicVersion::Draft27() // Enabled in M84. + }; + } // |visitor| and |context| must outlive this object. QuicTransportClient(const GURL& url, const url::Origin& origin, Visitor* visitor, const NetworkIsolationKey& isolation_key, - URLRequestContext* context); + URLRequestContext* context, + const Parameters& parameters); ~QuicTransportClient() override; State state() const { return state_; } @@ -154,6 +157,11 @@ class NET_EXPORT QuicTransportClient private: // State of the connection establishment process. + // + // These values are logged to UMA. Entries should not be renumbered and + // numeric values should never be reused. Please keep in sync with + // "QuicTransportClientConnectState" in + // src/tools/metrics/histograms/enums.xml. enum ConnectState { CONNECT_STATE_NONE, CONNECT_STATE_INIT, @@ -163,6 +171,8 @@ class NET_EXPORT QuicTransportClient CONNECT_STATE_RESOLVE_HOST_COMPLETE, CONNECT_STATE_CONNECT, CONNECT_STATE_CONFIRM_CONNECTION, + + CONNECT_STATE_NUM_STATES, }; // DoLoop processing the Connect() call. @@ -177,6 +187,7 @@ class NET_EXPORT QuicTransportClient int DoResolveHostComplete(int rv); // Establishes the QUIC connection. int DoConnect(); + void CreateConnection(); // Verifies that the connection has succeeded. int DoConfirmConnection(); @@ -201,6 +212,7 @@ class NET_EXPORT QuicTransportClient State state_ = NEW; ConnectState next_connect_state_ = CONNECT_STATE_NONE; QuicTransportError error_; + bool retried_with_new_version_ = false; ProxyInfo proxy_info_; std::unique_ptr<ProxyResolutionRequest> proxy_resolution_request_; diff --git a/chromium/net/quic/quic_transport_end_to_end_test.cc b/chromium/net/quic/quic_transport_end_to_end_test.cc index 9783a05ac60..95ae1538fa6 100644 --- a/chromium/net/quic/quic_transport_end_to_end_test.cc +++ b/chromium/net/quic/quic_transport_end_to_end_test.cc @@ -10,6 +10,7 @@ #include "net/cert/mock_cert_verifier.h" #include "net/dns/mock_host_resolver.h" #include "net/proxy_resolution/configured_proxy_resolution_service.h" +#include "net/quic/crypto/proof_source_chromium.h" #include "net/test/test_data_directory.h" #include "net/test/test_with_task_environment.h" #include "net/third_party/quiche/src/quic/test_tools/crypto_test_utils.h" @@ -37,10 +38,48 @@ class MockVisitor : public QuicTransportClient::Visitor { MOCK_METHOD0(OnCanCreateNewOutgoingUnidirectionalStream, void()); }; +// A clock that only mocks out WallNow(), but uses real Now() and +// ApproximateNow(). Useful for certificate verification. +class TestWallClock : public quic::QuicClock { + public: + quic::QuicTime Now() const override { + return quic::QuicChromiumClock::GetInstance()->Now(); + } + quic::QuicTime ApproximateNow() const override { + return quic::QuicChromiumClock::GetInstance()->ApproximateNow(); + } + quic::QuicWallTime WallNow() const override { return wall_now_; } + + void set_wall_now(quic::QuicWallTime now) { wall_now_ = now; } + + private: + quic::QuicWallTime wall_now_ = quic::QuicWallTime::Zero(); +}; + +class TestConnectionHelper : public quic::QuicConnectionHelperInterface { + public: + const quic::QuicClock* GetClock() const override { return &clock_; } + quic::QuicRandom* GetRandomGenerator() override { + return quic::QuicRandom::GetInstance(); + } + quic::QuicBufferAllocator* GetStreamSendBufferAllocator() override { + return &allocator_; + } + + TestWallClock& clock() { return clock_; } + + private: + TestWallClock clock_; + quic::SimpleBufferAllocator allocator_; +}; + class QuicTransportEndToEndTest : public TestWithTaskEnvironment { public: QuicTransportEndToEndTest() { - quic::QuicEnableVersion(QuicTransportClient::kQuicVersionForOriginTrial); + for (const quic::ParsedQuicVersion& version : + QuicTransportClient::QuicVersionsForWebTransportOriginTrial()) { + quic::QuicEnableVersion(version); + } origin_ = url::Origin::Create(GURL{"https://example.org"}); isolation_key_ = NetworkIsolationKey(origin_, origin_); @@ -56,7 +95,9 @@ class QuicTransportEndToEndTest : public TestWithTaskEnvironment { host_resolver->rules()->AddRule("test.example.com", "127.0.0.1"); builder.set_host_resolver(std::move(host_resolver)); - auto quic_context = std::make_unique<QuicContext>(); + auto helper = std::make_unique<TestConnectionHelper>(); + helper_ = helper.get(); + auto quic_context = std::make_unique<QuicContext>(std::move(helper)); quic_context->params()->supported_versions.clear(); // This is required to bypass the check that only allows known certificate // roots in QUIC. @@ -75,8 +116,6 @@ class QuicTransportEndToEndTest : public TestWithTaskEnvironment { LOG(INFO) << "Connection error: " << client_->error(); run_loop_->Quit(); }); - - StartServer(); } GURL GetURL(const std::string& suffix) { @@ -84,10 +123,13 @@ class QuicTransportEndToEndTest : public TestWithTaskEnvironment { "quic-transport://test.example.com:", port_, suffix)}; } - void StartServer() { + void StartServer(std::unique_ptr<quic::ProofSource> proof_source = nullptr) { + if (proof_source == nullptr) { + proof_source = quic::test::crypto_test_utils::ProofSourceForTesting(); + } server_ = std::make_unique<QuicTransportSimpleServer>( /* port */ 0, std::vector<url::Origin>({origin_}), - quic::test::crypto_test_utils::ProofSourceForTesting()); + std::move(proof_source)); ASSERT_EQ(EXIT_SUCCESS, server_->Start()); port_ = server_->server_address().port(); } @@ -105,6 +147,7 @@ class QuicTransportEndToEndTest : public TestWithTaskEnvironment { QuicFlagSaver flags_; // Save/restore all QUIC flag values. std::unique_ptr<URLRequestContext> context_; std::unique_ptr<QuicTransportClient> client_; + TestConnectionHelper* helper_; // Owned by |context_|. MockVisitor visitor_; std::unique_ptr<QuicTransportSimpleServer> server_; std::unique_ptr<base::RunLoop> run_loop_; @@ -115,8 +158,10 @@ class QuicTransportEndToEndTest : public TestWithTaskEnvironment { }; TEST_F(QuicTransportEndToEndTest, Connect) { + StartServer(); client_ = std::make_unique<QuicTransportClient>( - GetURL("/discard"), origin_, &visitor_, isolation_key_, context_.get()); + GetURL("/discard"), origin_, &visitor_, isolation_key_, context_.get(), + QuicTransportClient::Parameters()); client_->Connect(); EXPECT_CALL(visitor_, OnConnected()).WillOnce(StopRunning()); Run(); @@ -125,8 +170,10 @@ TEST_F(QuicTransportEndToEndTest, Connect) { } TEST_F(QuicTransportEndToEndTest, EchoUnidirectionalStream) { + StartServer(); client_ = std::make_unique<QuicTransportClient>( - GetURL("/echo"), origin_, &visitor_, isolation_key_, context_.get()); + GetURL("/echo"), origin_, &visitor_, isolation_key_, context_.get(), + QuicTransportClient::Parameters()); client_->Connect(); EXPECT_CALL(visitor_, OnConnected()).WillOnce(StopRunning()); Run(); @@ -151,6 +198,107 @@ TEST_F(QuicTransportEndToEndTest, EchoUnidirectionalStream) { EXPECT_EQ("test", data); } +TEST_F(QuicTransportEndToEndTest, CertificateFingerprint) { + auto proof_source = std::make_unique<net::ProofSourceChromium>(); + base::FilePath certs_dir = net::GetTestCertsDirectory(); + ASSERT_TRUE(proof_source->Initialize( + certs_dir.AppendASCII("quic-short-lived.pem"), + certs_dir.AppendASCII("quic-leaf-cert.key"), + certs_dir.AppendASCII("quic-leaf-cert.key.sct"))); + StartServer(std::move(proof_source)); + // Set clock to a time in which quic-short-lived.pem is valid + // (2020-06-05T20:35:00.000Z). + helper_->clock().set_wall_now( + quic::QuicWallTime::FromUNIXSeconds(1591389300)); + + QuicTransportClient::Parameters parameters; + parameters.server_certificate_fingerprints.push_back( + quic::CertificateFingerprint{ + .algorithm = quic::CertificateFingerprint::kSha256, + .fingerprint = "ED:3D:D7:C3:67:10:94:68:D1:DC:D1:26:5C:B2:74:D7:1C:" + "A2:63:3E:94:94:C0:84:39:D6:64:FA:08:B9:77:37"}); + client_ = std::make_unique<QuicTransportClient>(GetURL("/discard"), origin_, + &visitor_, isolation_key_, + context_.get(), parameters); + client_->Connect(); + EXPECT_CALL(visitor_, OnConnected()).WillOnce(StopRunning()); + Run(); + ASSERT_TRUE(client_->session() != nullptr); + EXPECT_TRUE(client_->session()->IsSessionReady()); +} + +TEST_F(QuicTransportEndToEndTest, CertificateFingerprintValidiyTooLong) { + StartServer(); + QuicTransportClient::Parameters parameters; + // The default QUIC test certificate is valid for ten years, which exceeds + // the two-week limit. + parameters.server_certificate_fingerprints.push_back( + quic::CertificateFingerprint{ + .algorithm = quic::CertificateFingerprint::kSha256, + .fingerprint = "25:17:B1:79:76:C8:94:BD:F0:B5:5C:0B:CC:70:C8:69:2B:" + "27:B8:84:F0:30:FE:A8:62:99:37:63:D2:A9:D6:EE"}); + client_ = std::make_unique<QuicTransportClient>(GetURL("/discard"), origin_, + &visitor_, isolation_key_, + context_.get(), parameters); + client_->Connect(); + EXPECT_CALL(visitor_, OnConnectionFailed()).WillOnce(StopRunning()); + Run(); + EXPECT_TRUE(client_->session() == nullptr); + EXPECT_EQ(client_->error().quic_error, quic::QUIC_HANDSHAKE_FAILED); +} + +TEST_F(QuicTransportEndToEndTest, CertificateFingerprintMismatch) { + StartServer(); + + QuicTransportClient::Parameters parameters; + parameters.server_certificate_fingerprints.push_back( + quic::CertificateFingerprint{ + .algorithm = quic::CertificateFingerprint::kSha256, + .fingerprint = "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:" + "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"}); + client_ = std::make_unique<QuicTransportClient>(GetURL("/discard"), origin_, + &visitor_, isolation_key_, + context_.get(), parameters); + client_->Connect(); + EXPECT_CALL(visitor_, OnConnectionFailed()).WillOnce(StopRunning()); + Run(); + EXPECT_TRUE(client_->session() == nullptr); + EXPECT_EQ(client_->error().quic_error, quic::QUIC_HANDSHAKE_FAILED); +} + +TEST_F(QuicTransportEndToEndTest, OldVersion) { + // Ensure all WebTransport versions are enabled except the first one. + quic::QuicDisableVersion( + QuicTransportClient::QuicVersionsForWebTransportOriginTrial().front()); + + StartServer(); + client_ = std::make_unique<QuicTransportClient>( + GetURL("/discard"), origin_, &visitor_, isolation_key_, context_.get(), + QuicTransportClient::Parameters()); + client_->Connect(); + EXPECT_CALL(visitor_, OnConnected()).WillOnce(StopRunning()); + Run(); + ASSERT_TRUE(client_->session() != nullptr); + EXPECT_TRUE(client_->session()->IsSessionReady()); +} + +TEST_F(QuicTransportEndToEndTest, NoCommonVersion) { + // Disable all WebTransport versions. + for (const quic::ParsedQuicVersion& version : + QuicTransportClient::QuicVersionsForWebTransportOriginTrial()) { + quic::QuicDisableVersion(version); + } + + StartServer(); + client_ = std::make_unique<QuicTransportClient>( + GetURL("/discard"), origin_, &visitor_, isolation_key_, context_.get(), + QuicTransportClient::Parameters()); + client_->Connect(); + EXPECT_CALL(visitor_, OnConnectionFailed()).WillOnce(StopRunning()); + Run(); + EXPECT_TRUE(client_->session() == nullptr); + EXPECT_EQ(client_->error().quic_error, quic::QUIC_INVALID_VERSION); +} } // namespace } // namespace test } // namespace net diff --git a/chromium/net/quic/quic_transport_error.cc b/chromium/net/quic/quic_transport_error.cc new file mode 100644 index 00000000000..8c76d6705e9 --- /dev/null +++ b/chromium/net/quic/quic_transport_error.cc @@ -0,0 +1,24 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_transport_error.h" + +#include "net/third_party/quiche/src/common/platform/api/quiche_str_cat.h" + +namespace net { + +std::string QuicTransportErrorToString(const QuicTransportError& error) { + std::string message = + ExtendedErrorToString(error.net_error, error.quic_error); + if (error.details == message) + return message; + return quiche::QuicheStrCat(message, " (", error.details, ")"); +} + +std::ostream& operator<<(std::ostream& os, const QuicTransportError& error) { + os << QuicTransportErrorToString(error); + return os; +} + +} // namespace net diff --git a/chromium/net/quic/quic_transport_error.h b/chromium/net/quic/quic_transport_error.h new file mode 100644 index 00000000000..d970e094074 --- /dev/null +++ b/chromium/net/quic/quic_transport_error.h @@ -0,0 +1,53 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_QUIC_QUIC_TRANSPORT_ERROR_H_ +#define NET_QUIC_QUIC_TRANSPORT_ERROR_H_ + +#include <ostream> +#include <string> + +#include "base/strings/string_piece.h" +#include "net/base/net_errors.h" +#include "net/base/net_export.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" + +namespace net { + +struct NET_EXPORT QuicTransportError { + QuicTransportError() = default; + QuicTransportError(int net_error, + quic::QuicErrorCode quic_error, + base::StringPiece details, + bool safe_to_report_details) + : net_error(net_error), + quic_error(quic_error), + details(details), + safe_to_report_details(safe_to_report_details) {} + + // |net_error| is always set to a meaningful value. + int net_error = OK; + + // |quic_error| is set to a QUIC error, or to quic::QUIC_NO_ERROR if the error + // originates non-QUIC parts of the stack. + quic::QuicErrorCode quic_error = quic::QUIC_NO_ERROR; + + // Human-readable error summary. + std::string details; + + // QuicTransport requires that the connection errors have to be + // undistinguishable until the peer is confirmed to be a QuicTransport + // endpoint. See https://wicg.github.io/web-transport/#protocol-security + bool safe_to_report_details = false; +}; + +NET_EXPORT +std::string QuicTransportErrorToString(const QuicTransportError& error); + +NET_EXPORT +std::ostream& operator<<(std::ostream& os, const QuicTransportError& error); + +} // namespace net + +#endif // NET_QUIC_QUIC_TRANSPORT_ERROR_H_ diff --git a/chromium/net/quic/quic_transport_parameters_fuzzer.cc b/chromium/net/quic/quic_transport_parameters_fuzzer.cc index 4225bc00b6b..e6b1b129f7a 100644 --- a/chromium/net/quic/quic_transport_parameters_fuzzer.cc +++ b/chromium/net/quic/quic_transport_parameters_fuzzer.cc @@ -19,14 +19,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { quic::TransportParameters transport_parameters; std::vector<uint8_t> remaining_bytes = data_provider.ConsumeRemainingBytes<uint8_t>(); - quic::ParsedQuicVersion version = quic::UnsupportedQuicVersion(); - for (const quic::ParsedQuicVersion& vers : quic::AllSupportedVersions()) { - if (vers.handshake_protocol == quic::PROTOCOL_TLS1_3) { - version = vers; - break; - } - } - CHECK_NE(version.transport_version, quic::QUIC_VERSION_UNSUPPORTED); + quic::ParsedQuicVersion version = quic::AllSupportedVersionsWithTls().front(); + CHECK(version.UsesTls()); std::string error_details; quic::ParseTransportParameters(version, perspective, remaining_bytes.data(), remaining_bytes.size(), &transport_parameters, diff --git a/chromium/net/quic/quic_utils_chromium.cc b/chromium/net/quic/quic_utils_chromium.cc deleted file mode 100644 index 90e08fcfa11..00000000000 --- a/chromium/net/quic/quic_utils_chromium.cc +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/quic_utils_chromium.h" - -#include "base/containers/adapters.h" -#include "base/strings/string_split.h" -#include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" - -namespace net { - -quic::QuicTagVector ParseQuicConnectionOptions( - const std::string& connection_options) { - quic::QuicTagVector options; - // Tokens are expected to be no more than 4 characters long, but - // handle overflow gracefully. - for (const quiche::QuicheStringPiece& token : - base::SplitStringPiece(connection_options, ",", base::TRIM_WHITESPACE, - base::SPLIT_WANT_ALL)) { - uint32_t option = 0; - for (char token_char : base::Reversed(token)) { - option <<= 8; - option |= static_cast<unsigned char>(token_char); - } - options.push_back(option); - } - return options; -} - -} // namespace net diff --git a/chromium/net/quic/quic_utils_chromium.h b/chromium/net/quic/quic_utils_chromium.h deleted file mode 100644 index 7f8a7de9064..00000000000 --- a/chromium/net/quic/quic_utils_chromium.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. -// -// Some helpers for quic that are for chromium codebase. - -#ifndef NET_QUIC_QUIC_UTILS_CHROMIUM_H_ -#define NET_QUIC_QUIC_UTILS_CHROMIUM_H_ - -#include <string> - -#include "base/logging.h" -#include "net/base/net_export.h" -#include "net/third_party/quiche/src/quic/core/quic_tag.h" -#include "net/third_party/quiche/src/quic/core/quic_versions.h" - -namespace net { - -// Returns the list of QUIC tags represented by the comma separated -// string in |connection_options|. -NET_EXPORT quic::QuicTagVector ParseQuicConnectionOptions( - const std::string& connection_options); - -} // namespace net - -#endif // NET_QUIC_QUIC_UTILS_CHROMIUM_H_ diff --git a/chromium/net/quic/quic_utils_chromium_test.cc b/chromium/net/quic/quic_utils_chromium_test.cc deleted file mode 100644 index 2af22a0d5e3..00000000000 --- a/chromium/net/quic/quic_utils_chromium_test.cc +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "net/quic/quic_utils_chromium.h" - -#include <map> - -#include "net/third_party/quiche/src/quic/core/crypto/crypto_protocol.h" -#include "testing/gmock/include/gmock/gmock.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::ElementsAre; -using ::testing::IsEmpty; - -namespace net { -namespace test { -namespace { - -TEST(QuicUtilsChromiumTest, ParseQuicConnectionOptions) { - quic::QuicTagVector empty_options = ParseQuicConnectionOptions(""); - EXPECT_TRUE(empty_options.empty()); - - quic::QuicTagVector parsed_options = - ParseQuicConnectionOptions("TIMER,TBBR,REJ"); - quic::QuicTagVector expected_options; - expected_options.push_back(quic::kTIME); - expected_options.push_back(quic::kTBBR); - expected_options.push_back(quic::kREJ); - EXPECT_EQ(expected_options, parsed_options); -} - -} // namespace -} // namespace test -} // namespace net |