|  | // 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/tools/quic_transport_simple_server_session.h" | 
|  |  | 
|  | #include <memory> | 
|  |  | 
|  | #include "url/gurl.h" | 
|  | #include "url/origin.h" | 
|  | #include "quic/core/quic_buffer_allocator.h" | 
|  | #include "quic/core/quic_types.h" | 
|  | #include "quic/core/quic_versions.h" | 
|  | #include "quic/platform/api/quic_flags.h" | 
|  | #include "quic/platform/api/quic_logging.h" | 
|  | #include "quic/quic_transport/quic_transport_protocol.h" | 
|  | #include "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 |