Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(173)

Side by Side Diff: remoting/protocol/libjingle_transport_factory.cc

Issue 1412313006: Remove remoting::protocol::Transport interface. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@transport_session.h
Patch Set: Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/protocol/libjingle_transport_factory.h ('k') | remoting/protocol/transport.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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.
4
5 #include "remoting/protocol/libjingle_transport_factory.h"
6
7 #include <algorithm>
8
9 #include "base/callback.h"
10 #include "base/callback_helpers.h"
11 #include "base/single_thread_task_runner.h"
12 #include "base/thread_task_runner_handle.h"
13 #include "base/timer/timer.h"
14 #include "jingle/glue/utils.h"
15 #include "net/base/net_errors.h"
16 #include "remoting/protocol/channel_socket_adapter.h"
17 #include "remoting/protocol/network_settings.h"
18 #include "remoting/signaling/jingle_info_request.h"
19 #include "third_party/webrtc/base/network.h"
20 #include "third_party/webrtc/p2p/base/constants.h"
21 #include "third_party/webrtc/p2p/base/p2ptransportchannel.h"
22 #include "third_party/webrtc/p2p/base/port.h"
23 #include "third_party/webrtc/p2p/client/httpportallocator.h"
24
25 namespace remoting {
26 namespace protocol {
27
28 namespace {
29
30 // Try connecting ICE twice with timeout of 15 seconds for each attempt.
31 const int kMaxReconnectAttempts = 2;
32 const int kReconnectDelaySeconds = 15;
33
34 // Get fresh STUN/Relay configuration every hour.
35 const int kJingleInfoUpdatePeriodSeconds = 3600;
36
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" || candidate_type == "prflx") {
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;
50 }
51 }
52
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 TransportRole role);
61 ~LibjingleTransport() override;
62
63 // Called by JingleTransportFactory when it has fresh Jingle info.
64 void OnCanStart();
65
66 // Transport interface.
67 void Connect(const std::string& name,
68 Transport::EventHandler* event_handler,
69 const Transport::ConnectedCallback& callback) override;
70 void SetRemoteCredentials(const std::string& ufrag,
71 const std::string& password) override;
72 void AddRemoteCandidate(const cricket::Candidate& candidate) override;
73 const std::string& name() const override;
74 bool is_connected() const override;
75
76 private:
77 void DoStart();
78 void NotifyConnected();
79
80 // Signal handlers for cricket::TransportChannel.
81 void OnCandidateGathered(cricket::TransportChannelImpl* channel,
82 const cricket::Candidate& candidate);
83 void OnRouteChange(cricket::TransportChannel* channel,
84 const cricket::Candidate& candidate);
85 void OnReceivingState(cricket::TransportChannel* channel);
86 void OnWritableState(cricket::TransportChannel* channel);
87
88 // Callback for TransportChannelSocketAdapter to notify when the socket is
89 // destroyed.
90 void OnChannelDestroyed();
91
92 void NotifyRouteChanged();
93
94 // Tries to connect by restarting ICE. Called by |reconnect_timer_|.
95 void TryReconnect();
96
97 cricket::PortAllocator* port_allocator_;
98 NetworkSettings network_settings_;
99 TransportRole role_;
100
101 std::string name_;
102 EventHandler* event_handler_;
103 Transport::ConnectedCallback callback_;
104 std::string ice_username_fragment_;
105
106 bool can_start_;
107
108 std::string remote_ice_username_fragment_;
109 std::string remote_ice_password_;
110 std::list<cricket::Candidate> pending_candidates_;
111 scoped_ptr<cricket::P2PTransportChannel> channel_;
112 int connect_attempts_left_;
113 base::RepeatingTimer reconnect_timer_;
114
115 base::WeakPtrFactory<LibjingleTransport> weak_factory_;
116
117 DISALLOW_COPY_AND_ASSIGN(LibjingleTransport);
118 };
119
120 LibjingleTransport::LibjingleTransport(cricket::PortAllocator* port_allocator,
121 const NetworkSettings& network_settings,
122 TransportRole role)
123 : port_allocator_(port_allocator),
124 network_settings_(network_settings),
125 role_(role),
126 event_handler_(nullptr),
127 ice_username_fragment_(
128 rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH)),
129 can_start_(false),
130 connect_attempts_left_(kMaxReconnectAttempts),
131 weak_factory_(this) {
132 DCHECK(!ice_username_fragment_.empty());
133 }
134
135 LibjingleTransport::~LibjingleTransport() {
136 DCHECK(event_handler_);
137
138 event_handler_->OnTransportDeleted(this);
139
140 if (channel_.get()) {
141 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
142 FROM_HERE, channel_.release());
143 }
144 }
145
146 void LibjingleTransport::OnCanStart() {
147 DCHECK(CalledOnValidThread());
148
149 DCHECK(!can_start_);
150 can_start_ = true;
151
152 // If Connect() has been called then start connection.
153 if (!callback_.is_null())
154 DoStart();
155
156 // Pass pending ICE credentials and candidates to the channel.
157 if (!remote_ice_username_fragment_.empty()) {
158 channel_->SetRemoteIceCredentials(remote_ice_username_fragment_,
159 remote_ice_password_);
160 }
161
162 while (!pending_candidates_.empty()) {
163 channel_->AddRemoteCandidate(pending_candidates_.front());
164 pending_candidates_.pop_front();
165 }
166 }
167
168 void LibjingleTransport::Connect(
169 const std::string& name,
170 Transport::EventHandler* event_handler,
171 const Transport::ConnectedCallback& callback) {
172 DCHECK(CalledOnValidThread());
173 DCHECK(!name.empty());
174 DCHECK(event_handler);
175 DCHECK(!callback.is_null());
176
177 DCHECK(name_.empty());
178 name_ = name;
179 event_handler_ = event_handler;
180 callback_ = callback;
181
182 if (can_start_)
183 DoStart();
184 }
185
186 void LibjingleTransport::DoStart() {
187 DCHECK(!channel_.get());
188
189 // Create P2PTransportChannel, attach signal handlers and connect it.
190 // TODO(sergeyu): Specify correct component ID for the channel.
191 channel_.reset(new cricket::P2PTransportChannel(
192 std::string(), 0, nullptr, port_allocator_));
193 std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
194 channel_->SetIceProtocolType(cricket::ICEPROTO_RFC5245);
195 channel_->SetIceRole((role_ == TransportRole::CLIENT)
196 ? cricket::ICEROLE_CONTROLLING
197 : cricket::ICEROLE_CONTROLLED);
198 event_handler_->OnTransportIceCredentials(this, ice_username_fragment_,
199 ice_password);
200 channel_->SetIceCredentials(ice_username_fragment_, ice_password);
201 channel_->SignalCandidateGathered.connect(
202 this, &LibjingleTransport::OnCandidateGathered);
203 channel_->SignalRouteChange.connect(
204 this, &LibjingleTransport::OnRouteChange);
205 channel_->SignalReceivingState.connect(
206 this, &LibjingleTransport::OnReceivingState);
207 channel_->SignalWritableState.connect(
208 this, &LibjingleTransport::OnWritableState);
209 channel_->set_incoming_only(
210 !(network_settings_.flags & NetworkSettings::NAT_TRAVERSAL_OUTGOING));
211
212 channel_->Connect();
213 channel_->MaybeStartGathering();
214
215 --connect_attempts_left_;
216
217 // Start reconnection timer.
218 reconnect_timer_.Start(
219 FROM_HERE, base::TimeDelta::FromSeconds(kReconnectDelaySeconds),
220 this, &LibjingleTransport::TryReconnect);
221 }
222
223 void LibjingleTransport::NotifyConnected() {
224 // Create P2PDatagramSocket adapter for the P2PTransportChannel.
225 scoped_ptr<TransportChannelSocketAdapter> socket(
226 new TransportChannelSocketAdapter(channel_.get()));
227 socket->SetOnDestroyedCallback(base::Bind(
228 &LibjingleTransport::OnChannelDestroyed, base::Unretained(this)));
229 base::ResetAndReturn(&callback_).Run(socket.Pass());
230 }
231
232 void LibjingleTransport::SetRemoteCredentials(const std::string& ufrag,
233 const std::string& password) {
234 DCHECK(CalledOnValidThread());
235
236 remote_ice_username_fragment_ = ufrag;
237 remote_ice_password_ = password;
238
239 if (channel_)
240 channel_->SetRemoteIceCredentials(ufrag, password);
241 }
242
243 void LibjingleTransport::AddRemoteCandidate(
244 const cricket::Candidate& candidate) {
245 DCHECK(CalledOnValidThread());
246
247 // To enforce the no-relay setting, it's not enough to not produce relay
248 // candidates. It's also necessary to discard remote relay candidates.
249 bool relay_allowed = (network_settings_.flags &
250 NetworkSettings::NAT_TRAVERSAL_RELAY) != 0;
251 if (!relay_allowed && candidate.type() == cricket::RELAY_PORT_TYPE)
252 return;
253
254 if (channel_) {
255 channel_->AddRemoteCandidate(candidate);
256 } else {
257 pending_candidates_.push_back(candidate);
258 }
259 }
260
261 const std::string& LibjingleTransport::name() const {
262 DCHECK(CalledOnValidThread());
263 return name_;
264 }
265
266 bool LibjingleTransport::is_connected() const {
267 DCHECK(CalledOnValidThread());
268 return callback_.is_null();
269 }
270
271 void LibjingleTransport::OnCandidateGathered(
272 cricket::TransportChannelImpl* channel,
273 const cricket::Candidate& candidate) {
274 DCHECK(CalledOnValidThread());
275 event_handler_->OnTransportCandidate(this, candidate);
276 }
277
278 void LibjingleTransport::OnRouteChange(
279 cricket::TransportChannel* channel,
280 const cricket::Candidate& candidate) {
281 // Ignore notifications if the channel is not writable.
282 if (channel_->writable())
283 NotifyRouteChanged();
284 }
285
286 void LibjingleTransport::OnReceivingState(cricket::TransportChannel* channel) {
287 DCHECK_EQ(channel, static_cast<cricket::TransportChannel*>(channel_.get()));
288
289 if (channel->receiving() && !callback_.is_null())
290 NotifyConnected();
291 }
292
293 void LibjingleTransport::OnWritableState(cricket::TransportChannel* channel) {
294 DCHECK_EQ(channel, static_cast<cricket::TransportChannel*>(channel_.get()));
295
296 if (channel->writable()) {
297 connect_attempts_left_ = kMaxReconnectAttempts;
298 reconnect_timer_.Stop();
299
300 // Route change notifications are ignored when the |channel_| is not
301 // writable. Notify the event handler about the current route once the
302 // channel is writable.
303 NotifyRouteChanged();
304 } else {
305 reconnect_timer_.Reset();
306 TryReconnect();
307 }
308 }
309
310 void LibjingleTransport::OnChannelDestroyed() {
311 // The connection socket is being deleted, so delete the transport too.
312 delete this;
313 }
314
315 void LibjingleTransport::NotifyRouteChanged() {
316 TransportRoute route;
317
318 DCHECK(channel_->best_connection());
319 const cricket::Connection* connection = channel_->best_connection();
320
321 // A connection has both a local and a remote candidate. For our purposes, the
322 // route type is determined by the most indirect candidate type. For example:
323 // it's possible for the local candidate be a "relay" type, while the remote
324 // candidate is "local". In this case, we still want to report a RELAY route
325 // type.
326 static_assert(TransportRoute::DIRECT < TransportRoute::STUN &&
327 TransportRoute::STUN < TransportRoute::RELAY,
328 "Route type enum values are ordered by 'indirectness'");
329 route.type = std::max(
330 CandidateTypeToTransportRouteType(connection->local_candidate().type()),
331 CandidateTypeToTransportRouteType(connection->remote_candidate().type()));
332
333 if (!jingle_glue::SocketAddressToIPEndPoint(
334 connection->remote_candidate().address(), &route.remote_address)) {
335 LOG(FATAL) << "Failed to convert peer IP address.";
336 }
337
338 const cricket::Candidate& local_candidate =
339 channel_->best_connection()->local_candidate();
340 if (!jingle_glue::SocketAddressToIPEndPoint(
341 local_candidate.address(), &route.local_address)) {
342 LOG(FATAL) << "Failed to convert local IP address.";
343 }
344
345 event_handler_->OnTransportRouteChange(this, route);
346 }
347
348 void LibjingleTransport::TryReconnect() {
349 DCHECK(!channel_->writable());
350
351 if (connect_attempts_left_ <= 0) {
352 reconnect_timer_.Stop();
353
354 // Notify the caller that ICE connection has failed - normally that will
355 // terminate Jingle connection (i.e. the transport will be destroyed).
356 event_handler_->OnTransportFailed(this);
357 return;
358 }
359 --connect_attempts_left_;
360
361 // Restart ICE by resetting ICE password.
362 std::string ice_password = rtc::CreateRandomString(cricket::ICE_PWD_LENGTH);
363 event_handler_->OnTransportIceCredentials(this, ice_username_fragment_,
364 ice_password);
365 channel_->SetIceCredentials(ice_username_fragment_, ice_password);
366 }
367
368 } // namespace
369
370 LibjingleTransportFactory::LibjingleTransportFactory(
371 SignalStrategy* signal_strategy,
372 scoped_ptr<cricket::HttpPortAllocatorBase> port_allocator,
373 const NetworkSettings& network_settings,
374 TransportRole role)
375 : signal_strategy_(signal_strategy),
376 port_allocator_(port_allocator.Pass()),
377 network_settings_(network_settings),
378 role_(role) {
379 }
380
381 LibjingleTransportFactory::~LibjingleTransportFactory() {
382 // This method may be called in response to a libjingle signal, so
383 // libjingle objects must be deleted asynchronously.
384 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
385 base::ThreadTaskRunnerHandle::Get();
386 task_runner->DeleteSoon(FROM_HERE, port_allocator_.release());
387 }
388
389 void LibjingleTransportFactory::PrepareTokens() {
390 EnsureFreshJingleInfo();
391 }
392
393 scoped_ptr<Transport> LibjingleTransportFactory::CreateTransport() {
394 scoped_ptr<LibjingleTransport> result(
395 new LibjingleTransport(port_allocator_.get(), network_settings_, role_));
396
397 EnsureFreshJingleInfo();
398
399 // If there is a pending |jingle_info_request_| delay starting the new
400 // transport until the request is finished.
401 if (jingle_info_request_) {
402 on_jingle_info_callbacks_.push_back(
403 base::Bind(&LibjingleTransport::OnCanStart,
404 result->AsWeakPtr()));
405 } else {
406 result->OnCanStart();
407 }
408
409 return result.Pass();
410 }
411
412 void LibjingleTransportFactory::EnsureFreshJingleInfo() {
413 uint32 stun_or_relay_flags = NetworkSettings::NAT_TRAVERSAL_STUN |
414 NetworkSettings::NAT_TRAVERSAL_RELAY;
415 if (!(network_settings_.flags & stun_or_relay_flags) ||
416 jingle_info_request_) {
417 return;
418 }
419
420 if (last_jingle_info_update_time_.is_null() ||
421 base::TimeTicks::Now() - last_jingle_info_update_time_ >
422 base::TimeDelta::FromSeconds(kJingleInfoUpdatePeriodSeconds)) {
423 jingle_info_request_.reset(new JingleInfoRequest(signal_strategy_));
424 jingle_info_request_->Send(base::Bind(
425 &LibjingleTransportFactory::OnJingleInfo, base::Unretained(this)));
426 }
427 }
428
429 void LibjingleTransportFactory::OnJingleInfo(
430 const std::string& relay_token,
431 const std::vector<std::string>& relay_hosts,
432 const std::vector<rtc::SocketAddress>& stun_hosts) {
433 if (!relay_token.empty() && !relay_hosts.empty()) {
434 port_allocator_->SetRelayHosts(relay_hosts);
435 port_allocator_->SetRelayToken(relay_token);
436 }
437 if (!stun_hosts.empty()) {
438 port_allocator_->SetStunHosts(stun_hosts);
439 }
440
441 jingle_info_request_.reset();
442 if ((!relay_token.empty() && !relay_hosts.empty()) || !stun_hosts.empty())
443 last_jingle_info_update_time_ = base::TimeTicks::Now();
444
445 while (!on_jingle_info_callbacks_.empty()) {
446 on_jingle_info_callbacks_.begin()->Run();
447 on_jingle_info_callbacks_.pop_front();
448 }
449 }
450
451 } // namespace protocol
452 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/protocol/libjingle_transport_factory.h ('k') | remoting/protocol/transport.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698