Introduced IDR_SIGNIN_INTERNALS_INDEX_* strings to grit_whitelist.txt
[chromium-blink-merge.git] / remoting / protocol / libjingle_transport_factory.cc
blobac8f0bce4cb2e0cb9a1bc08c495cb3c7de99929b
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/single_thread_task_runner.h"
8 #include "base/thread_task_runner_handle.h"
9 #include "base/timer/timer.h"
10 #include "jingle/glue/channel_socket_adapter.h"
11 #include "jingle/glue/pseudotcp_adapter.h"
12 #include "jingle/glue/thread_wrapper.h"
13 #include "jingle/glue/utils.h"
14 #include "net/base/net_errors.h"
15 #include "remoting/base/constants.h"
16 #include "remoting/jingle_glue/chromium_port_allocator.h"
17 #include "remoting/jingle_glue/chromium_socket_factory.h"
18 #include "remoting/jingle_glue/network_settings.h"
19 #include "remoting/protocol/channel_authenticator.h"
20 #include "remoting/protocol/transport_config.h"
21 #include "third_party/libjingle/source/talk/base/network.h"
22 #include "third_party/libjingle/source/talk/p2p/base/constants.h"
23 #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h"
24 #include "third_party/libjingle/source/talk/p2p/client/basicportallocator.h"
25 #include "third_party/libjingle/source/talk/p2p/client/httpportallocator.h"
27 namespace remoting {
28 namespace protocol {
30 namespace {
32 // Value is chosen to balance the extra latency against the reduced
33 // load due to ACK traffic.
34 const int kTcpAckDelayMilliseconds = 10;
36 // Values for the TCP send and receive buffer size. This should be tuned to
37 // accommodate high latency network but not backlog the decoding pipeline.
38 const int kTcpReceiveBufferSize = 256 * 1024;
39 const int kTcpSendBufferSize = kTcpReceiveBufferSize + 30 * 1024;
41 // Try connecting ICE twice with timeout of 15 seconds for each attempt.
42 const int kMaxReconnectAttempts = 2;
43 const int kReconnectDelaySeconds = 15;
45 class LibjingleStreamTransport : public StreamTransport,
46 public sigslot::has_slots<> {
47 public:
48 LibjingleStreamTransport(cricket::PortAllocator* port_allocator,
49 bool incoming_only);
50 virtual ~LibjingleStreamTransport();
52 // StreamTransport interface.
53 virtual void Initialize(
54 const std::string& name,
55 Transport::EventHandler* event_handler,
56 scoped_ptr<ChannelAuthenticator> authenticator) OVERRIDE;
57 virtual void Connect(
58 const StreamTransport::ConnectedCallback& callback) OVERRIDE;
59 virtual void AddRemoteCandidate(const cricket::Candidate& candidate) OVERRIDE;
60 virtual const std::string& name() const OVERRIDE;
61 virtual bool is_connected() const OVERRIDE;
63 private:
64 // Signal handlers for cricket::TransportChannel.
65 void OnRequestSignaling(cricket::TransportChannelImpl* channel);
66 void OnCandidateReady(cricket::TransportChannelImpl* channel,
67 const cricket::Candidate& candidate);
68 void OnRouteChange(cricket::TransportChannel* channel,
69 const cricket::Candidate& candidate);
70 void OnWritableState(cricket::TransportChannel* channel);
72 // Callback for PseudoTcpAdapter::Connect().
73 void OnTcpConnected(int result);
75 // Callback for Authenticator::SecureAndAuthenticate();
76 void OnAuthenticationDone(net::Error error,
77 scoped_ptr<net::StreamSocket> socket);
79 // Callback for jingle_glue::TransportChannelSocketAdapter to notify when the
80 // socket is destroyed.
81 void OnChannelDestroyed();
83 // Tries to connect by restarting ICE. Called by |reconnect_timer_|.
84 void TryReconnect();
86 // Helper methods to call |callback_|.
87 void NotifyConnected(scoped_ptr<net::StreamSocket> socket);
88 void NotifyConnectFailed();
90 cricket::PortAllocator* port_allocator_;
91 bool incoming_only_;
93 std::string name_;
94 EventHandler* event_handler_;
95 StreamTransport::ConnectedCallback callback_;
96 scoped_ptr<ChannelAuthenticator> authenticator_;
97 std::string ice_username_fragment_;
98 std::string ice_password_;
100 scoped_ptr<cricket::P2PTransportChannel> channel_;
101 bool channel_was_writable_;
102 int connect_attempts_left_;
103 base::RepeatingTimer<LibjingleStreamTransport> reconnect_timer_;
105 // We own |socket_| until it is connected.
106 scoped_ptr<jingle_glue::PseudoTcpAdapter> socket_;
108 DISALLOW_COPY_AND_ASSIGN(LibjingleStreamTransport);
111 LibjingleStreamTransport::LibjingleStreamTransport(
112 cricket::PortAllocator* port_allocator,
113 bool incoming_only)
114 : port_allocator_(port_allocator),
115 incoming_only_(incoming_only),
116 event_handler_(NULL),
117 ice_username_fragment_(
118 talk_base::CreateRandomString(cricket::ICE_UFRAG_LENGTH)),
119 ice_password_(talk_base::CreateRandomString(cricket::ICE_PWD_LENGTH)),
120 channel_was_writable_(false),
121 connect_attempts_left_(kMaxReconnectAttempts) {
124 LibjingleStreamTransport::~LibjingleStreamTransport() {
125 DCHECK(event_handler_);
126 event_handler_->OnTransportDeleted(this);
127 // Channel should be already destroyed if we were connected.
128 DCHECK(!is_connected() || socket_.get() == NULL);
130 if (channel_.get()) {
131 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
132 FROM_HERE, channel_.release());
136 void LibjingleStreamTransport::Initialize(
137 const std::string& name,
138 Transport::EventHandler* event_handler,
139 scoped_ptr<ChannelAuthenticator> authenticator) {
140 DCHECK(CalledOnValidThread());
142 DCHECK(!name.empty());
143 DCHECK(event_handler);
145 // Can be initialized only once.
146 DCHECK(name_.empty());
148 name_ = name;
149 event_handler_ = event_handler;
150 authenticator_ = authenticator.Pass();
153 void LibjingleStreamTransport::Connect(
154 const StreamTransport::ConnectedCallback& callback) {
155 DCHECK(CalledOnValidThread());
157 callback_ = callback;
159 DCHECK(!channel_.get());
161 // Create P2PTransportChannel, attach signal handlers and connect it.
162 // TODO(sergeyu): Specify correct component ID for the channel.
163 channel_.reset(new cricket::P2PTransportChannel(
164 std::string(), 0, NULL, port_allocator_));
165 channel_->SetIceCredentials(ice_username_fragment_, ice_password_);
166 channel_->SignalRequestSignaling.connect(
167 this, &LibjingleStreamTransport::OnRequestSignaling);
168 channel_->SignalCandidateReady.connect(
169 this, &LibjingleStreamTransport::OnCandidateReady);
170 channel_->SignalRouteChange.connect(
171 this, &LibjingleStreamTransport::OnRouteChange);
172 channel_->SignalWritableState.connect(
173 this, &LibjingleStreamTransport::OnWritableState);
174 channel_->set_incoming_only(incoming_only_);
176 channel_->Connect();
178 --connect_attempts_left_;
180 // Start reconnection timer.
181 reconnect_timer_.Start(
182 FROM_HERE, base::TimeDelta::FromSeconds(kReconnectDelaySeconds),
183 this, &LibjingleStreamTransport::TryReconnect);
185 // Create net::Socket adapter for the P2PTransportChannel.
186 scoped_ptr<jingle_glue::TransportChannelSocketAdapter> channel_adapter(
187 new jingle_glue::TransportChannelSocketAdapter(channel_.get()));
189 channel_adapter->SetOnDestroyedCallback(base::Bind(
190 &LibjingleStreamTransport::OnChannelDestroyed, base::Unretained(this)));
192 // Configure and connect PseudoTCP adapter.
193 socket_.reset(
194 new jingle_glue::PseudoTcpAdapter(channel_adapter.release()));
195 socket_->SetSendBufferSize(kTcpSendBufferSize);
196 socket_->SetReceiveBufferSize(kTcpReceiveBufferSize);
197 socket_->SetNoDelay(true);
198 socket_->SetAckDelay(kTcpAckDelayMilliseconds);
200 // TODO(sergeyu): This is a hack to improve latency of the video
201 // channel. Consider removing it once we have better flow control
202 // implemented.
203 if (name_ == kVideoChannelName)
204 socket_->SetWriteWaitsForSend(true);
206 int result = socket_->Connect(
207 base::Bind(&LibjingleStreamTransport::OnTcpConnected,
208 base::Unretained(this)));
209 if (result != net::ERR_IO_PENDING)
210 OnTcpConnected(result);
213 void LibjingleStreamTransport::AddRemoteCandidate(
214 const cricket::Candidate& candidate) {
215 DCHECK(CalledOnValidThread());
216 channel_->OnCandidate(candidate);
219 const std::string& LibjingleStreamTransport::name() const {
220 DCHECK(CalledOnValidThread());
221 return name_;
224 bool LibjingleStreamTransport::is_connected() const {
225 DCHECK(CalledOnValidThread());
226 return callback_.is_null();
229 void LibjingleStreamTransport::OnRequestSignaling(
230 cricket::TransportChannelImpl* channel) {
231 DCHECK(CalledOnValidThread());
232 channel_->OnSignalingReady();
235 void LibjingleStreamTransport::OnCandidateReady(
236 cricket::TransportChannelImpl* channel,
237 const cricket::Candidate& candidate) {
238 DCHECK(CalledOnValidThread());
239 event_handler_->OnTransportCandidate(this, candidate);
242 void LibjingleStreamTransport::OnRouteChange(
243 cricket::TransportChannel* channel,
244 const cricket::Candidate& candidate) {
245 TransportRoute route;
247 if (candidate.type() == "local") {
248 route.type = TransportRoute::DIRECT;
249 } else if (candidate.type() == "stun") {
250 route.type = TransportRoute::STUN;
251 } else if (candidate.type() == "relay") {
252 route.type = TransportRoute::RELAY;
253 } else {
254 LOG(FATAL) << "Unknown candidate type: " << candidate.type();
257 if (!jingle_glue::SocketAddressToIPEndPoint(
258 candidate.address(), &route.remote_address)) {
259 LOG(FATAL) << "Failed to convert peer IP address.";
262 DCHECK(channel_->best_connection());
263 const cricket::Candidate& local_candidate =
264 channel_->best_connection()->local_candidate();
265 if (!jingle_glue::SocketAddressToIPEndPoint(
266 local_candidate.address(), &route.local_address)) {
267 LOG(FATAL) << "Failed to convert local IP address.";
270 event_handler_->OnTransportRouteChange(this, route);
273 void LibjingleStreamTransport::OnWritableState(
274 cricket::TransportChannel* channel) {
275 DCHECK_EQ(channel, channel_.get());
277 event_handler_->OnTransportReady(this, channel->writable());
279 if (channel->writable()) {
280 channel_was_writable_ = true;
281 connect_attempts_left_ = kMaxReconnectAttempts;
282 reconnect_timer_.Stop();
283 } else if (!channel->writable() && channel_was_writable_) {
284 reconnect_timer_.Reset();
285 TryReconnect();
289 void LibjingleStreamTransport::OnTcpConnected(int result) {
290 DCHECK(CalledOnValidThread());
292 if (result != net::OK) {
293 NotifyConnectFailed();
294 return;
297 authenticator_->SecureAndAuthenticate(
298 socket_.PassAs<net::StreamSocket>(),
299 base::Bind(&LibjingleStreamTransport::OnAuthenticationDone,
300 base::Unretained(this)));
303 void LibjingleStreamTransport::OnAuthenticationDone(
304 net::Error error,
305 scoped_ptr<net::StreamSocket> socket) {
306 if (error != net::OK) {
307 NotifyConnectFailed();
308 return;
311 NotifyConnected(socket.Pass());
314 void LibjingleStreamTransport::OnChannelDestroyed() {
315 if (is_connected()) {
316 // The connection socket is being deleted, so delete the transport too.
317 delete this;
321 void LibjingleStreamTransport::TryReconnect() {
322 DCHECK(!channel_->writable());
324 if (connect_attempts_left_ <= 0) {
325 reconnect_timer_.Stop();
327 // Notify the caller that ICE connection has failed - normally that will
328 // terminate Jingle connection (i.e. the transport will be destroyed).
329 event_handler_->OnTransportFailed(this);
330 return;
332 --connect_attempts_left_;
334 // Restart ICE by resetting ICE password.
335 ice_password_ = talk_base::CreateRandomString(cricket::ICE_PWD_LENGTH);
336 channel_->SetIceCredentials(ice_username_fragment_, ice_password_);
339 void LibjingleStreamTransport::NotifyConnected(
340 scoped_ptr<net::StreamSocket> socket) {
341 DCHECK(!is_connected());
342 StreamTransport::ConnectedCallback callback = callback_;
343 callback_.Reset();
344 callback.Run(socket.Pass());
347 void LibjingleStreamTransport::NotifyConnectFailed() {
348 DCHECK(!is_connected());
350 socket_.reset();
352 // This method may be called in response to a libjingle signal, so
353 // libjingle objects must be deleted asynchronously.
354 if (channel_.get()) {
355 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
356 FROM_HERE, channel_.release());
359 authenticator_.reset();
361 NotifyConnected(scoped_ptr<net::StreamSocket>());
364 } // namespace
366 scoped_ptr<LibjingleTransportFactory> LibjingleTransportFactory::Create(
367 const NetworkSettings& network_settings,
368 const scoped_refptr<net::URLRequestContextGetter>&
369 url_request_context_getter) {
370 // Use Chrome's network stack to allocate ports for peer-to-peer channels.
371 scoped_ptr<ChromiumPortAllocator> port_allocator(
372 ChromiumPortAllocator::Create(url_request_context_getter,
373 network_settings));
375 bool incoming_only = network_settings.nat_traversal_mode ==
376 NetworkSettings::NAT_TRAVERSAL_DISABLED;
378 // Use libjingle for negotiation of peer-to-peer channels over
379 // NativePortAllocator allocated ports.
380 scoped_ptr<LibjingleTransportFactory> transport_factory(
381 new LibjingleTransportFactory(
382 port_allocator.PassAs<cricket::HttpPortAllocatorBase>(),
383 incoming_only));
384 return transport_factory.Pass();
387 LibjingleTransportFactory::LibjingleTransportFactory(
388 scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator,
389 bool incoming_only)
390 : http_port_allocator_(port_allocator.get()),
391 port_allocator_(port_allocator.Pass()),
392 incoming_only_(incoming_only) {
393 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
396 LibjingleTransportFactory::LibjingleTransportFactory()
397 : network_manager_(new talk_base::BasicNetworkManager()),
398 socket_factory_(new remoting::ChromiumPacketSocketFactory()),
399 http_port_allocator_(NULL),
400 port_allocator_(new cricket::BasicPortAllocator(
401 network_manager_.get(), socket_factory_.get())),
402 incoming_only_(false) {
403 jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
404 port_allocator_->set_flags(
405 cricket::PORTALLOCATOR_DISABLE_TCP |
406 cricket::PORTALLOCATOR_DISABLE_STUN |
407 cricket::PORTALLOCATOR_DISABLE_RELAY |
408 cricket::PORTALLOCATOR_ENABLE_SHARED_UFRAG |
409 cricket::PORTALLOCATOR_ENABLE_IPV6);
412 LibjingleTransportFactory::~LibjingleTransportFactory() {
413 // This method may be called in response to a libjingle signal, so
414 // libjingle objects must be deleted asynchronously.
415 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
416 base::ThreadTaskRunnerHandle::Get();
417 task_runner->DeleteSoon(FROM_HERE, port_allocator_.release());
418 task_runner->DeleteSoon(FROM_HERE, socket_factory_.release());
419 task_runner->DeleteSoon(FROM_HERE, network_manager_.release());
422 void LibjingleTransportFactory::SetTransportConfig(
423 const TransportConfig& config) {
424 if (http_port_allocator_) {
425 std::vector<talk_base::SocketAddress> stun_hosts;
426 talk_base::SocketAddress stun_address;
427 if (stun_address.FromString(config.stun_server)) {
428 stun_hosts.push_back(stun_address);
429 http_port_allocator_->SetStunHosts(stun_hosts);
430 } else {
431 LOG(ERROR) << "Failed to parse stun server address: "
432 << config.stun_server;
435 std::vector<std::string> relay_hosts;
436 relay_hosts.push_back(config.relay_server);
437 http_port_allocator_->SetRelayHosts(relay_hosts);
438 http_port_allocator_->SetRelayToken(config.relay_token);
442 scoped_ptr<StreamTransport> LibjingleTransportFactory::CreateStreamTransport() {
443 return scoped_ptr<StreamTransport>(
444 new LibjingleStreamTransport(port_allocator_.get(), incoming_only_));
447 scoped_ptr<DatagramTransport>
448 LibjingleTransportFactory::CreateDatagramTransport() {
449 NOTIMPLEMENTED();
450 return scoped_ptr<DatagramTransport>();
453 } // namespace protocol
454 } // namespace remoting