1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "osp/impl/quic/quic_connection_factory_impl.h"
6
7 #include <algorithm>
8 #include <memory>
9
10 #include "osp/impl/quic/quic_connection_impl.h"
11 #include "platform/api/task_runner.h"
12 #include "platform/api/time.h"
13 #include "platform/base/error.h"
14 #include "third_party/chromium_quic/src/base/location.h"
15 #include "third_party/chromium_quic/src/base/task_runner.h"
16 #include "third_party/chromium_quic/src/net/third_party/quic/core/quic_constants.h"
17 #include "third_party/chromium_quic/src/net/third_party/quic/platform/impl/quic_chromium_clock.h"
18 #include "util/osp_logging.h"
19 #include "util/trace_logging.h"
20
21 namespace openscreen {
22 namespace osp {
23
24 class QuicTaskRunner final : public ::base::TaskRunner {
25 public:
26 explicit QuicTaskRunner(openscreen::TaskRunner* task_runner);
27 ~QuicTaskRunner() override;
28
29 void RunTasks();
30
31 // base::TaskRunner overrides.
32 bool PostDelayedTask(const ::base::Location& whence,
33 ::base::OnceClosure task,
34 ::base::TimeDelta delay) override;
35
36 bool RunsTasksInCurrentSequence() const override;
37
38 private:
39 openscreen::TaskRunner* const task_runner_;
40 };
41
QuicTaskRunner(openscreen::TaskRunner * task_runner)42 QuicTaskRunner::QuicTaskRunner(openscreen::TaskRunner* task_runner)
43 : task_runner_(task_runner) {}
44
45 QuicTaskRunner::~QuicTaskRunner() = default;
46
RunTasks()47 void QuicTaskRunner::RunTasks() {}
48
PostDelayedTask(const::base::Location & whence,::base::OnceClosure task,::base::TimeDelta delay)49 bool QuicTaskRunner::PostDelayedTask(const ::base::Location& whence,
50 ::base::OnceClosure task,
51 ::base::TimeDelta delay) {
52 Clock::duration wait = Clock::duration(delay.InMilliseconds());
53 task_runner_->PostTaskWithDelay(
54 [closure = std::move(task)]() mutable { std::move(closure).Run(); },
55 wait);
56 return true;
57 }
58
RunsTasksInCurrentSequence() const59 bool QuicTaskRunner::RunsTasksInCurrentSequence() const {
60 return true;
61 }
62
QuicConnectionFactoryImpl(TaskRunner * task_runner)63 QuicConnectionFactoryImpl::QuicConnectionFactoryImpl(TaskRunner* task_runner)
64 : task_runner_(task_runner) {
65 quic_task_runner_ = ::base::MakeRefCounted<QuicTaskRunner>(task_runner);
66 alarm_factory_ = std::make_unique<::net::QuicChromiumAlarmFactory>(
67 quic_task_runner_.get(), ::quic::QuicChromiumClock::GetInstance());
68 ::quic::QuartcFactoryConfig factory_config;
69 factory_config.alarm_factory = alarm_factory_.get();
70 factory_config.clock = ::quic::QuicChromiumClock::GetInstance();
71 quartc_factory_ = std::make_unique<::quic::QuartcFactory>(factory_config);
72 }
73
~QuicConnectionFactoryImpl()74 QuicConnectionFactoryImpl::~QuicConnectionFactoryImpl() {
75 OSP_DCHECK(connections_.empty());
76 }
77
SetServerDelegate(ServerDelegate * delegate,const std::vector<IPEndpoint> & endpoints)78 void QuicConnectionFactoryImpl::SetServerDelegate(
79 ServerDelegate* delegate,
80 const std::vector<IPEndpoint>& endpoints) {
81 OSP_DCHECK(!delegate != !server_delegate_);
82
83 server_delegate_ = delegate;
84 sockets_.reserve(sockets_.size() + endpoints.size());
85
86 for (const auto& endpoint : endpoints) {
87 // TODO(mfoltz): Need to notify the caller and/or ServerDelegate if socket
88 // create/bind errors occur. Maybe return an Error immediately, and undo
89 // partial progress (i.e. "unwatch" all the sockets and call
90 // sockets_.clear() to close the sockets)?
91 auto create_result = UdpSocket::Create(task_runner_, this, endpoint);
92 if (!create_result) {
93 OSP_LOG_ERROR << "failed to create socket (for " << endpoint
94 << "): " << create_result.error().message();
95 continue;
96 }
97 std::unique_ptr<UdpSocket> server_socket = std::move(create_result.value());
98 server_socket->Bind();
99 sockets_.emplace_back(std::move(server_socket));
100 }
101 }
102
OnRead(UdpSocket * socket,ErrorOr<UdpPacket> packet_or_error)103 void QuicConnectionFactoryImpl::OnRead(UdpSocket* socket,
104 ErrorOr<UdpPacket> packet_or_error) {
105 TRACE_SCOPED(TraceCategory::kQuic, "QuicConnectionFactoryImpl::OnRead");
106 if (packet_or_error.is_error()) {
107 return;
108 }
109
110 UdpPacket packet = std::move(packet_or_error.value());
111 // Ensure that |packet.socket| is one of the instances owned by
112 // QuicConnectionFactoryImpl.
113 auto packet_ptr = &packet;
114 OSP_DCHECK(std::find_if(sockets_.begin(), sockets_.end(),
115 [packet_ptr](const std::unique_ptr<UdpSocket>& s) {
116 return s.get() == packet_ptr->socket();
117 }) != sockets_.end());
118
119 // TODO(btolsch): We will need to rethink this both for ICE and connection
120 // migration support.
121 auto conn_it = connections_.find(packet.source());
122 if (conn_it == connections_.end()) {
123 if (server_delegate_) {
124 OSP_VLOG << __func__ << ": spawning connection from " << packet.source();
125 auto transport =
126 std::make_unique<UdpTransport>(packet.socket(), packet.source());
127 ::quic::QuartcSessionConfig session_config;
128 session_config.perspective = ::quic::Perspective::IS_SERVER;
129 session_config.packet_transport = transport.get();
130
131 auto result = std::make_unique<QuicConnectionImpl>(
132 this, server_delegate_->NextConnectionDelegate(packet.source()),
133 std::move(transport),
134 quartc_factory_->CreateQuartcSession(session_config));
135 auto* result_ptr = result.get();
136 connections_.emplace(packet.source(),
137 OpenConnection{result_ptr, packet.socket()});
138 server_delegate_->OnIncomingConnection(std::move(result));
139 result_ptr->OnRead(socket, std::move(packet));
140 }
141 } else {
142 OSP_VLOG << __func__ << ": data for existing connection from "
143 << packet.source();
144 conn_it->second.connection->OnRead(socket, std::move(packet));
145 }
146 }
147
Connect(const IPEndpoint & endpoint,QuicConnection::Delegate * connection_delegate)148 std::unique_ptr<QuicConnection> QuicConnectionFactoryImpl::Connect(
149 const IPEndpoint& endpoint,
150 QuicConnection::Delegate* connection_delegate) {
151 auto create_result = UdpSocket::Create(task_runner_, this, endpoint);
152 if (!create_result) {
153 OSP_LOG_ERROR << "failed to create socket: "
154 << create_result.error().message();
155 // TODO(mfoltz): This method should return ErrorOr<uni_ptr<QuicConnection>>.
156 return nullptr;
157 }
158 std::unique_ptr<UdpSocket> socket = std::move(create_result.value());
159 auto transport = std::make_unique<UdpTransport>(socket.get(), endpoint);
160
161 ::quic::QuartcSessionConfig session_config;
162 session_config.perspective = ::quic::Perspective::IS_CLIENT;
163 // TODO(btolsch): Proper server id. Does this go in the QUIC server name
164 // parameter?
165 session_config.unique_remote_server_id = "turtle";
166 session_config.packet_transport = transport.get();
167
168 auto result = std::make_unique<QuicConnectionImpl>(
169 this, connection_delegate, std::move(transport),
170 quartc_factory_->CreateQuartcSession(session_config));
171
172 // TODO(btolsch): This presents a problem for multihomed receivers, which may
173 // register as a different endpoint in their response. I think QUIC is
174 // already tolerant of this via connection IDs but this hasn't been tested
175 // (and even so, those aren't necessarily stable either).
176 connections_.emplace(endpoint, OpenConnection{result.get(), socket.get()});
177 sockets_.emplace_back(std::move(socket));
178
179 return result;
180 }
181
OnConnectionClosed(QuicConnection * connection)182 void QuicConnectionFactoryImpl::OnConnectionClosed(QuicConnection* connection) {
183 auto entry = std::find_if(
184 connections_.begin(), connections_.end(),
185 [connection](const decltype(connections_)::value_type& entry) {
186 return entry.second.connection == connection;
187 });
188 OSP_DCHECK(entry != connections_.end());
189 UdpSocket* const socket = entry->second.socket;
190 connections_.erase(entry);
191
192 // If none of the remaining |connections_| reference the socket, close/destroy
193 // it.
194 if (std::find_if(connections_.begin(), connections_.end(),
195 [socket](const decltype(connections_)::value_type& entry) {
196 return entry.second.socket == socket;
197 }) == connections_.end()) {
198 auto socket_it =
199 std::find_if(sockets_.begin(), sockets_.end(),
200 [socket](const std::unique_ptr<UdpSocket>& s) {
201 return s.get() == socket;
202 });
203 OSP_DCHECK(socket_it != sockets_.end());
204 sockets_.erase(socket_it);
205 }
206 }
207
OnError(UdpSocket * socket,Error error)208 void QuicConnectionFactoryImpl::OnError(UdpSocket* socket, Error error) {
209 OSP_LOG_ERROR << "failed to configure socket " << error.message();
210 }
211
OnSendError(UdpSocket * socket,Error error)212 void QuicConnectionFactoryImpl::OnSendError(UdpSocket* socket, Error error) {
213 // TODO(crbug.com/openscreen/67): Implement this method.
214 OSP_UNIMPLEMENTED();
215 }
216
217 } // namespace osp
218 } // namespace openscreen
219