| // Copyright (c) 2019 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 "quic/quic_transport/quic_transport_stream.h" |
| |
| #include <sys/types.h> |
| |
| #include "absl/strings/string_view.h" |
| #include "quic/core/quic_buffer_allocator.h" |
| #include "quic/core/quic_error_codes.h" |
| #include "quic/core/quic_types.h" |
| #include "quic/core/quic_utils.h" |
| |
| namespace quic { |
| |
| QuicTransportStream::QuicTransportStream( |
| QuicStreamId id, |
| QuicSession* session, |
| QuicTransportSessionInterface* session_interface) |
| : QuicStream(id, |
| session, |
| /*is_static=*/false, |
| QuicUtils::GetStreamType(id, |
| session->connection()->perspective(), |
| session->IsIncomingStream(id), |
| session->version())), |
| session_interface_(session_interface) {} |
| |
| size_t QuicTransportStream::Read(char* buffer, size_t buffer_size) { |
| if (!session_interface_->IsSessionReady()) { |
| return 0; |
| } |
| |
| iovec iov; |
| iov.iov_base = buffer; |
| iov.iov_len = buffer_size; |
| const size_t result = sequencer()->Readv(&iov, 1); |
| if (sequencer()->IsClosed()) { |
| MaybeNotifyFinRead(); |
| } |
| return result; |
| } |
| |
| size_t QuicTransportStream::Read(std::string* output) { |
| const size_t old_size = output->size(); |
| const size_t bytes_to_read = ReadableBytes(); |
| output->resize(old_size + bytes_to_read); |
| size_t bytes_read = Read(&(*output)[old_size], bytes_to_read); |
| QUICHE_DCHECK_EQ(bytes_to_read, bytes_read); |
| output->resize(old_size + bytes_read); |
| return bytes_read; |
| } |
| |
| bool QuicTransportStream::Write(absl::string_view data) { |
| if (!CanWrite()) { |
| return false; |
| } |
| |
| 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(quic_bug_10893_1) |
| << "WriteMemSlices() unexpectedly partially consumed the input " |
| "data, provided: " |
| << data.size() << ", written: " << consumed.bytes_consumed; |
| OnUnrecoverableError( |
| QUIC_INTERNAL_ERROR, |
| "WriteMemSlices() unexpectedly partially consumed the input data"); |
| return false; |
| } |
| |
| bool QuicTransportStream::SendFin() { |
| if (!CanWrite()) { |
| return false; |
| } |
| |
| QuicMemSlice empty; |
| QuicConsumedData consumed = |
| WriteMemSlices(QuicMemSliceSpan(&empty), /*fin=*/true); |
| QUICHE_DCHECK_EQ(consumed.bytes_consumed, 0u); |
| return consumed.fin_consumed; |
| } |
| |
| bool QuicTransportStream::CanWrite() const { |
| return session_interface_->IsSessionReady() && CanWriteNewData() && |
| !write_side_closed(); |
| } |
| |
| size_t QuicTransportStream::ReadableBytes() const { |
| if (!session_interface_->IsSessionReady()) { |
| return 0; |
| } |
| |
| return sequencer()->ReadableBytes(); |
| } |
| |
| void QuicTransportStream::OnDataAvailable() { |
| if (sequencer()->IsClosed()) { |
| MaybeNotifyFinRead(); |
| return; |
| } |
| |
| if (visitor_ == nullptr) { |
| return; |
| } |
| if (ReadableBytes() == 0) { |
| return; |
| } |
| visitor_->OnCanRead(); |
| } |
| |
| void QuicTransportStream::OnCanWriteNewData() { |
| // Ensure the origin check has been completed, as the stream can be notified |
| // about being writable before that. |
| if (!CanWrite()) { |
| return; |
| } |
| if (visitor_ != nullptr) { |
| visitor_->OnCanWrite(); |
| } |
| } |
| |
| void QuicTransportStream::MaybeNotifyFinRead() { |
| if (visitor_ == nullptr || fin_read_notified_) { |
| return; |
| } |
| fin_read_notified_ = true; |
| visitor_->OnFinRead(); |
| OnFinRead(); |
| } |
| |
| } // namespace quic |