blob: 352098433955da355c707ac7b9a159db03807cb3 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
2 * libjingle
3 * Copyright 2004--2005, Google Inc.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * 3. The name of the author may not be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "talk/p2p/base/session.h"
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000029
30#include "talk/base/bind.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000031#include "talk/base/common.h"
32#include "talk/base/logging.h"
33#include "talk/base/helpers.h"
34#include "talk/base/scoped_ptr.h"
35#include "talk/base/sslstreamadapter.h"
36#include "talk/xmpp/constants.h"
37#include "talk/xmpp/jid.h"
38#include "talk/p2p/base/dtlstransport.h"
39#include "talk/p2p/base/p2ptransport.h"
40#include "talk/p2p/base/sessionclient.h"
41#include "talk/p2p/base/transport.h"
42#include "talk/p2p/base/transportchannelproxy.h"
43#include "talk/p2p/base/transportinfo.h"
44
45#include "talk/p2p/base/constants.h"
46
47namespace cricket {
48
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000049using talk_base::Bind;
50
henrike@webrtc.org28e20752013-07-10 00:45:36 +000051bool BadMessage(const buzz::QName type,
52 const std::string& text,
53 MessageError* err) {
54 err->SetType(type);
55 err->SetText(text);
56 return false;
57}
58
59TransportProxy::~TransportProxy() {
60 for (ChannelMap::iterator iter = channels_.begin();
61 iter != channels_.end(); ++iter) {
62 iter->second->SignalDestroyed(iter->second);
63 delete iter->second;
64 }
65}
66
67std::string TransportProxy::type() const {
68 return transport_->get()->type();
69}
70
71TransportChannel* TransportProxy::GetChannel(int component) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000072 ASSERT(talk_base::Thread::Current() == worker_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000073 return GetChannelProxy(component);
74}
75
76TransportChannel* TransportProxy::CreateChannel(
77 const std::string& name, int component) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000078 ASSERT(talk_base::Thread::Current() == worker_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000079 ASSERT(GetChannel(component) == NULL);
80 ASSERT(!transport_->get()->HasChannel(component));
81
82 // We always create a proxy in case we need to change out the transport later.
83 TransportChannelProxy* channel =
84 new TransportChannelProxy(content_name(), name, component);
85 channels_[component] = channel;
86
87 // If we're already negotiated, create an impl and hook it up to the proxy
88 // channel. If we're connecting, create an impl but don't hook it up yet.
89 if (negotiated_) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000090 SetupChannelProxy_w(component, channel);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000091 } else if (connecting_) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +000092 GetOrCreateChannelProxyImpl_w(component);
henrike@webrtc.org28e20752013-07-10 00:45:36 +000093 }
94 return channel;
95}
96
97bool TransportProxy::HasChannel(int component) {
98 return transport_->get()->HasChannel(component);
99}
100
101void TransportProxy::DestroyChannel(int component) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000102 ASSERT(talk_base::Thread::Current() == worker_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000103 TransportChannel* channel = GetChannel(component);
104 if (channel) {
105 // If the state of TransportProxy is not NEGOTIATED
106 // then TransportChannelProxy and its impl are not
107 // connected. Both must be connected before
108 // deletion.
109 if (!negotiated_) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000110 SetupChannelProxy_w(component, GetChannelProxy(component));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000111 }
112
113 channels_.erase(component);
114 channel->SignalDestroyed(channel);
115 delete channel;
116 }
117}
118
119void TransportProxy::ConnectChannels() {
120 if (!connecting_) {
121 if (!negotiated_) {
122 for (ChannelMap::iterator iter = channels_.begin();
123 iter != channels_.end(); ++iter) {
124 GetOrCreateChannelProxyImpl(iter->first);
125 }
126 }
127 connecting_ = true;
128 }
129 // TODO(juberti): Right now Transport::ConnectChannels doesn't work if we
130 // don't have any channels yet, so we need to allow this method to be called
131 // multiple times. Once we fix Transport, we can move this call inside the
132 // if (!connecting_) block.
133 transport_->get()->ConnectChannels();
134}
135
136void TransportProxy::CompleteNegotiation() {
137 if (!negotiated_) {
138 for (ChannelMap::iterator iter = channels_.begin();
139 iter != channels_.end(); ++iter) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000140 SetupChannelProxy(iter->first, iter->second);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000141 }
142 negotiated_ = true;
143 }
144}
145
146void TransportProxy::AddSentCandidates(const Candidates& candidates) {
147 for (Candidates::const_iterator cand = candidates.begin();
148 cand != candidates.end(); ++cand) {
149 sent_candidates_.push_back(*cand);
150 }
151}
152
153void TransportProxy::AddUnsentCandidates(const Candidates& candidates) {
154 for (Candidates::const_iterator cand = candidates.begin();
155 cand != candidates.end(); ++cand) {
156 unsent_candidates_.push_back(*cand);
157 }
158}
159
160bool TransportProxy::GetChannelNameFromComponent(
161 int component, std::string* channel_name) const {
162 const TransportChannelProxy* channel = GetChannelProxy(component);
163 if (channel == NULL) {
164 return false;
165 }
166
167 *channel_name = channel->name();
168 return true;
169}
170
171bool TransportProxy::GetComponentFromChannelName(
172 const std::string& channel_name, int* component) const {
173 const TransportChannelProxy* channel = GetChannelProxyByName(channel_name);
174 if (channel == NULL) {
175 return false;
176 }
177
178 *component = channel->component();
179 return true;
180}
181
182TransportChannelProxy* TransportProxy::GetChannelProxy(int component) const {
183 ChannelMap::const_iterator iter = channels_.find(component);
184 return (iter != channels_.end()) ? iter->second : NULL;
185}
186
187TransportChannelProxy* TransportProxy::GetChannelProxyByName(
188 const std::string& name) const {
189 for (ChannelMap::const_iterator iter = channels_.begin();
190 iter != channels_.end();
191 ++iter) {
192 if (iter->second->name() == name) {
193 return iter->second;
194 }
195 }
196 return NULL;
197}
198
199TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl(
200 int component) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000201 return worker_thread_->Invoke<TransportChannelImpl*>(Bind(
202 &TransportProxy::GetOrCreateChannelProxyImpl_w, this, component));
203}
204
205TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl_w(
206 int component) {
207 ASSERT(talk_base::Thread::Current() == worker_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000208 TransportChannelImpl* impl = transport_->get()->GetChannel(component);
209 if (impl == NULL) {
210 impl = transport_->get()->CreateChannel(component);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000211 }
212 return impl;
213}
214
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000215void TransportProxy::SetupChannelProxy(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000216 int component, TransportChannelProxy* transproxy) {
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000217 worker_thread_->Invoke<void>(Bind(
218 &TransportProxy::SetupChannelProxy_w, this, component, transproxy));
219}
220
221void TransportProxy::SetupChannelProxy_w(
222 int component, TransportChannelProxy* transproxy) {
223 ASSERT(talk_base::Thread::Current() == worker_thread_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000224 TransportChannelImpl* impl = GetOrCreateChannelProxyImpl(component);
225 ASSERT(impl != NULL);
226 transproxy->SetImplementation(impl);
227}
228
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000229void TransportProxy::ReplaceChannelProxyImpl(TransportChannelProxy* proxy,
230 TransportChannelImpl* impl) {
231 worker_thread_->Invoke<void>(Bind(
232 &TransportProxy::ReplaceChannelProxyImpl_w, this, proxy, impl));
233}
234
235void TransportProxy::ReplaceChannelProxyImpl_w(TransportChannelProxy* proxy,
236 TransportChannelImpl* impl) {
237 ASSERT(talk_base::Thread::Current() == worker_thread_);
238 ASSERT(proxy != NULL);
239 proxy->SetImplementation(impl);
240}
241
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000242// This function muxes |this| onto |target| by repointing |this| at
243// |target|'s transport and setting our TransportChannelProxies
244// to point to |target|'s underlying implementations.
245bool TransportProxy::SetupMux(TransportProxy* target) {
246 // Bail out if there's nothing to do.
247 if (transport_ == target->transport_) {
248 return true;
249 }
250
251 // Run through all channels and remove any non-rtp transport channels before
252 // setting target transport channels.
253 for (ChannelMap::const_iterator iter = channels_.begin();
254 iter != channels_.end(); ++iter) {
255 if (!target->transport_->get()->HasChannel(iter->first)) {
256 // Remove if channel doesn't exist in |transport_|.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000257 ReplaceChannelProxyImpl(iter->second, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000258 } else {
259 // Replace the impl for all the TransportProxyChannels with the channels
260 // from |target|'s transport. Fail if there's not an exact match.
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000261 ReplaceChannelProxyImpl(
262 iter->second, target->transport_->get()->CreateChannel(iter->first));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000263 }
264 }
265
266 // Now replace our transport. Must happen afterwards because
267 // it deletes all impls as a side effect.
268 transport_ = target->transport_;
269 transport_->get()->SignalCandidatesReady.connect(
270 this, &TransportProxy::OnTransportCandidatesReady);
271 set_candidates_allocated(target->candidates_allocated());
272 return true;
273}
274
mallinath@webrtc.orga5506692013-08-12 21:18:15 +0000275void TransportProxy::SetIceRole(IceRole role) {
276 transport_->get()->SetIceRole(role);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000277}
278
279bool TransportProxy::SetLocalTransportDescription(
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000280 const TransportDescription& description,
281 ContentAction action,
282 std::string* error_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000283 // If this is an answer, finalize the negotiation.
284 if (action == CA_ANSWER) {
285 CompleteNegotiation();
286 }
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000287 return transport_->get()->SetLocalTransportDescription(description,
288 action,
289 error_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000290}
291
292bool TransportProxy::SetRemoteTransportDescription(
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000293 const TransportDescription& description,
294 ContentAction action,
295 std::string* error_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000296 // If this is an answer, finalize the negotiation.
297 if (action == CA_ANSWER) {
298 CompleteNegotiation();
299 }
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000300 return transport_->get()->SetRemoteTransportDescription(description,
301 action,
302 error_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000303}
304
305void TransportProxy::OnSignalingReady() {
306 // If we're starting a new allocation sequence, reset our state.
307 set_candidates_allocated(false);
308 transport_->get()->OnSignalingReady();
309}
310
311bool TransportProxy::OnRemoteCandidates(const Candidates& candidates,
312 std::string* error) {
313 // Ensure the transport is negotiated before handling candidates.
314 // TODO(juberti): Remove this once everybody calls SetLocalTD.
315 CompleteNegotiation();
316
317 // Verify each candidate before passing down to transport layer.
318 for (Candidates::const_iterator cand = candidates.begin();
319 cand != candidates.end(); ++cand) {
320 if (!transport_->get()->VerifyCandidate(*cand, error))
321 return false;
322 if (!HasChannel(cand->component())) {
323 *error = "Candidate has unknown component: " + cand->ToString() +
324 " for content: " + content_name_;
325 return false;
326 }
327 }
328 transport_->get()->OnRemoteCandidates(candidates);
329 return true;
330}
331
wu@webrtc.org91053e72013-08-10 07:18:04 +0000332void TransportProxy::SetIdentity(
333 talk_base::SSLIdentity* identity) {
334 transport_->get()->SetIdentity(identity);
335}
336
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000337std::string BaseSession::StateToString(State state) {
338 switch (state) {
339 case Session::STATE_INIT:
340 return "STATE_INIT";
341 case Session::STATE_SENTINITIATE:
342 return "STATE_SENTINITIATE";
343 case Session::STATE_RECEIVEDINITIATE:
344 return "STATE_RECEIVEDINITIATE";
345 case Session::STATE_SENTPRACCEPT:
346 return "STATE_SENTPRACCEPT";
347 case Session::STATE_SENTACCEPT:
348 return "STATE_SENTACCEPT";
349 case Session::STATE_RECEIVEDPRACCEPT:
350 return "STATE_RECEIVEDPRACCEPT";
351 case Session::STATE_RECEIVEDACCEPT:
352 return "STATE_RECEIVEDACCEPT";
353 case Session::STATE_SENTMODIFY:
354 return "STATE_SENTMODIFY";
355 case Session::STATE_RECEIVEDMODIFY:
356 return "STATE_RECEIVEDMODIFY";
357 case Session::STATE_SENTREJECT:
358 return "STATE_SENTREJECT";
359 case Session::STATE_RECEIVEDREJECT:
360 return "STATE_RECEIVEDREJECT";
361 case Session::STATE_SENTREDIRECT:
362 return "STATE_SENTREDIRECT";
363 case Session::STATE_SENTTERMINATE:
364 return "STATE_SENTTERMINATE";
365 case Session::STATE_RECEIVEDTERMINATE:
366 return "STATE_RECEIVEDTERMINATE";
367 case Session::STATE_INPROGRESS:
368 return "STATE_INPROGRESS";
369 case Session::STATE_DEINIT:
370 return "STATE_DEINIT";
371 default:
372 break;
373 }
374 return "STATE_" + talk_base::ToString(state);
375}
376
377BaseSession::BaseSession(talk_base::Thread* signaling_thread,
378 talk_base::Thread* worker_thread,
379 PortAllocator* port_allocator,
380 const std::string& sid,
381 const std::string& content_type,
382 bool initiator)
383 : state_(STATE_INIT),
384 error_(ERROR_NONE),
385 signaling_thread_(signaling_thread),
386 worker_thread_(worker_thread),
387 port_allocator_(port_allocator),
388 sid_(sid),
389 content_type_(content_type),
390 transport_type_(NS_GINGLE_P2P),
391 initiator_(initiator),
392 identity_(NULL),
393 local_description_(NULL),
394 remote_description_(NULL),
395 ice_tiebreaker_(talk_base::CreateRandomId64()),
396 role_switch_(false) {
397 ASSERT(signaling_thread->IsCurrent());
398}
399
400BaseSession::~BaseSession() {
401 ASSERT(signaling_thread()->IsCurrent());
402
403 ASSERT(state_ != STATE_DEINIT);
404 LogState(state_, STATE_DEINIT);
405 state_ = STATE_DEINIT;
406 SignalState(this, state_);
407
408 for (TransportMap::iterator iter = transports_.begin();
409 iter != transports_.end(); ++iter) {
410 delete iter->second;
411 }
412
413 delete remote_description_;
414 delete local_description_;
415}
416
wu@webrtc.org91053e72013-08-10 07:18:04 +0000417bool BaseSession::SetIdentity(talk_base::SSLIdentity* identity) {
418 if (identity_)
419 return false;
420 identity_ = identity;
421 for (TransportMap::iterator iter = transports_.begin();
422 iter != transports_.end(); ++iter) {
423 iter->second->SetIdentity(identity_);
424 }
425 return true;
426}
427
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000428bool BaseSession::PushdownTransportDescription(ContentSource source,
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000429 ContentAction action,
430 std::string* error_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000431 if (source == CS_LOCAL) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000432 return PushdownLocalTransportDescription(local_description_,
433 action,
434 error_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000435 }
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000436 return PushdownRemoteTransportDescription(remote_description_,
437 action,
438 error_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000439}
440
441bool BaseSession::PushdownLocalTransportDescription(
442 const SessionDescription* sdesc,
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000443 ContentAction action,
444 std::string* error_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000445 // Update the Transports with the right information, and trigger them to
446 // start connecting.
447 for (TransportMap::iterator iter = transports_.begin();
448 iter != transports_.end(); ++iter) {
449 // If no transport info was in this session description, ret == false
450 // and we just skip this one.
451 TransportDescription tdesc;
452 bool ret = GetTransportDescription(
453 sdesc, iter->second->content_name(), &tdesc);
454 if (ret) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000455 if (!iter->second->SetLocalTransportDescription(tdesc, action,
456 error_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000457 return false;
458 }
459
460 iter->second->ConnectChannels();
461 }
462 }
463
464 return true;
465}
466
467bool BaseSession::PushdownRemoteTransportDescription(
468 const SessionDescription* sdesc,
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000469 ContentAction action,
470 std::string* error_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000471 // Update the Transports with the right information.
472 for (TransportMap::iterator iter = transports_.begin();
473 iter != transports_.end(); ++iter) {
474 TransportDescription tdesc;
475
476 // If no transport info was in this session description, ret == false
477 // and we just skip this one.
478 bool ret = GetTransportDescription(
479 sdesc, iter->second->content_name(), &tdesc);
480 if (ret) {
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000481 if (!iter->second->SetRemoteTransportDescription(tdesc, action,
482 error_desc)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000483 return false;
484 }
485 }
486 }
487
488 return true;
489}
490
491TransportChannel* BaseSession::CreateChannel(const std::string& content_name,
492 const std::string& channel_name,
493 int component) {
494 // We create the proxy "on demand" here because we need to support
495 // creating channels at any time, even before we send or receive
496 // initiate messages, which is before we create the transports.
497 TransportProxy* transproxy = GetOrCreateTransportProxy(content_name);
498 return transproxy->CreateChannel(channel_name, component);
499}
500
501TransportChannel* BaseSession::GetChannel(const std::string& content_name,
502 int component) {
503 TransportProxy* transproxy = GetTransportProxy(content_name);
504 if (transproxy == NULL)
505 return NULL;
506 else
507 return transproxy->GetChannel(component);
508}
509
510void BaseSession::DestroyChannel(const std::string& content_name,
511 int component) {
512 TransportProxy* transproxy = GetTransportProxy(content_name);
513 ASSERT(transproxy != NULL);
514 transproxy->DestroyChannel(component);
515}
516
517TransportProxy* BaseSession::GetOrCreateTransportProxy(
518 const std::string& content_name) {
519 TransportProxy* transproxy = GetTransportProxy(content_name);
520 if (transproxy)
521 return transproxy;
522
523 Transport* transport = CreateTransport(content_name);
mallinath@webrtc.orga5506692013-08-12 21:18:15 +0000524 transport->SetIceRole(initiator_ ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED);
525 transport->SetIceTiebreaker(ice_tiebreaker_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000526 // TODO: Connect all the Transport signals to TransportProxy
527 // then to the BaseSession.
528 transport->SignalConnecting.connect(
529 this, &BaseSession::OnTransportConnecting);
530 transport->SignalWritableState.connect(
531 this, &BaseSession::OnTransportWritable);
532 transport->SignalRequestSignaling.connect(
533 this, &BaseSession::OnTransportRequestSignaling);
534 transport->SignalTransportError.connect(
535 this, &BaseSession::OnTransportSendError);
536 transport->SignalRouteChange.connect(
537 this, &BaseSession::OnTransportRouteChange);
538 transport->SignalCandidatesAllocationDone.connect(
539 this, &BaseSession::OnTransportCandidatesAllocationDone);
540 transport->SignalRoleConflict.connect(
541 this, &BaseSession::OnRoleConflict);
mallinath@webrtc.org385857d2014-02-14 00:56:12 +0000542 transport->SignalCompleted.connect(
543 this, &BaseSession::OnTransportCompleted);
544 transport->SignalFailed.connect(
545 this, &BaseSession::OnTransportFailed);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000546
mallinath@webrtc.orga27be8e2013-09-27 23:04:10 +0000547 transproxy = new TransportProxy(worker_thread_, sid_, content_name,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000548 new TransportWrapper(transport));
549 transproxy->SignalCandidatesReady.connect(
550 this, &BaseSession::OnTransportProxyCandidatesReady);
henrike@webrtc.org10bd88e2014-03-11 21:07:25 +0000551 if (identity_)
552 transproxy->SetIdentity(identity_);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000553 transports_[content_name] = transproxy;
554
555 return transproxy;
556}
557
558Transport* BaseSession::GetTransport(const std::string& content_name) {
559 TransportProxy* transproxy = GetTransportProxy(content_name);
560 if (transproxy == NULL)
561 return NULL;
562 return transproxy->impl();
563}
564
565TransportProxy* BaseSession::GetTransportProxy(
566 const std::string& content_name) {
567 TransportMap::iterator iter = transports_.find(content_name);
568 return (iter != transports_.end()) ? iter->second : NULL;
569}
570
571TransportProxy* BaseSession::GetTransportProxy(const Transport* transport) {
572 for (TransportMap::iterator iter = transports_.begin();
573 iter != transports_.end(); ++iter) {
574 TransportProxy* transproxy = iter->second;
575 if (transproxy->impl() == transport) {
576 return transproxy;
577 }
578 }
579 return NULL;
580}
581
582TransportProxy* BaseSession::GetFirstTransportProxy() {
583 if (transports_.empty())
584 return NULL;
585 return transports_.begin()->second;
586}
587
588void BaseSession::DestroyTransportProxy(
589 const std::string& content_name) {
590 TransportMap::iterator iter = transports_.find(content_name);
591 if (iter != transports_.end()) {
592 delete iter->second;
593 transports_.erase(content_name);
594 }
595}
596
597cricket::Transport* BaseSession::CreateTransport(
598 const std::string& content_name) {
599 ASSERT(transport_type_ == NS_GINGLE_P2P);
600 return new cricket::DtlsTransport<P2PTransport>(
601 signaling_thread(), worker_thread(), content_name,
602 port_allocator(), identity_);
603}
604
605bool BaseSession::GetStats(SessionStats* stats) {
606 for (TransportMap::iterator iter = transports_.begin();
607 iter != transports_.end(); ++iter) {
608 std::string proxy_id = iter->second->content_name();
609 // We are ignoring not-yet-instantiated transports.
610 if (iter->second->impl()) {
611 std::string transport_id = iter->second->impl()->content_name();
612 stats->proxy_to_transport[proxy_id] = transport_id;
613 if (stats->transport_stats.find(transport_id)
614 == stats->transport_stats.end()) {
615 TransportStats subinfos;
616 if (!iter->second->impl()->GetStats(&subinfos)) {
617 return false;
618 }
619 stats->transport_stats[transport_id] = subinfos;
620 }
621 }
622 }
623 return true;
624}
625
626void BaseSession::SetState(State state) {
627 ASSERT(signaling_thread_->IsCurrent());
628 if (state != state_) {
629 LogState(state_, state);
630 state_ = state;
631 SignalState(this, state_);
632 signaling_thread_->Post(this, MSG_STATE);
633 }
634 SignalNewDescription();
635}
636
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000637void BaseSession::SetError(Error error, const std::string& error_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000638 ASSERT(signaling_thread_->IsCurrent());
639 if (error != error_) {
640 error_ = error;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000641 error_desc_ = error_desc;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000642 SignalError(this, error);
643 }
644}
645
646void BaseSession::OnSignalingReady() {
647 ASSERT(signaling_thread()->IsCurrent());
648 for (TransportMap::iterator iter = transports_.begin();
649 iter != transports_.end(); ++iter) {
650 iter->second->OnSignalingReady();
651 }
652}
653
654// TODO(juberti): Since PushdownLocalTD now triggers the connection process to
655// start, remove this method once everyone calls PushdownLocalTD.
656void BaseSession::SpeculativelyConnectAllTransportChannels() {
657 // Put all transports into the connecting state.
658 for (TransportMap::iterator iter = transports_.begin();
659 iter != transports_.end(); ++iter) {
660 iter->second->ConnectChannels();
661 }
662}
663
664bool BaseSession::OnRemoteCandidates(const std::string& content_name,
665 const Candidates& candidates,
666 std::string* error) {
667 // Give candidates to the appropriate transport, and tell that transport
668 // to start connecting, if it's not already doing so.
669 TransportProxy* transproxy = GetTransportProxy(content_name);
670 if (!transproxy) {
671 *error = "Unknown content name " + content_name;
672 return false;
673 }
674 if (!transproxy->OnRemoteCandidates(candidates, error)) {
675 return false;
676 }
677 // TODO(juberti): Remove this call once we can be sure that we always have
678 // a local transport description (which will trigger the connection).
679 transproxy->ConnectChannels();
680 return true;
681}
682
683bool BaseSession::MaybeEnableMuxingSupport() {
684 // We need both a local and remote description to decide if we should mux.
685 if ((state_ == STATE_SENTINITIATE ||
686 state_ == STATE_RECEIVEDINITIATE) &&
687 ((local_description_ == NULL) ||
688 (remote_description_ == NULL))) {
689 return false;
690 }
691
692 // In order to perform the multiplexing, we need all proxies to be in the
693 // negotiated state, i.e. to have implementations underneath.
694 // Ensure that this is the case, regardless of whether we are going to mux.
695 for (TransportMap::iterator iter = transports_.begin();
696 iter != transports_.end(); ++iter) {
697 ASSERT(iter->second->negotiated());
698 if (!iter->second->negotiated())
699 return false;
700 }
701
702 // If both sides agree to BUNDLE, mux all the specified contents onto the
703 // transport belonging to the first content name in the BUNDLE group.
704 // If the contents are already muxed, this will be a no-op.
705 // TODO(juberti): Should this check that local and remote have configured
706 // BUNDLE the same way?
707 bool candidates_allocated = IsCandidateAllocationDone();
708 const ContentGroup* local_bundle_group =
709 local_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
710 const ContentGroup* remote_bundle_group =
711 remote_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
712 if (local_bundle_group && remote_bundle_group &&
713 local_bundle_group->FirstContentName()) {
714 const std::string* content_name = local_bundle_group->FirstContentName();
715 const ContentInfo* content =
716 local_description_->GetContentByName(*content_name);
717 ASSERT(content != NULL);
718 if (!SetSelectedProxy(content->name, local_bundle_group)) {
719 LOG(LS_WARNING) << "Failed to set up BUNDLE";
720 return false;
721 }
722
723 // If we weren't done gathering before, we might be done now, as a result
724 // of enabling mux.
725 LOG(LS_INFO) << "Enabling BUNDLE, bundling onto transport: "
726 << *content_name;
727 if (!candidates_allocated) {
728 MaybeCandidateAllocationDone();
729 }
730 } else {
731 LOG(LS_INFO) << "No BUNDLE information, not bundling.";
732 }
733 return true;
734}
735
736bool BaseSession::SetSelectedProxy(const std::string& content_name,
737 const ContentGroup* muxed_group) {
738 TransportProxy* selected_proxy = GetTransportProxy(content_name);
739 if (!selected_proxy) {
740 return false;
741 }
742
743 ASSERT(selected_proxy->negotiated());
744 for (TransportMap::iterator iter = transports_.begin();
745 iter != transports_.end(); ++iter) {
746 // If content is part of the mux group, then repoint its proxy at the
747 // transport object that we have chosen to mux onto. If the proxy
748 // is already pointing at the right object, it will be a no-op.
749 if (muxed_group->HasContentName(iter->first) &&
750 !iter->second->SetupMux(selected_proxy)) {
751 return false;
752 }
753 }
754 return true;
755}
756
757void BaseSession::OnTransportCandidatesAllocationDone(Transport* transport) {
758 // TODO(juberti): This is a clunky way of processing the done signal. Instead,
759 // TransportProxy should receive the done signal directly, set its allocated
760 // flag internally, and then reissue the done signal to Session.
761 // Overall we should make TransportProxy receive *all* the signals from
762 // Transport, since this removes the need to manually iterate over all
763 // the transports, as is needed to make sure signals are handled properly
764 // when BUNDLEing.
henrike@webrtc.org704bf9e2014-02-27 17:52:04 +0000765 // TODO(juberti): Per b/7998978, devs and QA are hitting this assert in ways
766 // that make it prohibitively difficult to run dbg builds. Disabled for now.
767 //ASSERT(!IsCandidateAllocationDone());
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000768 for (TransportMap::iterator iter = transports_.begin();
769 iter != transports_.end(); ++iter) {
770 if (iter->second->impl() == transport) {
771 iter->second->set_candidates_allocated(true);
772 }
773 }
774 MaybeCandidateAllocationDone();
775}
776
777bool BaseSession::IsCandidateAllocationDone() const {
778 for (TransportMap::const_iterator iter = transports_.begin();
779 iter != transports_.end(); ++iter) {
780 if (!iter->second->candidates_allocated())
781 return false;
782 }
783 return true;
784}
785
786void BaseSession::MaybeCandidateAllocationDone() {
787 if (IsCandidateAllocationDone()) {
788 LOG(LS_INFO) << "Candidate gathering is complete.";
789 OnCandidatesAllocationDone();
790 }
791}
792
793void BaseSession::OnRoleConflict() {
794 if (role_switch_) {
795 LOG(LS_WARNING) << "Repeat of role conflict signal from Transport.";
796 return;
797 }
798
799 role_switch_ = true;
800 for (TransportMap::iterator iter = transports_.begin();
801 iter != transports_.end(); ++iter) {
802 // Role will be reverse of initial role setting.
mallinath@webrtc.orga5506692013-08-12 21:18:15 +0000803 IceRole role = initiator_ ? ICEROLE_CONTROLLED : ICEROLE_CONTROLLING;
804 iter->second->SetIceRole(role);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000805 }
806}
807
808void BaseSession::LogState(State old_state, State new_state) {
809 LOG(LS_INFO) << "Session:" << id()
810 << " Old state:" << StateToString(old_state)
811 << " New state:" << StateToString(new_state)
812 << " Type:" << content_type()
813 << " Transport:" << transport_type();
814}
815
816bool BaseSession::GetTransportDescription(const SessionDescription* description,
817 const std::string& content_name,
818 TransportDescription* tdesc) {
819 if (!description || !tdesc) {
820 return false;
821 }
822 const TransportInfo* transport_info =
823 description->GetTransportInfoByName(content_name);
824 if (!transport_info) {
825 return false;
826 }
827 *tdesc = transport_info->description;
828 return true;
829}
830
831void BaseSession::SignalNewDescription() {
832 ContentAction action;
833 ContentSource source;
834 if (!GetContentAction(&action, &source)) {
835 return;
836 }
837 if (source == CS_LOCAL) {
838 SignalNewLocalDescription(this, action);
839 } else {
840 SignalNewRemoteDescription(this, action);
841 }
842}
843
844bool BaseSession::GetContentAction(ContentAction* action,
845 ContentSource* source) {
846 switch (state_) {
847 // new local description
848 case STATE_SENTINITIATE:
849 *action = CA_OFFER;
850 *source = CS_LOCAL;
851 break;
852 case STATE_SENTPRACCEPT:
853 *action = CA_PRANSWER;
854 *source = CS_LOCAL;
855 break;
856 case STATE_SENTACCEPT:
857 *action = CA_ANSWER;
858 *source = CS_LOCAL;
859 break;
860 // new remote description
861 case STATE_RECEIVEDINITIATE:
862 *action = CA_OFFER;
863 *source = CS_REMOTE;
864 break;
865 case STATE_RECEIVEDPRACCEPT:
866 *action = CA_PRANSWER;
867 *source = CS_REMOTE;
868 break;
869 case STATE_RECEIVEDACCEPT:
870 *action = CA_ANSWER;
871 *source = CS_REMOTE;
872 break;
873 default:
874 return false;
875 }
876 return true;
877}
878
879void BaseSession::OnMessage(talk_base::Message *pmsg) {
880 switch (pmsg->message_id) {
881 case MSG_TIMEOUT:
882 // Session timeout has occured.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000883 SetError(ERROR_TIME, "Session timeout has occured.");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000884 break;
885
886 case MSG_STATE:
887 switch (state_) {
888 case STATE_SENTACCEPT:
889 case STATE_RECEIVEDACCEPT:
890 SetState(STATE_INPROGRESS);
891 break;
892
893 default:
894 // Explicitly ignoring some states here.
895 break;
896 }
897 break;
898 }
899}
900
901Session::Session(SessionManager* session_manager,
902 const std::string& local_name,
903 const std::string& initiator_name,
904 const std::string& sid,
905 const std::string& content_type,
906 SessionClient* client)
907 : BaseSession(session_manager->signaling_thread(),
908 session_manager->worker_thread(),
909 session_manager->port_allocator(),
910 sid, content_type, initiator_name == local_name) {
911 ASSERT(client != NULL);
912 session_manager_ = session_manager;
913 local_name_ = local_name;
914 initiator_name_ = initiator_name;
915 transport_parser_ = new P2PTransportParser();
916 client_ = client;
917 initiate_acked_ = false;
918 current_protocol_ = PROTOCOL_HYBRID;
919}
920
921Session::~Session() {
922 delete transport_parser_;
923}
924
925bool Session::Initiate(const std::string &to,
926 const SessionDescription* sdesc) {
927 ASSERT(signaling_thread()->IsCurrent());
928 SessionError error;
929
930 // Only from STATE_INIT
931 if (state() != STATE_INIT)
932 return false;
933
934 // Setup for signaling.
935 set_remote_name(to);
936 set_local_description(sdesc);
937 if (!CreateTransportProxies(GetEmptyTransportInfos(sdesc->contents()),
938 &error)) {
939 LOG(LS_ERROR) << "Could not create transports: " << error.text;
940 return false;
941 }
942
943 if (!SendInitiateMessage(sdesc, &error)) {
944 LOG(LS_ERROR) << "Could not send initiate message: " << error.text;
945 return false;
946 }
947
948 // We need to connect transport proxy and impl here so that we can process
949 // the TransportDescriptions.
950 SpeculativelyConnectAllTransportChannels();
951
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000952 PushdownTransportDescription(CS_LOCAL, CA_OFFER, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000953 SetState(Session::STATE_SENTINITIATE);
954 return true;
955}
956
957bool Session::Accept(const SessionDescription* sdesc) {
958 ASSERT(signaling_thread()->IsCurrent());
959
960 // Only if just received initiate
961 if (state() != STATE_RECEIVEDINITIATE)
962 return false;
963
964 // Setup for signaling.
965 set_local_description(sdesc);
966
967 SessionError error;
968 if (!SendAcceptMessage(sdesc, &error)) {
969 LOG(LS_ERROR) << "Could not send accept message: " << error.text;
970 return false;
971 }
972 // TODO(juberti): Add BUNDLE support to transport-info messages.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000973 PushdownTransportDescription(CS_LOCAL, CA_ANSWER, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000974 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported.
975 SetState(Session::STATE_SENTACCEPT);
976 return true;
977}
978
979bool Session::Reject(const std::string& reason) {
980 ASSERT(signaling_thread()->IsCurrent());
981
982 // Reject is sent in response to an initiate or modify, to reject the
983 // request
984 if (state() != STATE_RECEIVEDINITIATE && state() != STATE_RECEIVEDMODIFY)
985 return false;
986
987 SessionError error;
988 if (!SendRejectMessage(reason, &error)) {
989 LOG(LS_ERROR) << "Could not send reject message: " << error.text;
990 return false;
991 }
992
993 SetState(STATE_SENTREJECT);
994 return true;
995}
996
997bool Session::TerminateWithReason(const std::string& reason) {
998 ASSERT(signaling_thread()->IsCurrent());
999
1000 // Either side can terminate, at any time.
1001 switch (state()) {
1002 case STATE_SENTTERMINATE:
1003 case STATE_RECEIVEDTERMINATE:
1004 return false;
1005
1006 case STATE_SENTREJECT:
1007 case STATE_RECEIVEDREJECT:
1008 // We don't need to send terminate if we sent or received a reject...
1009 // it's implicit.
1010 break;
1011
1012 default:
1013 SessionError error;
1014 if (!SendTerminateMessage(reason, &error)) {
1015 LOG(LS_ERROR) << "Could not send terminate message: " << error.text;
1016 return false;
1017 }
1018 break;
1019 }
1020
1021 SetState(STATE_SENTTERMINATE);
1022 return true;
1023}
1024
wu@webrtc.org364f2042013-11-20 21:49:41 +00001025bool Session::SendInfoMessage(const XmlElements& elems,
1026 const std::string& remote_name) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001027 ASSERT(signaling_thread()->IsCurrent());
1028 SessionError error;
wu@webrtc.org364f2042013-11-20 21:49:41 +00001029 if (!SendMessage(ACTION_SESSION_INFO, elems, remote_name, &error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001030 LOG(LS_ERROR) << "Could not send info message " << error.text;
1031 return false;
1032 }
1033 return true;
1034}
1035
1036bool Session::SendDescriptionInfoMessage(const ContentInfos& contents) {
1037 XmlElements elems;
1038 WriteError write_error;
1039 if (!WriteDescriptionInfo(current_protocol_,
1040 contents,
1041 GetContentParsers(),
1042 &elems, &write_error)) {
1043 LOG(LS_ERROR) << "Could not write description info message: "
1044 << write_error.text;
1045 return false;
1046 }
1047 SessionError error;
1048 if (!SendMessage(ACTION_DESCRIPTION_INFO, elems, &error)) {
1049 LOG(LS_ERROR) << "Could not send description info message: "
1050 << error.text;
1051 return false;
1052 }
1053 return true;
1054}
1055
1056TransportInfos Session::GetEmptyTransportInfos(
1057 const ContentInfos& contents) const {
1058 TransportInfos tinfos;
1059 for (ContentInfos::const_iterator content = contents.begin();
1060 content != contents.end(); ++content) {
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001061 tinfos.push_back(TransportInfo(content->name,
1062 TransportDescription(transport_type(),
1063 std::string(),
1064 std::string())));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001065 }
1066 return tinfos;
1067}
1068
1069bool Session::OnRemoteCandidates(
1070 const TransportInfos& tinfos, ParseError* error) {
1071 for (TransportInfos::const_iterator tinfo = tinfos.begin();
1072 tinfo != tinfos.end(); ++tinfo) {
1073 std::string str_error;
1074 if (!BaseSession::OnRemoteCandidates(
1075 tinfo->content_name, tinfo->description.candidates, &str_error)) {
1076 return BadParse(str_error, error);
1077 }
1078 }
1079 return true;
1080}
1081
1082bool Session::CreateTransportProxies(const TransportInfos& tinfos,
1083 SessionError* error) {
1084 for (TransportInfos::const_iterator tinfo = tinfos.begin();
1085 tinfo != tinfos.end(); ++tinfo) {
1086 if (tinfo->description.transport_type != transport_type()) {
1087 error->SetText("No supported transport in offer.");
1088 return false;
1089 }
1090
1091 GetOrCreateTransportProxy(tinfo->content_name);
1092 }
1093 return true;
1094}
1095
1096TransportParserMap Session::GetTransportParsers() {
1097 TransportParserMap parsers;
1098 parsers[transport_type()] = transport_parser_;
1099 return parsers;
1100}
1101
1102CandidateTranslatorMap Session::GetCandidateTranslators() {
1103 CandidateTranslatorMap translators;
1104 // NOTE: This technique makes it impossible to parse G-ICE
1105 // candidates in session-initiate messages because the channels
1106 // aren't yet created at that point. Since we don't use candidates
1107 // in session-initiate messages, we should be OK. Once we switch to
1108 // ICE, this translation shouldn't be necessary.
1109 for (TransportMap::const_iterator iter = transport_proxies().begin();
1110 iter != transport_proxies().end(); ++iter) {
1111 translators[iter->first] = iter->second;
1112 }
1113 return translators;
1114}
1115
1116ContentParserMap Session::GetContentParsers() {
1117 ContentParserMap parsers;
1118 parsers[content_type()] = client_;
1119 // We need to be able parse both RTP-based and SCTP-based Jingle
1120 // with the same client.
1121 if (content_type() == NS_JINGLE_RTP) {
1122 parsers[NS_JINGLE_DRAFT_SCTP] = client_;
1123 }
1124 return parsers;
1125}
1126
1127void Session::OnTransportRequestSignaling(Transport* transport) {
1128 ASSERT(signaling_thread()->IsCurrent());
1129 TransportProxy* transproxy = GetTransportProxy(transport);
1130 ASSERT(transproxy != NULL);
1131 if (transproxy) {
1132 // Reset candidate allocation status for the transport proxy.
1133 transproxy->set_candidates_allocated(false);
1134 }
1135 SignalRequestSignaling(this);
1136}
1137
1138void Session::OnTransportConnecting(Transport* transport) {
1139 // This is an indication that we should begin watching the writability
1140 // state of the transport.
1141 OnTransportWritable(transport);
1142}
1143
1144void Session::OnTransportWritable(Transport* transport) {
1145 ASSERT(signaling_thread()->IsCurrent());
1146
1147 // If the transport is not writable, start a timer to make sure that it
1148 // becomes writable within a reasonable amount of time. If it does not, we
1149 // terminate since we can't actually send data. If the transport is writable,
1150 // cancel the timer. Note that writability transitions may occur repeatedly
1151 // during the lifetime of the session.
1152 signaling_thread()->Clear(this, MSG_TIMEOUT);
1153 if (transport->HasChannels() && !transport->writable()) {
1154 signaling_thread()->PostDelayed(
1155 session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
1156 }
1157}
1158
1159void Session::OnTransportProxyCandidatesReady(TransportProxy* transproxy,
1160 const Candidates& candidates) {
1161 ASSERT(signaling_thread()->IsCurrent());
1162 if (transproxy != NULL) {
1163 if (initiator() && !initiate_acked_) {
1164 // TODO: This is to work around server re-ordering
1165 // messages. We send the candidates once the session-initiate
1166 // is acked. Once we have fixed the server to guarantee message
1167 // order, we can remove this case.
1168 transproxy->AddUnsentCandidates(candidates);
1169 } else {
1170 if (!transproxy->negotiated()) {
1171 transproxy->AddSentCandidates(candidates);
1172 }
1173 SessionError error;
1174 if (!SendTransportInfoMessage(transproxy, candidates, &error)) {
1175 LOG(LS_ERROR) << "Could not send transport info message: "
1176 << error.text;
1177 return;
1178 }
1179 }
1180 }
1181}
1182
1183void Session::OnTransportSendError(Transport* transport,
1184 const buzz::XmlElement* stanza,
1185 const buzz::QName& name,
1186 const std::string& type,
1187 const std::string& text,
1188 const buzz::XmlElement* extra_info) {
1189 ASSERT(signaling_thread()->IsCurrent());
1190 SignalErrorMessage(this, stanza, name, type, text, extra_info);
1191}
1192
1193void Session::OnIncomingMessage(const SessionMessage& msg) {
1194 ASSERT(signaling_thread()->IsCurrent());
1195 ASSERT(state() == STATE_INIT || msg.from == remote_name());
1196
1197 if (current_protocol_== PROTOCOL_HYBRID) {
1198 if (msg.protocol == PROTOCOL_GINGLE) {
1199 current_protocol_ = PROTOCOL_GINGLE;
1200 } else {
1201 current_protocol_ = PROTOCOL_JINGLE;
1202 }
1203 }
1204
1205 bool valid = false;
1206 MessageError error;
1207 switch (msg.type) {
1208 case ACTION_SESSION_INITIATE:
1209 valid = OnInitiateMessage(msg, &error);
1210 break;
1211 case ACTION_SESSION_INFO:
1212 valid = OnInfoMessage(msg);
1213 break;
1214 case ACTION_SESSION_ACCEPT:
1215 valid = OnAcceptMessage(msg, &error);
1216 break;
1217 case ACTION_SESSION_REJECT:
1218 valid = OnRejectMessage(msg, &error);
1219 break;
1220 case ACTION_SESSION_TERMINATE:
1221 valid = OnTerminateMessage(msg, &error);
1222 break;
1223 case ACTION_TRANSPORT_INFO:
1224 valid = OnTransportInfoMessage(msg, &error);
1225 break;
1226 case ACTION_TRANSPORT_ACCEPT:
1227 valid = OnTransportAcceptMessage(msg, &error);
1228 break;
1229 case ACTION_DESCRIPTION_INFO:
1230 valid = OnDescriptionInfoMessage(msg, &error);
1231 break;
1232 default:
1233 valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST,
1234 "unknown session message type",
1235 &error);
1236 }
1237
1238 if (valid) {
1239 SendAcknowledgementMessage(msg.stanza);
1240 } else {
1241 SignalErrorMessage(this, msg.stanza, error.type,
1242 "modify", error.text, NULL);
1243 }
1244}
1245
1246void Session::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
1247 const buzz::XmlElement* response_stanza,
1248 const SessionMessage& msg) {
1249 ASSERT(signaling_thread()->IsCurrent());
1250
1251 if (msg.type == ACTION_SESSION_INITIATE) {
1252 OnInitiateAcked();
1253 }
1254}
1255
1256void Session::OnInitiateAcked() {
1257 // TODO: This is to work around server re-ordering
1258 // messages. We send the candidates once the session-initiate
1259 // is acked. Once we have fixed the server to guarantee message
1260 // order, we can remove this case.
1261 if (!initiate_acked_) {
1262 initiate_acked_ = true;
1263 SessionError error;
1264 SendAllUnsentTransportInfoMessages(&error);
1265 }
1266}
1267
1268void Session::OnFailedSend(const buzz::XmlElement* orig_stanza,
1269 const buzz::XmlElement* error_stanza) {
1270 ASSERT(signaling_thread()->IsCurrent());
1271
1272 SessionMessage msg;
1273 ParseError parse_error;
1274 if (!ParseSessionMessage(orig_stanza, &msg, &parse_error)) {
1275 LOG(LS_ERROR) << "Error parsing failed send: " << parse_error.text
1276 << ":" << orig_stanza;
1277 return;
1278 }
1279
1280 // If the error is a session redirect, call OnRedirectError, which will
1281 // continue the session with a new remote JID.
1282 SessionRedirect redirect;
1283 if (FindSessionRedirect(error_stanza, &redirect)) {
1284 SessionError error;
1285 if (!OnRedirectError(redirect, &error)) {
1286 // TODO: Should we send a message back? The standard
1287 // says nothing about it.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001288 std::ostringstream desc;
1289 desc << "Failed to redirect: " << error.text;
1290 LOG(LS_ERROR) << desc.str();
1291 SetError(ERROR_RESPONSE, desc.str());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001292 }
1293 return;
1294 }
1295
1296 std::string error_type = "cancel";
1297
1298 const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR);
1299 if (error) {
1300 error_type = error->Attr(buzz::QN_TYPE);
1301
1302 LOG(LS_ERROR) << "Session error:\n" << error->Str() << "\n"
1303 << "in response to:\n" << orig_stanza->Str();
1304 } else {
1305 // don't crash if <error> is missing
1306 LOG(LS_ERROR) << "Session error without <error/> element, ignoring";
1307 return;
1308 }
1309
1310 if (msg.type == ACTION_TRANSPORT_INFO) {
1311 // Transport messages frequently generate errors because they are sent right
1312 // when we detect a network failure. For that reason, we ignore such
1313 // errors, because if we do not establish writability again, we will
1314 // terminate anyway. The exceptions are transport-specific error tags,
1315 // which we pass on to the respective transport.
1316 } else if ((error_type != "continue") && (error_type != "wait")) {
1317 // We do not set an error if the other side said it is okay to continue
1318 // (possibly after waiting). These errors can be ignored.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001319 SetError(ERROR_RESPONSE, "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001320 }
1321}
1322
1323bool Session::OnInitiateMessage(const SessionMessage& msg,
1324 MessageError* error) {
1325 if (!CheckState(STATE_INIT, error))
1326 return false;
1327
1328 SessionInitiate init;
1329 if (!ParseSessionInitiate(msg.protocol, msg.action_elem,
1330 GetContentParsers(), GetTransportParsers(),
1331 GetCandidateTranslators(),
1332 &init, error))
1333 return false;
1334
1335 SessionError session_error;
1336 if (!CreateTransportProxies(init.transports, &session_error)) {
1337 return BadMessage(buzz::QN_STANZA_NOT_ACCEPTABLE,
1338 session_error.text, error);
1339 }
1340
1341 set_remote_name(msg.from);
1342 set_initiator_name(msg.initiator);
1343 set_remote_description(new SessionDescription(init.ClearContents(),
1344 init.transports,
1345 init.groups));
1346 // Updating transport with TransportDescription.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001347 PushdownTransportDescription(CS_REMOTE, CA_OFFER, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001348 SetState(STATE_RECEIVEDINITIATE);
1349
1350 // Users of Session may listen to state change and call Reject().
1351 if (state() != STATE_SENTREJECT) {
1352 if (!OnRemoteCandidates(init.transports, error))
1353 return false;
1354
1355 // TODO(juberti): Auto-generate and push down the local transport answer.
1356 // This is necessary for trickling to work with RFC 5245 ICE.
1357 }
1358 return true;
1359}
1360
1361bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) {
1362 if (!CheckState(STATE_SENTINITIATE, error))
1363 return false;
1364
1365 SessionAccept accept;
1366 if (!ParseSessionAccept(msg.protocol, msg.action_elem,
1367 GetContentParsers(), GetTransportParsers(),
1368 GetCandidateTranslators(),
1369 &accept, error)) {
1370 return false;
1371 }
1372
1373 // If we get an accept, we can assume the initiate has been
1374 // received, even if we haven't gotten an IQ response.
1375 OnInitiateAcked();
1376
1377 set_remote_description(new SessionDescription(accept.ClearContents(),
1378 accept.transports,
1379 accept.groups));
1380 // Updating transport with TransportDescription.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001381 PushdownTransportDescription(CS_REMOTE, CA_ANSWER, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001382 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported.
1383 SetState(STATE_RECEIVEDACCEPT);
1384
1385 if (!OnRemoteCandidates(accept.transports, error))
1386 return false;
1387
1388 return true;
1389}
1390
1391bool Session::OnRejectMessage(const SessionMessage& msg, MessageError* error) {
1392 if (!CheckState(STATE_SENTINITIATE, error))
1393 return false;
1394
1395 SetState(STATE_RECEIVEDREJECT);
1396 return true;
1397}
1398
1399bool Session::OnInfoMessage(const SessionMessage& msg) {
1400 SignalInfoMessage(this, msg.action_elem);
1401 return true;
1402}
1403
1404bool Session::OnTerminateMessage(const SessionMessage& msg,
1405 MessageError* error) {
1406 SessionTerminate term;
1407 if (!ParseSessionTerminate(msg.protocol, msg.action_elem, &term, error))
1408 return false;
1409
1410 SignalReceivedTerminateReason(this, term.reason);
1411 if (term.debug_reason != buzz::STR_EMPTY) {
1412 LOG(LS_VERBOSE) << "Received error on call: " << term.debug_reason;
1413 }
1414
1415 SetState(STATE_RECEIVEDTERMINATE);
1416 return true;
1417}
1418
1419bool Session::OnTransportInfoMessage(const SessionMessage& msg,
1420 MessageError* error) {
1421 TransportInfos tinfos;
1422 if (!ParseTransportInfos(msg.protocol, msg.action_elem,
1423 initiator_description()->contents(),
1424 GetTransportParsers(), GetCandidateTranslators(),
1425 &tinfos, error))
1426 return false;
1427
1428 if (!OnRemoteCandidates(tinfos, error))
1429 return false;
1430
1431 return true;
1432}
1433
1434bool Session::OnTransportAcceptMessage(const SessionMessage& msg,
1435 MessageError* error) {
1436 // TODO: Currently here only for compatibility with
1437 // Gingle 1.1 clients (notably, Google Voice).
1438 return true;
1439}
1440
1441bool Session::OnDescriptionInfoMessage(const SessionMessage& msg,
1442 MessageError* error) {
1443 if (!CheckState(STATE_INPROGRESS, error))
1444 return false;
1445
1446 DescriptionInfo description_info;
1447 if (!ParseDescriptionInfo(msg.protocol, msg.action_elem,
1448 GetContentParsers(), GetTransportParsers(),
1449 GetCandidateTranslators(),
1450 &description_info, error)) {
1451 return false;
1452 }
1453
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001454 ContentInfos& updated_contents = description_info.contents;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001455
1456 // TODO: Currently, reflector sends back
1457 // video stream updates even for an audio-only call, which causes
1458 // this to fail. Put this back once reflector is fixed.
1459 //
1460 // ContentInfos::iterator it;
1461 // First, ensure all updates are valid before modifying remote_description_.
1462 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
1463 // if (remote_description()->GetContentByName(it->name) == NULL) {
1464 // return false;
1465 // }
1466 // }
1467
1468 // TODO: We used to replace contents from an update, but
1469 // that no longer works with partial updates. We need to figure out
1470 // a way to merge patial updates into contents. For now, users of
1471 // Session should listen to SignalRemoteDescriptionUpdate and handle
1472 // updates. They should not expect remote_description to be the
1473 // latest value.
1474 //
1475 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
1476 // remote_description()->RemoveContentByName(it->name);
1477 // remote_description()->AddContent(it->name, it->type, it->description);
1478 // }
1479 // }
1480
1481 SignalRemoteDescriptionUpdate(this, updated_contents);
1482 return true;
1483}
1484
1485bool BareJidsEqual(const std::string& name1,
1486 const std::string& name2) {
1487 buzz::Jid jid1(name1);
1488 buzz::Jid jid2(name2);
1489
1490 return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2);
1491}
1492
1493bool Session::OnRedirectError(const SessionRedirect& redirect,
1494 SessionError* error) {
1495 MessageError message_error;
1496 if (!CheckState(STATE_SENTINITIATE, &message_error)) {
1497 return BadWrite(message_error.text, error);
1498 }
1499
1500 if (!BareJidsEqual(remote_name(), redirect.target))
1501 return BadWrite("Redirection not allowed: must be the same bare jid.",
1502 error);
1503
1504 // When we receive a redirect, we point the session at the new JID
1505 // and resend the candidates.
1506 set_remote_name(redirect.target);
1507 return (SendInitiateMessage(local_description(), error) &&
1508 ResendAllTransportInfoMessages(error));
1509}
1510
1511bool Session::CheckState(State expected, MessageError* error) {
1512 if (state() != expected) {
1513 // The server can deliver messages out of order/repeated for various
1514 // reasons. For example, if the server does not recive our iq response,
1515 // it could assume that the iq it sent was lost, and will then send
1516 // it again. Ideally, we should implement reliable messaging with
1517 // duplicate elimination.
1518 return BadMessage(buzz::QN_STANZA_NOT_ALLOWED,
1519 "message not allowed in current state",
1520 error);
1521 }
1522 return true;
1523}
1524
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001525void Session::SetError(Error error, const std::string& error_desc) {
1526 BaseSession::SetError(error, error_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001527 if (error != ERROR_NONE)
1528 signaling_thread()->Post(this, MSG_ERROR);
1529}
1530
1531void Session::OnMessage(talk_base::Message* pmsg) {
1532 // preserve this because BaseSession::OnMessage may modify it
1533 State orig_state = state();
1534
1535 BaseSession::OnMessage(pmsg);
1536
1537 switch (pmsg->message_id) {
1538 case MSG_ERROR:
1539 TerminateWithReason(STR_TERMINATE_ERROR);
1540 break;
1541
1542 case MSG_STATE:
1543 switch (orig_state) {
1544 case STATE_SENTREJECT:
1545 case STATE_RECEIVEDREJECT:
1546 // Assume clean termination.
1547 Terminate();
1548 break;
1549
1550 case STATE_SENTTERMINATE:
1551 case STATE_RECEIVEDTERMINATE:
1552 session_manager_->DestroySession(this);
1553 break;
1554
1555 default:
1556 // Explicitly ignoring some states here.
1557 break;
1558 }
1559 break;
1560 }
1561}
1562
1563bool Session::SendInitiateMessage(const SessionDescription* sdesc,
1564 SessionError* error) {
1565 SessionInitiate init;
1566 init.contents = sdesc->contents();
1567 init.transports = GetEmptyTransportInfos(init.contents);
1568 init.groups = sdesc->groups();
1569 return SendMessage(ACTION_SESSION_INITIATE, init, error);
1570}
1571
1572bool Session::WriteSessionAction(
1573 SignalingProtocol protocol, const SessionInitiate& init,
1574 XmlElements* elems, WriteError* error) {
1575 return WriteSessionInitiate(protocol, init.contents, init.transports,
1576 GetContentParsers(), GetTransportParsers(),
1577 GetCandidateTranslators(), init.groups,
1578 elems, error);
1579}
1580
1581bool Session::SendAcceptMessage(const SessionDescription* sdesc,
1582 SessionError* error) {
1583 XmlElements elems;
1584 if (!WriteSessionAccept(current_protocol_,
1585 sdesc->contents(),
1586 GetEmptyTransportInfos(sdesc->contents()),
1587 GetContentParsers(), GetTransportParsers(),
1588 GetCandidateTranslators(), sdesc->groups(),
1589 &elems, error)) {
1590 return false;
1591 }
1592 return SendMessage(ACTION_SESSION_ACCEPT, elems, error);
1593}
1594
1595bool Session::SendRejectMessage(const std::string& reason,
1596 SessionError* error) {
1597 SessionTerminate term(reason);
1598 return SendMessage(ACTION_SESSION_REJECT, term, error);
1599}
1600
1601bool Session::SendTerminateMessage(const std::string& reason,
1602 SessionError* error) {
1603 SessionTerminate term(reason);
1604 return SendMessage(ACTION_SESSION_TERMINATE, term, error);
1605}
1606
1607bool Session::WriteSessionAction(SignalingProtocol protocol,
1608 const SessionTerminate& term,
1609 XmlElements* elems, WriteError* error) {
1610 WriteSessionTerminate(protocol, term, elems);
1611 return true;
1612}
1613
1614bool Session::SendTransportInfoMessage(const TransportInfo& tinfo,
1615 SessionError* error) {
1616 return SendMessage(ACTION_TRANSPORT_INFO, tinfo, error);
1617}
1618
1619bool Session::SendTransportInfoMessage(const TransportProxy* transproxy,
1620 const Candidates& candidates,
1621 SessionError* error) {
1622 return SendTransportInfoMessage(TransportInfo(transproxy->content_name(),
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001623 TransportDescription(transproxy->type(), std::vector<std::string>(),
1624 std::string(), std::string(), ICEMODE_FULL,
1625 CONNECTIONROLE_NONE, NULL, candidates)), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001626}
1627
1628bool Session::WriteSessionAction(SignalingProtocol protocol,
1629 const TransportInfo& tinfo,
1630 XmlElements* elems, WriteError* error) {
1631 TransportInfos tinfos;
1632 tinfos.push_back(tinfo);
1633 return WriteTransportInfos(protocol, tinfos,
1634 GetTransportParsers(), GetCandidateTranslators(),
1635 elems, error);
1636}
1637
1638bool Session::ResendAllTransportInfoMessages(SessionError* error) {
1639 for (TransportMap::const_iterator iter = transport_proxies().begin();
1640 iter != transport_proxies().end(); ++iter) {
1641 TransportProxy* transproxy = iter->second;
1642 if (transproxy->sent_candidates().size() > 0) {
1643 if (!SendTransportInfoMessage(
1644 transproxy, transproxy->sent_candidates(), error)) {
1645 LOG(LS_ERROR) << "Could not resend transport info messages: "
1646 << error->text;
1647 return false;
1648 }
1649 transproxy->ClearSentCandidates();
1650 }
1651 }
1652 return true;
1653}
1654
1655bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) {
1656 for (TransportMap::const_iterator iter = transport_proxies().begin();
1657 iter != transport_proxies().end(); ++iter) {
1658 TransportProxy* transproxy = iter->second;
1659 if (transproxy->unsent_candidates().size() > 0) {
1660 if (!SendTransportInfoMessage(
1661 transproxy, transproxy->unsent_candidates(), error)) {
1662 LOG(LS_ERROR) << "Could not send unsent transport info messages: "
1663 << error->text;
1664 return false;
1665 }
1666 transproxy->ClearUnsentCandidates();
1667 }
1668 }
1669 return true;
1670}
1671
1672bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
1673 SessionError* error) {
wu@webrtc.org364f2042013-11-20 21:49:41 +00001674 return SendMessage(type, action_elems, remote_name(), error);
1675}
1676
1677bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
1678 const std::string& remote_name, SessionError* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001679 talk_base::scoped_ptr<buzz::XmlElement> stanza(
1680 new buzz::XmlElement(buzz::QN_IQ));
1681
1682 SessionMessage msg(current_protocol_, type, id(), initiator_name());
wu@webrtc.org364f2042013-11-20 21:49:41 +00001683 msg.to = remote_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001684 WriteSessionMessage(msg, action_elems, stanza.get());
1685
1686 SignalOutgoingMessage(this, stanza.get());
1687 return true;
1688}
1689
1690template <typename Action>
1691bool Session::SendMessage(ActionType type, const Action& action,
1692 SessionError* error) {
1693 talk_base::scoped_ptr<buzz::XmlElement> stanza(
1694 new buzz::XmlElement(buzz::QN_IQ));
1695 if (!WriteActionMessage(type, action, stanza.get(), error))
1696 return false;
1697
1698 SignalOutgoingMessage(this, stanza.get());
1699 return true;
1700}
1701
1702template <typename Action>
1703bool Session::WriteActionMessage(ActionType type, const Action& action,
1704 buzz::XmlElement* stanza,
1705 WriteError* error) {
1706 if (current_protocol_ == PROTOCOL_HYBRID) {
1707 if (!WriteActionMessage(PROTOCOL_JINGLE, type, action, stanza, error))
1708 return false;
1709 if (!WriteActionMessage(PROTOCOL_GINGLE, type, action, stanza, error))
1710 return false;
1711 } else {
1712 if (!WriteActionMessage(current_protocol_, type, action, stanza, error))
1713 return false;
1714 }
1715 return true;
1716}
1717
1718template <typename Action>
1719bool Session::WriteActionMessage(SignalingProtocol protocol,
1720 ActionType type, const Action& action,
1721 buzz::XmlElement* stanza, WriteError* error) {
1722 XmlElements action_elems;
1723 if (!WriteSessionAction(protocol, action, &action_elems, error))
1724 return false;
1725
1726 SessionMessage msg(protocol, type, id(), initiator_name());
1727 msg.to = remote_name();
1728
1729 WriteSessionMessage(msg, action_elems, stanza);
1730 return true;
1731}
1732
1733void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) {
1734 talk_base::scoped_ptr<buzz::XmlElement> ack(
1735 new buzz::XmlElement(buzz::QN_IQ));
1736 ack->SetAttr(buzz::QN_TO, remote_name());
1737 ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
1738 ack->SetAttr(buzz::QN_TYPE, "result");
1739
1740 SignalOutgoingMessage(this, ack.get());
1741}
1742
1743} // namespace cricket