gfe-relnote: Combine QuicPacketGenerator and QuicPacketCreator. Protected by gfe2_reloadable_flag_quic_combine_generator_and_creator.
PiperOrigin-RevId: 269432631
Change-Id: I8270ff623e168db2f780df1da26aad100c05b1b5
diff --git a/quic/core/quic_packet_creator.cc b/quic/core/quic_packet_creator.cc
index 1f985dc..e863814 100644
--- a/quic/core/quic_packet_creator.cc
+++ b/quic/core/quic_packet_creator.cc
@@ -22,6 +22,7 @@
#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_server_stats.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_str_cat.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
@@ -84,8 +85,16 @@
false),
pending_padding_bytes_(0),
needs_full_padding_(false),
- can_set_transmission_type_(false) {
+ can_set_transmission_type_(false),
+ next_transmission_type_(NOT_RETRANSMISSION),
+ flusher_attached_(false),
+ fully_pad_crypto_handshake_packets_(true),
+ combine_generator_and_creator_(
+ GetQuicReloadableFlag(quic_combine_generator_and_creator)) {
SetMaxPacketLength(kDefaultMaxPacketSize);
+ if (combine_generator_and_creator_) {
+ QUIC_RELOADABLE_FLAG_COUNT(quic_combine_generator_and_creator);
+ }
}
QuicPacketCreator::~QuicPacketCreator() {
@@ -886,6 +895,330 @@
retry_token_ = std::string(retry_token);
}
+bool QuicPacketCreator::ConsumeRetransmittableControlFrame(
+ const QuicFrame& frame) {
+ DCHECK(combine_generator_and_creator_);
+ QUIC_BUG_IF(IsControlFrame(frame.type) && !GetControlFrameId(frame))
+ << "Adding a control frame with no control frame id: " << frame;
+ DCHECK(QuicUtils::IsRetransmittableFrame(frame.type)) << frame;
+ MaybeBundleAckOpportunistically();
+ if (HasPendingFrames()) {
+ if (AddSavedFrame(frame, next_transmission_type_)) {
+ // There is pending frames and current frame fits.
+ return true;
+ }
+ }
+ DCHECK(!HasPendingFrames());
+ if (frame.type != PING_FRAME && frame.type != CONNECTION_CLOSE_FRAME &&
+ !delegate_->ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ // Do not check congestion window for ping or connection close frames.
+ return false;
+ }
+ const bool success = AddSavedFrame(frame, next_transmission_type_);
+ DCHECK(success);
+ return success;
+}
+
+QuicConsumedData QuicPacketCreator::ConsumeData(QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ StreamSendingState state) {
+ DCHECK(combine_generator_and_creator_);
+ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+ "generator tries to write stream data.";
+ bool has_handshake = QuicUtils::IsCryptoStreamId(transport_version(), id);
+ MaybeBundleAckOpportunistically();
+ bool fin = state != NO_FIN;
+ QUIC_BUG_IF(has_handshake && fin)
+ << "Handshake packets should never send a fin";
+ // To make reasoning about crypto frames easier, we don't combine them with
+ // other retransmittable frames in a single packet.
+ if (has_handshake && HasPendingRetransmittableFrames()) {
+ FlushCurrentPacket();
+ }
+
+ size_t total_bytes_consumed = 0;
+ bool fin_consumed = false;
+
+ if (!HasRoomForStreamFrame(id, offset, write_length)) {
+ FlushCurrentPacket();
+ }
+
+ if (!fin && (write_length == 0)) {
+ QUIC_BUG << "Attempt to consume empty data without FIN.";
+ return QuicConsumedData(0, false);
+ }
+ // We determine if we can enter the fast path before executing
+ // the slow path loop.
+ bool run_fast_path =
+ !has_handshake && state != FIN_AND_PADDING && !HasPendingFrames() &&
+ write_length - total_bytes_consumed > kMaxOutgoingPacketSize;
+
+ while (!run_fast_path && delegate_->ShouldGeneratePacket(
+ HAS_RETRANSMITTABLE_DATA,
+ has_handshake ? IS_HANDSHAKE : NOT_HANDSHAKE)) {
+ QuicFrame frame;
+ bool needs_full_padding =
+ has_handshake && fully_pad_crypto_handshake_packets_;
+
+ if (!ConsumeDataToFillCurrentPacket(id, write_length - total_bytes_consumed,
+ offset + total_bytes_consumed, fin,
+ needs_full_padding,
+ next_transmission_type_, &frame)) {
+ // The creator is always flushed if there's not enough room for a new
+ // stream frame before ConsumeData, so ConsumeData should always succeed.
+ QUIC_BUG << "Failed to ConsumeData, stream:" << id;
+ return QuicConsumedData(0, false);
+ }
+
+ // A stream frame is created and added.
+ size_t bytes_consumed = frame.stream_frame.data_length;
+ total_bytes_consumed += bytes_consumed;
+ fin_consumed = fin && total_bytes_consumed == write_length;
+ if (fin_consumed && state == FIN_AND_PADDING) {
+ AddRandomPadding();
+ }
+ DCHECK(total_bytes_consumed == write_length ||
+ (bytes_consumed > 0 && HasPendingFrames()));
+
+ if (total_bytes_consumed == write_length) {
+ // We're done writing the data. Exit the loop.
+ // We don't make this a precondition because we could have 0 bytes of data
+ // if we're simply writing a fin.
+ break;
+ }
+ FlushCurrentPacket();
+
+ run_fast_path =
+ !has_handshake && state != FIN_AND_PADDING && !HasPendingFrames() &&
+ write_length - total_bytes_consumed > kMaxOutgoingPacketSize;
+ }
+
+ if (run_fast_path) {
+ return ConsumeDataFastPath(id, write_length, offset, state != NO_FIN,
+ total_bytes_consumed);
+ }
+
+ // Don't allow the handshake to be bundled with other retransmittable frames.
+ if (has_handshake) {
+ FlushCurrentPacket();
+ }
+
+ return QuicConsumedData(total_bytes_consumed, fin_consumed);
+}
+
+QuicConsumedData QuicPacketCreator::ConsumeDataFastPath(
+ QuicStreamId id,
+ size_t write_length,
+ QuicStreamOffset offset,
+ bool fin,
+ size_t total_bytes_consumed) {
+ DCHECK(combine_generator_and_creator_);
+ DCHECK(!QuicUtils::IsCryptoStreamId(transport_version(), id));
+
+ while (total_bytes_consumed < write_length &&
+ delegate_->ShouldGeneratePacket(HAS_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ // Serialize and encrypt the packet.
+ size_t bytes_consumed = 0;
+ CreateAndSerializeStreamFrame(id, write_length, total_bytes_consumed,
+ offset + total_bytes_consumed, fin,
+ next_transmission_type_, &bytes_consumed);
+ total_bytes_consumed += bytes_consumed;
+ }
+
+ return QuicConsumedData(total_bytes_consumed,
+ fin && (total_bytes_consumed == write_length));
+}
+
+size_t QuicPacketCreator::ConsumeCryptoData(EncryptionLevel level,
+ size_t write_length,
+ QuicStreamOffset offset) {
+ DCHECK(combine_generator_and_creator_);
+ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+ "generator tries to write crypto data.";
+ MaybeBundleAckOpportunistically();
+ // To make reasoning about crypto frames easier, we don't combine them with
+ // other retransmittable frames in a single packet.
+ // TODO(nharper): Once we have separate packet number spaces, everything
+ // should be driven by encryption level, and we should stop flushing in this
+ // spot.
+ if (HasPendingRetransmittableFrames()) {
+ FlushCurrentPacket();
+ }
+
+ size_t total_bytes_consumed = 0;
+
+ while (total_bytes_consumed < write_length) {
+ QuicFrame frame;
+ if (!ConsumeCryptoDataToFillCurrentPacket(
+ level, write_length - total_bytes_consumed,
+ offset + total_bytes_consumed, fully_pad_crypto_handshake_packets_,
+ next_transmission_type_, &frame)) {
+ // The only pending data in the packet is non-retransmittable frames. I'm
+ // assuming here that they won't occupy so much of the packet that a
+ // CRYPTO frame won't fit.
+ QUIC_BUG << "Failed to ConsumeCryptoData at level " << level;
+ return 0;
+ }
+ total_bytes_consumed += frame.crypto_frame->data_length;
+ FlushCurrentPacket();
+ }
+
+ // Don't allow the handshake to be bundled with other retransmittable frames.
+ FlushCurrentPacket();
+
+ return total_bytes_consumed;
+}
+
+void QuicPacketCreator::GenerateMtuDiscoveryPacket(QuicByteCount target_mtu) {
+ DCHECK(combine_generator_and_creator_);
+ // MTU discovery frames must be sent by themselves.
+ if (!CanSetMaxPacketLength()) {
+ QUIC_BUG << "MTU discovery packets should only be sent when no other "
+ << "frames needs to be sent.";
+ return;
+ }
+ const QuicByteCount current_mtu = max_packet_length();
+
+ // The MTU discovery frame is allocated on the stack, since it is going to be
+ // serialized within this function.
+ QuicMtuDiscoveryFrame mtu_discovery_frame;
+ QuicFrame frame(mtu_discovery_frame);
+
+ // Send the probe packet with the new length.
+ SetMaxPacketLength(target_mtu);
+ const bool success = AddPaddedSavedFrame(frame, next_transmission_type_);
+ FlushCurrentPacket();
+ // The only reason AddFrame can fail is that the packet is too full to fit in
+ // a ping. This is not possible for any sane MTU.
+ DCHECK(success);
+
+ // Reset the packet length back.
+ SetMaxPacketLength(current_mtu);
+}
+
+void QuicPacketCreator::MaybeBundleAckOpportunistically() {
+ DCHECK(combine_generator_and_creator_);
+ if (has_ack()) {
+ // Ack already queued, nothing to do.
+ return;
+ }
+ if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ return;
+ }
+ const bool flushed =
+ FlushAckFrame(delegate_->MaybeBundleAckOpportunistically());
+ DCHECK(flushed);
+}
+
+bool QuicPacketCreator::FlushAckFrame(const QuicFrames& frames) {
+ DCHECK(combine_generator_and_creator_);
+ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+ "generator tries to send ACK frame.";
+ for (const auto& frame : frames) {
+ DCHECK(frame.type == ACK_FRAME || frame.type == STOP_WAITING_FRAME);
+ if (HasPendingFrames()) {
+ if (AddSavedFrame(frame, next_transmission_type_)) {
+ // There is pending frames and current frame fits.
+ continue;
+ }
+ }
+ DCHECK(!HasPendingFrames());
+ // There is no pending frames, consult the delegate whether a packet can be
+ // generated.
+ if (!delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA,
+ NOT_HANDSHAKE)) {
+ return false;
+ }
+ const bool success = AddSavedFrame(frame, next_transmission_type_);
+ QUIC_BUG_IF(!success) << "Failed to flush " << frame;
+ }
+ return true;
+}
+
+void QuicPacketCreator::AddRandomPadding() {
+ DCHECK(combine_generator_and_creator_);
+ AddPendingPadding(random_->RandUint64() % kMaxNumRandomPaddingBytes + 1);
+}
+
+void QuicPacketCreator::AttachPacketFlusher() {
+ DCHECK(combine_generator_and_creator_);
+ flusher_attached_ = true;
+ if (!write_start_packet_number_.IsInitialized()) {
+ write_start_packet_number_ = NextSendingPacketNumber();
+ }
+}
+
+void QuicPacketCreator::Flush() {
+ DCHECK(combine_generator_and_creator_);
+ FlushCurrentPacket();
+ SendRemainingPendingPadding();
+ flusher_attached_ = false;
+ if (GetQuicFlag(FLAGS_quic_export_server_num_packets_per_write_histogram)) {
+ if (!write_start_packet_number_.IsInitialized()) {
+ QUIC_BUG << "write_start_packet_number is not initialized";
+ return;
+ }
+ QUIC_SERVER_HISTOGRAM_COUNTS(
+ "quic_server_num_written_packets_per_write",
+ NextSendingPacketNumber() - write_start_packet_number_, 1, 200, 50,
+ "Number of QUIC packets written per write operation");
+ }
+ write_start_packet_number_.Clear();
+}
+
+void QuicPacketCreator::SendRemainingPendingPadding() {
+ DCHECK(combine_generator_and_creator_);
+ while (
+ pending_padding_bytes() > 0 && !HasPendingFrames() &&
+ delegate_->ShouldGeneratePacket(NO_RETRANSMITTABLE_DATA, NOT_HANDSHAKE)) {
+ FlushCurrentPacket();
+ }
+}
+
+void QuicPacketCreator::SetServerConnectionIdLength(uint32_t length) {
+ DCHECK(combine_generator_and_creator_);
+ if (length == 0) {
+ SetServerConnectionIdIncluded(CONNECTION_ID_ABSENT);
+ } else {
+ SetServerConnectionIdIncluded(CONNECTION_ID_PRESENT);
+ }
+}
+
+void QuicPacketCreator::SetTransmissionType(TransmissionType type) {
+ DCHECK(combine_generator_and_creator_);
+ SetTransmissionTypeOfNextPackets(type);
+ if (can_set_transmission_type()) {
+ next_transmission_type_ = type;
+ }
+}
+
+MessageStatus QuicPacketCreator::AddMessageFrame(QuicMessageId message_id,
+ QuicMemSliceSpan message) {
+ DCHECK(combine_generator_and_creator_);
+ QUIC_BUG_IF(!flusher_attached_) << "Packet flusher is not attached when "
+ "generator tries to add message frame.";
+ MaybeBundleAckOpportunistically();
+ const QuicByteCount message_length = message.total_length();
+ if (message_length > GetCurrentLargestMessagePayload()) {
+ return MESSAGE_STATUS_TOO_LARGE;
+ }
+ if (!HasRoomForMessageFrame(message_length)) {
+ FlushCurrentPacket();
+ }
+ QuicMessageFrame* frame = new QuicMessageFrame(message_id, message);
+ const bool success = AddSavedFrame(QuicFrame(frame), next_transmission_type_);
+ if (!success) {
+ QUIC_BUG << "Failed to send message " << message_id;
+ delete frame;
+ return MESSAGE_STATUS_INTERNAL_ERROR;
+ }
+ return MESSAGE_STATUS_SUCCESS;
+}
+
QuicVariableLengthIntegerLength QuicPacketCreator::GetLengthLength() const {
if (QuicVersionHasLongHeaderLengths(framer_->transport_version()) &&
HasIetfLongHeader()) {
@@ -1188,5 +1521,10 @@
return packet_number() + 1;
}
+bool QuicPacketCreator::PacketFlusherAttached() const {
+ DCHECK(combine_generator_and_creator_);
+ return flusher_attached_;
+}
+
#undef ENDPOINT // undef for jumbo builds
} // namespace quic