Use WriteMemSlices() instead of WriteOrBufferData() in QuicTransportStream. Originally, this was supposed to fix the fact that WriteOrBufferData() has unlimited buffering. However, as I wrote a test for that, I discovered that I've already added a check for this by accident when I originally wrote this code. I'm still switching this code to use WriteMemSlices() as it actually returns whether it has succeeded. gfe-relnote: n/a (not used in production) PiperOrigin-RevId: 288496889 Change-Id: I801c2f870dd04b9fa478e12452834ec38e20a870
diff --git a/quic/quic_transport/quic_transport_stream.cc b/quic/quic_transport/quic_transport_stream.cc index ac54fa3..db90597 100644 --- a/quic/quic_transport/quic_transport_stream.cc +++ b/quic/quic_transport/quic_transport_stream.cc
@@ -6,6 +6,8 @@ #include <sys/types.h> +#include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" +#include "net/third_party/quiche/src/quic/core/quic_error_codes.h" #include "net/third_party/quiche/src/quic/core/quic_types.h" #include "net/third_party/quiche/src/quic/core/quic_utils.h" #include "net/third_party/quiche/src/common/platform/api/quiche_string_piece.h" @@ -54,9 +56,32 @@ return false; } - // TODO(vasilvv): use WriteMemSlices() - WriteOrBufferData(data, /*fin=*/false, nullptr); - return true; + QuicUniqueBufferPtr buffer = MakeUniqueBuffer( + session()->connection()->helper()->GetStreamSendBufferAllocator(), + data.size()); + memcpy(buffer.get(), data.data(), data.size()); + QuicMemSlice memslice(std::move(buffer), data.size()); + QuicConsumedData consumed = + WriteMemSlices(QuicMemSliceSpan(&memslice), /*fin=*/false); + + if (consumed.bytes_consumed == data.size()) { + return true; + } + if (consumed.bytes_consumed == 0) { + return false; + } + // QuicTransportStream::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. + QUIC_BUG << "WriteMemSlices() unexpectedly partially consumed the input " + "data, provided: " + << data.size() << ", written: " << consumed.bytes_consumed; + CloseConnectionWithDetails( + QUIC_INTERNAL_ERROR, + "WriteMemSlices() unexpectedly partially consumed the input data"); + return false; } bool QuicTransportStream::SendFin() { @@ -64,8 +89,11 @@ return false; } - WriteOrBufferData(quiche::QuicheStringPiece(), /*fin=*/true, nullptr); - return true; + QuicMemSlice empty; + QuicConsumedData consumed = + WriteMemSlices(QuicMemSliceSpan(&empty), /*fin=*/true); + DCHECK_EQ(consumed.bytes_consumed, 0u); + return consumed.fin_consumed; } bool QuicTransportStream::CanWrite() const {
diff --git a/quic/quic_transport/quic_transport_stream_test.cc b/quic/quic_transport/quic_transport_stream_test.cc index eda6af6..f4311ce 100644 --- a/quic/quic_transport/quic_transport_stream_test.cc +++ b/quic/quic_transport/quic_transport_stream_test.cc
@@ -120,6 +120,22 @@ ASSERT_EQ(stream_->Read(&buffer), 4u); } +TEST_F(QuicTransportStreamTest, WritingTooMuchData) { + EXPECT_CALL(interface_, IsSessionReady()).WillRepeatedly(Return(true)); + ASSERT_TRUE(stream_->CanWrite()); + + std::string a_little_bit_of_data(128, 'A'); + std::string a_lot_of_data(GetQuicFlag(FLAGS_quic_buffered_data_threshold) * 2, + 'a'); + + EXPECT_TRUE(stream_->Write(a_little_bit_of_data)); + EXPECT_TRUE(stream_->Write(a_little_bit_of_data)); + EXPECT_TRUE(stream_->Write(a_little_bit_of_data)); + + EXPECT_TRUE(stream_->Write(a_lot_of_data)); + EXPECT_FALSE(stream_->Write(a_lot_of_data)); +} + } // namespace } // namespace test } // namespace quic