| // 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 << "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 |