blob: 386bd0056fab695891661e9045d40397daf8bfac [file] [log] [blame]
// 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