gfe-relnote: In QUIC T049+, support sending coalesced packets. PiperOrigin-RevId: 278634718 Change-Id: If6eadcb1a165e0cf4993f60d3a4faeb108fe8ccb
diff --git a/quic/core/http/end_to_end_test.cc b/quic/core/http/end_to_end_test.cc index 2f54a3c..2507c7a 100644 --- a/quic/core/http/end_to_end_test.cc +++ b/quic/core/http/end_to_end_test.cc
@@ -680,6 +680,26 @@ EXPECT_EQ("200", client_->response_headers()->find(":status")->second); } +TEST_P(EndToEndTestWithTls, SendAndReceiveCoalescedPackets) { + ASSERT_TRUE(Initialize()); + if (!GetClientConnection()->version().CanSendCoalescedPackets()) { + return; + } + EXPECT_EQ(kFooResponseBody, client_->SendSynchronousRequest("/foo")); + EXPECT_EQ("200", client_->response_headers()->find(":status")->second); + // Verify both endpoints successfully process coalesced packets. + QuicConnectionStats client_stats = GetClientConnection()->GetStats(); + EXPECT_LT(0u, client_stats.num_coalesced_packets_received); + EXPECT_EQ(client_stats.num_coalesced_packets_processed, + client_stats.num_coalesced_packets_received); + server_thread_->Pause(); + QuicConnectionStats server_stats = GetServerConnection()->GetStats(); + EXPECT_LT(0u, server_stats.num_coalesced_packets_received); + EXPECT_EQ(server_stats.num_coalesced_packets_processed, + server_stats.num_coalesced_packets_received); + server_thread_->Resume(); +} + // Simple transaction, but set a non-default ack delay at the client // and ensure it gets to the server. TEST_P(EndToEndTest, SimpleRequestResponseWithAckDelayChange) {
diff --git a/quic/core/quic_coalesced_packet.cc b/quic/core/quic_coalesced_packet.cc index 6d7f872..3dd9b24 100644 --- a/quic/core/quic_coalesced_packet.cc +++ b/quic/core/quic_coalesced_packet.cc
@@ -46,8 +46,7 @@ return false; } if (max_packet_length_ != current_max_packet_length) { - QUIC_DLOG(INFO) - << "Do not try to coalesce packet when max packet length changed."; + QUIC_BUG << "Max packet length changes in the middle of the write path"; return false; } if (!encrypted_buffers_[packet.encryption_level].empty() || @@ -67,6 +66,9 @@ << ", encrypted_length: " << packet.encrypted_length << ", current length: " << length_ << ", max_packet_length: " << max_packet_length_; + if (length_ > 0) { + QUIC_CODE_COUNT(QUIC_SUCCESSFULLY_COALESCED_MULTIPLE_PACKETS); + } length_ += packet.encrypted_length; if (packet.encryption_level == ENCRYPTION_INITIAL) { // Save a copy of ENCRYPTION_INITIAL packet (excluding encrypted buffer, as
diff --git a/quic/core/quic_coalesced_packet.h b/quic/core/quic_coalesced_packet.h index cb4825a..f03f875 100644 --- a/quic/core/quic_coalesced_packet.h +++ b/quic/core/quic_coalesced_packet.h
@@ -38,6 +38,10 @@ return initial_packet_.get(); } + const QuicSocketAddress& self_address() const { return self_address_; } + + const QuicSocketAddress& peer_address() const { return peer_address_; } + QuicPacketLength length() const { return length_; } QuicPacketLength max_packet_length() const { return max_packet_length_; }
diff --git a/quic/core/quic_coalesced_packet_test.cc b/quic/core/quic_coalesced_packet_test.cc index 6eb5a61..8213480 100644 --- a/quic/core/quic_coalesced_packet_test.cc +++ b/quic/core/quic_coalesced_packet_test.cc
@@ -4,6 +4,7 @@ #include "net/third_party/quiche/src/quic/core/quic_coalesced_packet.h" +#include "net/third_party/quiche/src/quic/platform/api/quic_expect_bug.h" #include "net/third_party/quiche/src/quic/platform/api/quic_test.h" #include "net/third_party/quiche/src/quic/test_tools/quic_test_utils.h" @@ -65,8 +66,9 @@ SerializedPacket packet6(QuicPacketNumber(6), PACKET_4BYTE_PACKET_NUMBER, buffer, 100, false, false); packet6.encryption_level = ENCRYPTION_FORWARD_SECURE; - EXPECT_FALSE(coalesced.MaybeCoalescePacket(packet6, self_address, - peer_address, &allocator, 1000)); + EXPECT_QUIC_BUG(coalesced.MaybeCoalescePacket(packet6, self_address, + peer_address, &allocator, 1000), + "Max packet length changes in the middle of the write path"); EXPECT_EQ(1500u, coalesced.max_packet_length()); EXPECT_EQ(1000u, coalesced.length()); }
diff --git a/quic/core/quic_connection.cc b/quic/core/quic_connection.cc index 443ecbc..64f7eea 100644 --- a/quic/core/quic_connection.cc +++ b/quic/core/quic_connection.cc
@@ -179,6 +179,17 @@ QuicConnection* connection_; }; +// When the clearer goes out of scope, the coalesced packet gets cleared. +class ScopedCoalescedPacketClearer { + public: + explicit ScopedCoalescedPacketClearer(QuicCoalescedPacket* coalesced) + : coalesced_(coalesced) {} + ~ScopedCoalescedPacketClearer() { coalesced_->Clear(); } + + private: + QuicCoalescedPacket* coalesced_; // Unowned. +}; + // Whether this incoming packet is allowed to replace our connection ID. bool PacketCanReplaceConnectionId(const QuicPacketHeader& header, Perspective perspective) { @@ -327,7 +338,8 @@ bytes_sent_before_address_validation_(0), address_validated_(false), treat_queued_packets_as_sent_( - GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)), + GetQuicReloadableFlag(quic_treat_queued_packets_as_sent) || + version().CanSendCoalescedPackets()), mtu_discovery_v2_(GetQuicReloadableFlag(quic_mtu_discovery_v2)) { QUIC_DLOG(INFO) << ENDPOINT << "Created connection with server connection ID " << server_connection_id @@ -2197,7 +2209,8 @@ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); return true; } - SerializedPacketFate fate = DeterminePacketFate(); + SerializedPacketFate fate = DeterminePacketFate( + /*is_mtu_discovery=*/packet->encrypted_length > long_term_mtu_); // Termination packets are encrypted and saved, so don't exit early. const bool is_termination_packet = IsTerminationPacket(*packet); if (!treat_queued_packets_as_sent_ && HandleWriteBlocked() && @@ -2238,7 +2251,8 @@ : " ack only ") << ", encryption level: " << EncryptionLevelToString(packet->encryption_level) - << ", encrypted length:" << encrypted_length; + << ", encrypted length:" << encrypted_length + << ", fate: " << SerializedPacketFateToString(fate); QUIC_DVLOG(2) << ENDPOINT << "packet(" << packet_number << "): " << std::endl << QuicTextUtils::HexDump(QuicStringPiece( packet->encrypted_buffer, encrypted_length)); @@ -2261,7 +2275,34 @@ WriteResult result(WRITE_STATUS_OK, encrypted_length); switch (fate) { case COALESCE: - DCHECK(false); + QUIC_BUG_IF(!version().CanSendCoalescedPackets()); + if (!coalesced_packet_.MaybeCoalescePacket( + *packet, self_address(), peer_address(), + helper_->GetStreamSendBufferAllocator(), + packet_generator_.GetCurrentMaxPacketLength())) { + // Failed to coalesce packet, flush current coalesced packet. + if (!FlushCoalescedPacket()) { + // Failed to flush coalesced packet, write error has been handled. + return false; + } + if (!coalesced_packet_.MaybeCoalescePacket( + *packet, self_address(), peer_address(), + helper_->GetStreamSendBufferAllocator(), + packet_generator_.GetCurrentMaxPacketLength())) { + // Failed to coalesce packet even it is the only packet, raise a write + // error. + QUIC_DLOG(ERROR) << ENDPOINT << "Failed to coalesce packet"; + result.error_code = WRITE_STATUS_FAILED_TO_COALESCE_PACKET; + break; + } + } + if (coalesced_packet_.length() < coalesced_packet_.max_packet_length()) { + QUIC_DVLOG(1) << ENDPOINT << "Trying to set soft max packet length to " + << coalesced_packet_.max_packet_length() - + coalesced_packet_.length(); + packet_generator_.SetSoftMaxPacketLength( + coalesced_packet_.max_packet_length() - coalesced_packet_.length()); + } break; case BUFFER: DCHECK(treat_queued_packets_as_sent_); @@ -2275,6 +2316,11 @@ self_address().host(), peer_address(), per_packet_options_); break; + case FAILED_TO_WRITE_COALESCED_PACKET: + // Failed to send existing coalesced packet when determining packet fate, + // write error has been handled. + QUIC_BUG_IF(!version().CanSendCoalescedPackets()); + return false; default: DCHECK(false); break; @@ -2473,6 +2519,12 @@ } char* QuicConnection::GetPacketBuffer() { + if (version().CanSendCoalescedPackets() && + !sent_packet_manager_.forward_secure_packet_acked()) { + // Do not use writer's packet buffer for coalesced packets which may contain + // multiple QUIC packets. + return nullptr; + } return writer_->GetNextWriteLocation(self_address().host(), peer_address()); } @@ -2838,12 +2890,13 @@ void QuicConnection::QueueCoalescedPacket(const QuicEncryptedPacket& packet) { QUIC_DVLOG(1) << ENDPOINT << "Queueing coalesced packet."; - coalesced_packets_.push_back(packet.Clone()); + received_coalesced_packets_.push_back(packet.Clone()); + ++stats_.num_coalesced_packets_received; } void QuicConnection::MaybeProcessCoalescedPackets() { bool processed = false; - while (connected_ && !coalesced_packets_.empty()) { + while (connected_ && !received_coalesced_packets_.empty()) { // Making sure there are no pending frames when processing the next // coalesced packet because the queued ack frame may change. packet_generator_.FlushAllQueuedFrames(); @@ -2852,12 +2905,13 @@ } std::unique_ptr<QuicEncryptedPacket> packet = - std::move(coalesced_packets_.front()); - coalesced_packets_.pop_front(); + std::move(received_coalesced_packets_.front()); + received_coalesced_packets_.pop_front(); QUIC_DVLOG(1) << ENDPOINT << "Processing coalesced packet"; if (framer_.ProcessPacket(*packet)) { processed = true; + ++stats_.num_coalesced_packets_processed; } else { // If we are unable to decrypt this packet, it might be // because the CHLO or SHLO packet was lost. @@ -2895,6 +2949,9 @@ if (!GetQuicReloadableFlag(quic_close_all_encryptions_levels)) { QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet."; SetDefaultEncryptionLevel(GetConnectionCloseEncryptionLevel()); + if (version().CanSendCoalescedPackets()) { + coalesced_packet_.Clear(); + } ClearQueuedPackets(); // If there was a packet write error, write the smallest close possible. ScopedPacketFlusher flusher(this); @@ -2911,6 +2968,9 @@ framer_.current_received_frame_type()); packet_generator_.ConsumeRetransmittableControlFrame(QuicFrame(frame)); packet_generator_.FlushAllQueuedFrames(); + if (version().CanSendCoalescedPackets()) { + FlushCoalescedPacket(); + } ClearQueuedPackets(); return; } @@ -2926,6 +2986,9 @@ QUIC_DLOG(INFO) << ENDPOINT << "Sending connection close packet at level: " << EncryptionLevelToString(level); SetDefaultEncryptionLevel(level); + if (version().CanSendCoalescedPackets()) { + coalesced_packet_.Clear(); + } ClearQueuedPackets(); // If there was a packet write error, write the smallest close possible. // When multiple packet number spaces are supported, an ACK frame will @@ -2941,6 +3004,12 @@ framer_.current_received_frame_type()); packet_generator_.ConsumeRetransmittableControlFrame(QuicFrame(frame)); packet_generator_.FlushAllQueuedFrames(); + if (!version().CanSendCoalescedPackets()) { + ClearQueuedPackets(); + } + } + if (version().CanSendCoalescedPackets()) { + FlushCoalescedPacket(); ClearQueuedPackets(); } SetDefaultEncryptionLevel(current_encryption_level); @@ -3269,6 +3338,9 @@ } } connection_->packet_generator_.Flush(); + if (connection_->version().CanSendCoalescedPackets()) { + connection_->FlushCoalescedPacket(); + } connection_->FlushPackets(); // Reset transmission type. connection_->SetTransmissionType(NOT_RETRANSMISSION); @@ -3310,6 +3382,16 @@ self_address(self_address), peer_address(peer_address) {} +QuicConnection::BufferedPacket::BufferedPacket( + char* encrypted_buffer, + QuicPacketLength encrypted_length, + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address) + : encrypted_buffer(CopyBuffer(encrypted_buffer, encrypted_length), + encrypted_length), + self_address(self_address), + peer_address(peer_address) {} + QuicConnection::BufferedPacket::~BufferedPacket() { delete[] encrypted_buffer.data(); } @@ -3906,6 +3988,65 @@ visitor_->OnAckNeedsRetransmittableFrame(); } +bool QuicConnection::FlushCoalescedPacket() { + ScopedCoalescedPacketClearer clearer(&coalesced_packet_); + if (!version().CanSendCoalescedPackets()) { + QUIC_BUG_IF(coalesced_packet_.length() > 0); + return true; + } + if (coalesced_packet_.length() == 0) { + return true; + } + QUIC_DVLOG(1) << ENDPOINT << "Sending coalesced packet"; + char buffer[kMaxOutgoingPacketSize]; + const size_t length = packet_generator_.SerializeCoalescedPacket( + coalesced_packet_, buffer, coalesced_packet_.max_packet_length()); + if (length == 0) { + return false; + } + + if (!buffered_packets_.empty() || HandleWriteBlocked()) { + QUIC_DVLOG(1) << ENDPOINT + << "Buffering coalesced packet of len: " << length; + buffered_packets_.emplace_back(buffer, length, + coalesced_packet_.self_address(), + coalesced_packet_.peer_address()); + return true; + } + + WriteResult result = writer_->WritePacket( + buffer, length, coalesced_packet_.self_address().host(), + coalesced_packet_.peer_address(), per_packet_options_); + if (IsWriteError(result.status)) { + OnWriteError(result.error_code); + return false; + } + if (IsWriteBlockedStatus(result.status)) { + visitor_->OnWriteBlocked(); + if (result.status != WRITE_STATUS_BLOCKED_DATA_BUFFERED) { + QUIC_DVLOG(1) << ENDPOINT + << "Buffering coalesced packet of len: " << length; + buffered_packets_.emplace_back(buffer, length, + coalesced_packet_.self_address(), + coalesced_packet_.peer_address()); + } + } + // Account for added padding. + if (length > coalesced_packet_.length()) { + size_t padding_size = length - coalesced_packet_.length(); + if (EnforceAntiAmplificationLimit()) { + bytes_sent_before_address_validation_ += padding_size; + } + stats_.bytes_sent += padding_size; + if (coalesced_packet_.initial_packet() != nullptr && + coalesced_packet_.initial_packet()->transmission_type != + NOT_RETRANSMISSION) { + stats_.bytes_retransmitted += padding_size; + } + } + return true; +} + void QuicConnection::MaybeEnableMultiplePacketNumberSpacesSupport() { if (version().handshake_protocol != PROTOCOL_TLS1_3) { return; @@ -3972,9 +4113,23 @@ bytes_received_before_address_validation_; } -QuicConnection::SerializedPacketFate QuicConnection::DeterminePacketFate() { - if (treat_queued_packets_as_sent_ && - (!buffered_packets_.empty() || HandleWriteBlocked())) { +SerializedPacketFate QuicConnection::DeterminePacketFate( + bool is_mtu_discovery) { + if (!treat_queued_packets_as_sent_) { + return SEND_TO_WRITER; + } + if (version().CanSendCoalescedPackets() && + !sent_packet_manager_.forward_secure_packet_acked() && + !is_mtu_discovery) { + // Before receiving ACK for any 1-RTT packets, always try to coalesce + // packet (except MTU discovery packet). + return COALESCE; + } + // Packet cannot be coalesced, flush existing coalesced packet. + if (version().CanSendCoalescedPackets() && !FlushCoalescedPacket()) { + return FAILED_TO_WRITE_COALESCED_PACKET; + } + if (!buffered_packets_.empty() || HandleWriteBlocked()) { return BUFFER; } return SEND_TO_WRITER;
diff --git a/quic/core/quic_connection.h b/quic/core/quic_connection.h index 9c57f86..7bbd931 100644 --- a/quic/core/quic_connection.h +++ b/quic/core/quic_connection.h
@@ -887,6 +887,10 @@ // or the one sent after an IETF Retry. void InstallInitialCrypters(QuicConnectionId connection_id); + bool treat_queued_packets_as_sent() const { + return treat_queued_packets_as_sent_; + } + protected: // Calls cancel() on all the alarms owned by this connection. void CancelAllAlarms(); @@ -968,13 +972,6 @@ typedef std::list<SerializedPacket> QueuedPacketList; - // Indicates the fate of a serialized packet in WritePacket(). - enum SerializedPacketFate : uint8_t { - COALESCE, // Try to coalesce packet. - BUFFER, // Buffer packet in buffered_packets_. - SEND_TO_WRITER, // Send packet to writer. - }; - // BufferedPacket stores necessary information (encrypted buffer and self/peer // addresses) of those packets which are serialized but failed to send because // socket is blocked. From unacked packet map and send algorithm's @@ -983,6 +980,10 @@ BufferedPacket(const SerializedPacket& packet, const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address); + BufferedPacket(char* encrypted_buffer, + QuicPacketLength encrypted_length, + const QuicSocketAddress& self_address, + const QuicSocketAddress& peer_address); BufferedPacket(const BufferedPacket& other) = delete; BufferedPacket(const BufferedPacket&& other) = delete; @@ -1137,8 +1138,12 @@ // and flags. void MaybeEnableMultiplePacketNumberSpacesSupport(); - // Returns packet fate when trying to write a packet. - SerializedPacketFate DeterminePacketFate(); + // Returns packet fate when trying to write a packet via WritePacket(). + SerializedPacketFate DeterminePacketFate(bool is_mtu_discovery); + + // Serialize and send coalesced_packet. Returns false if serialization fails + // or the write causes errors, otherwise, returns true. + bool FlushCoalescedPacket(); // Returns the encryption level the connection close packet should be sent at, // which is the highest encryption level that peer can guarantee to process. @@ -1250,7 +1255,7 @@ // Collection of coalesced packets which were received while processing // the current packet. - QuicDeque<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_; + QuicDeque<std::unique_ptr<QuicEncryptedPacket>> received_coalesced_packets_; // Maximum number of undecryptable packets the connection will store. size_t max_undecryptable_packets_; @@ -1503,6 +1508,11 @@ // treat_queued_packets_as_sent_ is true. std::list<BufferedPacket> buffered_packets_; + // Used to coalesce packets of different encryption level into the same UDP + // datagram. Connection stops trying to coalesce packets if a forward secure + // packet gets acknowledged. + QuicCoalescedPacket coalesced_packet_; + // Latched value of quic_treat_queued_packets_as_sent. const bool treat_queued_packets_as_sent_;
diff --git a/quic/core/quic_connection_stats.cc b/quic/core/quic_connection_stats.cc index 128bc93..e3652b0 100644 --- a/quic/core/quic_connection_stats.cc +++ b/quic/core/quic_connection_stats.cc
@@ -48,7 +48,9 @@ blocked_frames_received(0), blocked_frames_sent(0), num_connectivity_probing_received(0), - retry_packet_processed(false) {} + retry_packet_processed(false), + num_coalesced_packets_received(0), + num_coalesced_packets_processed(0) {} QuicConnectionStats::QuicConnectionStats(const QuicConnectionStats& other) = default; @@ -98,6 +100,9 @@ << s.num_connectivity_probing_received; os << " retry_packet_processed: " << (s.retry_packet_processed ? "yes" : "no"); + os << " num_coalesced_packets_received: " << s.num_coalesced_packets_received; + os << " num_coalesced_packets_processed: " + << s.num_coalesced_packets_processed; os << " }"; return os;
diff --git a/quic/core/quic_connection_stats.h b/quic/core/quic_connection_stats.h index 805afd1..a67d867 100644 --- a/quic/core/quic_connection_stats.h +++ b/quic/core/quic_connection_stats.h
@@ -109,6 +109,11 @@ // Whether a RETRY packet was successfully processed. bool retry_packet_processed; + + // Number of received coalesced packets. + uint64_t num_coalesced_packets_received; + // Number of successfully processed coalesced packets. + uint64_t num_coalesced_packets_processed; }; } // namespace quic
diff --git a/quic/core/quic_connection_test.cc b/quic/core/quic_connection_test.cc index b6aa85e..5a2e0ef 100644 --- a/quic/core/quic_connection_test.cc +++ b/quic/core/quic_connection_test.cc
@@ -514,6 +514,10 @@ return framer_.path_response_frames(); } + const QuicEncryptedPacket* coalesced_packet() const { + return framer_.coalesced_packet(); + } + size_t last_packet_size() { return last_packet_size_; } const QuicPacketHeader& last_packet_header() const { @@ -631,6 +635,7 @@ HasRetransmittableData retransmittable, bool has_ack, bool has_pending_frames) { + ScopedPacketFlusher flusher(this); char buffer[kMaxOutgoingPacketSize]; size_t encrypted_length = QuicConnectionPeer::GetFramer(this)->EncryptPayload( @@ -641,7 +646,7 @@ encrypted_length, has_ack, has_pending_frames); if (retransmittable == HAS_RETRANSMITTABLE_DATA) { serialized_packet.retransmittable_frames.push_back( - QuicFrame(QuicStreamFrame())); + QuicFrame(QuicPingFrame())); } OnSerializedPacket(&serialized_packet); } @@ -2010,7 +2015,7 @@ writer_->SetWritable(); connection_.SendConnectivityProbingPacket(writer_.get(), connection_.peer_address()); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(0); connection_.OnCanWrite(); return; @@ -2046,7 +2051,7 @@ connection_.SendStreamDataWithString(/*id=*/2, "foo", 0, NO_FIN); EXPECT_FALSE(connection_.connected()); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { // No need to buffer packets. EXPECT_EQ(0u, connection_.NumQueuedPackets()); } else { @@ -3054,7 +3059,9 @@ EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); QuicConnection::ScopedPacketFlusher flusher(&connection_); connection_.SendStreamData3(); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); connection_.SendCryptoStreamData(); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); } EXPECT_EQ(0u, connection_.NumQueuedPackets()); EXPECT_FALSE(connection_.HasQueuedData()); @@ -3352,7 +3359,7 @@ BlockOnNextWrite(); QuicStreamId stream_id = 2; - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); @@ -3364,7 +3371,7 @@ // Unblock the connection and verify that the RST_STREAM is sent and the data // packet is sent. - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)) .Times(AtLeast(1)); } else { @@ -3569,7 +3576,7 @@ EXPECT_CALL(*loss_algorithm_, DetectLosses(_, _, _, _, _, _)) .WillOnce(SetArgPointee<5>(lost_packets)); EXPECT_CALL(*send_algorithm_, OnCongestionEvent(true, _, _, _, _)); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(4), _, _)) .Times(1); } else { @@ -3584,7 +3591,7 @@ QuicAckFrame ack_all = InitAckFrame(3); ProcessAckPacket(&ack_all); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, QuicPacketNumber(4), _, _)) .Times(0); } else { @@ -3643,7 +3650,7 @@ // Block the writer and ensure they're queued. BlockOnNextWrite(); clock_.AdvanceTime(DefaultRetransmissionTime()); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); @@ -3655,7 +3662,7 @@ writer_->SetWritable(); clock_.AdvanceTime(QuicTime::Delta::FromMicroseconds( 2 * DefaultRetransmissionTime().ToMicroseconds())); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); } else { // 2 RTOs + 1 TLP, which is buggy. @@ -3680,13 +3687,13 @@ TEST_P(QuicConnectionTest, WriteBlockedThenSent) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); BlockOnNextWrite(); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); } connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); } else { EXPECT_FALSE(connection_.GetRetransmissionAlarm()->IsSet()); @@ -3696,7 +3703,7 @@ // The second packet should also be queued, in order to ensure packets are // never sent out of order. writer_->SetWritable(); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); @@ -3705,7 +3712,7 @@ EXPECT_EQ(2u, connection_.NumQueuedPackets()); // Now both are sent in order when we unblock. - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(2); @@ -4230,9 +4237,12 @@ // Manually mark both packets for retransmission. connection_.RetransmitUnackedPackets(ALL_UNACKED_RETRANSMISSION); - - // Packet should have been sent with ENCRYPTION_INITIAL. - EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet()); + if (!connection_.version().CanSendCoalescedPackets()) { + // Packet should have been sent with ENCRYPTION_INITIAL. + // If connection can send coalesced packet, both retransmissions will be + // coalesced in the same UDP datagram. + EXPECT_EQ(0x01010101u, writer_->final_bytes_of_previous_packet()); + } // Packet should have been sent with ENCRYPTION_ZERO_RTT. EXPECT_EQ(0x02020202u, writer_->final_bytes_of_last_packet()); @@ -4277,7 +4287,7 @@ // Simulate the retransmission alarm firing and the socket blocking. BlockOnNextWrite(); clock_.AdvanceTime(DefaultRetransmissionTime()); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); @@ -4431,13 +4441,13 @@ TEST_P(QuicConnectionTest, SetRTOAfterWritingToSocket) { BlockOnNextWrite(); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); } connection_.SendStreamDataWithString(1, "foo", 0, NO_FIN); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); } else { // Make sure that RTO is not started when the packet is queued. @@ -4446,7 +4456,7 @@ // Test that RTO is started once we write to the socket. writer_->SetWritable(); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); @@ -6492,7 +6502,7 @@ } // Check that ack is sent and that delayed ack alarm is reset. if (GetParam().no_stop_waiting) { - EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(writer_->padding_frames().size() + 1u, writer_->frame_count()); EXPECT_TRUE(writer_->stop_waiting_frames().empty()); } else { EXPECT_EQ(2u, writer_->frame_count()); @@ -6509,7 +6519,7 @@ ENCRYPTION_ZERO_RTT); // Check that ack is sent and that delayed ack alarm is reset. if (GetParam().no_stop_waiting) { - EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(writer_->padding_frames().size() + 1u, writer_->frame_count()); EXPECT_TRUE(writer_->stop_waiting_frames().empty()); } else { EXPECT_EQ(2u, writer_->frame_count()); @@ -6645,7 +6655,7 @@ } // Check that ack is sent and that delayed ack alarm is reset. if (GetParam().no_stop_waiting) { - EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(writer_->padding_frames().size() + 1u, writer_->frame_count()); EXPECT_TRUE(writer_->stop_waiting_frames().empty()); } else { EXPECT_EQ(2u, writer_->frame_count()); @@ -6662,7 +6672,7 @@ ENCRYPTION_ZERO_RTT); // Check that ack is sent and that delayed ack alarm is reset. if (GetParam().no_stop_waiting) { - EXPECT_EQ(1u, writer_->frame_count()); + EXPECT_EQ(writer_->padding_frames().size() + 1u, writer_->frame_count()); EXPECT_TRUE(writer_->stop_waiting_frames().empty()); } else { EXPECT_EQ(2u, writer_->frame_count()); @@ -8049,7 +8059,7 @@ EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(true)); BlockOnNextWrite(); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); @@ -8061,7 +8071,7 @@ writer_->SetWritable(); CongestionBlockWrites(); EXPECT_CALL(visitor_, WillingAndAbleToWrite()).WillRepeatedly(Return(false)); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); @@ -8199,8 +8209,15 @@ .WillRepeatedly(Invoke([this, &sent_count](const SerializedPacket&, TransmissionType, QuicTime) { ASSERT_EQ(1u, writer_->stream_frames().size()); - // Identify the frames by stream offset (0, 3, 6, 0, 3...). - EXPECT_EQ(3 * (sent_count % 3), writer_->stream_frames()[0]->offset); + if (connection_.version().CanSendCoalescedPackets()) { + // There is a delay of sending coalesced packet, so (6, 0, 3, 6, + // 0...). + EXPECT_EQ(3 * ((sent_count + 2) % 3), + writer_->stream_frames()[0]->offset); + } else { + // Identify the frames by stream offset (0, 3, 6, 0, 3...). + EXPECT_EQ(3 * (sent_count % 3), writer_->stream_frames()[0]->offset); + } sent_count++; })); EXPECT_CALL(*send_algorithm_, ShouldSendProbingPacket()) @@ -8681,14 +8698,14 @@ TEST_P(QuicConnectionTest, WriteBlockedWithInvalidAck) { EXPECT_CALL(visitor_, OnSuccessfulVersionNegotiation(_)); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(visitor_, OnConnectionClosed(_, _)).Times(0); } else { EXPECT_CALL(visitor_, OnConnectionClosed(_, _)) .WillOnce(Invoke(this, &QuicConnectionTest::SaveConnectionCloseFrame)); } BlockOnNextWrite(); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); @@ -8696,11 +8713,11 @@ connection_.SendStreamDataWithString(5, "foo", 0, FIN); // This causes connection to be closed because packet 1 has not been sent yet. QuicAckFrame frame = InitAckFrame(1); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnCongestionEvent(_, _, _, _, _)); } ProcessAckPacket(1, &frame); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_EQ(0, connection_close_frame_count_); } else { EXPECT_EQ(1, connection_close_frame_count_); @@ -9359,7 +9376,7 @@ EXPECT_CALL(visitor_, OnWriteBlocked()).Times(AtLeast(1)); SendRstStream(stream_id, QUIC_ERROR_PROCESSING_STREAM, 3); - if (GetQuicReloadableFlag(quic_treat_queued_packets_as_sent)) { + if (connection_.treat_queued_packets_as_sent()) { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(1); } else { EXPECT_CALL(*send_algorithm_, OnPacketSent(_, _, _, _, _)).Times(0); @@ -9731,6 +9748,44 @@ EXPECT_TRUE(connection_.GetRetransmissionAlarm()->IsSet()); } +TEST_P(QuicConnectionTest, SendCoalescedPackets) { + if (!connection_.version().CanSendCoalescedPackets()) { + return; + } + { + QuicConnection::ScopedPacketFlusher flusher(&connection_); + use_tagging_decrypter(); + connection_.SetEncrypter(ENCRYPTION_INITIAL, + std::make_unique<TaggingEncrypter>(0x01)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_INITIAL); + connection_.SendCryptoDataWithString("foo", 0); + // Verify this packet is on hold. + EXPECT_EQ(0u, writer_->packets_write_attempts()); + + connection_.SetEncrypter(ENCRYPTION_HANDSHAKE, + std::make_unique<TaggingEncrypter>(0x02)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_HANDSHAKE); + connection_.SendCryptoDataWithString("bar", 3); + EXPECT_EQ(0u, writer_->packets_write_attempts()); + + connection_.SetEncrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<TaggingEncrypter>(0x03)); + connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE); + SendStreamDataToPeer(2, "baz", 3, NO_FIN, nullptr); + } + // Verify all 3 packets are coalesced in the same UDP datagram. + EXPECT_EQ(1u, writer_->packets_write_attempts()); + EXPECT_EQ(0x03030303u, writer_->final_bytes_of_last_packet()); + // Verify the packet is padded to full. + EXPECT_EQ(connection_.max_packet_length(), writer_->last_packet_size()); + + // Verify packet process. + EXPECT_EQ(1u, writer_->crypto_frames().size()); + EXPECT_EQ(0u, writer_->stream_frames().size()); + // Verify there is coalesced packet. + EXPECT_NE(nullptr, writer_->coalesced_packet()); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc index 9f7f160..ac467a2 100644 --- a/quic/core/quic_packet_creator.cc +++ b/quic/core/quic_packet_creator.cc
@@ -963,6 +963,7 @@ QUIC_BUG << "Try to serialize coalesced packet with pending frames"; return 0; } + RemoveSoftMaxPacketLength(); QUIC_BUG_IF(coalesced.length() == 0) << "Attempt to serialize empty coalesced packet"; size_t packet_length = 0; @@ -1512,7 +1513,8 @@ packet_.has_crypto_handshake = IS_HANDSHAKE; } } else { - if (populate_nonretransmittable_frames_) { + if (populate_nonretransmittable_frames_ || + framer_->version().CanSendCoalescedPackets()) { if (frame.type == PADDING_FRAME && frame.padding_frame.num_padding_bytes == -1) { // Populate the actual length of full padding frame, such that one can @@ -1603,6 +1605,25 @@ needs_full_padding_ = true; } + // Packet coalescer pads INITIAL packets, so the creator should not. + if (framer_->version().CanSendCoalescedPackets() && + (packet_.encryption_level == ENCRYPTION_INITIAL || + packet_.encryption_level == ENCRYPTION_HANDSHAKE)) { + // TODO(fayang): MTU discovery packets should not ever be sent as + // ENCRYPTION_INITIAL or ENCRYPTION_HANDSHAKE. + bool is_mtu_discovery = false; + for (const auto& frame : packet_.nonretransmittable_frames) { + if (frame.type == MTU_DISCOVERY_FRAME) { + is_mtu_discovery = true; + break; + } + } + if (!is_mtu_discovery) { + // Do not add full padding if connection tries to coalesce packet. + needs_full_padding_ = false; + } + } + // Header protection requires a minimum plaintext packet size. size_t extra_padding_bytes = 0; if (framer_->version().HasHeaderProtection()) {
diff --git a/quic/core/quic_packet_creator_test.cc b/quic/core/quic_packet_creator_test.cc index 0ba7bd1..4efcbfe 100644 --- a/quic/core/quic_packet_creator_test.cc +++ b/quic/core/quic_packet_creator_test.cc
@@ -539,8 +539,10 @@ // If there is not enough space in the packet to fit a padding frame // (1 byte) and to expand the stream frame (another 2 bytes) the packet // will not be padded. - if (bytes_free < 3 && - !QuicVersionUsesCryptoFrames(client_framer_.transport_version())) { + // Padding is skipped when we try to send coalesced packets. + if ((bytes_free < 3 && + !QuicVersionUsesCryptoFrames(client_framer_.transport_version())) || + client_framer_.version().CanSendCoalescedPackets()) { EXPECT_EQ(kDefaultMaxPacketSize - bytes_free, serialized_packet_.encrypted_length); } else { @@ -2072,7 +2074,8 @@ } TEST_P(QuicPacketCreatorTest, SaveNonRetransmittableFrames) { - if (!GetQuicReloadableFlag(quic_populate_nonretransmittable_frames)) { + if (!GetQuicReloadableFlag(quic_populate_nonretransmittable_frames) && + !client_framer_.version().CanSendCoalescedPackets()) { return; } QuicAckFrame ack_frame(InitAckFrame(1));
diff --git a/quic/core/quic_packet_generator.cc b/quic/core/quic_packet_generator.cc index de548ac..f4a5773 100644 --- a/quic/core/quic_packet_generator.cc +++ b/quic/core/quic_packet_generator.cc
@@ -522,6 +522,18 @@ packet_creator_.SetClientConnectionId(client_connection_id); } +size_t QuicPacketGenerator::SerializeCoalescedPacket( + const QuicCoalescedPacket& coalesced, + char* buffer, + size_t buffer_len) { + return packet_creator_.SerializeCoalescedPacket(coalesced, buffer, + buffer_len); +} + +void QuicPacketGenerator::SetSoftMaxPacketLength(QuicByteCount length) { + packet_creator_.SetSoftMaxPacketLength(length); +} + void QuicPacketGenerator::set_fully_pad_crypto_handshake_packets( bool new_value) { if (packet_creator_.combine_generator_and_creator()) {
diff --git a/quic/core/quic_packet_generator.h b/quic/core/quic_packet_generator.h index 2a7f996..efeed75 100644 --- a/quic/core/quic_packet_generator.h +++ b/quic/core/quic_packet_generator.h
@@ -211,6 +211,17 @@ // Update the client connection ID used in outgoing packets. void SetClientConnectionId(QuicConnectionId client_connection_id); + // Serializes |coalesced| to provided |buffer|, returns coalesced packet + // length if serialization succeeds. Otherwise, returns 0. + size_t SerializeCoalescedPacket(const QuicCoalescedPacket& coalesced, + char* buffer, + size_t buffer_len); + + // Set a soft maximum packet length in the creator. If a packet cannot be + // successfully created, creator will remove the soft limit and use the actual + // max packet length. + void SetSoftMaxPacketLength(QuicByteCount length); + void set_debug_delegate(QuicPacketCreator::DebugDelegate* debug_delegate) { packet_creator_.set_debug_delegate(debug_delegate); }
diff --git a/quic/core/quic_packets.cc b/quic/core/quic_packets.cc index 467d346..87b310e 100644 --- a/quic/core/quic_packets.cc +++ b/quic/core/quic_packets.cc
@@ -526,6 +526,13 @@ return dst_buffer; } +char* CopyBuffer(const char* encrypted_buffer, + QuicPacketLength encrypted_length) { + char* dst_buffer = new char[encrypted_length]; + memcpy(dst_buffer, encrypted_buffer, encrypted_length); + return dst_buffer; +} + ReceivedPacketInfo::ReceivedPacketInfo(const QuicSocketAddress& self_address, const QuicSocketAddress& peer_address, const QuicReceivedPacket& packet)
diff --git a/quic/core/quic_packets.h b/quic/core/quic_packets.h index dab0849..91553d8 100644 --- a/quic/core/quic_packets.h +++ b/quic/core/quic_packets.h
@@ -406,6 +406,10 @@ // Allocates a new char[] of size |packet.encrypted_length| and copies in // |packet.encrypted_buffer|. QUIC_EXPORT_PRIVATE char* CopyBuffer(const SerializedPacket& packet); +// Allocates a new char[] of size |encrypted_length| and copies in +// |encrypted_buffer|. +QUIC_EXPORT_PRIVATE char* CopyBuffer(const char* encrypted_buffer, + QuicPacketLength encrypted_length); struct QUIC_EXPORT_PRIVATE SerializedPacketDeleter { void operator()(SerializedPacket* packet) {
diff --git a/quic/core/quic_types.cc b/quic/core/quic_types.cc index 5e717de..1f51dc5 100644 --- a/quic/core/quic_types.cc +++ b/quic/core/quic_types.cc
@@ -51,6 +51,8 @@ return "ERROR"; case WRITE_STATUS_MSG_TOO_BIG: return "MSG_TOO_BIG"; + case WRITE_STATUS_FAILED_TO_COALESCE_PACKET: + return "WRITE_STATUS_FAILED_TO_COALESCE_PACKET"; case WRITE_STATUS_NUM_VALUES: return "NUM_VALUES"; } @@ -524,6 +526,17 @@ } } +std::string SerializedPacketFateToString(SerializedPacketFate fate) { + switch (fate) { + RETURN_STRING_LITERAL(COALESCE); + RETURN_STRING_LITERAL(BUFFER); + RETURN_STRING_LITERAL(SEND_TO_WRITER); + RETURN_STRING_LITERAL(FAILED_TO_WRITE_COALESCED_PACKET); + default: + return QuicStrCat("Unknown(", static_cast<int>(fate), ")"); + } +} + std::string EncryptionLevelToString(EncryptionLevel level) { switch (level) { RETURN_STRING_LITERAL(ENCRYPTION_INITIAL);
diff --git a/quic/core/quic_types.h b/quic/core/quic_types.h index 60754c0..d7206d6 100644 --- a/quic/core/quic_types.h +++ b/quic/core/quic_types.h
@@ -95,6 +95,7 @@ // - Errors MUST be added after WRITE_STATUS_ERROR. WRITE_STATUS_ERROR, WRITE_STATUS_MSG_TOO_BIG, + WRITE_STATUS_FAILED_TO_COALESCE_PACKET, WRITE_STATUS_NUM_VALUES, }; @@ -667,6 +668,18 @@ PACKETS_ACKED_IN_WRONG_PACKET_NUMBER_SPACE, }; +// Indicates the fate of a serialized packet in WritePacket(). +enum SerializedPacketFate : uint8_t { + COALESCE, // Try to coalesce packet. + BUFFER, // Buffer packet in buffered_packets_. + SEND_TO_WRITER, // Send packet to writer. + FAILED_TO_WRITE_COALESCED_PACKET, // Packet cannot be coalesced, error occurs + // when sending existing coalesced packet. +}; + +QUIC_EXPORT_PRIVATE std::string SerializedPacketFateToString( + SerializedPacketFate fate); + // There are three different forms of CONNECTION_CLOSE. typedef enum QuicConnectionCloseType { GOOGLE_QUIC_CONNECTION_CLOSE = 0,
diff --git a/quic/core/quic_versions.cc b/quic/core/quic_versions.cc index 57bd3fa..8d666a3 100644 --- a/quic/core/quic_versions.cc +++ b/quic/core/quic_versions.cc
@@ -84,6 +84,11 @@ handshake_protocol == PROTOCOL_TLS1_3; } +bool ParsedQuicVersion::CanSendCoalescedPackets() const { + return QuicVersionHasLongHeaderLengths(transport_version) && + handshake_protocol == PROTOCOL_TLS1_3; +} + bool VersionHasLengthPrefixedConnectionIds( QuicTransportVersion transport_version) { return transport_version > QUIC_VERSION_48;
diff --git a/quic/core/quic_versions.h b/quic/core/quic_versions.h index 777d74f..7fd1d01 100644 --- a/quic/core/quic_versions.h +++ b/quic/core/quic_versions.h
@@ -193,6 +193,9 @@ // i.e., server will send no more than FLAGS_quic_anti_amplification_factor // times received bytes until address can be validated. bool SupportsAntiAmplificationLimit() const; + + // Returns true if this version can send coalesced packets. + bool CanSendCoalescedPackets() const; }; QUIC_EXPORT_PRIVATE ParsedQuicVersion UnsupportedQuicVersion();
diff --git a/quic/test_tools/simple_quic_framer.cc b/quic/test_tools/simple_quic_framer.cc index be03aa1..588f3b6 100644 --- a/quic/test_tools/simple_quic_framer.cc +++ b/quic/test_tools/simple_quic_framer.cc
@@ -59,7 +59,9 @@ return true; } - void OnCoalescedPacket(const QuicEncryptedPacket& /*packet*/) override {} + void OnCoalescedPacket(const QuicEncryptedPacket& packet) override { + coalesced_packet_ = packet.Clone(); + } void OnUndecryptablePacket(const QuicEncryptedPacket& /*packet*/, EncryptionLevel /*decryption_level*/, @@ -253,6 +255,9 @@ return version_negotiation_packet_.get(); } EncryptionLevel last_decrypted_level() const { return last_decrypted_level_; } + const QuicEncryptedPacket* coalesced_packet() const { + return coalesced_packet_.get(); + } private: QuicErrorCode error_; @@ -284,6 +289,7 @@ std::vector<std::unique_ptr<std::string>> stream_data_; std::vector<std::unique_ptr<std::string>> crypto_data_; EncryptionLevel last_decrypted_level_; + std::unique_ptr<QuicEncryptedPacket> coalesced_packet_; }; SimpleQuicFramer::SimpleQuicFramer() @@ -404,5 +410,9 @@ return visitor_->padding_frames(); } +const QuicEncryptedPacket* SimpleQuicFramer::coalesced_packet() const { + return visitor_->coalesced_packet(); +} + } // namespace test } // namespace quic
diff --git a/quic/test_tools/simple_quic_framer.h b/quic/test_tools/simple_quic_framer.h index a254ce5..3e063d0 100644 --- a/quic/test_tools/simple_quic_framer.h +++ b/quic/test_tools/simple_quic_framer.h
@@ -50,6 +50,7 @@ const std::vector<QuicPaddingFrame>& padding_frames() const; const QuicVersionNegotiationPacket* version_negotiation_packet() const; EncryptionLevel last_decrypted_level() const; + const QuicEncryptedPacket* coalesced_packet() const; QuicFramer* framer();
diff --git a/quic/test_tools/simple_session_notifier_test.cc b/quic/test_tools/simple_session_notifier_test.cc index 32dbce4..058f220 100644 --- a/quic/test_tools/simple_session_notifier_test.cc +++ b/quic/test_tools/simple_session_notifier_test.cc
@@ -42,6 +42,7 @@ : connection_(&helper_, &alarm_factory_, Perspective::IS_CLIENT), notifier_(&connection_) { connection_.set_visitor(&visitor_); + connection_.SetSessionNotifier(¬ifier_); EXPECT_FALSE(notifier_.WillingToWrite()); EXPECT_EQ(0u, notifier_.StreamBytesSent()); EXPECT_FALSE(notifier_.HasBufferedStreamData());