blob: c3d03d27f58731549c14e9107eba2d61d92b416a [file] [log] [blame]
// Copyright 2018 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 "osp/impl/quic/quic_connection_factory_impl.h"
#include <algorithm>
#include <memory>
#include "osp/impl/quic/quic_connection_impl.h"
#include "platform/api/task_runner.h"
#include "platform/api/time.h"
#include "platform/base/error.h"
#include "third_party/chromium_quic/src/base/location.h"
#include "third_party/chromium_quic/src/base/task_runner.h"
#include "third_party/chromium_quic/src/net/third_party/quic/core/quic_constants.h"
#include "third_party/chromium_quic/src/net/third_party/quic/platform/impl/quic_chromium_clock.h"
#include "util/osp_logging.h"
#include "util/trace_logging.h"
namespace openscreen {
namespace osp {
class QuicTaskRunner final : public ::base::TaskRunner {
public:
explicit QuicTaskRunner(openscreen::TaskRunner* task_runner);
~QuicTaskRunner() override;
void RunTasks();
// base::TaskRunner overrides.
bool PostDelayedTask(const ::base::Location& whence,
::base::OnceClosure task,
::base::TimeDelta delay) override;
bool RunsTasksInCurrentSequence() const override;
private:
openscreen::TaskRunner* const task_runner_;
};
QuicTaskRunner::QuicTaskRunner(openscreen::TaskRunner* task_runner)
: task_runner_(task_runner) {}
QuicTaskRunner::~QuicTaskRunner() = default;
void QuicTaskRunner::RunTasks() {}
bool QuicTaskRunner::PostDelayedTask(const ::base::Location& whence,
::base::OnceClosure task,
::base::TimeDelta delay) {
Clock::duration wait = Clock::duration(delay.InMilliseconds());
task_runner_->PostTaskWithDelay(
[closure = std::move(task)]() mutable { std::move(closure).Run(); },
wait);
return true;
}
bool QuicTaskRunner::RunsTasksInCurrentSequence() const {
return true;
}
QuicConnectionFactoryImpl::QuicConnectionFactoryImpl(TaskRunner* task_runner)
: task_runner_(task_runner) {
quic_task_runner_ = ::base::MakeRefCounted<QuicTaskRunner>(task_runner);
alarm_factory_ = std::make_unique<::net::QuicChromiumAlarmFactory>(
quic_task_runner_.get(), ::quic::QuicChromiumClock::GetInstance());
::quic::QuartcFactoryConfig factory_config;
factory_config.alarm_factory = alarm_factory_.get();
factory_config.clock = ::quic::QuicChromiumClock::GetInstance();
quartc_factory_ = std::make_unique<::quic::QuartcFactory>(factory_config);
}
QuicConnectionFactoryImpl::~QuicConnectionFactoryImpl() {
OSP_DCHECK(connections_.empty());
}
void QuicConnectionFactoryImpl::SetServerDelegate(
ServerDelegate* delegate,
const std::vector<IPEndpoint>& endpoints) {
OSP_DCHECK(!delegate != !server_delegate_);
server_delegate_ = delegate;
sockets_.reserve(sockets_.size() + endpoints.size());
for (const auto& endpoint : endpoints) {
// TODO(mfoltz): Need to notify the caller and/or ServerDelegate if socket
// create/bind errors occur. Maybe return an Error immediately, and undo
// partial progress (i.e. "unwatch" all the sockets and call
// sockets_.clear() to close the sockets)?
auto create_result = UdpSocket::Create(task_runner_, this, endpoint);
if (!create_result) {
OSP_LOG_ERROR << "failed to create socket (for " << endpoint
<< "): " << create_result.error().message();
continue;
}
std::unique_ptr<UdpSocket> server_socket = std::move(create_result.value());
server_socket->Bind();
sockets_.emplace_back(std::move(server_socket));
}
}
void QuicConnectionFactoryImpl::OnRead(UdpSocket* socket,
ErrorOr<UdpPacket> packet_or_error) {
TRACE_SCOPED(TraceCategory::kQuic, "QuicConnectionFactoryImpl::OnRead");
if (packet_or_error.is_error()) {
return;
}
UdpPacket packet = std::move(packet_or_error.value());
// Ensure that |packet.socket| is one of the instances owned by
// QuicConnectionFactoryImpl.
auto packet_ptr = &packet;
OSP_DCHECK(std::find_if(sockets_.begin(), sockets_.end(),
[packet_ptr](const std::unique_ptr<UdpSocket>& s) {
return s.get() == packet_ptr->socket();
}) != sockets_.end());
// TODO(btolsch): We will need to rethink this both for ICE and connection
// migration support.
auto conn_it = connections_.find(packet.source());
if (conn_it == connections_.end()) {
if (server_delegate_) {
OSP_VLOG << __func__ << ": spawning connection from " << packet.source();
auto transport =
std::make_unique<UdpTransport>(packet.socket(), packet.source());
::quic::QuartcSessionConfig session_config;
session_config.perspective = ::quic::Perspective::IS_SERVER;
session_config.packet_transport = transport.get();
auto result = std::make_unique<QuicConnectionImpl>(
this, server_delegate_->NextConnectionDelegate(packet.source()),
std::move(transport),
quartc_factory_->CreateQuartcSession(session_config));
auto* result_ptr = result.get();
connections_.emplace(packet.source(),
OpenConnection{result_ptr, packet.socket()});
server_delegate_->OnIncomingConnection(std::move(result));
result_ptr->OnRead(socket, std::move(packet));
}
} else {
OSP_VLOG << __func__ << ": data for existing connection from "
<< packet.source();
conn_it->second.connection->OnRead(socket, std::move(packet));
}
}
std::unique_ptr<QuicConnection> QuicConnectionFactoryImpl::Connect(
const IPEndpoint& endpoint,
QuicConnection::Delegate* connection_delegate) {
auto create_result = UdpSocket::Create(task_runner_, this, endpoint);
if (!create_result) {
OSP_LOG_ERROR << "failed to create socket: "
<< create_result.error().message();
// TODO(mfoltz): This method should return ErrorOr<uni_ptr<QuicConnection>>.
return nullptr;
}
std::unique_ptr<UdpSocket> socket = std::move(create_result.value());
auto transport = std::make_unique<UdpTransport>(socket.get(), endpoint);
::quic::QuartcSessionConfig session_config;
session_config.perspective = ::quic::Perspective::IS_CLIENT;
// TODO(btolsch): Proper server id. Does this go in the QUIC server name
// parameter?
session_config.unique_remote_server_id = "turtle";
session_config.packet_transport = transport.get();
auto result = std::make_unique<QuicConnectionImpl>(
this, connection_delegate, std::move(transport),
quartc_factory_->CreateQuartcSession(session_config));
// TODO(btolsch): This presents a problem for multihomed receivers, which may
// register as a different endpoint in their response. I think QUIC is
// already tolerant of this via connection IDs but this hasn't been tested
// (and even so, those aren't necessarily stable either).
connections_.emplace(endpoint, OpenConnection{result.get(), socket.get()});
sockets_.emplace_back(std::move(socket));
return result;
}
void QuicConnectionFactoryImpl::OnConnectionClosed(QuicConnection* connection) {
auto entry = std::find_if(
connections_.begin(), connections_.end(),
[connection](const decltype(connections_)::value_type& entry) {
return entry.second.connection == connection;
});
OSP_DCHECK(entry != connections_.end());
UdpSocket* const socket = entry->second.socket;
connections_.erase(entry);
// If none of the remaining |connections_| reference the socket, close/destroy
// it.
if (std::find_if(connections_.begin(), connections_.end(),
[socket](const decltype(connections_)::value_type& entry) {
return entry.second.socket == socket;
}) == connections_.end()) {
auto socket_it =
std::find_if(sockets_.begin(), sockets_.end(),
[socket](const std::unique_ptr<UdpSocket>& s) {
return s.get() == socket;
});
OSP_DCHECK(socket_it != sockets_.end());
sockets_.erase(socket_it);
}
}
void QuicConnectionFactoryImpl::OnError(UdpSocket* socket, Error error) {
OSP_LOG_ERROR << "failed to configure socket " << error.message();
}
void QuicConnectionFactoryImpl::OnSendError(UdpSocket* socket, Error error) {
// TODO(crbug.com/openscreen/67): Implement this method.
OSP_UNIMPLEMENTED();
}
} // namespace osp
} // namespace openscreen