Send-side RESET_STREAM_AT frame. Nothing utilizes the API yet, so not protected.
This API is documented at go/reset-stream-at.
The application calls SetReliableSize() at will, which means that all data in the send buffer at that point will be delivered reliably, even if it is later reset.
If reliable_size is set, then an incoming STOP_SENDING will result in RESET_STREAM_AT.
When the application calls PartialResetWriteSide(), send a RESET_STREAM_AT with the indicated reliable_size. Accept no more data from the application, and notionally acknowledge any sent data beyond reliable_size.
(a) if reliable_size has been sent, the write side is closed, and if the read side is closed, the stream will when reliable_size is acked.
(b) if reliable_size has been buffered, the write side will not close until the buffered data up to reliable_size has been sent.
PiperOrigin-RevId: 704756508
diff --git a/quiche/quic/core/quic_stream_test.cc b/quiche/quic/core/quic_stream_test.cc
index d738fd6..c7bd13d 100644
--- a/quiche/quic/core/quic_stream_test.cc
+++ b/quiche/quic/core/quic_stream_test.cc
@@ -4,6 +4,7 @@
#include "quiche/quic/core/quic_stream.h"
+#include <cmath>
#include <cstddef>
#include <memory>
#include <optional>
@@ -21,6 +22,7 @@
#include "quiche/quic/core/quic_connection.h"
#include "quiche/quic/core/quic_constants.h"
#include "quiche/quic/core/quic_error_codes.h"
+#include "quiche/quic/core/quic_stream_sequencer.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/quic/core/quic_utils.h"
#include "quiche/quic/core/quic_versions.h"
@@ -91,6 +93,8 @@
ASSERT_EQ(num_bytes, QuicStreamPeer::sequencer(this)->Readv(&iov, 1));
}
+ QuicStreamSequencer* sequencer() { return QuicStream::sequencer(); }
+
private:
std::string data_;
};
@@ -121,6 +125,7 @@
QuicConfigPeer::SetReceivedInitialMaxStreamDataBytesOutgoingBidirectional(
session_->config(), kMinimumFlowControlSendWindow);
QuicConfigPeer::SetReceivedMaxUnidirectionalStreams(session_->config(), 10);
+ session_->config()->SetReliableStreamReset(true);
session_->OnConfigNegotiated();
stream_ = new StrictMock<TestStream>(kTestStreamId, session_.get(),
@@ -167,6 +172,23 @@
return true;
}
+ // Use application stream interface for sending data. This will trigger a call
+ // to mock_stream->Writev(_, _) that will have to return QuicConsumedData.
+ QuicConsumedData SendApplicationData(TestStream* stream,
+ absl::string_view data, size_t iov_len,
+ bool fin) {
+ struct iovec iov = {const_cast<char*>(data.data()), iov_len};
+ quiche::QuicheMemSliceStorage storage(
+ &iov, 1,
+ session_->connection()->helper()->GetStreamSendBufferAllocator(), 1024);
+ return stream->WriteMemSlices(storage.ToSpan(), fin);
+ }
+
+ QuicConsumedData SendApplicationData(absl::string_view data, size_t iov_len,
+ bool fin) {
+ return SendApplicationData(stream_, data, iov_len, fin);
+ }
+
protected:
MockQuicConnectionHelper helper_;
MockAlarmFactory alarm_factory_;
@@ -1222,11 +1244,7 @@
// Testing Writev.
EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
.WillOnce(Return(QuicConsumedData(0, false)));
- struct iovec iov = {const_cast<char*>(data.data()), data.length()};
- quiche::QuicheMemSliceStorage storage(
- &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(),
- 1024);
- QuicConsumedData consumed = stream_->WriteMemSlices(storage.ToSpan(), false);
+ QuicConsumedData consumed = SendApplicationData(data, data.length(), false);
// There is no buffered data before, all data should be consumed without
// respecting buffered data upper limit.
@@ -1236,10 +1254,8 @@
EXPECT_FALSE(stream_->CanWriteNewData());
EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).Times(0);
- quiche::QuicheMemSliceStorage storage2(
- &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(),
- 1024);
- consumed = stream_->WriteMemSlices(storage2.ToSpan(), false);
+ consumed = SendApplicationData(data, data.length(), false);
+
// No Data can be consumed as buffered data is beyond upper limit.
EXPECT_EQ(0u, consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
@@ -1261,10 +1277,7 @@
EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).Times(0);
// All data can be consumed as buffered data is below upper limit.
- quiche::QuicheMemSliceStorage storage3(
- &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(),
- 1024);
- consumed = stream_->WriteMemSlices(storage3.ToSpan(), false);
+ consumed = SendApplicationData(data, data.length(), false);
EXPECT_EQ(data.length(), consumed.bytes_consumed);
EXPECT_FALSE(consumed.fin_consumed);
EXPECT_EQ(data.length() + GetQuicFlag(quic_buffered_data_threshold) - 1,
@@ -1279,21 +1292,13 @@
stream_);
EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
.WillOnce(Invoke(session_.get(), &MockQuicSession::ConsumeData));
- struct iovec iov = {const_cast<char*>(data.data()), 5u};
- quiche::QuicheMemSliceStorage storage(
- &iov, 1, session_->connection()->helper()->GetStreamSendBufferAllocator(),
- 1024);
- QuicConsumedData consumed = stream_->WriteMemSlices(storage.ToSpan(), false);
+ QuicConsumedData consumed = SendApplicationData(data, 5, false);
EXPECT_EQ(data.length(), consumed.bytes_consumed);
- struct iovec iov2 = {const_cast<char*>(data.data()), 1u};
- quiche::QuicheMemSliceStorage storage2(
- &iov2, 1,
- session_->connection()->helper()->GetStreamSendBufferAllocator(), 1024);
EXPECT_QUIC_BUG(
{
EXPECT_CALL(*connection_,
CloseConnection(QUIC_STREAM_LENGTH_OVERFLOW, _, _));
- stream_->WriteMemSlices(storage2.ToSpan(), false);
+ SendApplicationData(data, 1, false);
},
"Write too many data via stream");
}
@@ -1942,6 +1947,353 @@
QuicStreamFrame(stream_->id(), true, 0, absl::string_view(data, 100)));
}
+TEST_P(QuicStreamTest, ReliableSizeNotAckedAtTimeOfReset) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ char data[100];
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ SendApplicationData(data, 100, false);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ stream_->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_CALL(*stream_, OnWriteSideInDataRecvdState()).Times(1);
+ EXPECT_CALL(*connection_, OnStreamReset(stream_->id(), _)).Times(1);
+ stream_->OnStreamFrameAcked(0, 100, false, QuicTime::Delta::Zero(),
+ QuicTime::Zero(), &newly_acked_length);
+ std::vector<std::unique_ptr<QuicStream>>* closed_streams =
+ session_->ClosedStreams();
+ EXPECT_TRUE(closed_streams->empty());
+ // Peer sends RST_STREAM in response.
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ EXPECT_EQ((*(closed_streams->begin()))->id(), stream_->id());
+ ASSERT_EQ(closed_streams->size(), 1);
+}
+
+TEST_P(QuicStreamTest, ReliableSizeNotAckedAtTimeOfResetAndRetransmitted) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ char data[100];
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ SendApplicationData(data, 100, false);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ // Send 50 more bytes that aren't reliable.
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(50, false)));
+ SendApplicationData(data, 50, false);
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ stream_->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+
+ // Lose all the bytes.
+ stream_->OnStreamFrameLost(0, 150, false);
+ // Cause retransmission of the reliable bytes.
+ EXPECT_CALL(*session_, WritevData(stream_->id(), 100, 0, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ stream_->OnCanWrite();
+
+ // Ack the reliable bytes, and close.
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_CALL(*stream_, OnWriteSideInDataRecvdState()).Times(1);
+ EXPECT_CALL(*connection_, OnStreamReset(stream_->id(), _)).Times(1);
+ stream_->OnStreamFrameAcked(0, 100, false, QuicTime::Delta::Zero(),
+ QuicTime::Zero(), &newly_acked_length);
+ std::vector<std::unique_ptr<QuicStream>>* closed_streams =
+ session_->ClosedStreams();
+ EXPECT_TRUE(closed_streams->empty());
+ // Peer sends RST_STREAM in response.
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ EXPECT_EQ((*(closed_streams->begin()))->id(), stream_->id());
+ ASSERT_EQ(closed_streams->size(), 1);
+}
+
+TEST_P(QuicStreamTest, ReliableSizeNotAckedAtTimeOfResetThenReadSideReset) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ char data[100];
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ SendApplicationData(data, 100, false);
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ stream_->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+
+ // Peer sends RST_STREAM in response.
+ QuicRstStreamFrame rst_frame(kInvalidControlFrameId, stream_->id(),
+ QUIC_STREAM_CANCELLED, 1234);
+ stream_->OnStreamReset(rst_frame);
+ std::vector<std::unique_ptr<QuicStream>>* closed_streams =
+ session_->ClosedStreams();
+ ASSERT_TRUE(closed_streams->empty());
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_CALL(*stream_, OnWriteSideInDataRecvdState()).Times(1);
+ EXPECT_CALL(*connection_, OnStreamReset(stream_->id(), _)).Times(1);
+ stream_->OnStreamFrameAcked(0, 100, false, QuicTime::Delta::Zero(),
+ QuicTime::Zero(), &newly_acked_length);
+ ASSERT_EQ(closed_streams->size(), 1);
+ EXPECT_EQ((*(closed_streams->begin()))->id(), stream_->id());
+}
+
+TEST_P(QuicStreamTest, ReliableSizeNotAckedAtTimeOfResetThenReadSideFin) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ char data[100];
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ SendApplicationData(data, 100, false);
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ stream_->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+ EXPECT_TRUE(stream_->write_side_closed());
+
+ // Peer sends OOO FIN.
+ stream_->OnStreamFrame(
+ QuicStreamFrame(stream_->id(), true, sizeof(data), ""));
+ std::vector<std::unique_ptr<QuicStream>>* closed_streams =
+ session_->ClosedStreams();
+ ASSERT_TRUE(closed_streams->empty());
+ EXPECT_FALSE(stream_->read_side_closed()); // Missing the data before 100.
+
+ QuicByteCount newly_acked_length = 0;
+ EXPECT_CALL(*stream_, OnWriteSideInDataRecvdState()).Times(1);
+ EXPECT_CALL(*connection_, OnStreamReset(stream_->id(), _)).Times(1);
+ stream_->OnStreamFrameAcked(0, 100, false, QuicTime::Delta::Zero(),
+ QuicTime::Zero(), &newly_acked_length);
+ ASSERT_TRUE(closed_streams->empty());
+ // The rest of the stream arrives.
+ EXPECT_CALL(*stream_, OnDataAvailable()).WillOnce([&]() {
+ // Most classes derived from QuicStream do something like this in
+ // OnDataAvailable. This is how FIN-related state is updated.
+ std::string buffer;
+ stream_->sequencer()->Read(&buffer);
+ if (stream_->sequencer()->IsClosed()) {
+ stream_->OnFinRead();
+ }
+ });
+ stream_->OnStreamFrame(QuicStreamFrame(
+ stream_->id(), false, 0, absl::string_view(data, sizeof(data))));
+ EXPECT_TRUE(stream_->read_side_closed());
+ ASSERT_EQ(closed_streams->size(), 1);
+ EXPECT_EQ((*(closed_streams->begin()))->id(), stream_->id());
+}
+
+TEST_P(QuicStreamTest, ReliableSizeAckedAtTimeOfReset) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ char data[100];
+ SendApplicationData(data, 100, false);
+ QuicByteCount newly_acked_length = 0;
+ stream_->OnStreamFrameAcked(0, 100, false, QuicTime::Delta::Zero(),
+ QuicTime::Zero(), &newly_acked_length);
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ EXPECT_CALL(*connection_, OnStreamReset(stream_->id(), _)).Times(1);
+ stream_->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+}
+
+TEST_P(QuicStreamTest, BufferedDataInReliableSize) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ EXPECT_CALL(*session_, WritevData(stream_->id(), 100, 0, _, _, _))
+ .WillOnce(Return(QuicConsumedData(50, false)));
+ char data[100];
+ // 50 bytes of this will be buffered.
+ SendApplicationData(data, 100, false);
+ EXPECT_EQ(stream_->BufferedDataBytes(), 50);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ stream_->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+ EXPECT_FALSE(stream_->write_side_closed());
+ EXPECT_CALL(*session_, WritevData(stream_->id(), 50, 50, _, _, _))
+ .WillOnce(Return(QuicConsumedData(50, false)));
+ stream_->OnCanWrite();
+ // Now that the stream has sent 100 bytes, the write side can be closed.
+ EXPECT_TRUE(stream_->write_side_closed());
+ EXPECT_CALL(*stream_, OnWriteSideInDataRecvdState()).Times(1);
+ EXPECT_CALL(*connection_, OnStreamReset(stream_->id(), _)).Times(1);
+ QuicByteCount newly_acked_length = 0;
+ stream_->OnStreamFrameAcked(0, 100, false, QuicTime::Delta::Zero(),
+ QuicTime::Zero(), &newly_acked_length);
+}
+
+TEST_P(QuicStreamTest, ReliableSizeIsFinOffset) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ EXPECT_CALL(*session_, WritevData(_, 100, 0, FIN, _, _))
+ .WillOnce(Return(QuicConsumedData(100, true)));
+ char data[100];
+ SendApplicationData(data, 100, true);
+ // Send STOP_SENDING, but nothing else.
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ EXPECT_CALL(*session_, MaybeSendRstStreamFrame(_, _, _)).Times(0);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ stream_->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+ // Lose the packet; the stream will not be FINed again.
+ stream_->OnStreamFrameLost(0, 100, true);
+ EXPECT_CALL(*session_,
+ WritevData(stream_->id(), 100, 0, NO_FIN, LOSS_RETRANSMISSION, _))
+ .WillOnce(Return(QuicConsumedData(100, true)));
+ stream_->OnCanWrite();
+}
+
+TEST_P(QuicStreamTest, DataAfterResetStreamAt) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ char data[100];
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ SendApplicationData(data, 100, false);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ stream_->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _)).Times(0);
+ EXPECT_QUIC_BUG(SendApplicationData(data, 100, false),
+ "Fin already buffered or RESET_STREAM_AT sent");
+ EXPECT_EQ(stream_->stream_bytes_written(), 100);
+}
+
+TEST_P(QuicStreamTest, SetReliableSizeOnUnidirectionalRead) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ QuicStreamId stream_id = QuicUtils::GetFirstUnidirectionalStreamId(
+ connection_->transport_version(), Perspective::IS_CLIENT);
+ TestStream stream(stream_id, session_.get(), READ_UNIDIRECTIONAL);
+ EXPECT_FALSE(stream.SetReliableSize());
+}
+
+// This covers the case where the read side is already closed, that the zombie
+// stream is cleaned up.
+TEST_P(QuicStreamTest, ResetStreamAtUnidirectionalWrite) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ const QuicStreamId kId = 3;
+ std::unique_ptr<TestStream> stream =
+ std::make_unique<TestStream>(kId, session_.get(), WRITE_UNIDIRECTIONAL);
+ TestStream* stream_ptr = stream.get();
+ session_->ActivateStream(std::move(stream));
+ char data[100];
+ EXPECT_CALL(*session_, WritevData(kId, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ SendApplicationData(stream_ptr, data, 100, false);
+ EXPECT_TRUE(stream_ptr->SetReliableSize());
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ stream_ptr->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+ EXPECT_CALL(*stream_ptr, OnWriteSideInDataRecvdState());
+ EXPECT_CALL(*connection_, OnStreamReset(kId, _)).Times(1);
+ ;
+ QuicByteCount newly_acked_length = 0;
+ stream_ptr->OnStreamFrameAcked(0, 100, false, QuicTime::Delta::Zero(),
+ QuicTime::Zero(), &newly_acked_length);
+ std::vector<std::unique_ptr<QuicStream>>* closed_streams =
+ session_->ClosedStreams();
+ ASSERT_EQ(closed_streams->size(), 1);
+ EXPECT_EQ((*(closed_streams->begin()))->id(), kId);
+}
+
+// This covers the case where the read side is already closed with FIN, that the
+// zombie stream is cleaned up.
+TEST_P(QuicStreamTest, ResetStreamAtReadSideFin) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ // Fin the read side.
+ QuicStreamId stream_id = stream_->id();
+ EXPECT_CALL(*stream_, OnDataAvailable()).Times(1);
+ stream_->OnStreamFrame(QuicStreamFrame(stream_->id(), true, 0, ""));
+ stream_->OnFinRead();
+ char data[100];
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ SendApplicationData(data, 100, false);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ stream_->PartialResetWriteSide(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+ EXPECT_CALL(*stream_, OnWriteSideInDataRecvdState());
+ EXPECT_CALL(*connection_, OnStreamReset(stream_id, _)).Times(1);
+ QuicByteCount newly_acked_length = 0;
+ stream_->OnStreamFrameAcked(0, 100, false, QuicTime::Delta::Zero(),
+ QuicTime::Zero(), &newly_acked_length);
+ std::vector<std::unique_ptr<QuicStream>>* closed_streams =
+ session_->ClosedStreams();
+ ASSERT_EQ(closed_streams->size(), 1);
+ EXPECT_EQ((*(closed_streams->begin()))->id(), stream_id);
+}
+
+TEST_P(QuicStreamTest, ResetStreamAtAfterStopSending) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ char data[100];
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ stream_->WriteOrBufferData(absl::string_view(data, 100), false, nullptr);
+ EXPECT_TRUE(stream_->SetReliableSize());
+ EXPECT_CALL(*session_, MaybeSendResetStreamAtFrame(_, _, _, _)).Times(1);
+ stream_->OnStopSending(
+ QuicResetStreamError::FromInternal(QUIC_STREAM_CANCELLED));
+}
+
+TEST_P(QuicStreamTest, RejectReliableSizeOldVersion) {
+ Initialize();
+ if (VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ char data[100];
+ EXPECT_CALL(*session_, WritevData(_, _, _, _, _, _))
+ .WillOnce(Return(QuicConsumedData(100, false)));
+ stream_->WriteOrBufferData(absl::string_view(data, 100), false, nullptr);
+ EXPECT_FALSE(stream_->SetReliableSize());
+}
+
+TEST_P(QuicStreamTest, RejectReliableSizeReadOnlyStream) {
+ Initialize();
+ if (!VersionHasIetfQuicFrames(session_->transport_version())) {
+ return;
+ }
+ auto uni = new StrictMock<TestStream>(6, session_.get(), READ_UNIDIRECTIONAL);
+ session_->ActivateStream(absl::WrapUnique(uni));
+ EXPECT_FALSE(uni->SetReliableSize());
+}
+
} // namespace
} // namespace test
} // namespace quic