Support ACK_RECIEVE_TIMESTAMPS extension. This is from PR https://github.com/google/quiche/pull/8/files. PiperOrigin-RevId: 425401005
diff --git a/quic/core/frames/quic_ack_frame.h b/quic/core/frames/quic_ack_frame.h index 8a5c7e6..c9c46d4 100644 --- a/quic/core/frames/quic_ack_frame.h +++ b/quic/core/frames/quic_ack_frame.h
@@ -111,6 +111,8 @@ QuicTime::Delta ack_delay_time = QuicTime::Delta::Infinite(); // Vector of <packet_number, time> for when packets arrived. + // For IETF versions, packet numbers and timestamps in this vector are both in + // ascending orders. Packets received out of order are not saved here. PacketTimeVector received_packet_times; // Set of packets.
diff --git a/quic/core/quic_framer.cc b/quic/core/quic_framer.cc index 45166ec..1afbbdd 100644 --- a/quic/core/quic_framer.cc +++ b/quic/core/quic_framer.cc
@@ -414,6 +414,7 @@ perspective_(perspective), validate_flags_(true), process_timestamps_(false), + max_receive_timestamps_per_ack_(std::numeric_limits<uint32_t>::max()), receive_timestamps_exponent_(0), creation_time_(creation_time), last_timestamp_(QuicTime::Delta::Zero()), @@ -478,13 +479,14 @@ } // static -size_t QuicFramer::GetMinAckFrameSize(QuicTransportVersion version, - const QuicAckFrame& ack_frame, - uint32_t local_ack_delay_exponent) { +size_t QuicFramer::GetMinAckFrameSize( + QuicTransportVersion version, const QuicAckFrame& ack_frame, + uint32_t local_ack_delay_exponent, + bool use_ietf_ack_with_receive_timestamp) { if (VersionHasIetfQuicFrames(version)) { // The minimal ack frame consists of the following fields: Largest - // Acknowledged, ACK Delay, 0 ACK Block Count, First ACK Block and ECN - // counts. + // Acknowledged, ACK Delay, 0 ACK Block Count, First ACK Block and either 0 + // Timestamp Range Count or ECN counts. // Type byte + largest acked. size_t min_size = kQuicFrameTypeSize + @@ -498,10 +500,14 @@ min_size += QuicDataWriter::GetVarInt62Len( ack_frame.packets.Empty() ? 0 : ack_frame.packets.rbegin()->Length() - 1); - // ECN counts. - if (ack_frame.ecn_counters_populated && - (ack_frame.ect_0_count || ack_frame.ect_1_count || - ack_frame.ecn_ce_count)) { + + if (use_ietf_ack_with_receive_timestamp) { + // 0 Timestamp Range Count. + min_size += QuicDataWriter::GetVarInt62Len(0); + } else if (ack_frame.ecn_counters_populated && + (ack_frame.ect_0_count || ack_frame.ect_1_count || + ack_frame.ecn_ce_count)) { + // ECN counts. min_size += (QuicDataWriter::GetVarInt62Len(ack_frame.ect_0_count) + QuicDataWriter::GetVarInt62Len(ack_frame.ect_1_count) + QuicDataWriter::GetVarInt62Len(ack_frame.ecn_ce_count)); @@ -830,9 +836,10 @@ } bool can_truncate = frame.type == ACK_FRAME && - free_bytes >= GetMinAckFrameSize(version_.transport_version, - *frame.ack_frame, - local_ack_delay_exponent_); + free_bytes >= + GetMinAckFrameSize(version_.transport_version, *frame.ack_frame, + local_ack_delay_exponent_, + UseIetfAckWithReceiveTimestamp(*frame.ack_frame)); if (can_truncate) { // Truncate the frame so the packet will not exceed kMaxOutgoingPacketSize. // Note that we may not use every byte of the writer in this case. @@ -5061,9 +5068,11 @@ previous_smallest = iter->min(); } - // ECN counts. - if (frame.ecn_counters_populated && - (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { + if (UseIetfAckWithReceiveTimestamp(frame)) { + ack_frame_size += GetIetfAckFrameTimestampSize(frame); + } else if (frame.ecn_counters_populated && + (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { + // ECN counts. ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ect_0_count); ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ect_1_count); ack_frame_size += QuicDataWriter::GetVarInt62Len(frame.ecn_ce_count); @@ -5072,6 +5081,20 @@ return ack_frame_size; } +size_t QuicFramer::GetIetfAckFrameTimestampSize(const QuicAckFrame& ack) { + QUICHE_DCHECK(!ack.received_packet_times.empty()); + std::string detailed_error; + absl::InlinedVector<AckTimestampRange, 2> timestamp_ranges = + GetAckTimestampRanges(ack, detailed_error); + if (!detailed_error.empty()) { + return 0; + } + + ssize_t size = + FrameAckTimestampRanges(ack, timestamp_ranges, /*writer=*/nullptr); + return std::max<ssize_t>(0, size); +} + size_t QuicFramer::GetAckFrameSize( const QuicAckFrame& ack, QuicPacketNumberLength /*packet_number_length*/) { QUICHE_DCHECK(!ack.packets.Empty()); @@ -5085,7 +5108,8 @@ GetMinPacketNumberLength(QuicPacketNumber(ack_info.max_block_length)); ack_size = GetMinAckFrameSize(version_.transport_version, ack, - local_ack_delay_exponent_); + local_ack_delay_exponent_, + UseIetfAckWithReceiveTimestamp(ack)); // First ack block length. ack_size += ack_block_length; if (ack_info.num_ack_blocks != 0) { @@ -5582,7 +5606,8 @@ int32_t available_timestamp_and_ack_block_bytes = writer->capacity() - writer->length() - ack_block_length - GetMinAckFrameSize(version_.transport_version, frame, - local_ack_delay_exponent_) - + local_ack_delay_exponent_, + UseIetfAckWithReceiveTimestamp(frame)) - (new_ack_info.num_ack_blocks != 0 ? kNumberOfAckBlocksSize : 0); QUICHE_DCHECK_LE(0, available_timestamp_and_ack_block_bytes); @@ -5777,6 +5802,172 @@ return true; } +absl::InlinedVector<QuicFramer::AckTimestampRange, 2> +QuicFramer::GetAckTimestampRanges(const QuicAckFrame& frame, + std::string& detailed_error) const { + detailed_error = ""; + if (frame.received_packet_times.empty()) { + return {}; + } + + absl::InlinedVector<AckTimestampRange, 2> timestamp_ranges; + + for (size_t r = 0; r < std::min<size_t>(max_receive_timestamps_per_ack_, + frame.received_packet_times.size()); + ++r) { + const size_t i = frame.received_packet_times.size() - 1 - r; + const QuicPacketNumber packet_number = frame.received_packet_times[i].first; + const QuicTime receive_timestamp = frame.received_packet_times[i].second; + + if (timestamp_ranges.empty()) { + if (receive_timestamp < creation_time_ || + LargestAcked(frame) < packet_number) { + detailed_error = + "The first packet is either received earlier than framer creation " + "time, or larger than largest acked packet."; + QUIC_BUG(quic_framer_ack_ts_first_packet_bad) + << detailed_error << " receive_timestamp:" << receive_timestamp + << ", framer_creation_time:" << creation_time_ + << ", packet_number:" << packet_number + << ", largest_acked:" << LargestAcked(frame); + return {}; + } + timestamp_ranges.push_back(AckTimestampRange()); + timestamp_ranges.back().gap = LargestAcked(frame) - packet_number; + timestamp_ranges.back().range_begin = i; + timestamp_ranges.back().range_end = i; + continue; + } + + const size_t prev_i = timestamp_ranges.back().range_end; + const QuicPacketNumber prev_packet_number = + frame.received_packet_times[prev_i].first; + const QuicTime prev_receive_timestamp = + frame.received_packet_times[prev_i].second; + + QUIC_DVLOG(3) << "prev_packet_number:" << prev_packet_number + << ", packet_number:" << packet_number; + if (prev_receive_timestamp < receive_timestamp || + prev_packet_number <= packet_number) { + detailed_error = "Packet number and/or receive time not in order."; + QUIC_BUG(quic_framer_ack_ts_packet_out_of_order) + << detailed_error << " packet_number:" << packet_number + << ", receive_timestamp:" << receive_timestamp + << ", prev_packet_number:" << prev_packet_number + << ", prev_receive_timestamp:" << prev_receive_timestamp; + return {}; + } + + if (prev_packet_number == packet_number + 1) { + timestamp_ranges.back().range_end = i; + } else { + timestamp_ranges.push_back(AckTimestampRange()); + timestamp_ranges.back().gap = prev_packet_number - 2 - packet_number; + timestamp_ranges.back().range_begin = i; + timestamp_ranges.back().range_end = i; + } + } + + return timestamp_ranges; +} + +ssize_t QuicFramer::FrameAckTimestampRanges( + const QuicAckFrame& frame, + const absl::InlinedVector<AckTimestampRange, 2>& timestamp_ranges, + QuicDataWriter* writer) const { + ssize_t size = 0; + auto maybe_write_var_int62 = [&](uint64_t value) { + size += QuicDataWriter::GetVarInt62Len(value); + if (writer != nullptr && !writer->WriteVarInt62(value)) { + return false; + } + return true; + }; + + if (!maybe_write_var_int62(timestamp_ranges.size())) { + return -1; + } + + // |effective_prev_time| is the exponent-encoded timestamp of the previous + // packet. + absl::optional<QuicTime> effective_prev_time; + for (const AckTimestampRange& range : timestamp_ranges) { + QUIC_DVLOG(3) << "Range: gap:" << range.gap << ", beg:" << range.range_begin + << ", end:" << range.range_end; + if (!maybe_write_var_int62(range.gap)) { + return -1; + } + + if (!maybe_write_var_int62(range.range_begin - range.range_end + 1)) { + return -1; + } + + for (ssize_t i = range.range_begin; i >= range.range_end; --i) { + const QuicTime receive_timestamp = frame.received_packet_times[i].second; + uint64_t time_delta; + if (effective_prev_time.has_value()) { + time_delta = + (*effective_prev_time - receive_timestamp).ToMicroseconds(); + QUIC_DVLOG(3) << "time_delta:" << time_delta + << ", exponent:" << receive_timestamps_exponent_ + << ", effective_prev_time:" << *effective_prev_time + << ", recv_time:" << receive_timestamp; + time_delta = time_delta >> receive_timestamps_exponent_; + effective_prev_time = effective_prev_time.value() - + QuicTime::Delta::FromMicroseconds( + time_delta << receive_timestamps_exponent_); + } else { + // The first delta is from framer creation to the current receive + // timestamp (forward in time), whereas in the common case subsequent + // deltas move backwards in time. + time_delta = (receive_timestamp - creation_time_).ToMicroseconds(); + QUIC_DVLOG(3) << "First time_delta:" << time_delta + << ", exponent:" << receive_timestamps_exponent_ + << ", recv_time:" << receive_timestamp + << ", creation_time:" << creation_time_; + // Round up the first exponent-encoded time delta so that the next + // receive timestamp is guaranteed to be decreasing. + time_delta = ((time_delta - 1) >> receive_timestamps_exponent_) + 1; + effective_prev_time = + creation_time_ + QuicTime::Delta::FromMicroseconds( + time_delta << receive_timestamps_exponent_); + } + + if (!maybe_write_var_int62(time_delta)) { + return -1; + } + } + } + + return size; +} + +bool QuicFramer::AppendIetfTimestampsToAckFrame(const QuicAckFrame& frame, + QuicDataWriter* writer) { + QUICHE_DCHECK(!frame.received_packet_times.empty()); + std::string detailed_error; + const absl::InlinedVector<AckTimestampRange, 2> timestamp_ranges = + GetAckTimestampRanges(frame, detailed_error); + if (!detailed_error.empty()) { + set_detailed_error(std::move(detailed_error)); + return false; + } + + // Compute the size first using a null writer. + ssize_t size = + FrameAckTimestampRanges(frame, timestamp_ranges, /*writer=*/nullptr); + if (size > static_cast<ssize_t>(writer->capacity() - writer->length())) { + QUIC_DVLOG(1) << "Insufficient room to write IETF ack receive timestamps. " + "size_remain:" + << (writer->capacity() - writer->length()) + << ", size_needed:" << size; + // Write a Timestamp Range Count of 0. + return writer->WriteVarInt62(0); + } + + return FrameAckTimestampRanges(frame, timestamp_ranges, writer) > 0; +} + bool QuicFramer::AppendStopWaitingFrame(const QuicPacketHeader& header, const QuicStopWaitingFrame& frame, QuicDataWriter* writer) { @@ -5814,8 +6005,10 @@ QuicDataWriter* writer) { uint8_t type = IETF_ACK; uint64_t ecn_size = 0; - if (frame.ecn_counters_populated && - (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { + if (UseIetfAckWithReceiveTimestamp(frame)) { + type = IETF_ACK_RECEIVE_TIMESTAMPS; + } else if (frame.ecn_counters_populated && + (frame.ect_0_count || frame.ect_1_count || frame.ecn_ce_count)) { // Change frame type to ACK_ECN if any ECN count is available. type = IETF_ACK_ECN; ecn_size = (QuicDataWriter::GetVarInt62Len(frame.ect_0_count) + @@ -5873,10 +6066,19 @@ const uint64_t gap = previous_smallest - iter->max() - 1; const uint64_t ack_range = iter->Length() - 1; - if (writer->remaining() < ecn_size || - writer->remaining() - ecn_size < + if (type == IETF_ACK_RECEIVE_TIMESTAMPS && + writer->remaining() < static_cast<size_t>(QuicDataWriter::GetVarInt62Len(gap) + - QuicDataWriter::GetVarInt62Len(ack_range))) { + QuicDataWriter::GetVarInt62Len(ack_range) + + QuicDataWriter::GetVarInt62Len(0))) { + // If we write this ACK range we won't have space for a timestamp range + // count of 0. + break; + } else if (writer->remaining() < ecn_size || + writer->remaining() - ecn_size < + static_cast<size_t>( + QuicDataWriter::GetVarInt62Len(gap) + + QuicDataWriter::GetVarInt62Len(ack_range))) { // ACK range does not fit, truncate it. break; } @@ -5920,6 +6122,12 @@ } } + if (type == IETF_ACK_RECEIVE_TIMESTAMPS) { + if (!AppendIetfTimestampsToAckFrame(frame, writer)) { + return false; + } + } + return true; }
diff --git a/quic/core/quic_framer.h b/quic/core/quic_framer.h index cd9d8a2..a238f07 100644 --- a/quic/core/quic_framer.h +++ b/quic/core/quic_framer.h
@@ -315,6 +315,13 @@ process_timestamps_ = process_timestamps; } + // Sets the max number of receive timestamps to send per ACK frame. + // TODO(wub): Remove the const once timestamps are negotiated via + // transport params. + void set_max_receive_timestamps_per_ack(uint32_t max_timestamps) const { + max_receive_timestamps_per_ack_ = max_timestamps; + } + // Sets the exponent to use when writing/reading ACK receive timestamps. void set_receive_timestamps_exponent(uint32_t exponent) { receive_timestamps_exponent_ = exponent; @@ -347,7 +354,8 @@ // blocks. static size_t GetMinAckFrameSize(QuicTransportVersion version, const QuicAckFrame& ack_frame, - uint32_t local_ack_delay_exponent); + uint32_t local_ack_delay_exponent, + bool use_ietf_ack_with_receive_timestamp); // Size in bytes of a stop waiting frame. static size_t GetStopWaitingFrameSize( QuicPacketNumberLength packet_number_length); @@ -727,6 +735,23 @@ using NackRangeMap = std::map<QuicPacketNumber, uint8_t>; + // AckTimestampRange is a data structure derived from a QuicAckFrame. It is + // used to serialize timestamps in a IETF_ACK_RECEIVE_TIMESTAMPS frame. + struct QUIC_EXPORT_PRIVATE AckTimestampRange { + QuicPacketCount gap; + // |range_begin| and |range_end| are index(es) in + // QuicAckFrame.received_packet_times, representing a continuous range of + // packet numbers in descending order. |range_begin| >= |range_end|. + ssize_t range_begin; // Inclusive + ssize_t range_end; // Inclusive + }; + absl::InlinedVector<AckTimestampRange, 2> GetAckTimestampRanges( + const QuicAckFrame& frame, std::string& detailed_error) const; + ssize_t FrameAckTimestampRanges( + const QuicAckFrame& frame, + const absl::InlinedVector<AckTimestampRange, 2>& timestamp_ranges, + QuicDataWriter* writer) const; + struct QUIC_EXPORT_PRIVATE AckFrameInfo { AckFrameInfo(); AckFrameInfo(const AckFrameInfo& other); @@ -895,6 +920,7 @@ // Computes the wire size in bytes of time stamps in |ack|. size_t GetAckFrameTimeStampSize(const QuicAckFrame& ack); + size_t GetIetfAckFrameTimestampSize(const QuicAckFrame& ack); // Computes the wire size in bytes of the |ack| frame. size_t GetAckFrameSize(const QuicAckFrame& ack, @@ -958,6 +984,8 @@ // of the frame. bool AppendIetfAckFrameAndTypeByte(const QuicAckFrame& frame, QuicDataWriter* writer); + bool AppendIetfTimestampsToAckFrame(const QuicAckFrame& frame, + QuicDataWriter* writer); bool AppendStopWaitingFrame(const QuicPacketHeader& header, const QuicStopWaitingFrame& frame, @@ -1080,6 +1108,15 @@ bool ProcessPacketInternal(const QuicEncryptedPacket& packet); + // Determine whether the given QuicAckFrame should be serialized with a + // IETF_ACK_RECEIVE_TIMESTAMPS frame type. + bool UseIetfAckWithReceiveTimestamp(const QuicAckFrame& frame) const { + return VersionHasIetfQuicFrames(version_.transport_version) && + process_timestamps_ && + std::min<uint32_t>(max_receive_timestamps_per_ack_, + frame.received_packet_times.size()) > 0; + } + std::string detailed_error_; QuicFramerVisitorInterface* visitor_; QuicErrorCode error_; @@ -1123,6 +1160,8 @@ // If true, send and process timestamps in the ACK frame. // TODO(ianswett): Remove the mutable once set_process_timestamps isn't const. mutable bool process_timestamps_; + // The max number of receive timestamps to send per ACK frame. + mutable uint32_t max_receive_timestamps_per_ack_; // The exponent to use when writing/reading ACK receive timestamps. uint32_t receive_timestamps_exponent_; // The creation time of the connection, used to calculate timestamps.
diff --git a/quic/core/quic_framer_test.cc b/quic/core/quic_framer_test.cc index cd3a487..d24664d 100644 --- a/quic/core/quic_framer_test.cc +++ b/quic/core/quic_framer_test.cc
@@ -38,6 +38,8 @@ using testing::_; using testing::ContainerEq; +using testing::Eq; +using testing::IsNull; using testing::Return; namespace quic { @@ -7488,6 +7490,706 @@ "constructed packet", data->data(), data->length(), AsChars(p), p_size); } +TEST_P(QuicFramerTest, BuildAckReceiveTimestampsFrameMultipleRanges) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + ack_frame.received_packet_times = PacketTimeVector{ + // Timestamp Range 3. + {kSmallLargestObserved - 22, CreationTimePlus(0x29ffdddd)}, + {kSmallLargestObserved - 21, CreationTimePlus(0x29ffdedd)}, + // Timestamp Range 2. + {kSmallLargestObserved - 11, CreationTimePlus(0x29ffdeed)}, + // Timestamp Range 1. + {kSmallLargestObserved - 4, CreationTimePlus(0x29ffeeed)}, + {kSmallLargestObserved - 3, CreationTimePlus(0x29ffeeee)}, + {kSmallLargestObserved - 2, CreationTimePlus(0x29ffffff)}, + }; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + unsigned char packet_ietf[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, + 0xDC, + 0xBA, + 0x98, + 0x76, + 0x54, + 0x32, + 0x10, + // packet number + 0x12, + 0x34, + 0x56, + 0x78, + + // frame type (IETF_ACK_RECEIVE_TIMESTAMPS frame) + 0x22, + // largest acked + kVarInt62TwoBytes + 0x12, + 0x34, // = 4660 + // Zero delta time. + kVarInt62OneByte + 0x00, + // number of additional ack blocks + kVarInt62OneByte + 0x00, + // first ack block length. + kVarInt62TwoBytes + 0x12, + 0x33, + + // Receive Timestamps. + + // Timestamp Range Count + kVarInt62OneByte + 0x03, + + // Timestamp range 1 (three packets). + // Gap + kVarInt62OneByte + 0x02, + // Timestamp Range Count + kVarInt62OneByte + 0x03, + // Timestamp Delta + kVarInt62FourBytes + 0x29, + 0xff, + 0xff, + 0xff, + // Timestamp Delta + kVarInt62TwoBytes + 0x11, + 0x11, + // Timestamp Delta + kVarInt62OneByte + 0x01, + + // Timestamp range 2 (one packet). + // Gap + kVarInt62OneByte + 0x05, + // Timestamp Range Count + kVarInt62OneByte + 0x01, + // Timestamp Delta + kVarInt62TwoBytes + 0x10, + 0x00, + + // Timestamp range 3 (two packets). + // Gap + kVarInt62OneByte + 0x08, + // Timestamp Range Count + kVarInt62OneByte + 0x02, + // Timestamp Delta + kVarInt62OneByte + 0x10, + // Timestamp Delta + kVarInt62TwoBytes + 0x01, + 0x00, + }; + // clang-format on + + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(8); + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + quiche::test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); +} + +TEST_P(QuicFramerTest, BuildAckReceiveTimestampsFrameExceedsMaxTimestamps) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + ack_frame.received_packet_times = PacketTimeVector{ + // Timestamp Range 3 (not included because max receive timestamps = 4). + {kSmallLargestObserved - 20, CreationTimePlus(0x29ffdddd)}, + // Timestamp Range 2. + {kSmallLargestObserved - 10, CreationTimePlus(0x29ffdedd)}, + {kSmallLargestObserved - 9, CreationTimePlus(0x29ffdeed)}, + // Timestamp Range 1. + {kSmallLargestObserved - 2, CreationTimePlus(0x29ffeeed)}, + {kSmallLargestObserved - 1, CreationTimePlus(0x29ffeeee)}, + {kSmallLargestObserved, CreationTimePlus(0x29ffffff)}, + }; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + unsigned char packet_ietf[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, + 0xDC, + 0xBA, + 0x98, + 0x76, + 0x54, + 0x32, + 0x10, + // packet number + 0x12, + 0x34, + 0x56, + 0x78, + + // frame type (IETF_ACK_RECEIVE_TIMESTAMPS frame) + 0x22, + // largest acked + kVarInt62TwoBytes + 0x12, + 0x34, // = 4660 + // Zero delta time. + kVarInt62OneByte + 0x00, + // number of additional ack blocks + kVarInt62OneByte + 0x00, + // first ack block length. + kVarInt62TwoBytes + 0x12, + 0x33, + + // Receive Timestamps. + + // Timestamp Range Count + kVarInt62OneByte + 0x02, + + // Timestamp range 1 (three packets). + // Gap + kVarInt62OneByte + 0x00, + // Timestamp Range Count + kVarInt62OneByte + 0x03, + // Timestamp Delta + kVarInt62FourBytes + 0x29, + 0xff, + 0xff, + 0xff, + // Timestamp Delta + kVarInt62TwoBytes + 0x11, + 0x11, + // Timestamp Delta + kVarInt62OneByte + 0x01, + + // Timestamp range 2 (one packet). + // Gap + kVarInt62OneByte + 0x05, + // Timestamp Range Count + kVarInt62OneByte + 0x01, + // Timestamp Delta + kVarInt62TwoBytes + 0x10, + 0x00, + }; + // clang-format on + + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(4); + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + quiche::test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); +} + +TEST_P(QuicFramerTest, BuildAckReceiveTimestampsFrameWithExponentEncoding) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + ack_frame.received_packet_times = PacketTimeVector{ + // Timestamp Range 2. + {kSmallLargestObserved - 12, CreationTimePlus((0x06c00 << 3) + 0x03)}, + {kSmallLargestObserved - 11, CreationTimePlus((0x28e00 << 3) + 0x00)}, + // Timestamp Range 1. + {kSmallLargestObserved - 5, CreationTimePlus((0x29f00 << 3) + 0x00)}, + {kSmallLargestObserved - 4, CreationTimePlus((0x29f00 << 3) + 0x01)}, + {kSmallLargestObserved - 3, CreationTimePlus((0x29f00 << 3) + 0x02)}, + {kSmallLargestObserved - 2, CreationTimePlus((0x29f00 << 3) + 0x03)}, + }; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + + QuicFrames frames = {QuicFrame(&ack_frame)}; + + unsigned char packet_ietf[] = { + // type (short header, 4 byte packet number) + 0x43, + // connection_id + 0xFE, + 0xDC, + 0xBA, + 0x98, + 0x76, + 0x54, + 0x32, + 0x10, + // packet number + 0x12, + 0x34, + 0x56, + 0x78, + + // frame type (IETF_ACK_RECEIVE_TIMESTAMPS frame) + 0x22, + // largest acked + kVarInt62TwoBytes + 0x12, + 0x34, // = 4660 + // Zero delta time. + kVarInt62OneByte + 0x00, + // number of additional ack blocks + kVarInt62OneByte + 0x00, + // first ack block length. + kVarInt62TwoBytes + 0x12, + 0x33, + + // Receive Timestamps. + + // Timestamp Range Count + kVarInt62OneByte + 0x02, + + // Timestamp range 1 (three packets). + // Gap + kVarInt62OneByte + 0x02, + // Timestamp Range Count + kVarInt62OneByte + 0x04, + // Timestamp Delta + kVarInt62FourBytes + 0x00, + 0x02, + 0x9f, + 0x01, // round up + // Timestamp Delta + kVarInt62OneByte + 0x00, + // Timestamp Delta + kVarInt62OneByte + 0x00, + // Timestamp Delta + kVarInt62OneByte + 0x01, + + // Timestamp range 2 (one packet). + // Gap + kVarInt62OneByte + 0x04, + // Timestamp Range Count + kVarInt62OneByte + 0x02, + // Timestamp Delta + kVarInt62TwoBytes + 0x11, + 0x00, + // Timestamp Delta + kVarInt62FourBytes + 0x00, + 0x02, + 0x21, + 0xff, + }; + // clang-format on + + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(8); + framer_.set_receive_timestamps_exponent(3); + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + quiche::test::CompareCharArraysWithHexError( + "constructed packet", data->data(), data->length(), AsChars(packet_ietf), + ABSL_ARRAYSIZE(packet_ietf)); +} + +TEST_P(QuicFramerTest, BuildAndProcessAckReceiveTimestampsWithMultipleRanges) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + framer_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<StrictTaggingDecrypter>(/*key=*/0)); + framer_.SetKeyUpdateSupportForConnection(true); + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(8); + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + ack_frame.received_packet_times = PacketTimeVector{ + {kSmallLargestObserved - 1201, CreationTimePlus(0x8bcaef234)}, + {kSmallLargestObserved - 1200, CreationTimePlus(0x8bcdef123)}, + {kSmallLargestObserved - 1000, CreationTimePlus(0xaacdef123)}, + {kSmallLargestObserved - 4, CreationTimePlus(0xabcdea125)}, + {kSmallLargestObserved - 2, CreationTimePlus(0xabcdee124)}, + {kSmallLargestObserved - 1, CreationTimePlus(0xabcdef123)}, + {kSmallLargestObserved, CreationTimePlus(0xabcdef123)}, + }; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + std::unique_ptr<QuicEncryptedPacket> encrypted( + EncryptPacketWithTagAndPhase(*data, 0, false)); + ASSERT_TRUE(encrypted); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + EXPECT_THAT(framer_.error(), IsQuicNoError()); + + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_THAT(frame.received_packet_times, + ContainerEq(PacketTimeVector{ + {kSmallLargestObserved, CreationTimePlus(0xabcdef123)}, + {kSmallLargestObserved - 1, CreationTimePlus(0xabcdef123)}, + {kSmallLargestObserved - 2, CreationTimePlus(0xabcdee124)}, + {kSmallLargestObserved - 4, CreationTimePlus(0xabcdea125)}, + {kSmallLargestObserved - 1000, CreationTimePlus(0xaacdef123)}, + {kSmallLargestObserved - 1200, CreationTimePlus(0x8bcdef123)}, + {kSmallLargestObserved - 1201, CreationTimePlus(0x8bcaef234)}, + })); +} + +TEST_P(QuicFramerTest, + BuildAndProcessAckReceiveTimestampsExceedsMaxTimestamps) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + framer_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<StrictTaggingDecrypter>(/*key=*/0)); + framer_.SetKeyUpdateSupportForConnection(true); + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(2); + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + ack_frame.received_packet_times = PacketTimeVector{ + {kSmallLargestObserved - 1201, CreationTimePlus(0x8bcaef234)}, + {kSmallLargestObserved - 1200, CreationTimePlus(0x8bcdef123)}, + {kSmallLargestObserved - 1000, CreationTimePlus(0xaacdef123)}, + {kSmallLargestObserved - 5, CreationTimePlus(0xabcdea125)}, + {kSmallLargestObserved - 3, CreationTimePlus(0xabcded124)}, + {kSmallLargestObserved - 2, CreationTimePlus(0xabcdee124)}, + {kSmallLargestObserved - 1, CreationTimePlus(0xabcdef123)}, + }; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + std::unique_ptr<QuicEncryptedPacket> encrypted( + EncryptPacketWithTagAndPhase(*data, 0, false)); + ASSERT_TRUE(encrypted); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + EXPECT_THAT(framer_.error(), IsQuicNoError()); + + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_THAT(frame.received_packet_times, + ContainerEq(PacketTimeVector{ + {kSmallLargestObserved - 1, CreationTimePlus(0xabcdef123)}, + {kSmallLargestObserved - 2, CreationTimePlus(0xabcdee124)}, + })); +} + +TEST_P(QuicFramerTest, + BuildAndProcessAckReceiveTimestampsWithExponentNoTruncation) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + framer_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<StrictTaggingDecrypter>(/*key=*/0)); + framer_.SetKeyUpdateSupportForConnection(true); + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(8); + framer_.set_receive_timestamps_exponent(3); + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + ack_frame.received_packet_times = PacketTimeVector{ + {kSmallLargestObserved - 8, CreationTimePlus(0x1add << 3)}, + {kSmallLargestObserved - 7, CreationTimePlus(0x29ed << 3)}, + {kSmallLargestObserved - 3, CreationTimePlus(0x29fe << 3)}, + {kSmallLargestObserved - 2, CreationTimePlus(0x29ff << 3)}, + }; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + std::unique_ptr<QuicEncryptedPacket> encrypted( + EncryptPacketWithTagAndPhase(*data, 0, false)); + ASSERT_TRUE(encrypted); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + EXPECT_THAT(framer_.error(), IsQuicNoError()); + + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_THAT(frame.received_packet_times, + ContainerEq(PacketTimeVector{ + {kSmallLargestObserved - 2, CreationTimePlus(0x29ff << 3)}, + {kSmallLargestObserved - 3, CreationTimePlus(0x29fe << 3)}, + {kSmallLargestObserved - 7, CreationTimePlus(0x29ed << 3)}, + {kSmallLargestObserved - 8, CreationTimePlus(0x1add << 3)}, + })); +} + +TEST_P(QuicFramerTest, + BuildAndProcessAckReceiveTimestampsWithExponentTruncation) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + framer_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<StrictTaggingDecrypter>(/*key=*/0)); + framer_.SetKeyUpdateSupportForConnection(true); + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(8); + framer_.set_receive_timestamps_exponent(3); + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + ack_frame.received_packet_times = PacketTimeVector{ + {kSmallLargestObserved - 10, CreationTimePlus((0x1001 << 3) + 1)}, + {kSmallLargestObserved - 9, CreationTimePlus((0x2995 << 3) - 1)}, + {kSmallLargestObserved - 8, CreationTimePlus((0x2995 << 3) + 0)}, + {kSmallLargestObserved - 7, CreationTimePlus((0x2995 << 3) + 1)}, + {kSmallLargestObserved - 6, CreationTimePlus((0x2995 << 3) + 2)}, + {kSmallLargestObserved - 3, CreationTimePlus((0x2995 << 3) + 3)}, + {kSmallLargestObserved - 2, CreationTimePlus((0x2995 << 3) + 4)}, + }; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + std::unique_ptr<QuicEncryptedPacket> encrypted( + EncryptPacketWithTagAndPhase(*data, 0, false)); + ASSERT_TRUE(encrypted); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + EXPECT_THAT(framer_.error(), IsQuicNoError()); + + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_THAT(frame.received_packet_times, + ContainerEq(PacketTimeVector{ + {kSmallLargestObserved - 2, CreationTimePlus(0x2996 << 3)}, + {kSmallLargestObserved - 3, CreationTimePlus(0x2996 << 3)}, + {kSmallLargestObserved - 6, CreationTimePlus(0x2996 << 3)}, + {kSmallLargestObserved - 7, CreationTimePlus(0x2996 << 3)}, + {kSmallLargestObserved - 8, CreationTimePlus(0x2995 << 3)}, + {kSmallLargestObserved - 9, CreationTimePlus(0x2995 << 3)}, + {kSmallLargestObserved - 10, CreationTimePlus(0x1002 << 3)}, + })); +} + +TEST_P(QuicFramerTest, AckReceiveTimestamps) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + framer_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<StrictTaggingDecrypter>(/*key=*/0)); + framer_.SetKeyUpdateSupportForConnection(true); + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(8); + framer_.set_receive_timestamps_exponent(3); + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + // Use kSmallLargestObserved to make this test finished in a short time. + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + ack_frame.received_packet_times = PacketTimeVector{ + {kSmallLargestObserved - 5, CreationTimePlus((0x29ff << 3))}, + {kSmallLargestObserved - 4, CreationTimePlus((0x29ff << 3))}, + {kSmallLargestObserved - 3, CreationTimePlus((0x29ff << 3))}, + {kSmallLargestObserved - 2, CreationTimePlus((0x29ff << 3))}, + }; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + std::unique_ptr<QuicEncryptedPacket> encrypted( + EncryptPacketWithTagAndPhase(*data, 0, false)); + ASSERT_TRUE(encrypted); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + EXPECT_THAT(framer_.error(), IsQuicNoError()); + + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_THAT(frame.received_packet_times, + ContainerEq(PacketTimeVector{ + {kSmallLargestObserved - 2, CreationTimePlus(0x29ff << 3)}, + {kSmallLargestObserved - 3, CreationTimePlus(0x29ff << 3)}, + {kSmallLargestObserved - 4, CreationTimePlus(0x29ff << 3)}, + {kSmallLargestObserved - 5, CreationTimePlus(0x29ff << 3)}, + })); +} + +TEST_P(QuicFramerTest, AckReceiveTimestampsPacketOutOfOrder) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + framer_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<StrictTaggingDecrypter>(/*key=*/0)); + framer_.SetKeyUpdateSupportForConnection(true); + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(8); + framer_.set_receive_timestamps_exponent(3); + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + // Use kSmallLargestObserved to make this test finished in a short time. + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + + // The packet numbers below are out of order, this is impossible because we + // don't record out of order packets in received_packet_times. The test is + // intended to ensure this error is raised when it happens. + ack_frame.received_packet_times = PacketTimeVector{ + {kSmallLargestObserved - 5, CreationTimePlus((0x29ff << 3))}, + {kSmallLargestObserved - 2, CreationTimePlus((0x29ff << 3))}, + {kSmallLargestObserved - 4, CreationTimePlus((0x29ff << 3))}, + {kSmallLargestObserved - 3, CreationTimePlus((0x29ff << 3))}, + }; + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + EXPECT_QUIC_BUG(BuildDataPacket(header, frames), + "Packet number and/or receive time not in order."); +} + +// If there's insufficient room for IETF ack receive timestamps, don't write any +// timestamp ranges. +TEST_P(QuicFramerTest, IetfAckReceiveTimestampsTruncate) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + framer_.InstallDecrypter(ENCRYPTION_FORWARD_SECURE, + std::make_unique<StrictTaggingDecrypter>(/*key=*/0)); + framer_.SetKeyUpdateSupportForConnection(true); + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(8192); + framer_.set_receive_timestamps_exponent(3); + + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + // Use kSmallLargestObserved to make this test finished in a short time. + QuicAckFrame ack_frame = InitAckFrame(kSmallLargestObserved); + for (QuicPacketNumber i(1); i <= kSmallLargestObserved; i += 2) { + ack_frame.received_packet_times.push_back( + {i, CreationTimePlus((0x29ff << 3))}); + } + + ack_frame.ack_delay_time = QuicTime::Delta::Zero(); + QuicFrames frames = {QuicFrame(&ack_frame)}; + + std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames)); + ASSERT_TRUE(data != nullptr); + std::unique_ptr<QuicEncryptedPacket> encrypted( + EncryptPacketWithTagAndPhase(*data, 0, false)); + ASSERT_TRUE(encrypted); + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + EXPECT_TRUE(framer_.ProcessPacket(*encrypted)); + EXPECT_THAT(framer_.error(), IsQuicNoError()); + + const QuicAckFrame& frame = *visitor_.ack_frames_[0]; + EXPECT_TRUE(frame.received_packet_times.empty()); +} + +// If there are too many ack ranges, they will be truncated to make room for a +// timestamp range count of 0. +TEST_P(QuicFramerTest, IetfAckReceiveTimestampsAckRangeTruncation) { + if (!VersionHasIetfQuicFrames(framer_.transport_version())) { + return; + } + SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE); + framer_.set_process_timestamps(true); + framer_.set_max_receive_timestamps_per_ack(8); + framer_.set_receive_timestamps_exponent(3); + + QuicPacketHeader header; + header.destination_connection_id = FramerTestConnectionId(); + header.reset_flag = false; + header.version_flag = false; + header.packet_number = kPacketNumber; + + QuicAckFrame ack_frame; + // Create a packet with just the ack. + ack_frame = MakeAckFrameWithGaps(/*gap_size=*/0xffffffff, + /*max_num_gaps=*/200, + /*largest_acked=*/kMaxIetfVarInt); + ack_frame.received_packet_times = PacketTimeVector{ + {QuicPacketNumber(kMaxIetfVarInt) - 2, CreationTimePlus((0x29ff << 3))}, + }; + QuicFrames frames = {QuicFrame(&ack_frame)}; + // Build an ACK packet. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); + std::unique_ptr<QuicPacket> raw_ack_packet(BuildDataPacket(header, frames)); + ASSERT_TRUE(raw_ack_packet != nullptr); + char buffer[kMaxOutgoingPacketSize]; + size_t encrypted_length = + framer_.EncryptPayload(ENCRYPTION_INITIAL, header.packet_number, + *raw_ack_packet, buffer, kMaxOutgoingPacketSize); + ASSERT_NE(0u, encrypted_length); + // Now make sure we can turn our ack packet back into an ack frame. + QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_SERVER); + ASSERT_TRUE(framer_.ProcessPacket( + QuicEncryptedPacket(buffer, encrypted_length, false))); + ASSERT_EQ(1u, visitor_.ack_frames_.size()); + QuicAckFrame& processed_ack_frame = *visitor_.ack_frames_[0]; + EXPECT_EQ(QuicPacketNumber(kMaxIetfVarInt), + LargestAcked(processed_ack_frame)); + // Verify ACK ranges in the frame gets truncated. + ASSERT_LT(processed_ack_frame.packets.NumPacketsSlow(), + ack_frame.packets.NumIntervals()); + EXPECT_EQ(158u, processed_ack_frame.packets.NumPacketsSlow()); + EXPECT_LT(processed_ack_frame.packets.NumIntervals(), + ack_frame.packets.NumIntervals()); + EXPECT_EQ(QuicPacketNumber(kMaxIetfVarInt), + processed_ack_frame.packets.Max()); + // But the receive timestamps are not truncated because they are small. + EXPECT_FALSE(processed_ack_frame.received_packet_times.empty()); +} + TEST_P(QuicFramerTest, BuildAckFramePacketOneAckBlockMaxLength) { QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT); QuicPacketHeader header;