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