Re-land: C++ readability review
[chromium-blink-merge.git] / remoting / protocol / libjingle_transport_factory.cc
blob2c68d455b9cf37251ecc6524b6a35c7d2ca88340
1 // Copyright (c) 2012 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.
5 #include "remoting/protocol/libjingle_transport_factory.h"
7 #include <algorithm>
9 #include "base/callback.h"
10 #include "base/single_thread_task_runner.h"
11 #include "base/thread_task_runner_handle.h"
12 #include "base/timer/timer.h"
13 #include "jingle/glue/channel_socket_adapter.h"
14 #include "jingle/glue/utils.h"
15 #include "net/base/net_errors.h"
16 #include "remoting/protocol/network_settings.h"
17 #include "remoting/signaling/jingle_info_request.h"
18 #include "third_party/webrtc/base/network.h"
19 #include "third_party/webrtc/p2p/base/constants.h"
20 #include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
21 #include "third_party/webrtc/p2p/base/port.h"
22 #include "third_party/webrtc/p2p/client/basicportallocator.h"
23 #include "third_party/webrtc/p2p/client/httpportallocator.h"
25 namespace remoting {
26 namespace protocol {
28 namespace {
30 // Try connecting ICE twice with timeout of 15 seconds for each attempt.
31 const int kMaxReconnectAttempts = 2;
32 const int kReconnectDelaySeconds = 15;
34 // Get fresh STUN/Relay configuration every hour.
35 const int kJingleInfoUpdatePeriodSeconds = 3600;
37 // Utility function to map a cricket::Candidate string type to a
38 // TransportRoute::RouteType enum value.
39 TransportRoute::RouteType CandidateTypeToTransportRouteType(
40 const std::string& candidate_type) {
41 if (candidate_type == "local") {
42 return TransportRoute::DIRECT;
43 } else if (candidate_type == "stun") {
44 return TransportRoute::STUN;
45 } else if (candidate_type == "relay") {
46 return TransportRoute::RELAY;
47 } else {
48 LOG(FATAL) << "Unknown candidate type: " << candidate_type;
49 return TransportRoute::DIRECT;
53 class LibjingleTransport
54 : public Transport,
55 public base::SupportsWeakPtr<LibjingleTransport>,
56 public sigslot::has_slots<> {
57 public:
58 LibjingleTransport(cricket::PortAllocator* port_allocator,
59 const NetworkSettings& network_settings);
60 ~LibjingleTransport() override;
62 // Called by JingleTransportFactory when it has fresh Jingle info.
63 void OnCanStart();
65 // Transport interface.
66 void Connect(const std::string& name,
67 Transport::EventHandler* event_handler,
68 const Transport::ConnectedCallback& callback) override;
69 void AddRemoteCandidate(const cricket::Candidate& candidate) override;
70 const std::string& name() const override;
71 bool is_connected() const override;
73 private:
74 void DoStart();
75 void NotifyConnected();
77 // Signal handlers for cricket::TransportChannel.
78 void OnRequestSignaling(cricket::TransportChannelImpl* channel);
79 void OnCandidateReady(cricket::TransportChannelImpl* channel,
80 const cricket::Candidate& candidate);
81 void OnRouteChange(cricket::TransportChannel* channel,
82 const cricket::Candidate& candidate);
83 void OnWritableState(cricket::TransportChannel* channel);
85 // Callback for jingle_glue::TransportChannelSocketAdapter to notify when the
86 // socket is destroyed.
87 void OnChannelDestroyed();
89 void NotifyRouteChanged();
91 // Tries to connect by restarting ICE. Called by |reconnect_timer_|.
92 void TryReconnect();
94 cricket::PortAllocator* port_allocator_;
95 NetworkSettings network_settings_;
97 std::string name_;
98 EventHandler* event_handler_;
99 Transport::ConnectedCallback callback_;
100 std::string ice_username_fragment_;
101 std::string ice_password_;
103 bool can_start_;
105 std::list<cricket::Candidate> pending_candidates_;
106 scoped_ptr<cricket::P2PTransportChannel> channel_;
107 bool channel_was_writable_;
108 int connect_attempts_left_;
109 base::RepeatingTimer<LibjingleTransport> reconnect_timer_;
111 base::WeakPtrFactory<LibjingleTransport> weak_factory_;
113 DISALLOW_COPY_AND_ASSIGN(LibjingleTransport);
116 LibjingleTransport::LibjingleTransport(cricket::PortAllocator* port_allocator,
117 const NetworkSettings& network_settings)
118 : port_allocator_(port_allocator),
119 network_settings_(network_settings),
120 event_handler_(nullptr),
121 ice_username_fragment_(
122 rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH)),
123 ice_password_(rtc::CreateRandomString(cricket::ICE_PWD_LENGTH)),
124 can_start_(false),
125 channel_was_writable_(false),
126 connect_attempts_left_(kMaxReconnectAttempts),
127 weak_factory_(this) {
128 DCHECK(!ice_username_fragment_.empty());
129 DCHECK(!ice_password_.empty());
132 LibjingleTransport::~LibjingleTransport() {
133 DCHECK(event_handler_);
135 event_handler_->OnTransportDeleted(this);
137 if (channel_.get()) {
138 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
139 FROM_HERE, channel_.release());
143 void LibjingleTransport::OnCanStart() {
144 DCHECK(CalledOnValidThread());
146 DCHECK(!can_start_);
147 can_start_ = true;
149 // If Connect() has been called then start connection.
150 if (!callback_.is_null())
151 DoStart();
153 while (!pending_candidates_.empty()) {
154 channel_->OnCandidate(pending_candidates_.front());
155 pending_candidates_.pop_front();
159 void LibjingleTransport::Connect(
160 const std::string& name,
161 Transport::EventHandler* event_handler,
162 const Transport::ConnectedCallback& callback) {
163 DCHECK(CalledOnValidThread());
164 DCHECK(!name.empty());
165 DCHECK(event_handler);
166 DCHECK(!callback.is_null());
168 DCHECK(name_.empty());
169 name_ = name;
170 event_handler_ = event_handler;
171 callback_ = callback;
173 if (can_start_)
174 DoStart();
177 void LibjingleTransport::DoStart() {
178 DCHECK(!channel_.get());
180 // Create P2PTransportChannel, attach signal handlers and connect it.
181 // TODO(sergeyu): Specify correct component ID for the channel.
182 channel_.reset(new cricket::P2PTransportChannel(
183 std::string(), 0, nullptr, port_allocator_));
184 channel_->SetIceProtocolType(cricket::ICEPROTO_GOOGLE);
185 channel_->SetIceCredentials(ice_username_fragment_, ice_password_);
186 channel_->SignalRequestSignaling.connect(
187 this, &LibjingleTransport::OnRequestSignaling);
188 channel_->SignalCandidateReady.connect(
189 this, &LibjingleTransport::OnCandidateReady);
190 channel_->SignalRouteChange.connect(
191 this, &LibjingleTransport::OnRouteChange);
192 channel_->SignalWritableState.connect(
193 this, &LibjingleTransport::OnWritableState);
194 channel_->set_incoming_only(
195 !(network_settings_.flags & NetworkSettings::NAT_TRAVERSAL_OUTGOING));
197 channel_->Connect();
199 --connect_attempts_left_;
201 // Start reconnection timer.
202 reconnect_timer_.Start(
203 FROM_HERE, base::TimeDelta::FromSeconds(kReconnectDelaySeconds),
204 this, &LibjingleTransport::TryReconnect);
207 void LibjingleTransport::NotifyConnected() {
208 // Create net::Socket adapter for the P2PTransportChannel.
209 scoped_ptr<jingle_glue::TransportChannelSocketAdapter> socket(
210 new jingle_glue::TransportChannelSocketAdapter(channel_.get()));
211 socket->SetOnDestroyedCallback(base::Bind(
212 &LibjingleTransport::OnChannelDestroyed, base::Unretained(this)));
214 Transport::ConnectedCallback callback = callback_;
215 callback_.Reset();
216 callback.Run(socket.Pass());
219 void LibjingleTransport::AddRemoteCandidate(
220 const cricket::Candidate& candidate) {
221 DCHECK(CalledOnValidThread());
223 // To enforce the no-relay setting, it's not enough to not produce relay
224 // candidates. It's also necessary to discard remote relay candidates.
225 bool relay_allowed = (network_settings_.flags &
226 NetworkSettings::NAT_TRAVERSAL_RELAY) != 0;
227 if (!relay_allowed && candidate.type() == cricket::RELAY_PORT_TYPE)
228 return;
230 if (channel_) {
231 channel_->OnCandidate(candidate);
232 } else {
233 pending_candidates_.push_back(candidate);
237 const std::string& LibjingleTransport::name() const {
238 DCHECK(CalledOnValidThread());
239 return name_;
242 bool LibjingleTransport::is_connected() const {
243 DCHECK(CalledOnValidThread());
244 return callback_.is_null();
247 void LibjingleTransport::OnRequestSignaling(
248 cricket::TransportChannelImpl* channel) {
249 DCHECK(CalledOnValidThread());
250 channel_->OnSignalingReady();
253 void LibjingleTransport::OnCandidateReady(
254 cricket::TransportChannelImpl* channel,
255 const cricket::Candidate& candidate) {
256 DCHECK(CalledOnValidThread());
257 event_handler_->OnTransportCandidate(this, candidate);
260 void LibjingleTransport::OnRouteChange(
261 cricket::TransportChannel* channel,
262 const cricket::Candidate& candidate) {
263 // Ignore notifications if the channel is not writable.
264 if (channel_->writable())
265 NotifyRouteChanged();
268 void LibjingleTransport::OnWritableState(
269 cricket::TransportChannel* channel) {
270 DCHECK_EQ(channel, channel_.get());
272 if (channel->writable()) {
273 if (!channel_was_writable_) {
274 channel_was_writable_ = true;
275 base::ThreadTaskRunnerHandle::Get()->PostTask(
276 FROM_HERE,
277 base::Bind(&LibjingleTransport::NotifyConnected,
278 weak_factory_.GetWeakPtr()));
280 connect_attempts_left_ = kMaxReconnectAttempts;
281 reconnect_timer_.Stop();
283 // Route change notifications are ignored when the |channel_| is not
284 // writable. Notify the event handler about the current route once the
285 // channel is writable.
286 NotifyRouteChanged();
287 } else if (!channel->writable() && channel_was_writable_) {
288 reconnect_timer_.Reset();
289 TryReconnect();
293 void LibjingleTransport::OnChannelDestroyed() {
294 if (is_connected()) {
295 // The connection socket is being deleted, so delete the transport too.
296 delete this;
300 void LibjingleTransport::NotifyRouteChanged() {
301 TransportRoute route;
303 DCHECK(channel_->best_connection());
304 const cricket::Connection* connection = channel_->best_connection();
306 // A connection has both a local and a remote candidate. For our purposes, the
307 // route type is determined by the most indirect candidate type. For example:
308 // it's possible for the local candidate be a "relay" type, while the remote
309 // candidate is "local". In this case, we still want to report a RELAY route
310 // type.
311 static_assert(TransportRoute::DIRECT < TransportRoute::STUN &&
312 TransportRoute::STUN < TransportRoute::RELAY,
313 "Route type enum values are ordered by 'indirectness'");
314 route.type = std::max(
315 CandidateTypeToTransportRouteType(connection->local_candidate().type()),
316 CandidateTypeToTransportRouteType(connection->remote_candidate().type()));
318 if (!jingle_glue::SocketAddressToIPEndPoint(
319 connection->remote_candidate().address(), &route.remote_address)) {
320 LOG(FATAL) << "Failed to convert peer IP address.";
323 const cricket::Candidate& local_candidate =
324 channel_->best_connection()->local_candidate();
325 if (!jingle_glue::SocketAddressToIPEndPoint(
326 local_candidate.address(), &route.local_address)) {
327 LOG(FATAL) << "Failed to convert local IP address.";
330 event_handler_->OnTransportRouteChange(this, route);
333 void LibjingleTransport::TryReconnect() {
334 DCHECK(!channel_->writable());
336 if (connect_attempts_left_ <= 0) {
337 reconnect_timer_.Stop();
339 // Notify the caller that ICE connection has failed - normally that will
340 // terminate Jingle connection (i.e. the transport will be destroyed).
341 event_handler_->OnTransportFailed(this);
342 return;
344 --connect_attempts_left_;
346 // Restart ICE by resetting ICE password.
347 ice_password_ = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
348 channel_->SetIceCredentials(ice_username_fragment_, ice_password_);
351 } // namespace
353 LibjingleTransportFactory::LibjingleTransportFactory(
354 SignalStrategy* signal_strategy,
355 scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator,
356 const NetworkSettings& network_settings)
357 : signal_strategy_(signal_strategy),
358 port_allocator_(port_allocator.Pass()),
359 network_settings_(network_settings) {
362 LibjingleTransportFactory::~LibjingleTransportFactory() {
363 // This method may be called in response to a libjingle signal, so
364 // libjingle objects must be deleted asynchronously.
365 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
366 base::ThreadTaskRunnerHandle::Get();
367 task_runner->DeleteSoon(FROM_HERE, port_allocator_.release());
370 void LibjingleTransportFactory::PrepareTokens() {
371 EnsureFreshJingleInfo();
374 scoped_ptr<Transport> LibjingleTransportFactory::CreateTransport() {
375 scoped_ptr<LibjingleTransport> result(
376 new LibjingleTransport(port_allocator_.get(), network_settings_));
378 EnsureFreshJingleInfo();
380 // If there is a pending |jingle_info_request_| delay starting the new
381 // transport until the request is finished.
382 if (jingle_info_request_) {
383 on_jingle_info_callbacks_.push_back(
384 base::Bind(&LibjingleTransport::OnCanStart,
385 result->AsWeakPtr()));
386 } else {
387 result->OnCanStart();
390 return result.Pass();
393 void LibjingleTransportFactory::EnsureFreshJingleInfo() {
394 uint32 stun_or_relay_flags = NetworkSettings::NAT_TRAVERSAL_STUN |
395 NetworkSettings::NAT_TRAVERSAL_RELAY;
396 if (!(network_settings_.flags & stun_or_relay_flags) ||
397 jingle_info_request_) {
398 return;
401 if (base::TimeTicks::Now() - last_jingle_info_update_time_ >
402 base::TimeDelta::FromSeconds(kJingleInfoUpdatePeriodSeconds)) {
403 jingle_info_request_.reset(new JingleInfoRequest(signal_strategy_));
404 jingle_info_request_->Send(base::Bind(
405 &LibjingleTransportFactory::OnJingleInfo, base::Unretained(this)));
409 void LibjingleTransportFactory::OnJingleInfo(
410 const std::string& relay_token,
411 const std::vector<std::string>& relay_hosts,
412 const std::vector<rtc::SocketAddress>& stun_hosts) {
413 if (!relay_token.empty() && !relay_hosts.empty()) {
414 port_allocator_->SetRelayHosts(relay_hosts);
415 port_allocator_->SetRelayToken(relay_token);
417 if (!stun_hosts.empty()) {
418 port_allocator_->SetStunHosts(stun_hosts);
421 jingle_info_request_.reset();
422 if ((!relay_token.empty() && !relay_hosts.empty()) || !stun_hosts.empty())
423 last_jingle_info_update_time_ = base::TimeTicks::Now();
425 while (!on_jingle_info_callbacks_.empty()) {
426 on_jingle_info_callbacks_.begin()->Run();
427 on_jingle_info_callbacks_.pop_front();
431 } // namespace protocol
432 } // namespace remoting