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