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