Provide an abstract API for a stream that one can write bytes into.
For QUIC, we have a QuicPacketWriter as an API for outputting all datagrams used by the QUIC connection; since encapsulated WebTransport (aka WebTransport over HTTP/2) has a single bytestream as an output, we need a similar type for that.
This CL takes the existing WebTransport stream write API, cleans it up, and moves it into its own class.
Notable changes to the write API:
- Make it vectorized
- Let it return a status object instead of simple true/false
- Merge writing and sending fin into a single method, the same way as existing QUIC APIs work
Parts of the old API are still kept around for compatibility with existing calls.
PiperOrigin-RevId: 501849717
diff --git a/build/source_list.bzl b/build/source_list.bzl
index 28ba715..0db8800 100644
--- a/build/source_list.bzl
+++ b/build/source_list.bzl
@@ -56,6 +56,7 @@
"common/quiche_mem_slice_storage.h",
"common/quiche_protocol_flags_list.h",
"common/quiche_random.h",
+ "common/quiche_stream.h",
"common/quiche_text_utils.h",
"common/simple_buffer_allocator.h",
"common/structured_headers.h",
diff --git a/build/source_list.gni b/build/source_list.gni
index 5779607..62962e7 100644
--- a/build/source_list.gni
+++ b/build/source_list.gni
@@ -56,6 +56,7 @@
"src/quiche/common/quiche_mem_slice_storage.h",
"src/quiche/common/quiche_protocol_flags_list.h",
"src/quiche/common/quiche_random.h",
+ "src/quiche/common/quiche_stream.h",
"src/quiche/common/quiche_text_utils.h",
"src/quiche/common/simple_buffer_allocator.h",
"src/quiche/common/structured_headers.h",
diff --git a/build/source_list.json b/build/source_list.json
index ad20600..e7cde56 100644
--- a/build/source_list.json
+++ b/build/source_list.json
@@ -55,6 +55,7 @@
"quiche/common/quiche_mem_slice_storage.h",
"quiche/common/quiche_protocol_flags_list.h",
"quiche/common/quiche_random.h",
+ "quiche/common/quiche_stream.h",
"quiche/common/quiche_text_utils.h",
"quiche/common/simple_buffer_allocator.h",
"quiche/common/structured_headers.h",
diff --git a/quiche/common/quiche_stream.h b/quiche/common/quiche_stream.h
new file mode 100644
index 0000000..c3dc621
--- /dev/null
+++ b/quiche/common/quiche_stream.h
@@ -0,0 +1,94 @@
+// Copyright 2023 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.
+
+// General-purpose abstractions for a write stream.
+
+#ifndef QUICHE_COMMON_QUICHE_STREAM_H_
+#define QUICHE_COMMON_QUICHE_STREAM_H_
+
+#include "absl/status/status.h"
+#include "absl/strings/string_view.h"
+#include "absl/types/span.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace quiche {
+
+// A general-purpose visitor API that gets notifications for WriteStream-related
+// events.
+class QUICHE_EXPORT WriteStreamVisitor {
+ public:
+ virtual ~WriteStreamVisitor() {}
+
+ // Called whenever the stream is not write-blocked and can accept new data.
+ virtual void OnCanWrite() = 0;
+};
+
+// Options for writing data into a WriteStream.
+class QUICHE_EXPORT StreamWriteOptions {
+ public:
+ StreamWriteOptions() = default;
+
+ // If send_fin() is sent to true, the write operation also sends a FIN on the
+ // stream.
+ bool send_fin() const { return send_fin_; }
+ void set_send_fin(bool send_fin) { send_fin_ = send_fin; }
+
+ private:
+ bool send_fin_ = false;
+};
+
+inline constexpr StreamWriteOptions kDefaultStreamWriteOptions =
+ StreamWriteOptions();
+
+// WriteStream is an object that can accept a stream of bytes.
+//
+// The writes into a WriteStream are all-or-nothing. A WriteStream object has
+// to either accept all data written into it by returning absl::OkStatus, or ask
+// the caller to try again once via OnCanWrite() by returning
+// absl::UnavailableError.
+class QUICHE_EXPORT WriteStream {
+ public:
+ virtual ~WriteStream() {}
+
+ // Writes |data| into the stream.
+ virtual absl::Status Writev(absl::Span<const absl::string_view> data,
+ const StreamWriteOptions& options) = 0;
+
+ // Indicates whether it is possible to write into stream right now.
+ virtual bool CanWrite() const = 0;
+
+ // Legacy convenience method for writing a single string_view. New users
+ // should use quiche::WriteIntoStream instead, since this method does not
+ // return useful failure information.
+ [[nodiscard]] bool SendFin() {
+ StreamWriteOptions options;
+ options.set_send_fin(true);
+ return Writev(absl::Span<const absl::string_view>(), options).ok();
+ }
+
+ // Legacy convenience method for writing a single string_view. New users
+ // should use quiche::WriteIntoStream instead, since this method does not
+ // return useful failure information.
+ [[nodiscard]] bool Write(absl::string_view data) {
+ return Writev(absl::MakeSpan(&data, 1), kDefaultStreamWriteOptions).ok();
+ }
+};
+
+// Convenience methods to write a single chunk of data into the stream.
+inline absl::Status WriteIntoStream(
+ WriteStream& stream, absl::string_view data,
+ const StreamWriteOptions& options = kDefaultStreamWriteOptions) {
+ return stream.Writev(absl::MakeSpan(&data, 1), options);
+}
+
+// Convenience methods to send a FIN on the stream.
+inline absl::Status SendFinOnStream(WriteStream& stream) {
+ StreamWriteOptions options;
+ options.set_send_fin(true);
+ return stream.Writev(absl::Span<const absl::string_view>(), options);
+}
+
+} // namespace quiche
+
+#endif // QUICHE_COMMON_QUICHE_STREAM_H_
diff --git a/quiche/common/test_tools/quiche_test_utils.h b/quiche/common/test_tools/quiche_test_utils.h
index 8fe1f7a..c233051 100644
--- a/quiche/common/test_tools/quiche_test_utils.h
+++ b/quiche/common/test_tools/quiche_test_utils.h
@@ -9,6 +9,7 @@
#include "absl/strings/string_view.h"
#include "quiche/common/platform/api/quiche_iovec.h"
+#include "quiche/common/platform/api/quiche_test.h"
namespace quiche {
namespace test {
@@ -26,6 +27,19 @@
// This function checks if IDNAs are supported.
bool GoogleUrlSupportsIdnaForTest();
+// Abseil does not provide absl::Status-related macros, so we have to provide
+// those instead.
+MATCHER(IsOk, "Checks if an instance of absl::Status is ok.") {
+ if (arg.ok()) {
+ return true;
+ }
+ *result_listener << "Expected status OK, got " << arg;
+ return false;
+}
+
+#define QUICHE_EXPECT_OK(arg) EXPECT_THAT((arg), ::quiche::test::IsOk())
+#define QUICHE_ASSERT_OK(arg) ASSERT_THAT((arg), ::quiche::test::IsOk())
+
} // namespace test
} // namespace quiche
diff --git a/quiche/quic/core/http/end_to_end_test.cc b/quiche/quic/core/http/end_to_end_test.cc
index 2524521..4625ebb 100644
--- a/quiche/quic/core/http/end_to_end_test.cc
+++ b/quiche/quic/core/http/end_to_end_test.cc
@@ -15,6 +15,7 @@
#include "absl/strings/string_view.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
+#include "absl/types/span.h"
#include "quiche/quic/core/crypto/null_encrypter.h"
#include "quiche/quic/core/crypto/quic_client_session_cache.h"
#include "quiche/quic/core/frames/quic_blocked_frame.h"
@@ -72,6 +73,8 @@
#include "quiche/quic/tools/quic_simple_client_stream.h"
#include "quiche/quic/tools/quic_simple_server_stream.h"
#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/quiche_stream.h"
+#include "quiche/common/test_tools/quiche_test_utils.h"
#include "quiche/spdy/core/http2_header_block.h"
using spdy::Http2HeaderBlock;
@@ -831,7 +834,8 @@
visitor = visitor_owned.get();
stream->SetVisitor(std::move(visitor_owned));
}
- EXPECT_CALL(*visitor, OnCanRead()).WillOnce(Assign(&can_read, true));
+ EXPECT_CALL(*visitor, OnCanRead())
+ .WillRepeatedly(Assign(&can_read, true));
client_->WaitUntil(5000 /*ms*/, [&can_read]() { return can_read; });
if (!can_read) {
ADD_FAILURE() << "Waiting for readable data on stream " << id
@@ -6600,7 +6604,7 @@
.WillOnce(Assign(&data_acknowledged, true));
outgoing_stream->SetVisitor(std::move(stream_visitor));
- EXPECT_TRUE(outgoing_stream->Write("test"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*outgoing_stream, "test"));
EXPECT_TRUE(outgoing_stream->SendFin());
bool stream_received = false;
@@ -6639,7 +6643,7 @@
WebTransportStream* outgoing_stream =
session->OpenOutgoingUnidirectionalStream();
ASSERT_TRUE(outgoing_stream != nullptr);
- EXPECT_TRUE(outgoing_stream->Write("test"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*outgoing_stream, "test"));
EXPECT_TRUE(outgoing_stream->SendFin());
bool stream_received = false;
@@ -6679,7 +6683,7 @@
.WillOnce(Assign(&data_acknowledged, true));
stream->SetVisitor(std::move(stream_visitor_owned));
- EXPECT_TRUE(stream->Write("test"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*stream, "test"));
EXPECT_TRUE(stream->SendFin());
std::string received_data =
@@ -6706,7 +6710,7 @@
WebTransportStream* stream = session->OpenOutgoingBidirectionalStream();
ASSERT_TRUE(stream != nullptr);
- EXPECT_TRUE(stream->Write("test"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*stream, "test"));
EXPECT_TRUE(stream->SendFin());
std::string received_data = ReadDataFromWebTransportStreamUntilFin(stream);
@@ -6735,11 +6739,16 @@
WebTransportStream* stream = session->AcceptIncomingBidirectionalStream();
ASSERT_TRUE(stream != nullptr);
- EXPECT_TRUE(stream->Write("test"));
- EXPECT_TRUE(stream->SendFin());
+ // Test the full Writev() API.
+ const std::string kLongString = std::string(16 * 1024, 'a');
+ std::vector<absl::string_view> write_vector = {"foo", "bar", "test",
+ kLongString};
+ quiche::StreamWriteOptions options;
+ options.set_send_fin(true);
+ QUICHE_EXPECT_OK(stream->Writev(absl::MakeConstSpan(write_vector), options));
std::string received_data = ReadDataFromWebTransportStreamUntilFin(stream);
- EXPECT_EQ(received_data, "test");
+ EXPECT_EQ(received_data, absl::StrCat("foobartest", kLongString));
}
TEST_P(EndToEndTest, WebTransportDatagrams) {
@@ -6786,7 +6795,7 @@
WebTransportStream* stream = session->OpenOutgoingBidirectionalStream();
ASSERT_TRUE(stream != nullptr);
QuicStreamId stream_id = stream->GetStreamId();
- EXPECT_TRUE(stream->Write("test"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*stream, "test"));
// Keep stream open.
bool close_received = false;
@@ -6818,7 +6827,7 @@
WebTransportStream* stream = session->OpenOutgoingBidirectionalStream();
ASSERT_TRUE(stream != nullptr);
QuicStreamId stream_id = stream->GetStreamId();
- EXPECT_TRUE(stream->Write("test"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*stream, "test"));
// Keep stream open.
bool close_received = false;
@@ -6850,7 +6859,7 @@
WebTransportStream* stream = session->OpenOutgoingUnidirectionalStream();
ASSERT_TRUE(stream != nullptr);
QuicStreamId stream_id = stream->GetStreamId();
- EXPECT_TRUE(stream->Write("42 test error"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*stream, "42 test error"));
EXPECT_TRUE(stream->SendFin());
// Have some other streams open pending, to ensure they are closed properly.
@@ -6890,7 +6899,7 @@
WebTransportStream* stream = session->OpenOutgoingBidirectionalStream();
QuicStreamId id1 = stream->GetStreamId();
ASSERT_TRUE(stream != nullptr);
- EXPECT_TRUE(stream->Write("test"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*stream, "test"));
stream->ResetWithUserCode(42);
// This read fails if the stream is closed in both directions, since that
@@ -6901,7 +6910,7 @@
stream = session->OpenOutgoingBidirectionalStream();
QuicStreamId id2 = stream->GetStreamId();
ASSERT_TRUE(stream != nullptr);
- EXPECT_TRUE(stream->Write("test"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*stream, "test"));
stream->SendStopSending(24);
std::array<std::string, 2> expected_log = {
@@ -6941,7 +6950,7 @@
WebTransportStream* stream = session->OpenOutgoingBidirectionalStream();
ASSERT_TRUE(stream != nullptr);
- EXPECT_TRUE(stream->Write("test"));
+ QUICHE_EXPECT_OK(quiche::WriteIntoStream(*stream, "test"));
EXPECT_TRUE(stream->SendFin());
EXPECT_TRUE(client_->WaitUntil(-1, [this, connect_stream_id]() {
diff --git a/quiche/quic/core/http/web_transport_stream_adapter.cc b/quiche/quic/core/http/web_transport_stream_adapter.cc
index 24d7bf5..6ed6975 100644
--- a/quiche/quic/core/http/web_transport_stream_adapter.cc
+++ b/quiche/quic/core/http/web_transport_stream_adapter.cc
@@ -4,10 +4,13 @@
#include "quiche/quic/core/http/web_transport_stream_adapter.h"
+#include "absl/status/status.h"
#include "quiche/quic/core/http/web_transport_http3.h"
#include "quiche/quic/core/quic_error_codes.h"
#include "quiche/quic/core/quic_types.h"
#include "quiche/common/platform/api/quiche_mem_slice.h"
+#include "quiche/common/quiche_buffer_allocator.h"
+#include "quiche/common/quiche_mem_slice_storage.h"
#include "quiche/web_transport/web_transport.h"
namespace quic {
@@ -41,51 +44,67 @@
return result;
}
-bool WebTransportStreamAdapter::Write(absl::string_view data) {
- if (!CanWrite()) {
- return false;
+absl::Status WebTransportStreamAdapter::Writev(
+ absl::Span<const absl::string_view> data,
+ const quiche::StreamWriteOptions& options) {
+ if (data.empty() && !options.send_fin()) {
+ return absl::InvalidArgumentError(
+ "Writev() called without any data or a FIN");
+ }
+ const absl::Status initial_check_status = CheckBeforeStreamWrite();
+ if (!initial_check_status.ok()) {
+ return initial_check_status;
}
- quiche::QuicheMemSlice memslice(quiche::QuicheBuffer::Copy(
- session_->connection()->helper()->GetStreamSendBufferAllocator(), data));
+ std::vector<iovec> iovecs;
+ size_t total_size = 0;
+ iovecs.resize(data.size());
+ for (size_t i = 0; i < data.size(); i++) {
+ // QuicheMemSliceStorage only reads iovec, thus this is safe.
+ iovecs[i].iov_base = const_cast<char*>(data[i].data());
+ iovecs[i].iov_len = data[i].size();
+ total_size += data[i].size();
+ }
+ quiche::QuicheMemSliceStorage storage(
+ iovecs.data(), iovecs.size(),
+ session_->connection()->helper()->GetStreamSendBufferAllocator(),
+ GetQuicFlag(quic_send_buffer_max_data_slice_size));
QuicConsumedData consumed =
- stream_->WriteMemSlices(absl::MakeSpan(&memslice, 1), /*fin=*/false);
+ stream_->WriteMemSlices(storage.ToSpan(), /*fin=*/options.send_fin());
- if (consumed.bytes_consumed == data.size()) {
- return true;
+ if (consumed.bytes_consumed == total_size) {
+ return absl::OkStatus();
}
if (consumed.bytes_consumed == 0) {
- return false;
+ return absl::UnavailableError("Stream write-blocked");
}
// WebTransportStream::Write() is an all-or-nothing write API. To achieve
// that property, it relies on WriteMemSlices() being an all-or-nothing API.
// If WriteMemSlices() fails to provide that guarantee, we have no way to
// communicate a partial write to the caller, and thus it's safer to just
// close the connection.
+ constexpr absl::string_view kErrorMessage =
+ "WriteMemSlices() unexpectedly partially consumed the input data";
QUIC_BUG(WebTransportStreamAdapter partial write)
- << "WriteMemSlices() unexpectedly partially consumed the input "
- "data, provided: "
- << data.size() << ", written: " << consumed.bytes_consumed;
- stream_->OnUnrecoverableError(
- QUIC_INTERNAL_ERROR,
- "WriteMemSlices() unexpectedly partially consumed the input data");
- return false;
+ << kErrorMessage << ", provided: " << total_size
+ << ", written: " << consumed.bytes_consumed;
+ stream_->OnUnrecoverableError(QUIC_INTERNAL_ERROR,
+ std::string(kErrorMessage));
+ return absl::InternalError(kErrorMessage);
}
-bool WebTransportStreamAdapter::SendFin() {
- if (!CanWrite()) {
- return false;
+absl::Status WebTransportStreamAdapter::CheckBeforeStreamWrite() const {
+ if (stream_->write_side_closed() || stream_->fin_buffered()) {
+ return absl::FailedPreconditionError("Stream write side is closed");
}
-
- quiche::QuicheMemSlice empty;
- QuicConsumedData consumed =
- stream_->WriteMemSlices(absl::MakeSpan(&empty, 1), /*fin=*/true);
- QUICHE_DCHECK_EQ(consumed.bytes_consumed, 0u);
- return consumed.fin_consumed;
+ if (!stream_->CanWriteNewData()) {
+ return absl::UnavailableError("Stream write-blocked");
+ }
+ return absl::OkStatus();
}
bool WebTransportStreamAdapter::CanWrite() const {
- return stream_->CanWriteNewData() && !stream_->write_side_closed();
+ return CheckBeforeStreamWrite().ok();
}
size_t WebTransportStreamAdapter::ReadableBytes() const {
diff --git a/quiche/quic/core/http/web_transport_stream_adapter.h b/quiche/quic/core/http/web_transport_stream_adapter.h
index 621120b..6a6f9fc 100644
--- a/quiche/quic/core/http/web_transport_stream_adapter.h
+++ b/quiche/quic/core/http/web_transport_stream_adapter.h
@@ -25,8 +25,8 @@
// WebTransportStream implementation.
ABSL_MUST_USE_RESULT ReadResult Read(absl::Span<char> output) override;
ABSL_MUST_USE_RESULT ReadResult Read(std::string* output) override;
- ABSL_MUST_USE_RESULT bool Write(absl::string_view data) override;
- ABSL_MUST_USE_RESULT bool SendFin() override;
+ absl::Status Writev(absl::Span<const absl::string_view> data,
+ const quiche::StreamWriteOptions& options) override;
bool CanWrite() const override;
size_t ReadableBytes() const override;
void SetVisitor(std::unique_ptr<WebTransportStreamVisitor> visitor) override {
@@ -53,6 +53,8 @@
void OnCanWriteNewData();
private:
+ absl::Status CheckBeforeStreamWrite() const;
+
QuicSession* session_; // Unowned.
QuicStream* stream_; // Unowned.
QuicStreamSequencer* sequencer_; // Unowned.
diff --git a/quiche/quic/tools/web_transport_test_visitors.h b/quiche/quic/tools/web_transport_test_visitors.h
index c311ff4..0770af6 100644
--- a/quiche/quic/tools/web_transport_test_visitors.h
+++ b/quiche/quic/tools/web_transport_test_visitors.h
@@ -7,11 +7,13 @@
#include <string>
+#include "absl/status/status.h"
#include "quiche/quic/core/web_transport_interface.h"
#include "quiche/quic/platform/api/quic_logging.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/platform/api/quiche_mem_slice.h"
#include "quiche/common/quiche_circular_deque.h"
+#include "quiche/common/quiche_stream.h"
#include "quiche/common/simple_buffer_allocator.h"
#include "quiche/spdy/core/http2_header_block.h"
@@ -63,11 +65,10 @@
}
if (!buffer_.empty()) {
- bool success = stream_->Write(buffer_);
+ absl::Status status = quiche::WriteIntoStream(*stream_, buffer_);
QUIC_DVLOG(1) << "Attempted writing on WebTransport bidirectional stream "
- << stream_->GetStreamId()
- << ", success: " << (success ? "yes" : "no");
- if (!success) {
+ << stream_->GetStreamId() << ", success: " << status;
+ if (!status.ok()) {
return;
}
@@ -75,8 +76,8 @@
}
if (send_fin_ && !fin_sent_) {
- bool success = stream_->SendFin();
- if (success) {
+ absl::Status status = quiche::SendFinOnStream(*stream_);
+ if (status.ok()) {
fin_sent_ = true;
}
}
@@ -153,14 +154,17 @@
if (data_.empty()) {
return;
}
- if (!stream_->Write(data_)) {
+ absl::Status write_status = quiche::WriteIntoStream(*stream_, data_);
+ if (!write_status.ok()) {
+ QUICHE_DLOG_IF(WARNING, !absl::IsUnavailable(write_status))
+ << "Failed to write into stream: " << write_status;
return;
}
data_ = "";
- bool fin_sent = stream_->SendFin();
+ absl::Status fin_status = quiche::SendFinOnStream(*stream_);
QUICHE_DVLOG(1)
<< "WebTransportUnidirectionalEchoWriteVisitor finished sending data.";
- QUICHE_DCHECK(fin_sent);
+ QUICHE_DCHECK(fin_status.ok());
}
void OnResetStreamReceived(WebTransportStreamError /*error*/) override {}
diff --git a/quiche/web_transport/test_tools/mock_web_transport.h b/quiche/web_transport/test_tools/mock_web_transport.h
index abd6d01..7df7e68 100644
--- a/quiche/web_transport/test_tools/mock_web_transport.h
+++ b/quiche/web_transport/test_tools/mock_web_transport.h
@@ -24,8 +24,10 @@
class QUICHE_NO_EXPORT MockStream : public Stream {
MOCK_METHOD(ReadResult, Read, (absl::Span<char> buffer), (override));
MOCK_METHOD(ReadResult, Read, (std::string * output), (override));
- MOCK_METHOD(bool, Write, (absl::string_view data), (override));
- MOCK_METHOD(bool, SendFin, (), (override));
+ MOCK_METHOD(absl::Status, Writev,
+ (absl::Span<const absl::string_view> data,
+ const quiche::StreamWriteOptions& options),
+ (override));
MOCK_METHOD(bool, CanWrite, (), (const, override));
MOCK_METHOD(size_t, ReadableBytes, (), (const, override));
MOCK_METHOD(StreamId, GetStreamId, (), (const, override));
diff --git a/quiche/web_transport/web_transport.h b/quiche/web_transport/web_transport.h
index ce66db5..b5ec5fc 100644
--- a/quiche/web_transport/web_transport.h
+++ b/quiche/web_transport/web_transport.h
@@ -18,6 +18,7 @@
#include "absl/time/time.h"
#include "absl/types/span.h"
#include "quiche/common/platform/api/quiche_export.h"
+#include "quiche/common/quiche_stream.h"
#include "quiche/spdy/core/http2_header_block.h"
namespace webtransport {
@@ -61,14 +62,12 @@
// events related to a WebTransport stream. The visitor object is owned by the
// stream itself, meaning that if the stream is ever fully closed, the visitor
// will be garbage-collected.
-class QUICHE_EXPORT StreamVisitor {
+class QUICHE_EXPORT StreamVisitor : public quiche::WriteStreamVisitor {
public:
virtual ~StreamVisitor() {}
// Called whenever the stream has readable data available.
virtual void OnCanRead() = 0;
- // Called whenever the stream is not write-blocked and can accept new data.
- virtual void OnCanWrite() = 0;
// Called when RESET_STREAM is received for the stream.
virtual void OnResetStreamReceived(StreamErrorCode error) = 0;
@@ -82,7 +81,7 @@
// A stream (either bidirectional or unidirectional) that is contained within a
// WebTransport session.
-class QUICHE_EXPORT Stream {
+class QUICHE_EXPORT Stream : public quiche::WriteStream {
public:
struct QUICHE_EXPORT ReadResult {
// Number of bytes actually read.
@@ -99,16 +98,7 @@
[[nodiscard]] virtual ReadResult Read(absl::Span<char> buffer) = 0;
// Reads all available data and appends it to the end of |output|.
[[nodiscard]] virtual ReadResult Read(std::string* output) = 0;
- // Writes |data| into the stream. WebTransport writes are all-or-nothing: the
- // stream will either accept all of the data (potentially buffering some of
- // it), or reject it. In the latter case, the method will return false, and
- // the sender has to wait until OnCanWrite() is called.
- [[nodiscard]] virtual bool Write(absl::string_view data) = 0;
- // Sends the FIN on the stream. Returns true on success.
- [[nodiscard]] virtual bool SendFin() = 0;
- // Indicates whether it is possible to write into stream right now.
- virtual bool CanWrite() const = 0;
// Indicates the number of bytes that can be read from the stream.
virtual size_t ReadableBytes() const = 0;