| // Copyright (c) 2017 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/third_party/quiche/src/quic/quartc/quartc_session.h" |
| |
| #include <utility> |
| |
| #include "net/third_party/quiche/src/quic/core/quic_simple_buffer_allocator.h" |
| #include "net/third_party/quiche/src/quic/core/quic_types.h" |
| #include "net/third_party/quiche/src/quic/core/tls_client_handshaker.h" |
| #include "net/third_party/quiche/src/quic/core/tls_server_handshaker.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_clock.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_test_mem_slice_vector.h" |
| #include "net/third_party/quiche/src/quic/quartc/counting_packet_filter.h" |
| #include "net/third_party/quiche/src/quic/quartc/quartc_endpoint.h" |
| #include "net/third_party/quiche/src/quic/quartc/quartc_fakes.h" |
| #include "net/third_party/quiche/src/quic/quartc/quartc_packet_writer.h" |
| #include "net/third_party/quiche/src/quic/quartc/simulated_packet_transport.h" |
| #include "net/third_party/quiche/src/quic/test_tools/mock_clock.h" |
| #include "net/third_party/quiche/src/quic/test_tools/simulator/packet_filter.h" |
| #include "net/third_party/quiche/src/quic/test_tools/simulator/simulator.h" |
| |
| namespace quic { |
| |
| namespace { |
| |
| using ::testing::ElementsAre; |
| using ::testing::ElementsAreArray; |
| using ::testing::Gt; |
| using ::testing::Pair; |
| |
| constexpr QuicTime::Delta kPropagationDelay = |
| QuicTime::Delta::FromMilliseconds(10); |
| // Propagation delay and a bit, but no more than full RTT. |
| constexpr QuicTime::Delta kPropagationDelayAndABit = |
| QuicTime::Delta::FromMilliseconds(12); |
| |
| static QuicByteCount kDefaultMaxPacketSize = 1200; |
| |
| test::QuicTestMemSliceVector CreateMemSliceVector(QuicStringPiece data) { |
| return test::QuicTestMemSliceVector( |
| {std::pair<char*, size_t>(const_cast<char*>(data.data()), data.size())}); |
| } |
| |
| class QuartcSessionTest : public QuicTest { |
| public: |
| ~QuartcSessionTest() override {} |
| |
| void Init(bool create_client_endpoint = true) { |
| client_transport_ = |
| std::make_unique<simulator::SimulatedQuartcPacketTransport>( |
| &simulator_, "client_transport", "server_transport", |
| 10 * kDefaultMaxPacketSize); |
| server_transport_ = |
| std::make_unique<simulator::SimulatedQuartcPacketTransport>( |
| &simulator_, "server_transport", "client_transport", |
| 10 * kDefaultMaxPacketSize); |
| |
| client_filter_ = std::make_unique<simulator::CountingPacketFilter>( |
| &simulator_, "client_filter", client_transport_.get()); |
| |
| client_server_link_ = std::make_unique<simulator::SymmetricLink>( |
| client_filter_.get(), server_transport_.get(), |
| QuicBandwidth::FromKBitsPerSecond(10 * 1000), kPropagationDelay); |
| |
| client_stream_delegate_ = std::make_unique<FakeQuartcStreamDelegate>(); |
| client_session_delegate_ = std::make_unique<FakeQuartcEndpointDelegate>( |
| client_stream_delegate_.get(), simulator_.GetClock()); |
| |
| server_stream_delegate_ = std::make_unique<FakeQuartcStreamDelegate>(); |
| server_session_delegate_ = std::make_unique<FakeQuartcEndpointDelegate>( |
| server_stream_delegate_.get(), simulator_.GetClock()); |
| |
| // No 0-rtt setup, because server config is empty. |
| // CannotCreateDataStreamBeforeHandshake depends on 1-rtt setup. |
| if (create_client_endpoint) { |
| client_endpoint_ = std::make_unique<QuartcClientEndpoint>( |
| simulator_.GetAlarmFactory(), simulator_.GetClock(), |
| simulator_.GetRandomGenerator(), client_session_delegate_.get(), |
| quic::QuartcSessionConfig(), |
| /*serialized_server_config=*/""); |
| } |
| server_endpoint_ = std::make_unique<QuartcServerEndpoint>( |
| simulator_.GetAlarmFactory(), simulator_.GetClock(), |
| simulator_.GetRandomGenerator(), server_session_delegate_.get(), |
| quic::QuartcSessionConfig()); |
| } |
| |
| // Note that input session config will apply to both server and client. |
| // Perspective and packet_transport will be overwritten. |
| void CreateClientAndServerSessions( |
| const QuartcSessionConfig& /*session_config*/, |
| bool init = true) { |
| if (init) { |
| Init(); |
| } |
| |
| server_endpoint_->Connect(server_transport_.get()); |
| client_endpoint_->Connect(client_transport_.get()); |
| |
| CHECK(simulator_.RunUntil([this] { |
| return client_session_delegate_->session() != nullptr && |
| server_session_delegate_->session() != nullptr; |
| })); |
| |
| client_peer_ = client_session_delegate_->session(); |
| server_peer_ = server_session_delegate_->session(); |
| } |
| |
| // Runs all tasks scheduled in the next 200 ms. |
| void RunTasks() { simulator_.RunFor(QuicTime::Delta::FromMilliseconds(200)); } |
| |
| void AwaitHandshake() { |
| simulator_.RunUntil([this] { |
| return client_peer_->IsCryptoHandshakeConfirmed() && |
| server_peer_->IsCryptoHandshakeConfirmed(); |
| }); |
| } |
| |
| // Test handshake establishment and sending/receiving of data for two |
| // directions. |
| void TestSendReceiveStreams() { |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsEncryptionEstablished()); |
| ASSERT_TRUE(client_peer_->IsEncryptionEstablished()); |
| |
| // Now we can establish encrypted outgoing stream. |
| QuartcStream* outgoing_stream = |
| server_peer_->CreateOutgoingBidirectionalStream(); |
| QuicStreamId stream_id = outgoing_stream->id(); |
| ASSERT_NE(nullptr, outgoing_stream); |
| EXPECT_TRUE(server_peer_->ShouldKeepConnectionAlive()); |
| |
| outgoing_stream->SetDelegate(server_stream_delegate_.get()); |
| |
| // Send a test message from peer 1 to peer 2. |
| test::QuicTestMemSliceVector data = CreateMemSliceVector("Hello"); |
| outgoing_stream->WriteMemSlices(data.span(), /*fin=*/false); |
| RunTasks(); |
| |
| // Wait for peer 2 to receive messages. |
| ASSERT_TRUE(client_stream_delegate_->has_data()); |
| |
| QuartcStream* incoming = client_session_delegate_->last_incoming_stream(); |
| ASSERT_TRUE(incoming); |
| EXPECT_EQ(incoming->id(), stream_id); |
| EXPECT_TRUE(client_peer_->ShouldKeepConnectionAlive()); |
| |
| EXPECT_EQ(client_stream_delegate_->data()[stream_id], "Hello"); |
| // Send a test message from peer 2 to peer 1. |
| test::QuicTestMemSliceVector response = CreateMemSliceVector("Response"); |
| incoming->WriteMemSlices(response.span(), /*fin=*/false); |
| RunTasks(); |
| // Wait for peer 1 to receive messages. |
| ASSERT_TRUE(server_stream_delegate_->has_data()); |
| |
| EXPECT_EQ(server_stream_delegate_->data()[stream_id], "Response"); |
| } |
| |
| // Test sending/receiving of messages for two directions. |
| void TestSendReceiveMessage() { |
| ASSERT_TRUE(server_peer_->CanSendMessage()); |
| ASSERT_TRUE(client_peer_->CanSendMessage()); |
| |
| int64_t server_datagram_id = 111; |
| int64_t client_datagram_id = 222; |
| |
| // Send message from peer 1 to peer 2. |
| test::QuicTestMemSliceVector message = |
| CreateMemSliceVector("Message from server"); |
| ASSERT_TRUE( |
| server_peer_->SendOrQueueMessage(message.span(), server_datagram_id)); |
| |
| // First message in each direction should not be queued. |
| EXPECT_EQ(server_peer_->send_message_queue_size(), 0u); |
| |
| // Wait for peer 2 to receive message. |
| RunTasks(); |
| |
| EXPECT_THAT(client_session_delegate_->incoming_messages(), |
| testing::ElementsAre("Message from server")); |
| |
| EXPECT_THAT(server_session_delegate_->sent_datagram_ids(), |
| testing::ElementsAre(server_datagram_id)); |
| |
| EXPECT_THAT( |
| server_session_delegate_->acked_datagram_id_to_receive_timestamp(), |
| ElementsAre(Pair(server_datagram_id, Gt(QuicTime::Zero())))); |
| |
| // Send message from peer 2 to peer 1. |
| message = CreateMemSliceVector("Message from client"); |
| ASSERT_TRUE( |
| client_peer_->SendOrQueueMessage(message.span(), client_datagram_id)); |
| |
| // First message in each direction should not be queued. |
| EXPECT_EQ(client_peer_->send_message_queue_size(), 0u); |
| |
| // Wait for peer 1 to receive message. |
| RunTasks(); |
| |
| EXPECT_THAT(server_session_delegate_->incoming_messages(), |
| testing::ElementsAre("Message from client")); |
| |
| EXPECT_THAT(client_session_delegate_->sent_datagram_ids(), |
| testing::ElementsAre(client_datagram_id)); |
| |
| EXPECT_THAT( |
| client_session_delegate_->acked_datagram_id_to_receive_timestamp(), |
| ElementsAre(Pair(client_datagram_id, Gt(QuicTime::Zero())))); |
| } |
| |
| // Test for sending multiple messages that also result in queueing. |
| // This is one-way test, which is run in given direction. |
| void TestSendReceiveQueuedMessages(bool direction_from_server) { |
| // Send until queue_size number of messages are queued. |
| constexpr size_t queue_size = 10; |
| |
| ASSERT_TRUE(server_peer_->CanSendMessage()); |
| ASSERT_TRUE(client_peer_->CanSendMessage()); |
| |
| QuartcSession* const peer_sending = |
| direction_from_server ? server_peer_ : client_peer_; |
| |
| FakeQuartcEndpointDelegate* const delegate_receiving = |
| direction_from_server ? client_session_delegate_.get() |
| : server_session_delegate_.get(); |
| |
| FakeQuartcEndpointDelegate* const delegate_sending = |
| direction_from_server ? server_session_delegate_.get() |
| : client_session_delegate_.get(); |
| |
| // There should be no messages in the queue before we start sending. |
| EXPECT_EQ(peer_sending->send_message_queue_size(), 0u); |
| |
| // Send messages from peer 1 to peer 2 until required number of messages |
| // are queued in unsent message queue. |
| std::vector<std::string> sent_messages; |
| std::vector<int64_t> sent_datagram_ids; |
| int64_t current_datagram_id = 0; |
| while (peer_sending->send_message_queue_size() < queue_size) { |
| sent_messages.push_back( |
| QuicStrCat("Sending message, index=", sent_messages.size())); |
| ASSERT_TRUE(peer_sending->SendOrQueueMessage( |
| CreateMemSliceVector(sent_messages.back()).span(), |
| current_datagram_id)); |
| |
| sent_datagram_ids.push_back(current_datagram_id); |
| ++current_datagram_id; |
| } |
| |
| // Wait for peer 2 to receive all messages. |
| RunTasks(); |
| |
| std::vector<testing::Matcher<std::pair<int64_t, QuicTime>>> ack_matchers; |
| for (int64_t id : sent_datagram_ids) { |
| ack_matchers.push_back(Pair(id, Gt(QuicTime::Zero()))); |
| } |
| EXPECT_EQ(delegate_receiving->incoming_messages(), sent_messages); |
| EXPECT_EQ(delegate_sending->sent_datagram_ids(), sent_datagram_ids); |
| EXPECT_THAT(delegate_sending->acked_datagram_id_to_receive_timestamp(), |
| ElementsAreArray(ack_matchers)); |
| } |
| |
| // Test sending long messages: |
| // - message of maximum allowed length should succeed |
| // - message of > maximum allowed length should fail. |
| void TestSendLongMessage() { |
| ASSERT_TRUE(server_peer_->CanSendMessage()); |
| ASSERT_TRUE(client_peer_->CanSendMessage()); |
| |
| // Send message of maximum allowed length. |
| std::string message_max_long = |
| std::string(server_peer_->GetCurrentLargestMessagePayload(), 'A'); |
| test::QuicTestMemSliceVector message = |
| CreateMemSliceVector(message_max_long); |
| ASSERT_TRUE( |
| server_peer_->SendOrQueueMessage(message.span(), /*datagram_id=*/0)); |
| |
| // Send long message which should fail. |
| std::string message_too_long = |
| std::string(server_peer_->GetCurrentLargestMessagePayload() + 1, 'B'); |
| message = CreateMemSliceVector(message_too_long); |
| ASSERT_FALSE( |
| server_peer_->SendOrQueueMessage(message.span(), /*datagram_id=*/0)); |
| |
| // Wait for peer 2 to receive message. |
| RunTasks(); |
| |
| // Client should only receive one message of allowed length. |
| EXPECT_THAT(client_session_delegate_->incoming_messages(), |
| testing::ElementsAre(message_max_long)); |
| } |
| |
| // Test that client and server are not connected after handshake failure. |
| void TestDisconnectAfterFailedHandshake() { |
| EXPECT_TRUE(!client_session_delegate_->connected()); |
| EXPECT_TRUE(!server_session_delegate_->connected()); |
| |
| EXPECT_FALSE(client_peer_->IsEncryptionEstablished()); |
| EXPECT_FALSE(client_peer_->IsCryptoHandshakeConfirmed()); |
| |
| EXPECT_FALSE(server_peer_->IsEncryptionEstablished()); |
| EXPECT_FALSE(server_peer_->IsCryptoHandshakeConfirmed()); |
| } |
| |
| protected: |
| simulator::Simulator simulator_; |
| |
| std::unique_ptr<simulator::SimulatedQuartcPacketTransport> client_transport_; |
| std::unique_ptr<simulator::SimulatedQuartcPacketTransport> server_transport_; |
| std::unique_ptr<simulator::CountingPacketFilter> client_filter_; |
| std::unique_ptr<simulator::SymmetricLink> client_server_link_; |
| |
| std::unique_ptr<FakeQuartcStreamDelegate> client_stream_delegate_; |
| std::unique_ptr<FakeQuartcEndpointDelegate> client_session_delegate_; |
| std::unique_ptr<FakeQuartcStreamDelegate> server_stream_delegate_; |
| std::unique_ptr<FakeQuartcEndpointDelegate> server_session_delegate_; |
| |
| std::unique_ptr<QuartcClientEndpoint> client_endpoint_; |
| std::unique_ptr<QuartcServerEndpoint> server_endpoint_; |
| |
| QuartcSession* client_peer_ = nullptr; |
| QuartcSession* server_peer_ = nullptr; |
| }; |
| |
| TEST_F(QuartcSessionTest, SendReceiveStreams) { |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| AwaitHandshake(); |
| TestSendReceiveStreams(); |
| } |
| |
| TEST_F(QuartcSessionTest, SendReceiveMessages) { |
| // TODO(b/134175506): Remove when IETF QUIC supports receive timestamps. |
| SetQuicReloadableFlag(quic_enable_version_99, false); |
| |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| AwaitHandshake(); |
| TestSendReceiveMessage(); |
| } |
| |
| TEST_F(QuartcSessionTest, SendReceiveQueuedMessages) { |
| // TODO(b/134175506): Remove when IETF QUIC supports receive timestamps. |
| SetQuicReloadableFlag(quic_enable_version_99, false); |
| |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| AwaitHandshake(); |
| TestSendReceiveQueuedMessages(/*direction_from_server=*/true); |
| TestSendReceiveQueuedMessages(/*direction_from_server=*/false); |
| } |
| |
| TEST_F(QuartcSessionTest, SendMultiMemSliceMessage) { |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| AwaitHandshake(); |
| ASSERT_TRUE(server_peer_->CanSendMessage()); |
| |
| std::vector<std::pair<char*, size_t>> buffers; |
| char first_piece[] = "Hello, "; |
| char second_piece[] = "world!"; |
| buffers.emplace_back(first_piece, 7); |
| buffers.emplace_back(second_piece, 6); |
| test::QuicTestMemSliceVector message(buffers); |
| ASSERT_TRUE( |
| server_peer_->SendOrQueueMessage(message.span(), /*datagram_id=*/1)); |
| |
| // Wait for the client to receive the message. |
| RunTasks(); |
| |
| // The message is not fragmented along MemSlice boundaries. |
| EXPECT_THAT(client_session_delegate_->incoming_messages(), |
| testing::ElementsAre("Hello, world!")); |
| } |
| |
| TEST_F(QuartcSessionTest, SendMessageFails) { |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| AwaitHandshake(); |
| TestSendLongMessage(); |
| } |
| |
| TEST_F(QuartcSessionTest, TestCryptoHandshakeCanWriteTriggers) { |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| |
| AwaitHandshake(); |
| |
| RunTasks(); |
| |
| ASSERT_TRUE(client_session_delegate_->writable_time().IsInitialized()); |
| ASSERT_TRUE( |
| client_session_delegate_->crypto_handshake_time().IsInitialized()); |
| // On client, we are writable 1-rtt before crypto handshake is complete. |
| ASSERT_LT(client_session_delegate_->writable_time(), |
| client_session_delegate_->crypto_handshake_time()); |
| |
| ASSERT_TRUE(server_session_delegate_->writable_time().IsInitialized()); |
| ASSERT_TRUE( |
| server_session_delegate_->crypto_handshake_time().IsInitialized()); |
| // On server, the writable time and crypto handshake are the same. (when SHLO |
| // is sent). |
| ASSERT_EQ(server_session_delegate_->writable_time(), |
| server_session_delegate_->crypto_handshake_time()); |
| } |
| |
| TEST_F(QuartcSessionTest, PreSharedKeyHandshake) { |
| // TODO(b/134175506): Remove when IETF QUIC supports receive timestamps. |
| SetQuicReloadableFlag(quic_enable_version_99, false); |
| |
| QuartcSessionConfig config; |
| config.pre_shared_key = "foo"; |
| CreateClientAndServerSessions(config); |
| AwaitHandshake(); |
| TestSendReceiveStreams(); |
| TestSendReceiveMessage(); |
| } |
| |
| // Test that data streams are not created before handshake. |
| TEST_F(QuartcSessionTest, CannotCreateDataStreamBeforeHandshake) { |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| EXPECT_EQ(nullptr, server_peer_->CreateOutgoingBidirectionalStream()); |
| EXPECT_EQ(nullptr, client_peer_->CreateOutgoingBidirectionalStream()); |
| } |
| |
| TEST_F(QuartcSessionTest, CancelQuartcStream) { |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| AwaitHandshake(); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| |
| QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); |
| ASSERT_NE(nullptr, stream); |
| |
| uint32_t id = stream->id(); |
| EXPECT_FALSE(client_peer_->IsClosedStream(id)); |
| stream->SetDelegate(client_stream_delegate_.get()); |
| client_peer_->CancelStream(id); |
| EXPECT_EQ(stream->stream_error(), |
| QuicRstStreamErrorCode::QUIC_STREAM_CANCELLED); |
| EXPECT_TRUE(client_peer_->IsClosedStream(id)); |
| } |
| |
| // TODO(b/112561077): This is the wrong layer for this test. We should write a |
| // test specifically for QuartcPacketWriter with a stubbed-out |
| // QuartcPacketTransport and remove |
| // SimulatedQuartcPacketTransport::last_packet_number(). |
| TEST_F(QuartcSessionTest, WriterGivesPacketNumberToTransport) { |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| AwaitHandshake(); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| |
| QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); |
| stream->SetDelegate(client_stream_delegate_.get()); |
| |
| test::QuicTestMemSliceVector stream_data = CreateMemSliceVector("Hello"); |
| stream->WriteMemSlices(stream_data.span(), /*fin=*/false); |
| RunTasks(); |
| |
| // The transport should see the latest packet number sent by QUIC. |
| EXPECT_EQ( |
| client_transport_->last_packet_number(), |
| client_peer_->connection()->sent_packet_manager().GetLargestSentPacket()); |
| } |
| |
| TEST_F(QuartcSessionTest, CloseConnection) { |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| AwaitHandshake(); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| |
| client_peer_->CloseConnection("Connection closed by client"); |
| EXPECT_FALSE(client_session_delegate_->connected()); |
| RunTasks(); |
| EXPECT_FALSE(server_session_delegate_->connected()); |
| } |
| |
| TEST_F(QuartcSessionTest, StreamRetransmissionEnabled) { |
| CreateClientAndServerSessions(QuartcSessionConfig()); |
| AwaitHandshake(); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| |
| QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); |
| QuicStreamId stream_id = stream->id(); |
| stream->SetDelegate(client_stream_delegate_.get()); |
| stream->set_cancel_on_loss(false); |
| |
| client_filter_->set_packets_to_drop(1); |
| |
| test::QuicTestMemSliceVector stream_data = CreateMemSliceVector("Hello"); |
| stream->WriteMemSlices(stream_data.span(), /*fin=*/false); |
| RunTasks(); |
| |
| // Stream data should make it despite packet loss. |
| ASSERT_TRUE(server_stream_delegate_->has_data()); |
| EXPECT_EQ(server_stream_delegate_->data()[stream_id], "Hello"); |
| } |
| |
| TEST_F(QuartcSessionTest, StreamRetransmissionDisabled) { |
| // Disable tail loss probe, otherwise test maybe flaky because dropped |
| // message will be retransmitted to detect tail loss. |
| QuartcSessionConfig session_config; |
| session_config.enable_tail_loss_probe = false; |
| CreateClientAndServerSessions(session_config); |
| |
| // Disable probing retransmissions, otherwise test maybe flaky because dropped |
| // message will be retransmitted to to probe for more bandwidth. |
| client_peer_->connection()->set_fill_up_link_during_probing(false); |
| |
| AwaitHandshake(); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| |
| // The client sends an ACK for the crypto handshake next. This must be |
| // flushed before we set the filter to drop the next packet, in order to |
| // ensure that the filter drops a data-bearing packet instead of just an ack. |
| RunTasks(); |
| |
| QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); |
| QuicStreamId stream_id = stream->id(); |
| stream->SetDelegate(client_stream_delegate_.get()); |
| stream->set_cancel_on_loss(true); |
| |
| client_filter_->set_packets_to_drop(1); |
| |
| test::QuicTestMemSliceVector stream_data = CreateMemSliceVector("Hello"); |
| stream->WriteMemSlices(stream_data.span(), /*fin=*/false); |
| simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1)); |
| |
| // Send another packet to trigger loss detection. |
| QuartcStream* stream_1 = client_peer_->CreateOutgoingBidirectionalStream(); |
| stream_1->SetDelegate(client_stream_delegate_.get()); |
| |
| test::QuicTestMemSliceVector stream_data_1 = |
| CreateMemSliceVector("Second message"); |
| stream_1->WriteMemSlices(stream_data_1.span(), /*fin=*/false); |
| RunTasks(); |
| |
| // QUIC should try to retransmit the first stream by loss detection. Instead, |
| // it will cancel itself. |
| EXPECT_THAT(server_stream_delegate_->data()[stream_id], testing::IsEmpty()); |
| |
| EXPECT_TRUE(client_peer_->IsClosedStream(stream_id)); |
| EXPECT_TRUE(server_peer_->IsClosedStream(stream_id)); |
| EXPECT_EQ(client_stream_delegate_->stream_error(stream_id), |
| QUIC_STREAM_CANCELLED); |
| EXPECT_EQ(server_stream_delegate_->stream_error(stream_id), |
| QUIC_STREAM_CANCELLED); |
| } |
| |
| TEST_F(QuartcSessionTest, LostDatagramNotifications) { |
| // TODO(b/134175506): Remove when IETF QUIC supports receive timestamps. |
| SetQuicReloadableFlag(quic_enable_version_99, false); |
| |
| // Disable tail loss probe, otherwise test maybe flaky because dropped |
| // message will be retransmitted to detect tail loss. |
| QuartcSessionConfig session_config; |
| session_config.enable_tail_loss_probe = false; |
| CreateClientAndServerSessions(session_config); |
| |
| // Disable probing retransmissions, otherwise test maybe flaky because dropped |
| // message will be retransmitted to to probe for more bandwidth. |
| client_peer_->connection()->set_fill_up_link_during_probing(false); |
| server_peer_->connection()->set_fill_up_link_during_probing(false); |
| |
| AwaitHandshake(); |
| ASSERT_TRUE(client_peer_->IsCryptoHandshakeConfirmed()); |
| ASSERT_TRUE(server_peer_->IsCryptoHandshakeConfirmed()); |
| |
| // The client sends an ACK for the crypto handshake next. This must be |
| // flushed before we set the filter to drop the next packet, in order to |
| // ensure that the filter drops a data-bearing packet instead of just an ack. |
| RunTasks(); |
| |
| // Drop the next packet. |
| client_filter_->set_packets_to_drop(1); |
| |
| test::QuicTestMemSliceVector message = |
| CreateMemSliceVector("This message will be lost"); |
| ASSERT_TRUE(client_peer_->SendOrQueueMessage(message.span(), 1)); |
| |
| RunTasks(); |
| |
| // Send another packet to elicit an ack and trigger loss detection. |
| message = CreateMemSliceVector("This message will arrive"); |
| ASSERT_TRUE(client_peer_->SendOrQueueMessage(message.span(), 2)); |
| |
| RunTasks(); |
| |
| EXPECT_THAT(server_session_delegate_->incoming_messages(), |
| ElementsAre("This message will arrive")); |
| EXPECT_THAT(client_session_delegate_->sent_datagram_ids(), ElementsAre(1, 2)); |
| EXPECT_THAT( |
| client_session_delegate_->acked_datagram_id_to_receive_timestamp(), |
| ElementsAre(Pair(2, Gt(QuicTime::Zero())))); |
| EXPECT_THAT(client_session_delegate_->lost_datagram_ids(), ElementsAre(1)); |
| } |
| |
| TEST_F(QuartcSessionTest, ServerRegistersAsWriteBlocked) { |
| // Initialize client and server session, but with the server write-blocked. |
| Init(); |
| server_transport_->SetWritable(false); |
| CreateClientAndServerSessions(QuartcSessionConfig(), /*init=*/false); |
| |
| // Let the client send a few copies of the CHLO. The server can't respond, as |
| // it's still write-blocked. |
| RunTasks(); |
| |
| // Making the server's transport writable should trigger a callback that |
| // reaches the server session, allowing it to write packets. |
| server_transport_->SetWritable(true); |
| |
| // Now the server should respond with the SHLO, encryption should be |
| // established, and data should flow normally. |
| // Note that if the server is *not* correctly registered as write-blocked, |
| // it will crash here (see b/124527328 for details). |
| AwaitHandshake(); |
| TestSendReceiveStreams(); |
| } |
| |
| TEST_F(QuartcSessionTest, PreSharedKeyHandshakeIs0RTT) { |
| QuartcSessionConfig session_config; |
| session_config.pre_shared_key = "foo"; |
| |
| // Client endpoint is created below. Destructing client endpoint |
| // causes issues with the simulator. |
| Init(/*create_client_endpoint=*/false); |
| |
| server_endpoint_->Connect(server_transport_.get()); |
| |
| client_endpoint_ = std::make_unique<QuartcClientEndpoint>( |
| simulator_.GetAlarmFactory(), simulator_.GetClock(), |
| simulator_.GetRandomGenerator(), client_session_delegate_.get(), |
| QuartcSessionConfig(), |
| // This is the key line here. It passes through the server config |
| // from the server to the client. |
| server_endpoint_->server_crypto_config()); |
| |
| client_endpoint_->Connect(client_transport_.get()); |
| |
| // Running for 1ms. This is shorter than the RTT, so the |
| // client session should be created, but server won't be created yet. |
| simulator_.RunFor(QuicTime::Delta::FromMilliseconds(1)); |
| |
| client_peer_ = client_session_delegate_->session(); |
| server_peer_ = server_session_delegate_->session(); |
| |
| ASSERT_NE(client_peer_, nullptr); |
| ASSERT_EQ(server_peer_, nullptr); |
| |
| // Write data to the client before running tasks. This should be sent by the |
| // client and received by the server if the handshake is 0RTT. |
| // If this test fails, add 'RunTasks()' above, and see what error is sent |
| // by the server in the rejection message. |
| QuartcStream* stream = client_peer_->CreateOutgoingBidirectionalStream(); |
| ASSERT_NE(stream, nullptr); |
| QuicStreamId stream_id = stream->id(); |
| stream->SetDelegate(client_stream_delegate_.get()); |
| |
| char message[] = "Hello in 0RTTs!"; |
| test::QuicTestMemSliceVector data({std::make_pair(message, strlen(message))}); |
| stream->WriteMemSlices(data.span(), /*fin=*/false); |
| |
| // This will now run the rest of the connection. But the |
| // Server peer will receive the CHLO and message after 1 delay. |
| simulator_.RunFor(kPropagationDelayAndABit); |
| |
| // If we can decrypt the data, it means that 0 rtt was successful. |
| // This is because we waited only a propagation delay. So if the decryption |
| // failed, we would send sREJ instead of SHLO, but it wouldn't be delivered to |
| // the client yet. |
| ASSERT_TRUE(server_stream_delegate_->has_data()); |
| EXPECT_EQ(server_stream_delegate_->data()[stream_id], message); |
| } |
| |
| } // namespace |
| |
| } // namespace quic |