No public description
PiperOrigin-RevId: 604325619
diff --git a/quiche/quic/core/chlo_extractor.cc b/quiche/quic/core/chlo_extractor.cc
index ae4d6e3..800efbd 100644
--- a/quiche/quic/core/chlo_extractor.cc
+++ b/quiche/quic/core/chlo_extractor.cc
@@ -12,6 +12,7 @@
#include "quiche/quic/core/crypto/quic_decrypter.h"
#include "quiche/quic/core/crypto/quic_encrypter.h"
#include "quiche/quic/core/frames/quic_ack_frequency_frame.h"
+#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/quic_framer.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
@@ -78,6 +79,7 @@
bool OnMessageFrame(const QuicMessageFrame& frame) override;
bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override;
bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& farme) override;
+ bool OnResetStreamAtFrame(const QuicResetStreamAtFrame& frame) override;
void OnPacketComplete() override {}
bool IsValidStatelessResetToken(
const StatelessResetToken& token) const override;
@@ -207,6 +209,11 @@
return true;
}
+bool ChloFramerVisitor::OnResetStreamAtFrame(
+ const QuicResetStreamAtFrame& /*frame*/) {
+ return true;
+}
+
bool ChloFramerVisitor::OnAckRange(QuicPacketNumber /*start*/,
QuicPacketNumber /*end*/) {
return true;
diff --git a/quiche/quic/core/frames/quic_frame.cc b/quiche/quic/core/frames/quic_frame.cc
index df4d9d3..b0bf329 100644
--- a/quiche/quic/core/frames/quic_frame.cc
+++ b/quiche/quic/core/frames/quic_frame.cc
@@ -5,7 +5,9 @@
#include "quiche/quic/core/frames/quic_frame.h"
#include "quiche/quic/core/frames/quic_new_connection_id_frame.h"
+#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/frames/quic_retire_connection_id_frame.h"
+#include "quiche/quic/core/frames/quic_rst_stream_frame.h"
#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/platform/api/quic_bug_tracker.h"
@@ -80,6 +82,9 @@
QuicFrame::QuicFrame(QuicAckFrequencyFrame* frame)
: type(ACK_FREQUENCY_FRAME), ack_frequency_frame(frame) {}
+QuicFrame::QuicFrame(QuicResetStreamAtFrame* frame)
+ : type(RESET_STREAM_AT_FRAME), reset_stream_at_frame(frame) {}
+
void DeleteFrames(QuicFrames* frames) {
for (QuicFrame& frame : *frames) {
DeleteFrame(&frame);
@@ -148,6 +153,9 @@
case ACK_FREQUENCY_FRAME:
delete frame->ack_frequency_frame;
break;
+ case RESET_STREAM_AT_FRAME:
+ delete frame->reset_stream_at_frame;
+ break;
case NUM_FRAME_TYPES:
QUICHE_DCHECK(false) << "Cannot delete type: " << frame->type;
}
@@ -179,6 +187,7 @@
case HANDSHAKE_DONE_FRAME:
case ACK_FREQUENCY_FRAME:
case NEW_TOKEN_FRAME:
+ case RESET_STREAM_AT_FRAME:
return true;
default:
return false;
@@ -213,6 +222,8 @@
return frame.ack_frequency_frame->control_frame_id;
case NEW_TOKEN_FRAME:
return frame.new_token_frame->control_frame_id;
+ case RESET_STREAM_AT_FRAME:
+ return frame.reset_stream_at_frame->control_frame_id;
default:
return kInvalidControlFrameId;
}
@@ -259,6 +270,9 @@
case NEW_TOKEN_FRAME:
frame->new_token_frame->control_frame_id = control_frame_id;
return;
+ case RESET_STREAM_AT_FRAME:
+ frame->reset_stream_at_frame->control_frame_id = control_frame_id;
+ return;
default:
QUIC_BUG(quic_bug_12594_1)
<< "Try to set control frame id of a frame without control frame id";
@@ -310,6 +324,10 @@
case NEW_TOKEN_FRAME:
copy = QuicFrame(new QuicNewTokenFrame(*frame.new_token_frame));
break;
+ case RESET_STREAM_AT_FRAME:
+ copy =
+ QuicFrame(new QuicResetStreamAtFrame(*frame.reset_stream_at_frame));
+ break;
default:
QUIC_BUG(quic_bug_10533_1)
<< "Try to copy a non-retransmittable control frame: " << frame;
@@ -404,6 +422,10 @@
case ACK_FREQUENCY_FRAME:
copy = QuicFrame(new QuicAckFrequencyFrame(*frame.ack_frequency_frame));
break;
+ case RESET_STREAM_AT_FRAME:
+ copy =
+ QuicFrame(new QuicResetStreamAtFrame(*frame.reset_stream_at_frame));
+ break;
default:
QUIC_BUG(quic_bug_10533_2) << "Cannot copy frame: " << frame;
copy = QuicFrame(QuicPingFrame(kInvalidControlFrameId));
@@ -506,6 +528,9 @@
case ACK_FREQUENCY_FRAME:
os << "type { ACK_FREQUENCY_FRAME } " << *(frame.ack_frequency_frame);
break;
+ case RESET_STREAM_AT_FRAME:
+ os << "type { RESET_STREAM_AT_FRAME } " << *(frame.reset_stream_at_frame);
+ break;
default: {
QUIC_LOG(ERROR) << "Unknown frame type: " << frame.type;
break;
diff --git a/quiche/quic/core/frames/quic_frame.h b/quiche/quic/core/frames/quic_frame.h
index 289b08d..dc99fc4 100644
--- a/quiche/quic/core/frames/quic_frame.h
+++ b/quiche/quic/core/frames/quic_frame.h
@@ -5,9 +5,10 @@
#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_FRAME_H_
#define QUICHE_QUIC_CORE_FRAMES_QUIC_FRAME_H_
+#include <cstddef>
#include <ostream>
+#include <string>
#include <type_traits>
-#include <vector>
#include "absl/container/inlined_vector.h"
#include "quiche/quic/core/frames/quic_ack_frame.h"
@@ -26,6 +27,7 @@
#include "quiche/quic/core/frames/quic_path_challenge_frame.h"
#include "quiche/quic/core/frames/quic_path_response_frame.h"
#include "quiche/quic/core/frames/quic_ping_frame.h"
+#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/frames/quic_retire_connection_id_frame.h"
#include "quiche/quic/core/frames/quic_rst_stream_frame.h"
#include "quiche/quic/core/frames/quic_stop_sending_frame.h"
@@ -34,7 +36,8 @@
#include "quiche/quic/core/frames/quic_streams_blocked_frame.h"
#include "quiche/quic/core/frames/quic_window_update_frame.h"
#include "quiche/quic/core/quic_types.h"
-#include "quiche/quic/platform/api/quic_export.h"
+#include "quiche/common/platform/api/quiche_export.h"
+#include "quiche/common/quiche_buffer_allocator.h"
#ifndef QUIC_FRAME_DEBUG
#if !defined(NDEBUG) || defined(ADDRESS_SANITIZER)
@@ -73,6 +76,7 @@
explicit QuicFrame(QuicMessageFrame* message_frame);
explicit QuicFrame(QuicCryptoFrame* crypto_frame);
explicit QuicFrame(QuicAckFrequencyFrame* ack_frequency_frame);
+ explicit QuicFrame(QuicResetStreamAtFrame* reset_stream_at_frame);
QUICHE_EXPORT friend std::ostream& operator<<(std::ostream& os,
const QuicFrame& frame);
@@ -115,6 +119,7 @@
QuicCryptoFrame* crypto_frame;
QuicAckFrequencyFrame* ack_frequency_frame;
QuicNewTokenFrame* new_token_frame;
+ QuicResetStreamAtFrame* reset_stream_at_frame;
};
};
};
diff --git a/quiche/quic/core/frames/quic_frames_test.cc b/quiche/quic/core/frames/quic_frames_test.cc
index 671e772..d585c68 100644
--- a/quiche/quic/core/frames/quic_frames_test.cc
+++ b/quiche/quic/core/frames/quic_frames_test.cc
@@ -616,6 +616,9 @@
case ACK_FREQUENCY_FRAME:
frames.push_back(QuicFrame(new QuicAckFrequencyFrame()));
break;
+ case RESET_STREAM_AT_FRAME:
+ frames.push_back(QuicFrame(new QuicResetStreamAtFrame()));
+ break;
default:
ASSERT_TRUE(false)
<< "Please fix CopyQuicFrames if a new frame type is added.";
diff --git a/quiche/quic/core/frames/quic_reset_stream_at_frame.cc b/quiche/quic/core/frames/quic_reset_stream_at_frame.cc
new file mode 100644
index 0000000..c8009c5
--- /dev/null
+++ b/quiche/quic/core/frames/quic_reset_stream_at_frame.cc
@@ -0,0 +1,44 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
+
+#include <cstdint>
+#include <ostream>
+
+#include "quiche/quic/core/quic_types.h"
+
+namespace quic {
+
+QuicResetStreamAtFrame::QuicResetStreamAtFrame(
+ QuicControlFrameId control_frame_id, QuicStreamId stream_id, uint64_t error,
+ QuicStreamOffset final_offset, QuicStreamOffset reliable_offset)
+ : control_frame_id(control_frame_id),
+ stream_id(stream_id),
+ error(error),
+ final_offset(final_offset),
+ reliable_offset(reliable_offset) {}
+
+std::ostream& operator<<(std::ostream& os,
+ const QuicResetStreamAtFrame& frame) {
+ os << "{ control_frame_id: " << frame.control_frame_id
+ << ", stream_id: " << frame.stream_id << ", error_code: " << frame.error
+ << ", final_offset: " << frame.final_offset
+ << ", reliable_offset: " << frame.reliable_offset << " }\n";
+ return os;
+}
+
+bool QuicResetStreamAtFrame::operator==(
+ const QuicResetStreamAtFrame& rhs) const {
+ return control_frame_id == rhs.control_frame_id &&
+ stream_id == rhs.stream_id && error == rhs.error &&
+ final_offset == rhs.final_offset &&
+ reliable_offset == rhs.reliable_offset;
+}
+bool QuicResetStreamAtFrame::operator!=(
+ const QuicResetStreamAtFrame& rhs) const {
+ return !(*this == rhs);
+}
+
+} // namespace quic
diff --git a/quiche/quic/core/frames/quic_reset_stream_at_frame.h b/quiche/quic/core/frames/quic_reset_stream_at_frame.h
new file mode 100644
index 0000000..95e7ee9
--- /dev/null
+++ b/quiche/quic/core/frames/quic_reset_stream_at_frame.h
@@ -0,0 +1,49 @@
+// Copyright 2024 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_QUIC_CORE_FRAMES_QUIC_RESET_STREAM_AT_FRAME_H_
+#define QUICHE_QUIC_CORE_FRAMES_QUIC_RESET_STREAM_AT_FRAME_H_
+
+#include <cstdint>
+#include <ostream>
+
+#include "quiche/quic/core/quic_constants.h"
+#include "quiche/quic/core/quic_types.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace quic {
+
+// RESET_STREAM_AT allows a QUIC application to reset a stream, but only after
+// the receiver consumes data up to a certain point. Defined in
+// <https://datatracker.ietf.org/doc/draft-ietf-quic-reliable-stream-reset/>.
+struct QUICHE_EXPORT QuicResetStreamAtFrame {
+ QuicResetStreamAtFrame() = default;
+ QuicResetStreamAtFrame(QuicControlFrameId control_frame_id,
+ QuicStreamId stream_id, uint64_t error,
+ QuicStreamOffset final_offset,
+ QuicStreamOffset reliable_offset);
+
+ friend QUICHE_EXPORT std::ostream& operator<<(
+ std::ostream& os, const QuicResetStreamAtFrame& frame);
+
+ bool operator==(const QuicResetStreamAtFrame& rhs) const;
+ bool operator!=(const QuicResetStreamAtFrame& rhs) const;
+
+ // A unique identifier of this control frame. 0 when this frame is received,
+ // and non-zero when sent.
+ QuicControlFrameId control_frame_id = kInvalidControlFrameId;
+
+ QuicStreamId stream_id = 0;
+ uint64_t error = 0;
+
+ // The total number of bytes ever sent on the stream; used for flow control.
+ QuicStreamOffset final_offset = 0;
+ // The RESET_STREAM is active only after the application reads up to
+ // `reliable_offset` bytes.
+ QuicStreamOffset reliable_offset = 0;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_FRAMES_QUIC_RESET_STREAM_AT_FRAME_H_
diff --git a/quiche/quic/core/quic_connection.cc b/quiche/quic/core/quic_connection.cc
index 88f52d8..f1d65b9 100644
--- a/quiche/quic/core/quic_connection.cc
+++ b/quiche/quic/core/quic_connection.cc
@@ -2120,6 +2120,25 @@
return true;
}
+bool QuicConnection::OnResetStreamAtFrame(const QuicResetStreamAtFrame& frame) {
+ QUIC_BUG_IF(OnResetStreamAtFrame_connection_closed, !connected_)
+ << "Processing RESET_STREAM_AT frame while the connection is closed. "
+ "Received packet info: "
+ << last_received_packet_info_;
+
+ if (debug_visitor_ != nullptr) {
+ debug_visitor_->OnResetStreamAtFrame(frame);
+ }
+ if (!UpdatePacketContent(RESET_STREAM_AT_FRAME)) {
+ return false;
+ }
+
+ // TODO(b/278878322): implement.
+
+ MaybeUpdateAckTimeout();
+ return true;
+}
+
bool QuicConnection::OnBlockedFrame(const QuicBlockedFrame& frame) {
QUIC_BUG_IF(quic_bug_12714_17, !connected_)
<< "Processing BLOCKED frame when connection is closed. Received packet "
diff --git a/quiche/quic/core/quic_connection.h b/quiche/quic/core/quic_connection.h
index 14033bd..598a11e 100644
--- a/quiche/quic/core/quic_connection.h
+++ b/quiche/quic/core/quic_connection.h
@@ -34,6 +34,7 @@
#include "quiche/quic/core/frames/quic_ack_frequency_frame.h"
#include "quiche/quic/core/frames/quic_max_streams_frame.h"
#include "quiche/quic/core/frames/quic_new_connection_id_frame.h"
+#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/quic_alarm.h"
#include "quiche/quic/core/quic_alarm_factory.h"
#include "quiche/quic/core/quic_blocked_writer_interface.h"
@@ -424,6 +425,9 @@
// Called when an AckFrequencyFrame has been parsed.
virtual void OnAckFrequencyFrame(const QuicAckFrequencyFrame& /*frame*/) {}
+ // Called when a ResetStreamAtFrame has been parsed.
+ virtual void OnResetStreamAtFrame(const QuicResetStreamAtFrame& /*frame*/) {}
+
// Called when |count| packet numbers have been skipped.
virtual void OnNPacketNumbersSkipped(QuicPacketCount /*count*/,
QuicTime /*now*/) {}
@@ -713,6 +717,7 @@
bool OnMessageFrame(const QuicMessageFrame& frame) override;
bool OnHandshakeDoneFrame(const QuicHandshakeDoneFrame& frame) override;
bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) override;
+ bool OnResetStreamAtFrame(const QuicResetStreamAtFrame& frame) override;
void OnPacketComplete() override;
bool IsValidStatelessResetToken(
const StatelessResetToken& token) const override;
diff --git a/quiche/quic/core/quic_connection_test.cc b/quiche/quic/core/quic_connection_test.cc
index bbdb4f4..b767764 100644
--- a/quiche/quic/core/quic_connection_test.cc
+++ b/quiche/quic/core/quic_connection_test.cc
@@ -15376,10 +15376,14 @@
QuicMessageFrame message_frame;
QuicNewTokenFrame new_token_frame;
QuicAckFrequencyFrame ack_frequency_frame;
+ QuicResetStreamAtFrame reset_stream_at_frame;
QuicBlockedFrame blocked_frame;
size_t packet_number = 1;
connection_.SetDefaultEncryptionLevel(ENCRYPTION_FORWARD_SECURE);
+ QuicFramer* framer = const_cast<QuicFramer*>(&connection_.framer());
+ framer->set_process_reset_stream_at(true);
+ peer_framer_.set_process_reset_stream_at(true);
for (uint8_t i = 0; i < NUM_FRAME_TYPES; ++i) {
QuicFrameType frame_type = static_cast<QuicFrameType>(i);
@@ -15463,6 +15467,9 @@
case ACK_FREQUENCY_FRAME:
frame = QuicFrame(&ack_frequency_frame);
break;
+ case RESET_STREAM_AT_FRAME:
+ frame = QuicFrame(&reset_stream_at_frame);
+ break;
case NUM_FRAME_TYPES:
skipped = true;
break;
diff --git a/quiche/quic/core/quic_framer.cc b/quiche/quic/core/quic_framer.cc
index 8f3a736..d4bb1bc 100644
--- a/quiche/quic/core/quic_framer.cc
+++ b/quiche/quic/core/quic_framer.cc
@@ -19,6 +19,7 @@
#include "absl/base/macros.h"
#include "absl/base/optimization.h"
#include "absl/cleanup/cleanup.h"
+#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "absl/strings/numbers.h"
#include "absl/strings/str_cat.h"
@@ -35,6 +36,7 @@
#include "quiche/quic/core/crypto/quic_encrypter.h"
#include "quiche/quic/core/crypto/quic_random.h"
#include "quiche/quic/core/frames/quic_ack_frequency_frame.h"
+#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/quic_connection_context.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_constants.h"
@@ -56,6 +58,7 @@
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/quic/platform/api/quic_stack_trace.h"
#include "quiche/common/quiche_text_utils.h"
+#include "quiche/common/wire_serialization.h"
namespace quic {
@@ -401,6 +404,7 @@
process_timestamps_(false),
max_receive_timestamps_per_ack_(std::numeric_limits<uint32_t>::max()),
receive_timestamps_exponent_(0),
+ process_reset_stream_at_(false),
creation_time_(creation_time),
last_timestamp_(QuicTime::Delta::Zero()),
support_key_update_for_connection_(false),
@@ -625,6 +629,16 @@
}
// static
+size_t QuicFramer::GetResetStreamAtFrameSize(
+ const QuicResetStreamAtFrame& frame) {
+ return QuicDataWriter::GetVarInt62Len(IETF_RESET_STREAM_AT) +
+ QuicDataWriter::GetVarInt62Len(frame.stream_id) +
+ QuicDataWriter::GetVarInt62Len(frame.error) +
+ QuicDataWriter::GetVarInt62Len(frame.final_offset) +
+ QuicDataWriter::GetVarInt62Len(frame.reliable_offset);
+}
+
+// static
size_t QuicFramer::GetPathChallengeFrameSize(
const QuicPathChallengeFrame& frame) {
return kQuicFrameTypeSize + sizeof(frame.data_buffer);
@@ -679,6 +693,8 @@
return kQuicFrameTypeSize;
case ACK_FREQUENCY_FRAME:
return GetAckFrequencyFrameSize(*frame.ack_frequency_frame);
+ case RESET_STREAM_AT_FRAME:
+ return GetResetStreamAtFrameSize(*frame.reset_stream_at_frame);
case STREAM_FRAME:
case ACK_FRAME:
case STOP_WAITING_FRAME:
@@ -1193,6 +1209,17 @@
return 0;
}
break;
+ case RESET_STREAM_AT_FRAME:
+ QUIC_BUG_IF(reset_stream_at_appended_while_disabled,
+ !process_reset_stream_at_)
+ << "Requested serialization of RESET_STREAM_AT_FRAME while it is "
+ "not explicitly enabled in the framer";
+ if (!AppendResetFrameAtFrame(*frame.reset_stream_at_frame, *writer)) {
+ QUIC_BUG(cannot_append_reset_stream_at)
+ << "AppendResetStreamAtFram failed: " << detailed_error();
+ return 0;
+ }
+ break;
default:
set_detailed_error("Tried to append unknown frame type.");
RaiseError(QUIC_INVALID_FRAME_DATA);
@@ -3126,6 +3153,24 @@
}
break;
}
+ case IETF_RESET_STREAM_AT: {
+ if (!process_reset_stream_at_) {
+ set_detailed_error("RESET_STREAM_AT not enabled.");
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+ QuicResetStreamAtFrame frame;
+ if (!ProcessResetStreamAtFrame(*reader, frame)) {
+ return RaiseError(QUIC_INVALID_FRAME_DATA);
+ }
+ QUIC_DVLOG(2) << ENDPOINT << "Processing RESET_STREAM_AT frame "
+ << frame;
+ if (!visitor_->OnResetStreamAtFrame(frame)) {
+ QUIC_DVLOG(1) << "Visitor asked to stop further processing.";
+ // Returning true since there was no parsing error.
+ return true;
+ }
+ break;
+ }
default:
set_detailed_error("Illegal frame type.");
QUIC_DLOG(WARNING)
@@ -3345,6 +3390,31 @@
return true;
}
+bool QuicFramer::ProcessResetStreamAtFrame(QuicDataReader& reader,
+ QuicResetStreamAtFrame& frame) {
+ if (!ReadUint32FromVarint62(&reader, IETF_RESET_STREAM_AT,
+ &frame.stream_id)) {
+ return false;
+ }
+ if (!reader.ReadVarInt62(&frame.error)) {
+ set_detailed_error("Failed to read the error code.");
+ return false;
+ }
+ if (!reader.ReadVarInt62(&frame.final_offset)) {
+ set_detailed_error("Failed to read the final offset.");
+ return false;
+ }
+ if (!reader.ReadVarInt62(&frame.reliable_offset)) {
+ set_detailed_error("Failed to read the reliable offset.");
+ return false;
+ }
+ if (frame.reliable_offset > frame.final_offset) {
+ set_detailed_error("reliable_offset > final_offset");
+ return false;
+ }
+ return true;
+}
+
bool QuicFramer::ProcessAckFrame(QuicDataReader* reader, uint8_t frame_type) {
const bool has_ack_blocks =
ExtractBit(frame_type, kQuicHasMultipleAckBlocksOffset);
@@ -4951,6 +5021,9 @@
case ACK_FREQUENCY_FRAME:
type_byte = IETF_ACK_FREQUENCY;
break;
+ case RESET_STREAM_AT_FRAME:
+ type_byte = IETF_RESET_STREAM_AT;
+ break;
default:
QUIC_BUG(quic_bug_10850_75)
<< "Attempt to generate a frame type for an unsupported value: "
@@ -5194,6 +5267,26 @@
return true;
}
+bool QuicFramer::AppendResetFrameAtFrame(const QuicResetStreamAtFrame& frame,
+ QuicDataWriter& writer) {
+ if (frame.reliable_offset > frame.final_offset) {
+ QUIC_BUG(AppendResetFrameAtFrame_offset_mismatch)
+ << "reliable_offset > final_offset";
+ set_detailed_error("reliable_offset > final_offset");
+ return false;
+ }
+ absl::Status status =
+ quiche::SerializeIntoWriter(writer, quiche::WireVarInt62(frame.stream_id),
+ quiche::WireVarInt62(frame.error),
+ quiche::WireVarInt62(frame.final_offset),
+ quiche::WireVarInt62(frame.reliable_offset));
+ if (!status.ok()) {
+ set_detailed_error(std::string(status.message()));
+ return false;
+ }
+ return true;
+}
+
void QuicFramer::set_version(const ParsedQuicVersion version) {
QUICHE_DCHECK(IsSupportedVersion(version))
<< ParsedQuicVersionToString(version);
diff --git a/quiche/quic/core/quic_framer.h b/quiche/quic/core/quic_framer.h
index 432d052..5e54bb1 100644
--- a/quiche/quic/core/quic_framer.h
+++ b/quiche/quic/core/quic_framer.h
@@ -15,6 +15,7 @@
#include "quiche/quic/core/crypto/quic_decrypter.h"
#include "quiche/quic/core/crypto/quic_encrypter.h"
#include "quiche/quic/core/crypto/quic_random.h"
+#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_types.h"
@@ -222,6 +223,9 @@
// Called when an AckFrequencyFrame has been parsed.
virtual bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& frame) = 0;
+ // Called when an ResetStreamAtFrame has been parsed.
+ virtual bool OnResetStreamAtFrame(const QuicResetStreamAtFrame& frame) = 0;
+
// Called when a packet has been completely processed.
virtual void OnPacketComplete() = 0;
@@ -324,6 +328,11 @@
receive_timestamps_exponent_ = exponent;
}
+ // Allows enabling RESET_STREAM_AT frame processing.
+ void set_process_reset_stream_at(bool process_reset_stream_at) {
+ process_reset_stream_at_ = process_reset_stream_at;
+ }
+
// Pass a UDP packet into the framer for parsing.
// Return true if the packet was processed successfully. |packet| must be a
// single, complete UDP packet (not a frame of a packet). This packet
@@ -360,6 +369,8 @@
const QuicRstStreamFrame& frame);
// Size in bytes of all ack frenquency frame fields.
static size_t GetAckFrequencyFrameSize(const QuicAckFrequencyFrame& frame);
+ // Size in bytes of all RESET_STREAM_AT frame fields.
+ static size_t GetResetStreamAtFrameSize(const QuicResetStreamAtFrame& frame);
// Size in bytes of all connection close frame fields, including the error
// details.
static size_t GetConnectionCloseFrameSize(
@@ -539,6 +550,8 @@
bool AppendCryptoFrame(const QuicCryptoFrame& frame, QuicDataWriter* writer);
bool AppendAckFrequencyFrame(const QuicAckFrequencyFrame& frame,
QuicDataWriter* writer);
+ bool AppendResetFrameAtFrame(const QuicResetStreamAtFrame& frame,
+ QuicDataWriter& writer);
// SetDecrypter sets the primary decrypter, replacing any that already exists.
// If an alternative decrypter is in place then the function QUICHE_DCHECKs.
@@ -978,6 +991,8 @@
QuicCryptoFrame* frame);
bool ProcessAckFrequencyFrame(QuicDataReader* reader,
QuicAckFrequencyFrame* frame);
+ bool ProcessResetStreamAtFrame(QuicDataReader& reader,
+ QuicResetStreamAtFrame& frame);
// IETF frame appending methods. All methods append the type byte as well.
bool AppendIetfStreamFrame(const QuicStreamFrame& frame,
bool last_frame_in_packet, QuicDataWriter* writer);
@@ -1112,6 +1127,8 @@
mutable uint32_t max_receive_timestamps_per_ack_;
// The exponent to use when writing/reading ACK receive timestamps.
mutable uint32_t receive_timestamps_exponent_;
+ // If true, process RESET_STREAM_AT frames.
+ bool process_reset_stream_at_;
// The creation time of the connection, used to calculate timestamps.
QuicTime creation_time_;
// The last timestamp received if process_timestamps_ is true.
diff --git a/quiche/quic/core/quic_framer_test.cc b/quiche/quic/core/quic_framer_test.cc
index e9dded2..7f4e53a 100644
--- a/quiche/quic/core/quic_framer_test.cc
+++ b/quiche/quic/core/quic_framer_test.cc
@@ -22,6 +22,7 @@
#include "quiche/quic/core/crypto/null_encrypter.h"
#include "quiche/quic/core/crypto/quic_decrypter.h"
#include "quiche/quic/core/crypto/quic_encrypter.h"
+#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/quic_connection_id.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_packets.h"
@@ -460,6 +461,15 @@
return true;
}
+ bool OnResetStreamAtFrame(const QuicResetStreamAtFrame& frame) override {
+ ++frame_count_;
+ reset_stream_at_frames_.push_back(
+ std::make_unique<QuicResetStreamAtFrame>(frame));
+ EXPECT_TRUE(VersionHasIetfQuicFrames(transport_version_));
+ EXPECT_EQ(IETF_RESET_STREAM_AT, framer_->current_received_frame_type());
+ return true;
+ }
+
void OnPacketComplete() override { ++complete_packets_; }
bool OnRstStreamFrame(const QuicRstStreamFrame& frame) override {
@@ -651,6 +661,7 @@
std::vector<std::unique_ptr<QuicMessageFrame>> message_frames_;
std::vector<std::unique_ptr<QuicHandshakeDoneFrame>> handshake_done_frames_;
std::vector<std::unique_ptr<QuicAckFrequencyFrame>> ack_frequency_frames_;
+ std::vector<std::unique_ptr<QuicResetStreamAtFrame>> reset_stream_at_frames_;
std::vector<std::unique_ptr<QuicEncryptedPacket>> coalesced_packets_;
std::vector<std::unique_ptr<QuicEncryptedPacket>> undecryptable_packets_;
std::vector<EncryptionLevel> undecryptable_decryption_levels_;
@@ -4791,6 +4802,87 @@
EXPECT_EQ(true, frame->ignore_order);
}
+TEST_P(QuicFramerTest, ParseResetStreamAtFrame) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // type = RESET_STREAM_AT
+ 0x24,
+ // stream ID
+ 0x00,
+ // application error code
+ 0x1e,
+ // final size
+ 0x20,
+ // reliable size
+ 0x10,
+ };
+ // clang-format on
+
+ if (!VersionHasIetfQuicFrames(framer_.transport_version())) {
+ return;
+ }
+ framer_.set_process_reset_stream_at(true);
+
+ QuicEncryptedPacket encrypted(AsChars(packet), ABSL_ARRAYSIZE(packet), false);
+ EXPECT_TRUE(framer_.ProcessPacket(encrypted));
+
+ EXPECT_THAT(framer_.error(), IsQuicNoError());
+ ASSERT_TRUE(visitor_.header_.get());
+ EXPECT_TRUE(CheckDecryption(
+ encrypted, !kIncludeVersion, !kIncludeDiversificationNonce,
+ kPacket8ByteConnectionId, kPacket0ByteConnectionId));
+
+ ASSERT_EQ(visitor_.reset_stream_at_frames_.size(), 1);
+ const QuicResetStreamAtFrame& frame = *visitor_.reset_stream_at_frames_[0];
+ EXPECT_EQ(frame.stream_id, 0x00);
+ EXPECT_EQ(frame.error, 0x1e);
+ EXPECT_EQ(frame.final_offset, 0x20);
+ EXPECT_EQ(frame.reliable_offset, 0x10);
+}
+
+TEST_P(QuicFramerTest, ParseInvalidResetStreamAtFrame) {
+ SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // type = RESET_STREAM_AT
+ 0x24,
+ // stream ID
+ 0x00,
+ // application error code
+ 0x1e,
+ // final size
+ 0x20,
+ // reliable size
+ 0x30,
+ };
+ // clang-format on
+
+ if (!VersionHasIetfQuicFrames(framer_.transport_version())) {
+ return;
+ }
+ framer_.set_process_reset_stream_at(true);
+
+ QuicEncryptedPacket encrypted(AsChars(packet), ABSL_ARRAYSIZE(packet), false);
+ EXPECT_FALSE(framer_.ProcessPacket(encrypted));
+ EXPECT_EQ(framer_.error(), QUIC_INVALID_FRAME_DATA);
+ EXPECT_EQ(visitor_.reset_stream_at_frames_.size(), 0);
+}
+
TEST_P(QuicFramerTest, MessageFrame) {
SetDecrypterLevel(ENCRYPTION_FORWARD_SECURE);
// clang-format off
@@ -8092,6 +8184,55 @@
ABSL_ARRAYSIZE(packet));
}
+TEST_P(QuicFramerTest, BuildResetStreamAtPacket) {
+ if (!VersionHasIetfQuicFrames(framer_.transport_version())) {
+ return;
+ }
+
+ QuicPacketHeader header;
+ header.destination_connection_id = FramerTestConnectionId();
+ header.reset_flag = false;
+ header.version_flag = false;
+ header.packet_number = kPacketNumber;
+
+ QuicResetStreamAtFrame frame;
+ frame.stream_id = 0x00;
+ frame.error = 0x1e;
+ frame.final_offset = 0x20;
+ frame.reliable_offset = 0x10;
+ QuicFrames frames = {QuicFrame(&frame)};
+
+ framer_.set_process_reset_stream_at(true);
+ std::unique_ptr<QuicPacket> data(BuildDataPacket(header, frames));
+ ASSERT_TRUE(data != nullptr);
+
+ // clang-format off
+ unsigned char packet[] = {
+ // type (short header, 4 byte packet number)
+ 0x43,
+ // connection_id
+ 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10,
+ // packet number
+ 0x12, 0x34, 0x56, 0x78,
+
+ // type = RESET_STREAM_AT
+ 0x24,
+ // stream ID
+ 0x00,
+ // application error code
+ 0x1e,
+ // final size
+ 0x20,
+ // reliable size
+ 0x10,
+ };
+ // clang-format on
+
+ quiche::test::CompareCharArraysWithHexError(
+ "constructed packet", data->data(), data->length(), AsChars(packet),
+ ABSL_ARRAYSIZE(packet));
+}
+
TEST_P(QuicFramerTest, BuildMessagePacket) {
QuicFramerPeer::SetPerspective(&framer_, Perspective::IS_CLIENT);
QuicPacketHeader header;
diff --git a/quiche/quic/core/quic_trace_visitor.cc b/quiche/quic/core/quic_trace_visitor.cc
index 7906536..2b049ca 100644
--- a/quiche/quic/core/quic_trace_visitor.cc
+++ b/quiche/quic/core/quic_trace_visitor.cc
@@ -94,6 +94,7 @@
case MESSAGE_FRAME:
case CRYPTO_FRAME:
case NEW_TOKEN_FRAME:
+ case RESET_STREAM_AT_FRAME:
break;
// Ignore gQUIC-specific frames.
@@ -226,6 +227,7 @@
case CRYPTO_FRAME:
case NEW_TOKEN_FRAME:
case ACK_FREQUENCY_FRAME:
+ case RESET_STREAM_AT_FRAME:
break;
case NUM_FRAME_TYPES:
diff --git a/quiche/quic/core/quic_types.cc b/quiche/quic/core/quic_types.cc
index b7b2b26..2555384 100644
--- a/quiche/quic/core/quic_types.cc
+++ b/quiche/quic/core/quic_types.cc
@@ -157,6 +157,7 @@
RETURN_STRING_LITERAL(NEW_TOKEN_FRAME)
RETURN_STRING_LITERAL(RETIRE_CONNECTION_ID_FRAME)
RETURN_STRING_LITERAL(ACK_FREQUENCY_FRAME)
+ RETURN_STRING_LITERAL(RESET_STREAM_AT_FRAME)
RETURN_STRING_LITERAL(NUM_FRAME_TYPES)
}
return absl::StrCat("Unknown(", static_cast<int>(t), ")");
diff --git a/quiche/quic/core/quic_types.h b/quiche/quic/core/quic_types.h
index b626531..119b266 100644
--- a/quiche/quic/core/quic_types.h
+++ b/quiche/quic/core/quic_types.h
@@ -275,6 +275,7 @@
NEW_TOKEN_FRAME,
RETIRE_CONNECTION_ID_FRAME,
ACK_FREQUENCY_FRAME,
+ RESET_STREAM_AT_FRAME,
NUM_FRAME_TYPES
};
@@ -347,6 +348,9 @@
// packet receive timestamps.
// TODO(ianswett): Determine a proper value to replace this temporary value.
IETF_ACK_RECEIVE_TIMESTAMPS = 0x22,
+
+ // https://datatracker.ietf.org/doc/html/draft-ietf-quic-reliable-stream-reset
+ IETF_RESET_STREAM_AT = 0x24,
};
QUICHE_EXPORT std::ostream& operator<<(std::ostream& os,
const QuicIetfFrameType& c);
@@ -357,7 +361,7 @@
#define IETF_STREAM_FRAME_TYPE_MASK 0xfffffffffffffff8
#define IETF_STREAM_FRAME_FLAG_MASK 0x07
#define IS_IETF_STREAM_FRAME(_stype_) \
- (((_stype_)&IETF_STREAM_FRAME_TYPE_MASK) == IETF_STREAM)
+ (((_stype_) & IETF_STREAM_FRAME_TYPE_MASK) == IETF_STREAM)
// These are the values encoded in the low-order 3 bits of the
// IETF_STREAMx frame type.
diff --git a/quiche/quic/core/quic_unacked_packet_map.cc b/quiche/quic/core/quic_unacked_packet_map.cc
index a89cf80..adebba4 100644
--- a/quiche/quic/core/quic_unacked_packet_map.cc
+++ b/quiche/quic/core/quic_unacked_packet_map.cc
@@ -52,6 +52,7 @@
kNewTokenFrameBitfield = 1 << 20,
kRetireConnectionIdFrameBitfield = 1 << 21,
kAckFrequencyFrameBitfield = 1 << 22,
+ kResetStreamAtFrameBitfield = 1 << 23,
};
QuicFrameTypeBitfield GetFrameTypeBitfield(QuicFrameType type) {
@@ -102,6 +103,8 @@
return kRetireConnectionIdFrameBitfield;
case ACK_FREQUENCY_FRAME:
return kAckFrequencyFrameBitfield;
+ case RESET_STREAM_AT_FRAME:
+ return kResetStreamAtFrameBitfield;
case NUM_FRAME_TYPES:
QUIC_BUG(quic_bug_10518_1) << "Unexpected frame type";
return kInvalidFrameBitfield;
diff --git a/quiche/quic/core/tls_chlo_extractor.h b/quiche/quic/core/tls_chlo_extractor.h
index c8d7810..2aaa0eb 100644
--- a/quiche/quic/core/tls_chlo_extractor.h
+++ b/quiche/quic/core/tls_chlo_extractor.h
@@ -13,6 +13,7 @@
#include "absl/types/span.h"
#include "openssl/ssl.h"
#include "quiche/quic/core/frames/quic_ack_frequency_frame.h"
+#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h"
#include "quiche/quic/core/quic_framer.h"
#include "quiche/quic/core/quic_packets.h"
#include "quiche/quic/core/quic_stream_sequencer.h"
@@ -177,6 +178,9 @@
bool OnAckFrequencyFrame(const QuicAckFrequencyFrame& /*frame*/) override {
return true;
}
+ bool OnResetStreamAtFrame(const QuicResetStreamAtFrame& /*frame*/) override {
+ return true;
+ }
void OnPacketComplete() override {}
bool IsValidStatelessResetToken(
const StatelessResetToken& /*token*/) const override {