Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / remoting / protocol / libjingle_transport_factory.cc
blobb7b87559eefec642692e9fe4b378bd81bae91b60
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 "base/callback.h"
8 #include "base/single_thread_task_runner.h"
9 #include "base/thread_task_runner_handle.h"
10 #include "base/timer/timer.h"
11 #include "jingle/glue/channel_socket_adapter.h"
12 #include "jingle/glue/utils.h"
13 #include "net/base/net_errors.h"
14 #include "remoting/protocol/network_settings.h"
15 #include "remoting/signaling/jingle_info_request.h"
16 #include "third_party/webrtc/base/network.h"
17 #include "third_party/webrtc/p2p/base/constants.h"
18 #include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
19 #include "third_party/webrtc/p2p/base/port.h"
20 #include "third_party/webrtc/p2p/client/basicportallocator.h"
21 #include "third_party/webrtc/p2p/client/httpportallocator.h"
23 namespace remoting {
24 namespace protocol {
26 namespace {
28 // Try connecting ICE twice with timeout of 15 seconds for each attempt.
29 const int kMaxReconnectAttempts = 2;
30 const int kReconnectDelaySeconds = 15;
32 // Get fresh STUN/Relay configuration every hour.
33 const int kJingleInfoUpdatePeriodSeconds = 3600;
35 class LibjingleTransport
36 : public Transport,
37 public base::SupportsWeakPtr<LibjingleTransport>,
38 public sigslot::has_slots<> {
39 public:
40 LibjingleTransport(cricket::PortAllocator* port_allocator,
41 const NetworkSettings& network_settings);
42 ~LibjingleTransport() override;
44 // Called by JingleTransportFactory when it has fresh Jingle info.
45 void OnCanStart();
47 // Transport interface.
48 void Connect(const std::string& name,
49 Transport::EventHandler* event_handler,
50 const Transport::ConnectedCallback& callback) override;
51 void AddRemoteCandidate(const cricket::Candidate& candidate) override;
52 const std::string& name() const override;
53 bool is_connected() const override;
55 private:
56 void DoStart();
57 void NotifyConnected();
59 // Signal handlers for cricket::TransportChannel.
60 void OnRequestSignaling(cricket::TransportChannelImpl* channel);
61 void OnCandidateReady(cricket::TransportChannelImpl* channel,
62 const cricket::Candidate& candidate);
63 void OnRouteChange(cricket::TransportChannel* channel,
64 const cricket::Candidate& candidate);
65 void OnWritableState(cricket::TransportChannel* channel);
67 // Callback for jingle_glue::TransportChannelSocketAdapter to notify when the
68 // socket is destroyed.
69 void OnChannelDestroyed();
71 // Tries to connect by restarting ICE. Called by |reconnect_timer_|.
72 void TryReconnect();
74 cricket::PortAllocator* port_allocator_;
75 NetworkSettings network_settings_;
77 std::string name_;
78 EventHandler* event_handler_;
79 Transport::ConnectedCallback callback_;
80 std::string ice_username_fragment_;
81 std::string ice_password_;
83 bool can_start_;
85 std::list<cricket::Candidate> pending_candidates_;
86 scoped_ptr<cricket::P2PTransportChannel> channel_;
87 bool channel_was_writable_;
88 int connect_attempts_left_;
89 base::RepeatingTimer<LibjingleTransport> reconnect_timer_;
91 base::WeakPtrFactory<LibjingleTransport> weak_factory_;
93 DISALLOW_COPY_AND_ASSIGN(LibjingleTransport);
96 LibjingleTransport::LibjingleTransport(cricket::PortAllocator* port_allocator,
97 const NetworkSettings& network_settings)
98 : port_allocator_(port_allocator),
99 network_settings_(network_settings),
100 event_handler_(NULL),
101 ice_username_fragment_(
102 rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH)),
103 ice_password_(rtc::CreateRandomString(cricket::ICE_PWD_LENGTH)),
104 can_start_(false),
105 channel_was_writable_(false),
106 connect_attempts_left_(kMaxReconnectAttempts),
107 weak_factory_(this) {
108 DCHECK(!ice_username_fragment_.empty());
109 DCHECK(!ice_password_.empty());
112 LibjingleTransport::~LibjingleTransport() {
113 DCHECK(event_handler_);
115 event_handler_->OnTransportDeleted(this);
117 if (channel_.get()) {
118 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
119 FROM_HERE, channel_.release());
123 void LibjingleTransport::OnCanStart() {
124 DCHECK(CalledOnValidThread());
126 DCHECK(!can_start_);
127 can_start_ = true;
129 // If Connect() has been called then start connection.
130 if (!callback_.is_null())
131 DoStart();
133 while (!pending_candidates_.empty()) {
134 channel_->OnCandidate(pending_candidates_.front());
135 pending_candidates_.pop_front();
139 void LibjingleTransport::Connect(
140 const std::string& name,
141 Transport::EventHandler* event_handler,
142 const Transport::ConnectedCallback& callback) {
143 DCHECK(CalledOnValidThread());
144 DCHECK(!name.empty());
145 DCHECK(event_handler);
146 DCHECK(!callback.is_null());
148 DCHECK(name_.empty());
149 name_ = name;
150 event_handler_ = event_handler;
151 callback_ = callback;
153 if (can_start_)
154 DoStart();
157 void LibjingleTransport::DoStart() {
158 DCHECK(!channel_.get());
160 // Create P2PTransportChannel, attach signal handlers and connect it.
161 // TODO(sergeyu): Specify correct component ID for the channel.
162 channel_.reset(new cricket::P2PTransportChannel(
163 std::string(), 0, NULL, port_allocator_));
164 channel_->SetIceProtocolType(cricket::ICEPROTO_GOOGLE);
165 channel_->SetIceCredentials(ice_username_fragment_, ice_password_);
166 channel_->SignalRequestSignaling.connect(
167 this, &LibjingleTransport::OnRequestSignaling);
168 channel_->SignalCandidateReady.connect(
169 this, &LibjingleTransport::OnCandidateReady);
170 channel_->SignalRouteChange.connect(
171 this, &LibjingleTransport::OnRouteChange);
172 channel_->SignalWritableState.connect(
173 this, &LibjingleTransport::OnWritableState);
174 channel_->set_incoming_only(
175 !(network_settings_.flags & NetworkSettings::NAT_TRAVERSAL_OUTGOING));
177 channel_->Connect();
179 --connect_attempts_left_;
181 // Start reconnection timer.
182 reconnect_timer_.Start(
183 FROM_HERE, base::TimeDelta::FromSeconds(kReconnectDelaySeconds),
184 this, &LibjingleTransport::TryReconnect);
187 void LibjingleTransport::NotifyConnected() {
188 // Create net::Socket adapter for the P2PTransportChannel.
189 scoped_ptr<jingle_glue::TransportChannelSocketAdapter> socket(
190 new jingle_glue::TransportChannelSocketAdapter(channel_.get()));
191 socket->SetOnDestroyedCallback(base::Bind(
192 &LibjingleTransport::OnChannelDestroyed, base::Unretained(this)));
194 Transport::ConnectedCallback callback = callback_;
195 callback_.Reset();
196 callback.Run(socket.Pass());
199 void LibjingleTransport::AddRemoteCandidate(
200 const cricket::Candidate& candidate) {
201 DCHECK(CalledOnValidThread());
203 // To enforce the no-relay setting, it's not enough to not produce relay
204 // candidates. It's also necessary to discard remote relay candidates.
205 bool relay_allowed = (network_settings_.flags &
206 NetworkSettings::NAT_TRAVERSAL_RELAY) != 0;
207 if (!relay_allowed && candidate.type() == cricket::RELAY_PORT_TYPE)
208 return;
210 if (channel_) {
211 channel_->OnCandidate(candidate);
212 } else {
213 pending_candidates_.push_back(candidate);
217 const std::string& LibjingleTransport::name() const {
218 DCHECK(CalledOnValidThread());
219 return name_;
222 bool LibjingleTransport::is_connected() const {
223 DCHECK(CalledOnValidThread());
224 return callback_.is_null();
227 void LibjingleTransport::OnRequestSignaling(
228 cricket::TransportChannelImpl* channel) {
229 DCHECK(CalledOnValidThread());
230 channel_->OnSignalingReady();
233 void LibjingleTransport::OnCandidateReady(
234 cricket::TransportChannelImpl* channel,
235 const cricket::Candidate& candidate) {
236 DCHECK(CalledOnValidThread());
237 event_handler_->OnTransportCandidate(this, candidate);
240 void LibjingleTransport::OnRouteChange(
241 cricket::TransportChannel* channel,
242 const cricket::Candidate& candidate) {
243 TransportRoute route;
245 if (candidate.type() == "local") {
246 route.type = TransportRoute::DIRECT;
247 } else if (candidate.type() == "stun") {
248 route.type = TransportRoute::STUN;
249 } else if (candidate.type() == "relay") {
250 route.type = TransportRoute::RELAY;
251 } else {
252 LOG(FATAL) << "Unknown candidate type: " << candidate.type();
255 if (!jingle_glue::SocketAddressToIPEndPoint(
256 candidate.address(), &route.remote_address)) {
257 LOG(FATAL) << "Failed to convert peer IP address.";
260 DCHECK(channel_->best_connection());
261 const cricket::Candidate& local_candidate =
262 channel_->best_connection()->local_candidate();
263 if (!jingle_glue::SocketAddressToIPEndPoint(
264 local_candidate.address(), &route.local_address)) {
265 LOG(FATAL) << "Failed to convert local IP address.";
268 event_handler_->OnTransportRouteChange(this, route);
271 void LibjingleTransport::OnWritableState(
272 cricket::TransportChannel* channel) {
273 DCHECK_EQ(channel, channel_.get());
275 if (channel->writable()) {
276 if (!channel_was_writable_) {
277 channel_was_writable_ = true;
278 base::ThreadTaskRunnerHandle::Get()->PostTask(
279 FROM_HERE,
280 base::Bind(&LibjingleTransport::NotifyConnected,
281 weak_factory_.GetWeakPtr()));
283 connect_attempts_left_ = kMaxReconnectAttempts;
284 reconnect_timer_.Stop();
285 } else if (!channel->writable() && channel_was_writable_) {
286 reconnect_timer_.Reset();
287 TryReconnect();
291 void LibjingleTransport::OnChannelDestroyed() {
292 if (is_connected()) {
293 // The connection socket is being deleted, so delete the transport too.
294 delete this;
298 void LibjingleTransport::TryReconnect() {
299 DCHECK(!channel_->writable());
301 if (connect_attempts_left_ <= 0) {
302 reconnect_timer_.Stop();
304 // Notify the caller that ICE connection has failed - normally that will
305 // terminate Jingle connection (i.e. the transport will be destroyed).
306 event_handler_->OnTransportFailed(this);
307 return;
309 --connect_attempts_left_;
311 // Restart ICE by resetting ICE password.
312 ice_password_ = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
313 channel_->SetIceCredentials(ice_username_fragment_, ice_password_);
316 } // namespace
318 LibjingleTransportFactory::LibjingleTransportFactory(
319 SignalStrategy* signal_strategy,
320 scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator,
321 const NetworkSettings& network_settings)
322 : signal_strategy_(signal_strategy),
323 port_allocator_(port_allocator.Pass()),
324 network_settings_(network_settings) {
327 LibjingleTransportFactory::~LibjingleTransportFactory() {
328 // This method may be called in response to a libjingle signal, so
329 // libjingle objects must be deleted asynchronously.
330 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
331 base::ThreadTaskRunnerHandle::Get();
332 task_runner->DeleteSoon(FROM_HERE, port_allocator_.release());
335 void LibjingleTransportFactory::PrepareTokens() {
336 EnsureFreshJingleInfo();
339 scoped_ptr<Transport> LibjingleTransportFactory::CreateTransport() {
340 scoped_ptr<LibjingleTransport> result(
341 new LibjingleTransport(port_allocator_.get(), network_settings_));
343 EnsureFreshJingleInfo();
345 // If there is a pending |jingle_info_request_| delay starting the new
346 // transport until the request is finished.
347 if (jingle_info_request_) {
348 on_jingle_info_callbacks_.push_back(
349 base::Bind(&LibjingleTransport::OnCanStart,
350 result->AsWeakPtr()));
351 } else {
352 result->OnCanStart();
355 return result.Pass();
358 void LibjingleTransportFactory::EnsureFreshJingleInfo() {
359 uint32 stun_or_relay_flags = NetworkSettings::NAT_TRAVERSAL_STUN |
360 NetworkSettings::NAT_TRAVERSAL_RELAY;
361 if (!(network_settings_.flags & stun_or_relay_flags) ||
362 jingle_info_request_) {
363 return;
366 if (base::TimeTicks::Now() - last_jingle_info_update_time_ >
367 base::TimeDelta::FromSeconds(kJingleInfoUpdatePeriodSeconds)) {
368 jingle_info_request_.reset(new JingleInfoRequest(signal_strategy_));
369 jingle_info_request_->Send(base::Bind(
370 &LibjingleTransportFactory::OnJingleInfo, base::Unretained(this)));
374 void LibjingleTransportFactory::OnJingleInfo(
375 const std::string& relay_token,
376 const std::vector<std::string>& relay_hosts,
377 const std::vector<rtc::SocketAddress>& stun_hosts) {
378 if (!relay_token.empty() && !relay_hosts.empty()) {
379 port_allocator_->SetRelayHosts(relay_hosts);
380 port_allocator_->SetRelayToken(relay_token);
382 if (!stun_hosts.empty()) {
383 port_allocator_->SetStunHosts(stun_hosts);
386 jingle_info_request_.reset();
387 if ((!relay_token.empty() && !relay_hosts.empty()) || !stun_hosts.empty())
388 last_jingle_info_update_time_ = base::TimeTicks::Now();
390 while (!on_jingle_info_callbacks_.empty()) {
391 on_jingle_info_callbacks_.begin()->Run();
392 on_jingle_info_callbacks_.pop_front();
396 } // namespace protocol
397 } // namespace remoting