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;