| // 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 "net/third_party/quiche/src/quic/tools/quic_transport_simple_server_session.h" |
| |
| #include <memory> |
| |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| #include "net/third_party/quiche/src/quic/core/quic_buffer_allocator.h" |
| #include "net/third_party/quiche/src/quic/core/quic_types.h" |
| #include "net/third_party/quiche/src/quic/core/quic_versions.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" |
| #include "net/third_party/quiche/src/quic/platform/api/quic_logging.h" |
| #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_protocol.h" |
| #include "net/third_party/quiche/src/quic/quic_transport/quic_transport_stream.h" |
| |
| namespace quic { |
| |
| namespace { |
| |
| // Discards any incoming data. |
| class DiscardVisitor : public QuicTransportStream::Visitor { |
| public: |
| DiscardVisitor(QuicTransportStream* stream) : stream_(stream) {} |
| |
| void OnCanRead() override { |
| std::string buffer; |
| size_t bytes_read = stream_->Read(&buffer); |
| QUIC_DVLOG(2) << "Read " << bytes_read << " bytes from stream " |
| << stream_->id(); |
| } |
| |
| void OnFinRead() override {} |
| void OnCanWrite() override {} |
| |
| private: |
| QuicTransportStream* stream_; |
| }; |
| |
| // Echoes any incoming data back on the same stream. |
| class BidirectionalEchoVisitor : public QuicTransportStream::Visitor { |
| public: |
| BidirectionalEchoVisitor(QuicTransportStream* stream) : stream_(stream) {} |
| |
| void OnCanRead() override { |
| stream_->Read(&buffer_); |
| OnCanWrite(); |
| } |
| |
| void OnFinRead() override { |
| bool success = stream_->SendFin(); |
| DCHECK(success); |
| } |
| |
| void OnCanWrite() override { |
| if (buffer_.empty()) { |
| return; |
| } |
| |
| bool success = stream_->Write(buffer_); |
| if (success) { |
| buffer_ = ""; |
| } |
| } |
| |
| private: |
| QuicTransportStream* stream_; |
| std::string buffer_; |
| }; |
| |
| // Buffers all of the data and calls EchoStreamBack() on the parent session. |
| class UnidirectionalEchoReadVisitor : public QuicTransportStream::Visitor { |
| public: |
| UnidirectionalEchoReadVisitor(QuicTransportSimpleServerSession* session, |
| QuicTransportStream* stream) |
| : session_(session), stream_(stream) {} |
| |
| void OnCanRead() override { |
| bool success = stream_->Read(&buffer_); |
| DCHECK(success); |
| } |
| |
| void OnFinRead() override { |
| QUIC_DVLOG(1) << "Finished receiving data on stream " << stream_->id() |
| << ", queueing up the echo"; |
| session_->EchoStreamBack(buffer_); |
| } |
| |
| void OnCanWrite() override { QUIC_NOTREACHED(); } |
| |
| private: |
| QuicTransportSimpleServerSession* session_; |
| QuicTransportStream* stream_; |
| std::string buffer_; |
| }; |
| |
| // Sends supplied data. |
| class UnidirectionalEchoWriteVisitor : public QuicTransportStream::Visitor { |
| public: |
| UnidirectionalEchoWriteVisitor(QuicTransportStream* stream, |
| const std::string& data) |
| : stream_(stream), data_(data) {} |
| |
| void OnCanRead() override { QUIC_NOTREACHED(); } |
| void OnFinRead() override { QUIC_NOTREACHED(); } |
| void OnCanWrite() override { |
| if (data_.empty()) { |
| return; |
| } |
| if (!stream_->Write(data_)) { |
| return; |
| } |
| data_ = ""; |
| bool fin_sent = stream_->SendFin(); |
| DCHECK(fin_sent); |
| } |
| |
| private: |
| QuicTransportStream* stream_; |
| std::string data_; |
| }; |
| |
| } // namespace |
| |
| QuicTransportSimpleServerSession::QuicTransportSimpleServerSession( |
| QuicConnection* connection, |
| bool owns_connection, |
| Visitor* owner, |
| const QuicConfig& config, |
| const ParsedQuicVersionVector& supported_versions, |
| const QuicCryptoServerConfig* crypto_config, |
| QuicCompressedCertsCache* compressed_certs_cache, |
| std::vector<url::Origin> accepted_origins) |
| : QuicTransportServerSession(connection, |
| owner, |
| config, |
| supported_versions, |
| crypto_config, |
| compressed_certs_cache, |
| this), |
| owns_connection_(owns_connection), |
| mode_(DISCARD), |
| accepted_origins_(accepted_origins) {} |
| |
| QuicTransportSimpleServerSession::~QuicTransportSimpleServerSession() { |
| if (owns_connection_) { |
| DeleteConnection(); |
| } |
| } |
| |
| void QuicTransportSimpleServerSession::OnIncomingDataStream( |
| QuicTransportStream* stream) { |
| switch (mode_) { |
| case DISCARD: |
| stream->set_visitor(std::make_unique<DiscardVisitor>(stream)); |
| break; |
| |
| case ECHO: |
| switch (stream->type()) { |
| case BIDIRECTIONAL: |
| QUIC_DVLOG(1) << "Opening bidirectional echo stream " << stream->id(); |
| stream->set_visitor( |
| std::make_unique<BidirectionalEchoVisitor>(stream)); |
| break; |
| case READ_UNIDIRECTIONAL: |
| QUIC_DVLOG(1) |
| << "Started receiving data on unidirectional echo stream " |
| << stream->id(); |
| stream->set_visitor( |
| std::make_unique<UnidirectionalEchoReadVisitor>(this, stream)); |
| break; |
| default: |
| QUIC_NOTREACHED(); |
| break; |
| } |
| break; |
| |
| case OUTGOING_BIDIRECTIONAL: |
| stream->set_visitor(std::make_unique<DiscardVisitor>(stream)); |
| ++pending_outgoing_bidirectional_streams_; |
| MaybeCreateOutgoingBidirectionalStream(); |
| break; |
| } |
| } |
| |
| void QuicTransportSimpleServerSession::OnCanCreateNewOutgoingStream( |
| bool unidirectional) { |
| if (mode_ == ECHO && unidirectional) { |
| MaybeEchoStreamsBack(); |
| } else if (mode_ == OUTGOING_BIDIRECTIONAL && !unidirectional) { |
| MaybeCreateOutgoingBidirectionalStream(); |
| } |
| } |
| |
| bool QuicTransportSimpleServerSession::CheckOrigin(url::Origin origin) { |
| if (accepted_origins_.empty()) { |
| return true; |
| } |
| |
| for (const url::Origin& accepted_origin : accepted_origins_) { |
| if (origin.IsSameOriginWith(accepted_origin)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| bool QuicTransportSimpleServerSession::ProcessPath(const GURL& url) { |
| if (url.path() == "/discard") { |
| mode_ = DISCARD; |
| return true; |
| } |
| if (url.path() == "/echo") { |
| mode_ = ECHO; |
| return true; |
| } |
| if (url.path() == "/receive-bidirectional") { |
| mode_ = OUTGOING_BIDIRECTIONAL; |
| return true; |
| } |
| |
| QUIC_DLOG(WARNING) << "Unknown path requested: " << url.path(); |
| return false; |
| } |
| |
| void QuicTransportSimpleServerSession::OnMessageReceived( |
| absl::string_view message) { |
| if (mode_ != ECHO) { |
| return; |
| } |
| QuicUniqueBufferPtr buffer = MakeUniqueBuffer( |
| connection()->helper()->GetStreamSendBufferAllocator(), message.size()); |
| memcpy(buffer.get(), message.data(), message.size()); |
| datagram_queue()->SendOrQueueDatagram( |
| QuicMemSlice(std::move(buffer), message.size())); |
| } |
| |
| void QuicTransportSimpleServerSession::MaybeEchoStreamsBack() { |
| while (!streams_to_echo_back_.empty() && |
| CanOpenNextOutgoingUnidirectionalStream()) { |
| // Remove the stream from the queue first, in order to avoid accidentally |
| // entering an infinite loop in case any of the following code calls |
| // OnCanCreateNewOutgoingStream(). |
| std::string data = std::move(streams_to_echo_back_.front()); |
| streams_to_echo_back_.pop_front(); |
| |
| auto stream_owned = std::make_unique<QuicTransportStream>( |
| GetNextOutgoingUnidirectionalStreamId(), this, this); |
| QuicTransportStream* stream = stream_owned.get(); |
| ActivateStream(std::move(stream_owned)); |
| QUIC_DVLOG(1) << "Opened echo response stream " << stream->id(); |
| |
| stream->set_visitor( |
| std::make_unique<UnidirectionalEchoWriteVisitor>(stream, data)); |
| stream->visitor()->OnCanWrite(); |
| } |
| } |
| |
| void QuicTransportSimpleServerSession:: |
| MaybeCreateOutgoingBidirectionalStream() { |
| while (pending_outgoing_bidirectional_streams_ > 0 && |
| CanOpenNextOutgoingBidirectionalStream()) { |
| auto stream_owned = std::make_unique<QuicTransportStream>( |
| GetNextOutgoingBidirectionalStreamId(), this, this); |
| QuicTransportStream* stream = stream_owned.get(); |
| ActivateStream(std::move(stream_owned)); |
| QUIC_DVLOG(1) << "Opened outgoing bidirectional stream " << stream->id(); |
| stream->set_visitor(std::make_unique<BidirectionalEchoVisitor>(stream)); |
| if (!stream->Write("hello")) { |
| QUIC_DVLOG(1) << "Write failed."; |
| } |
| --pending_outgoing_bidirectional_streams_; |
| } |
| } |
| |
| } // namespace quic |