blob: e0911e13d41ed3f191a6cb5d11c168d76eb64abf [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);
551 transports_[content_name] = transproxy;
552
553 return transproxy;
554}
555
556Transport* BaseSession::GetTransport(const std::string& content_name) {
557 TransportProxy* transproxy = GetTransportProxy(content_name);
558 if (transproxy == NULL)
559 return NULL;
560 return transproxy->impl();
561}
562
563TransportProxy* BaseSession::GetTransportProxy(
564 const std::string& content_name) {
565 TransportMap::iterator iter = transports_.find(content_name);
566 return (iter != transports_.end()) ? iter->second : NULL;
567}
568
569TransportProxy* BaseSession::GetTransportProxy(const Transport* transport) {
570 for (TransportMap::iterator iter = transports_.begin();
571 iter != transports_.end(); ++iter) {
572 TransportProxy* transproxy = iter->second;
573 if (transproxy->impl() == transport) {
574 return transproxy;
575 }
576 }
577 return NULL;
578}
579
580TransportProxy* BaseSession::GetFirstTransportProxy() {
581 if (transports_.empty())
582 return NULL;
583 return transports_.begin()->second;
584}
585
586void BaseSession::DestroyTransportProxy(
587 const std::string& content_name) {
588 TransportMap::iterator iter = transports_.find(content_name);
589 if (iter != transports_.end()) {
590 delete iter->second;
591 transports_.erase(content_name);
592 }
593}
594
595cricket::Transport* BaseSession::CreateTransport(
596 const std::string& content_name) {
597 ASSERT(transport_type_ == NS_GINGLE_P2P);
598 return new cricket::DtlsTransport<P2PTransport>(
599 signaling_thread(), worker_thread(), content_name,
600 port_allocator(), identity_);
601}
602
603bool BaseSession::GetStats(SessionStats* stats) {
604 for (TransportMap::iterator iter = transports_.begin();
605 iter != transports_.end(); ++iter) {
606 std::string proxy_id = iter->second->content_name();
607 // We are ignoring not-yet-instantiated transports.
608 if (iter->second->impl()) {
609 std::string transport_id = iter->second->impl()->content_name();
610 stats->proxy_to_transport[proxy_id] = transport_id;
611 if (stats->transport_stats.find(transport_id)
612 == stats->transport_stats.end()) {
613 TransportStats subinfos;
614 if (!iter->second->impl()->GetStats(&subinfos)) {
615 return false;
616 }
617 stats->transport_stats[transport_id] = subinfos;
618 }
619 }
620 }
621 return true;
622}
623
624void BaseSession::SetState(State state) {
625 ASSERT(signaling_thread_->IsCurrent());
626 if (state != state_) {
627 LogState(state_, state);
628 state_ = state;
629 SignalState(this, state_);
630 signaling_thread_->Post(this, MSG_STATE);
631 }
632 SignalNewDescription();
633}
634
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000635void BaseSession::SetError(Error error, const std::string& error_desc) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000636 ASSERT(signaling_thread_->IsCurrent());
637 if (error != error_) {
638 error_ = error;
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000639 error_desc_ = error_desc;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000640 SignalError(this, error);
641 }
642}
643
644void BaseSession::OnSignalingReady() {
645 ASSERT(signaling_thread()->IsCurrent());
646 for (TransportMap::iterator iter = transports_.begin();
647 iter != transports_.end(); ++iter) {
648 iter->second->OnSignalingReady();
649 }
650}
651
652// TODO(juberti): Since PushdownLocalTD now triggers the connection process to
653// start, remove this method once everyone calls PushdownLocalTD.
654void BaseSession::SpeculativelyConnectAllTransportChannels() {
655 // Put all transports into the connecting state.
656 for (TransportMap::iterator iter = transports_.begin();
657 iter != transports_.end(); ++iter) {
658 iter->second->ConnectChannels();
659 }
660}
661
662bool BaseSession::OnRemoteCandidates(const std::string& content_name,
663 const Candidates& candidates,
664 std::string* error) {
665 // Give candidates to the appropriate transport, and tell that transport
666 // to start connecting, if it's not already doing so.
667 TransportProxy* transproxy = GetTransportProxy(content_name);
668 if (!transproxy) {
669 *error = "Unknown content name " + content_name;
670 return false;
671 }
672 if (!transproxy->OnRemoteCandidates(candidates, error)) {
673 return false;
674 }
675 // TODO(juberti): Remove this call once we can be sure that we always have
676 // a local transport description (which will trigger the connection).
677 transproxy->ConnectChannels();
678 return true;
679}
680
681bool BaseSession::MaybeEnableMuxingSupport() {
682 // We need both a local and remote description to decide if we should mux.
683 if ((state_ == STATE_SENTINITIATE ||
684 state_ == STATE_RECEIVEDINITIATE) &&
685 ((local_description_ == NULL) ||
686 (remote_description_ == NULL))) {
687 return false;
688 }
689
690 // In order to perform the multiplexing, we need all proxies to be in the
691 // negotiated state, i.e. to have implementations underneath.
692 // Ensure that this is the case, regardless of whether we are going to mux.
693 for (TransportMap::iterator iter = transports_.begin();
694 iter != transports_.end(); ++iter) {
695 ASSERT(iter->second->negotiated());
696 if (!iter->second->negotiated())
697 return false;
698 }
699
700 // If both sides agree to BUNDLE, mux all the specified contents onto the
701 // transport belonging to the first content name in the BUNDLE group.
702 // If the contents are already muxed, this will be a no-op.
703 // TODO(juberti): Should this check that local and remote have configured
704 // BUNDLE the same way?
705 bool candidates_allocated = IsCandidateAllocationDone();
706 const ContentGroup* local_bundle_group =
707 local_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
708 const ContentGroup* remote_bundle_group =
709 remote_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
710 if (local_bundle_group && remote_bundle_group &&
711 local_bundle_group->FirstContentName()) {
712 const std::string* content_name = local_bundle_group->FirstContentName();
713 const ContentInfo* content =
714 local_description_->GetContentByName(*content_name);
715 ASSERT(content != NULL);
716 if (!SetSelectedProxy(content->name, local_bundle_group)) {
717 LOG(LS_WARNING) << "Failed to set up BUNDLE";
718 return false;
719 }
720
721 // If we weren't done gathering before, we might be done now, as a result
722 // of enabling mux.
723 LOG(LS_INFO) << "Enabling BUNDLE, bundling onto transport: "
724 << *content_name;
725 if (!candidates_allocated) {
726 MaybeCandidateAllocationDone();
727 }
728 } else {
729 LOG(LS_INFO) << "No BUNDLE information, not bundling.";
730 }
731 return true;
732}
733
734bool BaseSession::SetSelectedProxy(const std::string& content_name,
735 const ContentGroup* muxed_group) {
736 TransportProxy* selected_proxy = GetTransportProxy(content_name);
737 if (!selected_proxy) {
738 return false;
739 }
740
741 ASSERT(selected_proxy->negotiated());
742 for (TransportMap::iterator iter = transports_.begin();
743 iter != transports_.end(); ++iter) {
744 // If content is part of the mux group, then repoint its proxy at the
745 // transport object that we have chosen to mux onto. If the proxy
746 // is already pointing at the right object, it will be a no-op.
747 if (muxed_group->HasContentName(iter->first) &&
748 !iter->second->SetupMux(selected_proxy)) {
749 return false;
750 }
751 }
752 return true;
753}
754
755void BaseSession::OnTransportCandidatesAllocationDone(Transport* transport) {
756 // TODO(juberti): This is a clunky way of processing the done signal. Instead,
757 // TransportProxy should receive the done signal directly, set its allocated
758 // flag internally, and then reissue the done signal to Session.
759 // Overall we should make TransportProxy receive *all* the signals from
760 // Transport, since this removes the need to manually iterate over all
761 // the transports, as is needed to make sure signals are handled properly
762 // when BUNDLEing.
763#if 0
764 ASSERT(!IsCandidateAllocationDone());
765#endif
766 for (TransportMap::iterator iter = transports_.begin();
767 iter != transports_.end(); ++iter) {
768 if (iter->second->impl() == transport) {
769 iter->second->set_candidates_allocated(true);
770 }
771 }
772 MaybeCandidateAllocationDone();
773}
774
775bool BaseSession::IsCandidateAllocationDone() const {
776 for (TransportMap::const_iterator iter = transports_.begin();
777 iter != transports_.end(); ++iter) {
778 if (!iter->second->candidates_allocated())
779 return false;
780 }
781 return true;
782}
783
784void BaseSession::MaybeCandidateAllocationDone() {
785 if (IsCandidateAllocationDone()) {
786 LOG(LS_INFO) << "Candidate gathering is complete.";
787 OnCandidatesAllocationDone();
788 }
789}
790
791void BaseSession::OnRoleConflict() {
792 if (role_switch_) {
793 LOG(LS_WARNING) << "Repeat of role conflict signal from Transport.";
794 return;
795 }
796
797 role_switch_ = true;
798 for (TransportMap::iterator iter = transports_.begin();
799 iter != transports_.end(); ++iter) {
800 // Role will be reverse of initial role setting.
mallinath@webrtc.orga5506692013-08-12 21:18:15 +0000801 IceRole role = initiator_ ? ICEROLE_CONTROLLED : ICEROLE_CONTROLLING;
802 iter->second->SetIceRole(role);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000803 }
804}
805
806void BaseSession::LogState(State old_state, State new_state) {
807 LOG(LS_INFO) << "Session:" << id()
808 << " Old state:" << StateToString(old_state)
809 << " New state:" << StateToString(new_state)
810 << " Type:" << content_type()
811 << " Transport:" << transport_type();
812}
813
814bool BaseSession::GetTransportDescription(const SessionDescription* description,
815 const std::string& content_name,
816 TransportDescription* tdesc) {
817 if (!description || !tdesc) {
818 return false;
819 }
820 const TransportInfo* transport_info =
821 description->GetTransportInfoByName(content_name);
822 if (!transport_info) {
823 return false;
824 }
825 *tdesc = transport_info->description;
826 return true;
827}
828
829void BaseSession::SignalNewDescription() {
830 ContentAction action;
831 ContentSource source;
832 if (!GetContentAction(&action, &source)) {
833 return;
834 }
835 if (source == CS_LOCAL) {
836 SignalNewLocalDescription(this, action);
837 } else {
838 SignalNewRemoteDescription(this, action);
839 }
840}
841
842bool BaseSession::GetContentAction(ContentAction* action,
843 ContentSource* source) {
844 switch (state_) {
845 // new local description
846 case STATE_SENTINITIATE:
847 *action = CA_OFFER;
848 *source = CS_LOCAL;
849 break;
850 case STATE_SENTPRACCEPT:
851 *action = CA_PRANSWER;
852 *source = CS_LOCAL;
853 break;
854 case STATE_SENTACCEPT:
855 *action = CA_ANSWER;
856 *source = CS_LOCAL;
857 break;
858 // new remote description
859 case STATE_RECEIVEDINITIATE:
860 *action = CA_OFFER;
861 *source = CS_REMOTE;
862 break;
863 case STATE_RECEIVEDPRACCEPT:
864 *action = CA_PRANSWER;
865 *source = CS_REMOTE;
866 break;
867 case STATE_RECEIVEDACCEPT:
868 *action = CA_ANSWER;
869 *source = CS_REMOTE;
870 break;
871 default:
872 return false;
873 }
874 return true;
875}
876
877void BaseSession::OnMessage(talk_base::Message *pmsg) {
878 switch (pmsg->message_id) {
879 case MSG_TIMEOUT:
880 // Session timeout has occured.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000881 SetError(ERROR_TIME, "Session timeout has occured.");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000882 break;
883
884 case MSG_STATE:
885 switch (state_) {
886 case STATE_SENTACCEPT:
887 case STATE_RECEIVEDACCEPT:
888 SetState(STATE_INPROGRESS);
889 break;
890
891 default:
892 // Explicitly ignoring some states here.
893 break;
894 }
895 break;
896 }
897}
898
899Session::Session(SessionManager* session_manager,
900 const std::string& local_name,
901 const std::string& initiator_name,
902 const std::string& sid,
903 const std::string& content_type,
904 SessionClient* client)
905 : BaseSession(session_manager->signaling_thread(),
906 session_manager->worker_thread(),
907 session_manager->port_allocator(),
908 sid, content_type, initiator_name == local_name) {
909 ASSERT(client != NULL);
910 session_manager_ = session_manager;
911 local_name_ = local_name;
912 initiator_name_ = initiator_name;
913 transport_parser_ = new P2PTransportParser();
914 client_ = client;
915 initiate_acked_ = false;
916 current_protocol_ = PROTOCOL_HYBRID;
917}
918
919Session::~Session() {
920 delete transport_parser_;
921}
922
923bool Session::Initiate(const std::string &to,
924 const SessionDescription* sdesc) {
925 ASSERT(signaling_thread()->IsCurrent());
926 SessionError error;
927
928 // Only from STATE_INIT
929 if (state() != STATE_INIT)
930 return false;
931
932 // Setup for signaling.
933 set_remote_name(to);
934 set_local_description(sdesc);
935 if (!CreateTransportProxies(GetEmptyTransportInfos(sdesc->contents()),
936 &error)) {
937 LOG(LS_ERROR) << "Could not create transports: " << error.text;
938 return false;
939 }
940
941 if (!SendInitiateMessage(sdesc, &error)) {
942 LOG(LS_ERROR) << "Could not send initiate message: " << error.text;
943 return false;
944 }
945
946 // We need to connect transport proxy and impl here so that we can process
947 // the TransportDescriptions.
948 SpeculativelyConnectAllTransportChannels();
949
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000950 PushdownTransportDescription(CS_LOCAL, CA_OFFER, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000951 SetState(Session::STATE_SENTINITIATE);
952 return true;
953}
954
955bool Session::Accept(const SessionDescription* sdesc) {
956 ASSERT(signaling_thread()->IsCurrent());
957
958 // Only if just received initiate
959 if (state() != STATE_RECEIVEDINITIATE)
960 return false;
961
962 // Setup for signaling.
963 set_local_description(sdesc);
964
965 SessionError error;
966 if (!SendAcceptMessage(sdesc, &error)) {
967 LOG(LS_ERROR) << "Could not send accept message: " << error.text;
968 return false;
969 }
970 // TODO(juberti): Add BUNDLE support to transport-info messages.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +0000971 PushdownTransportDescription(CS_LOCAL, CA_ANSWER, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000972 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported.
973 SetState(Session::STATE_SENTACCEPT);
974 return true;
975}
976
977bool Session::Reject(const std::string& reason) {
978 ASSERT(signaling_thread()->IsCurrent());
979
980 // Reject is sent in response to an initiate or modify, to reject the
981 // request
982 if (state() != STATE_RECEIVEDINITIATE && state() != STATE_RECEIVEDMODIFY)
983 return false;
984
985 SessionError error;
986 if (!SendRejectMessage(reason, &error)) {
987 LOG(LS_ERROR) << "Could not send reject message: " << error.text;
988 return false;
989 }
990
991 SetState(STATE_SENTREJECT);
992 return true;
993}
994
995bool Session::TerminateWithReason(const std::string& reason) {
996 ASSERT(signaling_thread()->IsCurrent());
997
998 // Either side can terminate, at any time.
999 switch (state()) {
1000 case STATE_SENTTERMINATE:
1001 case STATE_RECEIVEDTERMINATE:
1002 return false;
1003
1004 case STATE_SENTREJECT:
1005 case STATE_RECEIVEDREJECT:
1006 // We don't need to send terminate if we sent or received a reject...
1007 // it's implicit.
1008 break;
1009
1010 default:
1011 SessionError error;
1012 if (!SendTerminateMessage(reason, &error)) {
1013 LOG(LS_ERROR) << "Could not send terminate message: " << error.text;
1014 return false;
1015 }
1016 break;
1017 }
1018
1019 SetState(STATE_SENTTERMINATE);
1020 return true;
1021}
1022
wu@webrtc.org364f2042013-11-20 21:49:41 +00001023bool Session::SendInfoMessage(const XmlElements& elems,
1024 const std::string& remote_name) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001025 ASSERT(signaling_thread()->IsCurrent());
1026 SessionError error;
wu@webrtc.org364f2042013-11-20 21:49:41 +00001027 if (!SendMessage(ACTION_SESSION_INFO, elems, remote_name, &error)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001028 LOG(LS_ERROR) << "Could not send info message " << error.text;
1029 return false;
1030 }
1031 return true;
1032}
1033
1034bool Session::SendDescriptionInfoMessage(const ContentInfos& contents) {
1035 XmlElements elems;
1036 WriteError write_error;
1037 if (!WriteDescriptionInfo(current_protocol_,
1038 contents,
1039 GetContentParsers(),
1040 &elems, &write_error)) {
1041 LOG(LS_ERROR) << "Could not write description info message: "
1042 << write_error.text;
1043 return false;
1044 }
1045 SessionError error;
1046 if (!SendMessage(ACTION_DESCRIPTION_INFO, elems, &error)) {
1047 LOG(LS_ERROR) << "Could not send description info message: "
1048 << error.text;
1049 return false;
1050 }
1051 return true;
1052}
1053
1054TransportInfos Session::GetEmptyTransportInfos(
1055 const ContentInfos& contents) const {
1056 TransportInfos tinfos;
1057 for (ContentInfos::const_iterator content = contents.begin();
1058 content != contents.end(); ++content) {
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001059 tinfos.push_back(TransportInfo(content->name,
1060 TransportDescription(transport_type(),
1061 std::string(),
1062 std::string())));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001063 }
1064 return tinfos;
1065}
1066
1067bool Session::OnRemoteCandidates(
1068 const TransportInfos& tinfos, ParseError* error) {
1069 for (TransportInfos::const_iterator tinfo = tinfos.begin();
1070 tinfo != tinfos.end(); ++tinfo) {
1071 std::string str_error;
1072 if (!BaseSession::OnRemoteCandidates(
1073 tinfo->content_name, tinfo->description.candidates, &str_error)) {
1074 return BadParse(str_error, error);
1075 }
1076 }
1077 return true;
1078}
1079
1080bool Session::CreateTransportProxies(const TransportInfos& tinfos,
1081 SessionError* error) {
1082 for (TransportInfos::const_iterator tinfo = tinfos.begin();
1083 tinfo != tinfos.end(); ++tinfo) {
1084 if (tinfo->description.transport_type != transport_type()) {
1085 error->SetText("No supported transport in offer.");
1086 return false;
1087 }
1088
1089 GetOrCreateTransportProxy(tinfo->content_name);
1090 }
1091 return true;
1092}
1093
1094TransportParserMap Session::GetTransportParsers() {
1095 TransportParserMap parsers;
1096 parsers[transport_type()] = transport_parser_;
1097 return parsers;
1098}
1099
1100CandidateTranslatorMap Session::GetCandidateTranslators() {
1101 CandidateTranslatorMap translators;
1102 // NOTE: This technique makes it impossible to parse G-ICE
1103 // candidates in session-initiate messages because the channels
1104 // aren't yet created at that point. Since we don't use candidates
1105 // in session-initiate messages, we should be OK. Once we switch to
1106 // ICE, this translation shouldn't be necessary.
1107 for (TransportMap::const_iterator iter = transport_proxies().begin();
1108 iter != transport_proxies().end(); ++iter) {
1109 translators[iter->first] = iter->second;
1110 }
1111 return translators;
1112}
1113
1114ContentParserMap Session::GetContentParsers() {
1115 ContentParserMap parsers;
1116 parsers[content_type()] = client_;
1117 // We need to be able parse both RTP-based and SCTP-based Jingle
1118 // with the same client.
1119 if (content_type() == NS_JINGLE_RTP) {
1120 parsers[NS_JINGLE_DRAFT_SCTP] = client_;
1121 }
1122 return parsers;
1123}
1124
1125void Session::OnTransportRequestSignaling(Transport* transport) {
1126 ASSERT(signaling_thread()->IsCurrent());
1127 TransportProxy* transproxy = GetTransportProxy(transport);
1128 ASSERT(transproxy != NULL);
1129 if (transproxy) {
1130 // Reset candidate allocation status for the transport proxy.
1131 transproxy->set_candidates_allocated(false);
1132 }
1133 SignalRequestSignaling(this);
1134}
1135
1136void Session::OnTransportConnecting(Transport* transport) {
1137 // This is an indication that we should begin watching the writability
1138 // state of the transport.
1139 OnTransportWritable(transport);
1140}
1141
1142void Session::OnTransportWritable(Transport* transport) {
1143 ASSERT(signaling_thread()->IsCurrent());
1144
1145 // If the transport is not writable, start a timer to make sure that it
1146 // becomes writable within a reasonable amount of time. If it does not, we
1147 // terminate since we can't actually send data. If the transport is writable,
1148 // cancel the timer. Note that writability transitions may occur repeatedly
1149 // during the lifetime of the session.
1150 signaling_thread()->Clear(this, MSG_TIMEOUT);
1151 if (transport->HasChannels() && !transport->writable()) {
1152 signaling_thread()->PostDelayed(
1153 session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
1154 }
1155}
1156
1157void Session::OnTransportProxyCandidatesReady(TransportProxy* transproxy,
1158 const Candidates& candidates) {
1159 ASSERT(signaling_thread()->IsCurrent());
1160 if (transproxy != NULL) {
1161 if (initiator() && !initiate_acked_) {
1162 // TODO: This is to work around server re-ordering
1163 // messages. We send the candidates once the session-initiate
1164 // is acked. Once we have fixed the server to guarantee message
1165 // order, we can remove this case.
1166 transproxy->AddUnsentCandidates(candidates);
1167 } else {
1168 if (!transproxy->negotiated()) {
1169 transproxy->AddSentCandidates(candidates);
1170 }
1171 SessionError error;
1172 if (!SendTransportInfoMessage(transproxy, candidates, &error)) {
1173 LOG(LS_ERROR) << "Could not send transport info message: "
1174 << error.text;
1175 return;
1176 }
1177 }
1178 }
1179}
1180
1181void Session::OnTransportSendError(Transport* transport,
1182 const buzz::XmlElement* stanza,
1183 const buzz::QName& name,
1184 const std::string& type,
1185 const std::string& text,
1186 const buzz::XmlElement* extra_info) {
1187 ASSERT(signaling_thread()->IsCurrent());
1188 SignalErrorMessage(this, stanza, name, type, text, extra_info);
1189}
1190
1191void Session::OnIncomingMessage(const SessionMessage& msg) {
1192 ASSERT(signaling_thread()->IsCurrent());
1193 ASSERT(state() == STATE_INIT || msg.from == remote_name());
1194
1195 if (current_protocol_== PROTOCOL_HYBRID) {
1196 if (msg.protocol == PROTOCOL_GINGLE) {
1197 current_protocol_ = PROTOCOL_GINGLE;
1198 } else {
1199 current_protocol_ = PROTOCOL_JINGLE;
1200 }
1201 }
1202
1203 bool valid = false;
1204 MessageError error;
1205 switch (msg.type) {
1206 case ACTION_SESSION_INITIATE:
1207 valid = OnInitiateMessage(msg, &error);
1208 break;
1209 case ACTION_SESSION_INFO:
1210 valid = OnInfoMessage(msg);
1211 break;
1212 case ACTION_SESSION_ACCEPT:
1213 valid = OnAcceptMessage(msg, &error);
1214 break;
1215 case ACTION_SESSION_REJECT:
1216 valid = OnRejectMessage(msg, &error);
1217 break;
1218 case ACTION_SESSION_TERMINATE:
1219 valid = OnTerminateMessage(msg, &error);
1220 break;
1221 case ACTION_TRANSPORT_INFO:
1222 valid = OnTransportInfoMessage(msg, &error);
1223 break;
1224 case ACTION_TRANSPORT_ACCEPT:
1225 valid = OnTransportAcceptMessage(msg, &error);
1226 break;
1227 case ACTION_DESCRIPTION_INFO:
1228 valid = OnDescriptionInfoMessage(msg, &error);
1229 break;
1230 default:
1231 valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST,
1232 "unknown session message type",
1233 &error);
1234 }
1235
1236 if (valid) {
1237 SendAcknowledgementMessage(msg.stanza);
1238 } else {
1239 SignalErrorMessage(this, msg.stanza, error.type,
1240 "modify", error.text, NULL);
1241 }
1242}
1243
1244void Session::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
1245 const buzz::XmlElement* response_stanza,
1246 const SessionMessage& msg) {
1247 ASSERT(signaling_thread()->IsCurrent());
1248
1249 if (msg.type == ACTION_SESSION_INITIATE) {
1250 OnInitiateAcked();
1251 }
1252}
1253
1254void Session::OnInitiateAcked() {
1255 // TODO: This is to work around server re-ordering
1256 // messages. We send the candidates once the session-initiate
1257 // is acked. Once we have fixed the server to guarantee message
1258 // order, we can remove this case.
1259 if (!initiate_acked_) {
1260 initiate_acked_ = true;
1261 SessionError error;
1262 SendAllUnsentTransportInfoMessages(&error);
1263 }
1264}
1265
1266void Session::OnFailedSend(const buzz::XmlElement* orig_stanza,
1267 const buzz::XmlElement* error_stanza) {
1268 ASSERT(signaling_thread()->IsCurrent());
1269
1270 SessionMessage msg;
1271 ParseError parse_error;
1272 if (!ParseSessionMessage(orig_stanza, &msg, &parse_error)) {
1273 LOG(LS_ERROR) << "Error parsing failed send: " << parse_error.text
1274 << ":" << orig_stanza;
1275 return;
1276 }
1277
1278 // If the error is a session redirect, call OnRedirectError, which will
1279 // continue the session with a new remote JID.
1280 SessionRedirect redirect;
1281 if (FindSessionRedirect(error_stanza, &redirect)) {
1282 SessionError error;
1283 if (!OnRedirectError(redirect, &error)) {
1284 // TODO: Should we send a message back? The standard
1285 // says nothing about it.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001286 std::ostringstream desc;
1287 desc << "Failed to redirect: " << error.text;
1288 LOG(LS_ERROR) << desc.str();
1289 SetError(ERROR_RESPONSE, desc.str());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001290 }
1291 return;
1292 }
1293
1294 std::string error_type = "cancel";
1295
1296 const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR);
1297 if (error) {
1298 error_type = error->Attr(buzz::QN_TYPE);
1299
1300 LOG(LS_ERROR) << "Session error:\n" << error->Str() << "\n"
1301 << "in response to:\n" << orig_stanza->Str();
1302 } else {
1303 // don't crash if <error> is missing
1304 LOG(LS_ERROR) << "Session error without <error/> element, ignoring";
1305 return;
1306 }
1307
1308 if (msg.type == ACTION_TRANSPORT_INFO) {
1309 // Transport messages frequently generate errors because they are sent right
1310 // when we detect a network failure. For that reason, we ignore such
1311 // errors, because if we do not establish writability again, we will
1312 // terminate anyway. The exceptions are transport-specific error tags,
1313 // which we pass on to the respective transport.
1314 } else if ((error_type != "continue") && (error_type != "wait")) {
1315 // We do not set an error if the other side said it is okay to continue
1316 // (possibly after waiting). These errors can be ignored.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001317 SetError(ERROR_RESPONSE, "");
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001318 }
1319}
1320
1321bool Session::OnInitiateMessage(const SessionMessage& msg,
1322 MessageError* error) {
1323 if (!CheckState(STATE_INIT, error))
1324 return false;
1325
1326 SessionInitiate init;
1327 if (!ParseSessionInitiate(msg.protocol, msg.action_elem,
1328 GetContentParsers(), GetTransportParsers(),
1329 GetCandidateTranslators(),
1330 &init, error))
1331 return false;
1332
1333 SessionError session_error;
1334 if (!CreateTransportProxies(init.transports, &session_error)) {
1335 return BadMessage(buzz::QN_STANZA_NOT_ACCEPTABLE,
1336 session_error.text, error);
1337 }
1338
1339 set_remote_name(msg.from);
1340 set_initiator_name(msg.initiator);
1341 set_remote_description(new SessionDescription(init.ClearContents(),
1342 init.transports,
1343 init.groups));
1344 // Updating transport with TransportDescription.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001345 PushdownTransportDescription(CS_REMOTE, CA_OFFER, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001346 SetState(STATE_RECEIVEDINITIATE);
1347
1348 // Users of Session may listen to state change and call Reject().
1349 if (state() != STATE_SENTREJECT) {
1350 if (!OnRemoteCandidates(init.transports, error))
1351 return false;
1352
1353 // TODO(juberti): Auto-generate and push down the local transport answer.
1354 // This is necessary for trickling to work with RFC 5245 ICE.
1355 }
1356 return true;
1357}
1358
1359bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) {
1360 if (!CheckState(STATE_SENTINITIATE, error))
1361 return false;
1362
1363 SessionAccept accept;
1364 if (!ParseSessionAccept(msg.protocol, msg.action_elem,
1365 GetContentParsers(), GetTransportParsers(),
1366 GetCandidateTranslators(),
1367 &accept, error)) {
1368 return false;
1369 }
1370
1371 // If we get an accept, we can assume the initiate has been
1372 // received, even if we haven't gotten an IQ response.
1373 OnInitiateAcked();
1374
1375 set_remote_description(new SessionDescription(accept.ClearContents(),
1376 accept.transports,
1377 accept.groups));
1378 // Updating transport with TransportDescription.
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001379 PushdownTransportDescription(CS_REMOTE, CA_ANSWER, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001380 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported.
1381 SetState(STATE_RECEIVEDACCEPT);
1382
1383 if (!OnRemoteCandidates(accept.transports, error))
1384 return false;
1385
1386 return true;
1387}
1388
1389bool Session::OnRejectMessage(const SessionMessage& msg, MessageError* error) {
1390 if (!CheckState(STATE_SENTINITIATE, error))
1391 return false;
1392
1393 SetState(STATE_RECEIVEDREJECT);
1394 return true;
1395}
1396
1397bool Session::OnInfoMessage(const SessionMessage& msg) {
1398 SignalInfoMessage(this, msg.action_elem);
1399 return true;
1400}
1401
1402bool Session::OnTerminateMessage(const SessionMessage& msg,
1403 MessageError* error) {
1404 SessionTerminate term;
1405 if (!ParseSessionTerminate(msg.protocol, msg.action_elem, &term, error))
1406 return false;
1407
1408 SignalReceivedTerminateReason(this, term.reason);
1409 if (term.debug_reason != buzz::STR_EMPTY) {
1410 LOG(LS_VERBOSE) << "Received error on call: " << term.debug_reason;
1411 }
1412
1413 SetState(STATE_RECEIVEDTERMINATE);
1414 return true;
1415}
1416
1417bool Session::OnTransportInfoMessage(const SessionMessage& msg,
1418 MessageError* error) {
1419 TransportInfos tinfos;
1420 if (!ParseTransportInfos(msg.protocol, msg.action_elem,
1421 initiator_description()->contents(),
1422 GetTransportParsers(), GetCandidateTranslators(),
1423 &tinfos, error))
1424 return false;
1425
1426 if (!OnRemoteCandidates(tinfos, error))
1427 return false;
1428
1429 return true;
1430}
1431
1432bool Session::OnTransportAcceptMessage(const SessionMessage& msg,
1433 MessageError* error) {
1434 // TODO: Currently here only for compatibility with
1435 // Gingle 1.1 clients (notably, Google Voice).
1436 return true;
1437}
1438
1439bool Session::OnDescriptionInfoMessage(const SessionMessage& msg,
1440 MessageError* error) {
1441 if (!CheckState(STATE_INPROGRESS, error))
1442 return false;
1443
1444 DescriptionInfo description_info;
1445 if (!ParseDescriptionInfo(msg.protocol, msg.action_elem,
1446 GetContentParsers(), GetTransportParsers(),
1447 GetCandidateTranslators(),
1448 &description_info, error)) {
1449 return false;
1450 }
1451
henrike@webrtc.org1e09a712013-07-26 19:17:59 +00001452 ContentInfos& updated_contents = description_info.contents;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001453
1454 // TODO: Currently, reflector sends back
1455 // video stream updates even for an audio-only call, which causes
1456 // this to fail. Put this back once reflector is fixed.
1457 //
1458 // ContentInfos::iterator it;
1459 // First, ensure all updates are valid before modifying remote_description_.
1460 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
1461 // if (remote_description()->GetContentByName(it->name) == NULL) {
1462 // return false;
1463 // }
1464 // }
1465
1466 // TODO: We used to replace contents from an update, but
1467 // that no longer works with partial updates. We need to figure out
1468 // a way to merge patial updates into contents. For now, users of
1469 // Session should listen to SignalRemoteDescriptionUpdate and handle
1470 // updates. They should not expect remote_description to be the
1471 // latest value.
1472 //
1473 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
1474 // remote_description()->RemoveContentByName(it->name);
1475 // remote_description()->AddContent(it->name, it->type, it->description);
1476 // }
1477 // }
1478
1479 SignalRemoteDescriptionUpdate(this, updated_contents);
1480 return true;
1481}
1482
1483bool BareJidsEqual(const std::string& name1,
1484 const std::string& name2) {
1485 buzz::Jid jid1(name1);
1486 buzz::Jid jid2(name2);
1487
1488 return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2);
1489}
1490
1491bool Session::OnRedirectError(const SessionRedirect& redirect,
1492 SessionError* error) {
1493 MessageError message_error;
1494 if (!CheckState(STATE_SENTINITIATE, &message_error)) {
1495 return BadWrite(message_error.text, error);
1496 }
1497
1498 if (!BareJidsEqual(remote_name(), redirect.target))
1499 return BadWrite("Redirection not allowed: must be the same bare jid.",
1500 error);
1501
1502 // When we receive a redirect, we point the session at the new JID
1503 // and resend the candidates.
1504 set_remote_name(redirect.target);
1505 return (SendInitiateMessage(local_description(), error) &&
1506 ResendAllTransportInfoMessages(error));
1507}
1508
1509bool Session::CheckState(State expected, MessageError* error) {
1510 if (state() != expected) {
1511 // The server can deliver messages out of order/repeated for various
1512 // reasons. For example, if the server does not recive our iq response,
1513 // it could assume that the iq it sent was lost, and will then send
1514 // it again. Ideally, we should implement reliable messaging with
1515 // duplicate elimination.
1516 return BadMessage(buzz::QN_STANZA_NOT_ALLOWED,
1517 "message not allowed in current state",
1518 error);
1519 }
1520 return true;
1521}
1522
sergeyu@chromium.org4b26e2e2014-01-15 23:15:54 +00001523void Session::SetError(Error error, const std::string& error_desc) {
1524 BaseSession::SetError(error, error_desc);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001525 if (error != ERROR_NONE)
1526 signaling_thread()->Post(this, MSG_ERROR);
1527}
1528
1529void Session::OnMessage(talk_base::Message* pmsg) {
1530 // preserve this because BaseSession::OnMessage may modify it
1531 State orig_state = state();
1532
1533 BaseSession::OnMessage(pmsg);
1534
1535 switch (pmsg->message_id) {
1536 case MSG_ERROR:
1537 TerminateWithReason(STR_TERMINATE_ERROR);
1538 break;
1539
1540 case MSG_STATE:
1541 switch (orig_state) {
1542 case STATE_SENTREJECT:
1543 case STATE_RECEIVEDREJECT:
1544 // Assume clean termination.
1545 Terminate();
1546 break;
1547
1548 case STATE_SENTTERMINATE:
1549 case STATE_RECEIVEDTERMINATE:
1550 session_manager_->DestroySession(this);
1551 break;
1552
1553 default:
1554 // Explicitly ignoring some states here.
1555 break;
1556 }
1557 break;
1558 }
1559}
1560
1561bool Session::SendInitiateMessage(const SessionDescription* sdesc,
1562 SessionError* error) {
1563 SessionInitiate init;
1564 init.contents = sdesc->contents();
1565 init.transports = GetEmptyTransportInfos(init.contents);
1566 init.groups = sdesc->groups();
1567 return SendMessage(ACTION_SESSION_INITIATE, init, error);
1568}
1569
1570bool Session::WriteSessionAction(
1571 SignalingProtocol protocol, const SessionInitiate& init,
1572 XmlElements* elems, WriteError* error) {
1573 return WriteSessionInitiate(protocol, init.contents, init.transports,
1574 GetContentParsers(), GetTransportParsers(),
1575 GetCandidateTranslators(), init.groups,
1576 elems, error);
1577}
1578
1579bool Session::SendAcceptMessage(const SessionDescription* sdesc,
1580 SessionError* error) {
1581 XmlElements elems;
1582 if (!WriteSessionAccept(current_protocol_,
1583 sdesc->contents(),
1584 GetEmptyTransportInfos(sdesc->contents()),
1585 GetContentParsers(), GetTransportParsers(),
1586 GetCandidateTranslators(), sdesc->groups(),
1587 &elems, error)) {
1588 return false;
1589 }
1590 return SendMessage(ACTION_SESSION_ACCEPT, elems, error);
1591}
1592
1593bool Session::SendRejectMessage(const std::string& reason,
1594 SessionError* error) {
1595 SessionTerminate term(reason);
1596 return SendMessage(ACTION_SESSION_REJECT, term, error);
1597}
1598
1599bool Session::SendTerminateMessage(const std::string& reason,
1600 SessionError* error) {
1601 SessionTerminate term(reason);
1602 return SendMessage(ACTION_SESSION_TERMINATE, term, error);
1603}
1604
1605bool Session::WriteSessionAction(SignalingProtocol protocol,
1606 const SessionTerminate& term,
1607 XmlElements* elems, WriteError* error) {
1608 WriteSessionTerminate(protocol, term, elems);
1609 return true;
1610}
1611
1612bool Session::SendTransportInfoMessage(const TransportInfo& tinfo,
1613 SessionError* error) {
1614 return SendMessage(ACTION_TRANSPORT_INFO, tinfo, error);
1615}
1616
1617bool Session::SendTransportInfoMessage(const TransportProxy* transproxy,
1618 const Candidates& candidates,
1619 SessionError* error) {
1620 return SendTransportInfoMessage(TransportInfo(transproxy->content_name(),
sergeyu@chromium.org0be6aa02013-08-23 23:21:25 +00001621 TransportDescription(transproxy->type(), std::vector<std::string>(),
1622 std::string(), std::string(), ICEMODE_FULL,
1623 CONNECTIONROLE_NONE, NULL, candidates)), error);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001624}
1625
1626bool Session::WriteSessionAction(SignalingProtocol protocol,
1627 const TransportInfo& tinfo,
1628 XmlElements* elems, WriteError* error) {
1629 TransportInfos tinfos;
1630 tinfos.push_back(tinfo);
1631 return WriteTransportInfos(protocol, tinfos,
1632 GetTransportParsers(), GetCandidateTranslators(),
1633 elems, error);
1634}
1635
1636bool Session::ResendAllTransportInfoMessages(SessionError* error) {
1637 for (TransportMap::const_iterator iter = transport_proxies().begin();
1638 iter != transport_proxies().end(); ++iter) {
1639 TransportProxy* transproxy = iter->second;
1640 if (transproxy->sent_candidates().size() > 0) {
1641 if (!SendTransportInfoMessage(
1642 transproxy, transproxy->sent_candidates(), error)) {
1643 LOG(LS_ERROR) << "Could not resend transport info messages: "
1644 << error->text;
1645 return false;
1646 }
1647 transproxy->ClearSentCandidates();
1648 }
1649 }
1650 return true;
1651}
1652
1653bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) {
1654 for (TransportMap::const_iterator iter = transport_proxies().begin();
1655 iter != transport_proxies().end(); ++iter) {
1656 TransportProxy* transproxy = iter->second;
1657 if (transproxy->unsent_candidates().size() > 0) {
1658 if (!SendTransportInfoMessage(
1659 transproxy, transproxy->unsent_candidates(), error)) {
1660 LOG(LS_ERROR) << "Could not send unsent transport info messages: "
1661 << error->text;
1662 return false;
1663 }
1664 transproxy->ClearUnsentCandidates();
1665 }
1666 }
1667 return true;
1668}
1669
1670bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
1671 SessionError* error) {
wu@webrtc.org364f2042013-11-20 21:49:41 +00001672 return SendMessage(type, action_elems, remote_name(), error);
1673}
1674
1675bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
1676 const std::string& remote_name, SessionError* error) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001677 talk_base::scoped_ptr<buzz::XmlElement> stanza(
1678 new buzz::XmlElement(buzz::QN_IQ));
1679
1680 SessionMessage msg(current_protocol_, type, id(), initiator_name());
wu@webrtc.org364f2042013-11-20 21:49:41 +00001681 msg.to = remote_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001682 WriteSessionMessage(msg, action_elems, stanza.get());
1683
1684 SignalOutgoingMessage(this, stanza.get());
1685 return true;
1686}
1687
1688template <typename Action>
1689bool Session::SendMessage(ActionType type, const Action& action,
1690 SessionError* error) {
1691 talk_base::scoped_ptr<buzz::XmlElement> stanza(
1692 new buzz::XmlElement(buzz::QN_IQ));
1693 if (!WriteActionMessage(type, action, stanza.get(), error))
1694 return false;
1695
1696 SignalOutgoingMessage(this, stanza.get());
1697 return true;
1698}
1699
1700template <typename Action>
1701bool Session::WriteActionMessage(ActionType type, const Action& action,
1702 buzz::XmlElement* stanza,
1703 WriteError* error) {
1704 if (current_protocol_ == PROTOCOL_HYBRID) {
1705 if (!WriteActionMessage(PROTOCOL_JINGLE, type, action, stanza, error))
1706 return false;
1707 if (!WriteActionMessage(PROTOCOL_GINGLE, type, action, stanza, error))
1708 return false;
1709 } else {
1710 if (!WriteActionMessage(current_protocol_, type, action, stanza, error))
1711 return false;
1712 }
1713 return true;
1714}
1715
1716template <typename Action>
1717bool Session::WriteActionMessage(SignalingProtocol protocol,
1718 ActionType type, const Action& action,
1719 buzz::XmlElement* stanza, WriteError* error) {
1720 XmlElements action_elems;
1721 if (!WriteSessionAction(protocol, action, &action_elems, error))
1722 return false;
1723
1724 SessionMessage msg(protocol, type, id(), initiator_name());
1725 msg.to = remote_name();
1726
1727 WriteSessionMessage(msg, action_elems, stanza);
1728 return true;
1729}
1730
1731void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) {
1732 talk_base::scoped_ptr<buzz::XmlElement> ack(
1733 new buzz::XmlElement(buzz::QN_IQ));
1734 ack->SetAttr(buzz::QN_TO, remote_name());
1735 ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
1736 ack->SetAttr(buzz::QN_TYPE, "result");
1737
1738 SignalOutgoingMessage(this, ack.get());
1739}
1740
1741} // namespace cricket