No public description PiperOrigin-RevId: 604325619
diff --git a/build/source_list.bzl b/build/source_list.bzl index 7c1fbdc..e904ef4 100644 --- a/build/source_list.bzl +++ b/build/source_list.bzl
@@ -209,6 +209,7 @@ "quic/core/frames/quic_path_challenge_frame.h", "quic/core/frames/quic_path_response_frame.h", "quic/core/frames/quic_ping_frame.h", + "quic/core/frames/quic_reset_stream_at_frame.h", "quic/core/frames/quic_retire_connection_id_frame.h", "quic/core/frames/quic_rst_stream_frame.h", "quic/core/frames/quic_stop_sending_frame.h", @@ -548,6 +549,7 @@ "quic/core/frames/quic_path_challenge_frame.cc", "quic/core/frames/quic_path_response_frame.cc", "quic/core/frames/quic_ping_frame.cc", + "quic/core/frames/quic_reset_stream_at_frame.cc", "quic/core/frames/quic_retire_connection_id_frame.cc", "quic/core/frames/quic_rst_stream_frame.cc", "quic/core/frames/quic_stop_sending_frame.cc",
diff --git a/build/source_list.gni b/build/source_list.gni index caddc96..f27df5b 100644 --- a/build/source_list.gni +++ b/build/source_list.gni
@@ -209,6 +209,7 @@ "src/quiche/quic/core/frames/quic_path_challenge_frame.h", "src/quiche/quic/core/frames/quic_path_response_frame.h", "src/quiche/quic/core/frames/quic_ping_frame.h", + "src/quiche/quic/core/frames/quic_reset_stream_at_frame.h", "src/quiche/quic/core/frames/quic_retire_connection_id_frame.h", "src/quiche/quic/core/frames/quic_rst_stream_frame.h", "src/quiche/quic/core/frames/quic_stop_sending_frame.h", @@ -548,6 +549,7 @@ "src/quiche/quic/core/frames/quic_path_challenge_frame.cc", "src/quiche/quic/core/frames/quic_path_response_frame.cc", "src/quiche/quic/core/frames/quic_ping_frame.cc", + "src/quiche/quic/core/frames/quic_reset_stream_at_frame.cc", "src/quiche/quic/core/frames/quic_retire_connection_id_frame.cc", "src/quiche/quic/core/frames/quic_rst_stream_frame.cc", "src/quiche/quic/core/frames/quic_stop_sending_frame.cc",
diff --git a/build/source_list.json b/build/source_list.json index 69f3934..0f63fbd 100644 --- a/build/source_list.json +++ b/build/source_list.json
@@ -208,6 +208,7 @@ "quiche/quic/core/frames/quic_path_challenge_frame.h", "quiche/quic/core/frames/quic_path_response_frame.h", "quiche/quic/core/frames/quic_ping_frame.h", + "quiche/quic/core/frames/quic_reset_stream_at_frame.h", "quiche/quic/core/frames/quic_retire_connection_id_frame.h", "quiche/quic/core/frames/quic_rst_stream_frame.h", "quiche/quic/core/frames/quic_stop_sending_frame.h", @@ -547,6 +548,7 @@ "quiche/quic/core/frames/quic_path_challenge_frame.cc", "quiche/quic/core/frames/quic_path_response_frame.cc", "quiche/quic/core/frames/quic_ping_frame.cc", + "quiche/quic/core/frames/quic_reset_stream_at_frame.cc", "quiche/quic/core/frames/quic_retire_connection_id_frame.cc", "quiche/quic/core/frames/quic_rst_stream_frame.cc", "quiche/quic/core/frames/quic_stop_sending_frame.cc",
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 {
diff --git a/quiche/quic/test_tools/quic_test_utils.cc b/quiche/quic/test_tools/quic_test_utils.cc index 8ffb8e8..58277df 100644 --- a/quiche/quic/test_tools/quic_test_utils.cc +++ b/quiche/quic/test_tools/quic_test_utils.cc
@@ -447,6 +447,11 @@ return true; } +bool NoOpFramerVisitor::OnResetStreamAtFrame( + const QuicResetStreamAtFrame& /*frame*/) { + return true; +} + bool NoOpFramerVisitor::IsValidStatelessResetToken( const StatelessResetToken& /*token*/) const { return false;
diff --git a/quiche/quic/test_tools/quic_test_utils.h b/quiche/quic/test_tools/quic_test_utils.h index 8fdaf98..7de2c2a 100644 --- a/quiche/quic/test_tools/quic_test_utils.h +++ b/quiche/quic/test_tools/quic_test_utils.h
@@ -20,6 +20,7 @@ #include "quiche/quic/core/congestion_control/loss_detection_interface.h" #include "quiche/quic/core/congestion_control/send_algorithm_interface.h" #include "quiche/quic/core/crypto/transport_parameters.h" +#include "quiche/quic/core/frames/quic_reset_stream_at_frame.h" #include "quiche/quic/core/http/http_decoder.h" #include "quiche/quic/core/http/quic_server_session_base.h" #include "quiche/quic/core/http/quic_spdy_client_session_base.h" @@ -353,6 +354,8 @@ (override)); MOCK_METHOD(bool, OnAckFrequencyFrame, (const QuicAckFrequencyFrame& frame), (override)); + MOCK_METHOD(bool, OnResetStreamAtFrame, (const QuicResetStreamAtFrame& frame), + (override)); MOCK_METHOD(void, OnPacketComplete, (), (override)); MOCK_METHOD(bool, IsValidStatelessResetToken, (const StatelessResetToken&), (const, override)); @@ -420,6 +423,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/test_tools/simple_quic_framer.cc b/quiche/quic/test_tools/simple_quic_framer.cc index fc50251..54ae4b3 100644 --- a/quiche/quic/test_tools/simple_quic_framer.cc +++ b/quiche/quic/test_tools/simple_quic_framer.cc
@@ -11,6 +11,7 @@ #include "absl/strings/string_view.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_types.h" namespace quic { @@ -210,6 +211,11 @@ return true; } + bool OnResetStreamAtFrame(const QuicResetStreamAtFrame& frame) override { + reset_stream_at_frames_.push_back(frame); + return true; + } + void OnPacketComplete() override {} bool IsValidStatelessResetToken( @@ -312,6 +318,7 @@ std::vector<QuicMessageFrame> message_frames_; std::vector<QuicHandshakeDoneFrame> handshake_done_frames_; std::vector<QuicAckFrequencyFrame> ack_frequency_frames_; + std::vector<QuicResetStreamAtFrame> reset_stream_at_frames_; std::vector<std::unique_ptr<std::string>> stream_data_; std::vector<std::unique_ptr<std::string>> crypto_data_; EncryptionLevel last_decrypted_level_;
diff --git a/quiche/quic/tools/quic_packet_printer_bin.cc b/quiche/quic/tools/quic_packet_printer_bin.cc index e507c5e..5ed7701 100644 --- a/quiche/quic/tools/quic_packet_printer_bin.cc +++ b/quiche/quic/tools/quic_packet_printer_bin.cc
@@ -213,6 +213,10 @@ std::cerr << "OnAckFrequencyFrame: " << frame; return true; } + bool OnResetStreamAtFrame(const QuicResetStreamAtFrame& frame) override { + std::cerr << "OnResetStreamAtFrame: " << frame; + return true; + } void OnPacketComplete() override { std::cerr << "OnPacketComplete\n"; } bool IsValidStatelessResetToken( const StatelessResetToken& /*token*/) const override {