src/net/quic/quic_network_transaction_unittest.cc
TEST_P(QuicNetworkTransactionTest, RetryOnAlternateNetworkWhileTCPSucceeds) {
  if (version_.handshake_protocol == quic::PROTOCOL_TLS1_3) {
    // QUIC with TLS1.3 handshake doesn't support 0-rtt.
    return;
  }

  SetUpTestForRetryConnectionOnAlternateNetwork();

  client_maker_->SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);

  // The request will initially go out over QUIC.
  MockQuicData quic_data(version_);
  quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Hanging read
  int packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));  // CHLO
  // Retranmit the handshake messages.
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));
  // After timeout, connection will be closed with QUIC_NETWORK_IDLE_TIMEOUT.
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeConnectionClosePacket(
                         packet_num++, true, quic::QUIC_NETWORK_IDLE_TIMEOUT,
                         "No recent network activity after 4s. Timeout:4s"));
  quic_data.AddSocketDataToFactory(&socket_factory_);

  // Add successful TCP data so that TCP job will succeed.
  MockWrite http_writes[] = {
      MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n"),
      MockWrite(SYNCHRONOUS, 1, "Host: mail.example.org\r\n"),
      MockWrite(SYNCHRONOUS, 2, "Connection: keep-alive\r\n\r\n")};

  MockRead http_reads[] = {
      MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"),
      MockRead(SYNCHRONOUS, 4, kQuicAlternativeServiceHeader),
      MockRead(SYNCHRONOUS, 5, "TCP succeeds"), MockRead(SYNCHRONOUS, OK, 6)};
  SequencedSocketData http_data(http_reads, http_writes);
  socket_factory_.AddSocketDataProvider(&http_data);
  socket_factory_.AddSSLSocketDataProvider(&ssl_data_);

  // Quic connection will be retried on the alternate network after the initial
  // one fails on the default network.
  MockQuicData quic_data2(version_);
  quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  // Handing read.
  quic_data2.AddWrite(SYNCHRONOUS,
                      client_maker_->MakeDummyCHLOPacket(1));  // CHLO

  client_maker_->SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE);
  if (VersionUsesHttp3(version_.transport_version))
    quic_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(2));
  quic_data2.AddSocketDataToFactory(&socket_factory_);

  // Resolve the host resolution synchronously.
  host_resolver_.set_synchronous_mode(true);
  host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                           "");

  CreateSession();
  session_->quic_stream_factory()->set_is_quic_known_to_work_on_current_network(
      false);
  // Use a TestTaskRunner to avoid waiting in real time for timeouts.
  QuicStreamFactoryPeer::SetAlarmFactory(
      session_->quic_stream_factory(),
      std::make_unique<QuicChromiumAlarmFactory>(quic_task_runner_.get(),
                                                 context_.clock()));
  // Add alternate protocol mapping to race QUIC and TCP.
  // QUIC connection requires handshake to be confirmed and sends CHLO to the
  // peer.
  AddQuicAlternateProtocolMapping(
      MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);

  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));

  // Pump the message loop to get the request started.
  // Request will be served with TCP job.
  base::RunLoop().RunUntilIdle();
  EXPECT_THAT(callback.WaitForResult(), IsOk());
  CheckResponseData(&trans, "TCP succeeds");

  // Fast forward to idle timeout the original connection. A new connection will
  // be kicked off on the alternate network.
  quic_task_runner_->FastForwardBy(quic::QuicTime::Delta::FromSeconds(4));
  ASSERT_TRUE(quic_data.AllReadDataConsumed());
  ASSERT_TRUE(quic_data.AllWriteDataConsumed());

  // The second connection hasn't finish handshake, verify that QUIC is not
  // marked as broken.
  ExpectQuicAlternateProtocolMapping();
  // Explicitly confirm the handshake on the second connection.
  crypto_client_stream_factory_.last_stream()
      ->NotifySessionOneRttKeyAvailable();
  // Run message loop to execute posted tasks, which will notify JoController
  // about the orphaned job status.
  base::RunLoop().RunUntilIdle();

  // Verify that QUIC is marked as broken.
  ExpectBrokenAlternateProtocolMapping();

  // Deliver a message to notify the new network becomes default, the previous
  // brokenness will be clear as the brokenness is bond with old default
  // network.
  scoped_mock_change_notifier_->mock_network_change_notifier()
      ->NotifyNetworkMadeDefault(kNewNetworkForTests);
  ExpectQuicAlternateProtocolMapping();

  ASSERT_TRUE(quic_data2.AllReadDataConsumed());
  ASSERT_TRUE(quic_data2.AllWriteDataConsumed());
}
// This test verifies that a new QUIC connection will be attempted on the
// alternate network if the original QUIC connection fails with idle timeout
// before handshake is confirmed. If TCP succeeds and QUIC succeeds on the
// alternate network, QUIC is marked as broken. The brokenness will expire when
// the default network changes.
This test verifies that a new QUIC connection will be attempted on the
alternate network if the original QUIC connection fails with idle timeout
before handshake is confirmed. If TCP succeeds and QUIC succeeds on the
alternate network, QUIC is marked as broken. The brokenness will expire when
the default network changes.
This test verifies that a new QUIC connection will be attempted on the alternate network if the original QUIC connection fails with idle timeout before handshake is confirmed . If TCP succeeds and QUIC succeeds on the 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) {
    return;
  }
  SetUpTestForRetryConnectionOnAlternateNetwork();
  client_maker_->SetEncryptionLevel(quic::ENCRYPTION_ZERO_RTT);
  MockQuicData quic_data(version_);
  quic_data.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  
  int packet_num = 1;
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));  
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeDummyCHLOPacket(packet_num++));
  quic_data.AddWrite(SYNCHRONOUS,
                     client_maker_->MakeConnectionClosePacket(
                         packet_num++, true, quic::QUIC_NETWORK_IDLE_TIMEOUT,
                         "No recent network activity after 4s. Timeout:4s"));
  quic_data.AddSocketDataToFactory(&socket_factory_);
  MockWrite http_writes[] = {
      MockWrite(SYNCHRONOUS, 0, "GET / HTTP/1.1\r\n"),
      MockWrite(SYNCHRONOUS, 1, "Host: mail.example.org\r\n"),
      MockWrite(SYNCHRONOUS, 2, "Connection: keep-alive\r\n\r\n")};
  MockRead http_reads[] = {
      MockRead(SYNCHRONOUS, 3, "HTTP/1.1 200 OK\r\n"),
      MockRead(SYNCHRONOUS, 4, kQuicAlternativeServiceHeader),
      MockRead(SYNCHRONOUS, 5, "TCP succeeds"), MockRead(SYNCHRONOUS, OK, 6)};
  SequencedSocketData http_data(http_reads, http_writes);
  socket_factory_.AddSocketDataProvider(&http_data);
  socket_factory_.AddSSLSocketDataProvider(&ssl_data_);
  MockQuicData quic_data2(version_);
  quic_data2.AddRead(SYNCHRONOUS, ERR_IO_PENDING);  
  quic_data2.AddWrite(SYNCHRONOUS,
                      client_maker_->MakeDummyCHLOPacket(1));  
  client_maker_->SetEncryptionLevel(quic::ENCRYPTION_FORWARD_SECURE);
  if (VersionUsesHttp3(version_.transport_version))
    quic_data2.AddWrite(SYNCHRONOUS, ConstructInitialSettingsPacket(2));
  quic_data2.AddSocketDataToFactory(&socket_factory_);
  host_resolver_.set_synchronous_mode(true);
  host_resolver_.rules()->AddIPLiteralRule("mail.example.org", "192.168.0.1",
                                           "");
  CreateSession();
  session_->quic_stream_factory()->set_is_quic_known_to_work_on_current_network(
      false);
  QuicStreamFactoryPeer::SetAlarmFactory(
      session_->quic_stream_factory(),
      std::make_unique<QuicChromiumAlarmFactory>(quic_task_runner_.get(),
                                                 context_.clock()));
  AddQuicAlternateProtocolMapping(
      MockCryptoClientStream::COLD_START_WITH_CHLO_SENT);
  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();
  EXPECT_THAT(callback.WaitForResult(), IsOk());
  CheckResponseData(&trans, "TCP succeeds");
  quic_task_runner_->FastForwardBy(quic::QuicTime::Delta::FromSeconds(4));
  ASSERT_TRUE(quic_data.AllReadDataConsumed());
  ASSERT_TRUE(quic_data.AllWriteDataConsumed());
  ExpectQuicAlternateProtocolMapping();
  crypto_client_stream_factory_.last_stream()
      ->NotifySessionOneRttKeyAvailable();
  base::RunLoop().RunUntilIdle();
  ExpectBrokenAlternateProtocolMapping();
  scoped_mock_change_notifier_->mock_network_change_notifier()
      ->NotifyNetworkMadeDefault(kNewNetworkForTests);
  ExpectQuicAlternateProtocolMapping();
  ASSERT_TRUE(quic_data2.AllReadDataConsumed());
  ASSERT_TRUE(quic_data2.AllWriteDataConsumed());
}
TEST_P ( QuicNetworkTransactionTest , RetryOnAlternateNetworkWhileTCPSucceeds ) { if ( version_ . handshake_protocol == quic :: PROTOCOL_TLS1_3 ) { return ; } SetUpTestForRetryConnectionOnAlternateNetwork ( ) ; client_maker_ -> SetEncryptionLevel ( quic :: ENCRYPTION_ZERO_RTT ) ; MockQuicData quic_data ( version_ ) ; quic_data . AddRead ( SYNCHRONOUS , ERR_IO_PENDING ) ; int packet_num = 1 ; quic_data . AddWrite ( SYNCHRONOUS , client_maker_ -> MakeDummyCHLOPacket ( packet_num ++ ) ) ; quic_data . AddWrite ( SYNCHRONOUS , client_maker_ -> MakeDummyCHLOPacket ( packet_num ++ ) ) ; quic_data . AddWrite ( SYNCHRONOUS , client_maker_ -> MakeDummyCHLOPacket ( packet_num ++ ) ) ; quic_data . AddWrite ( SYNCHRONOUS , client_maker_ -> MakeDummyCHLOPacket ( packet_num ++ ) ) ; quic_data . AddWrite ( SYNCHRONOUS , client_maker_ -> MakeDummyCHLOPacket ( packet_num ++ ) ) ; quic_data . AddWrite ( SYNCHRONOUS , client_maker_ -> MakeConnectionClosePacket ( packet_num ++ , true , quic :: QUIC_NETWORK_IDLE_TIMEOUT , " " ) ) ; quic_data . AddSocketDataToFactory ( & socket_factory_ ) ; MockWrite http_writes [ ] = { MockWrite ( SYNCHRONOUS , 0 , " \r \n " ) , MockWrite ( SYNCHRONOUS , 1 , " \r \n " ) , MockWrite ( SYNCHRONOUS , 2 , " \r \n \r \n " ) } ; MockRead http_reads [ ] = { MockRead ( SYNCHRONOUS , 3 , " \r \n " ) , MockRead ( SYNCHRONOUS , 4 , kQuicAlternativeServiceHeader ) , MockRead ( SYNCHRONOUS , 5 , " " ) , MockRead ( SYNCHRONOUS , OK , 6 ) } ; SequencedSocketData http_data ( http_reads , http_writes ) ; socket_factory_ . AddSocketDataProvider ( & http_data ) ; socket_factory_ . AddSSLSocketDataProvider ( & ssl_data_ ) ; MockQuicData quic_data2 ( version_ ) ; quic_data2 . AddRead ( SYNCHRONOUS , ERR_IO_PENDING ) ; quic_data2 . AddWrite ( SYNCHRONOUS , client_maker_ -> MakeDummyCHLOPacket ( 1 ) ) ; client_maker_ -> SetEncryptionLevel ( quic :: ENCRYPTION_FORWARD_SECURE ) ; if ( VersionUsesHttp3 ( version_ . transport_version ) ) quic_data2 . AddWrite ( SYNCHRONOUS , ConstructInitialSettingsPacket ( 2 ) ) ; quic_data2 . AddSocketDataToFactory ( & socket_factory_ ) ; host_resolver_ . set_synchronous_mode ( true ) ; host_resolver_ . rules ( ) -> AddIPLiteralRule ( " " , " " , " " ) ; CreateSession ( ) ; session_ -> quic_stream_factory ( ) -> set_is_quic_known_to_work_on_current_network ( false ) ; QuicStreamFactoryPeer :: SetAlarmFactory ( session_ -> quic_stream_factory ( ) , std :: make_unique < QuicChromiumAlarmFactory > ( quic_task_runner_ . get ( ) , context_ . clock ( ) ) ) ; AddQuicAlternateProtocolMapping ( MockCryptoClientStream :: COLD_START_WITH_CHLO_SENT ) ; 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 ( ) ; EXPECT_THAT ( callback . WaitForResult ( ) , IsOk ( ) ) ; CheckResponseData ( & trans , " " ) ; quic_task_runner_ -> FastForwardBy ( quic :: QuicTime :: Delta :: FromSeconds ( 4 ) ) ; ASSERT_TRUE ( quic_data . AllReadDataConsumed ( ) ) ; ASSERT_TRUE ( quic_data . AllWriteDataConsumed ( ) ) ; ExpectQuicAlternateProtocolMapping ( ) ; crypto_client_stream_factory_ . last_stream ( ) -> NotifySessionOneRttKeyAvailable ( ) ; base :: RunLoop ( ) . RunUntilIdle ( ) ; ExpectBrokenAlternateProtocolMapping ( ) ; scoped_mock_change_notifier_ -> mock_network_change_notifier ( ) -> NotifyNetworkMadeDefault ( kNewNetworkForTests ) ; ExpectQuicAlternateProtocolMapping ( ) ; ASSERT_TRUE ( quic_data2 . AllReadDataConsumed ( ) ) ; ASSERT_TRUE ( quic_data2 . AllWriteDataConsumed ( ) ) ; }
This test verifies that a new QUIC connection will be attempted on the
alternate network if the original QUIC connection fails with idle timeout
before handshake is confirmed.
This test verifies that a new QUIC connection will be attempted on the alternate network if the original QUIC connection fails with idle timeout before handshake is confirmed .
// QUIC with TLS1.3 handshake doesn't support 0-rtt. // The request will initially go out over QUIC. // Hanging read // CHLO // Retranmit the handshake messages. // After timeout, connection will be closed with QUIC_NETWORK_IDLE_TIMEOUT. // Add successful TCP data so that TCP job will succeed. // Quic connection will be retried on the alternate network after the initial // one fails on the default network. // Handing read. // CHLO // Resolve the host resolution synchronously. // Use a TestTaskRunner to avoid waiting in real time for timeouts. // Add alternate protocol mapping to race QUIC and TCP. // QUIC connection requires handshake to be confirmed and sends CHLO to the // peer. // Pump the message loop to get the request started. // Request will be served with TCP job. // Fast forward to idle timeout the original connection. A new connection will // be kicked off on the alternate network. // The second connection hasn't finish handshake, verify that QUIC is not // marked as broken. // Explicitly confirm the handshake on the second connection. // Run message loop to execute posted tasks, which will notify JoController // about the orphaned job status. // Verify that QUIC is marked as broken. // Deliver a message to notify the new network becomes default, the previous // brokenness will be clear as the brokenness is bond with old default // network.