3 * Copyright 2004--2005, Google Inc.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
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.
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.
28 #include "talk/p2p/base/session.h"
30 #include "talk/base/bind.h"
31 #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"
45 #include "talk/p2p/base/constants.h"
49 using talk_base::Bind;
51 bool BadMessage(const buzz::QName type,
52 const std::string& text,
59 TransportProxy::~TransportProxy() {
60 for (ChannelMap::iterator iter = channels_.begin();
61 iter != channels_.end(); ++iter) {
62 iter->second->SignalDestroyed(iter->second);
67 std::string TransportProxy::type() const {
68 return transport_->get()->type();
71 TransportChannel* TransportProxy::GetChannel(int component) {
72 ASSERT(talk_base::Thread::Current() == worker_thread_);
73 return GetChannelProxy(component);
76 TransportChannel* TransportProxy::CreateChannel(
77 const std::string& name, int component) {
78 ASSERT(talk_base::Thread::Current() == worker_thread_);
79 ASSERT(GetChannel(component) == NULL);
80 ASSERT(!transport_->get()->HasChannel(component));
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;
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.
90 SetupChannelProxy_w(component, channel);
91 } else if (connecting_) {
92 GetOrCreateChannelProxyImpl_w(component);
97 bool TransportProxy::HasChannel(int component) {
98 return transport_->get()->HasChannel(component);
101 void TransportProxy::DestroyChannel(int component) {
102 ASSERT(talk_base::Thread::Current() == worker_thread_);
103 TransportChannel* channel = GetChannel(component);
105 // If the state of TransportProxy is not NEGOTIATED
106 // then TransportChannelProxy and its impl are not
107 // connected. Both must be connected before
110 SetupChannelProxy_w(component, GetChannelProxy(component));
113 channels_.erase(component);
114 channel->SignalDestroyed(channel);
119 void TransportProxy::ConnectChannels() {
122 for (ChannelMap::iterator iter = channels_.begin();
123 iter != channels_.end(); ++iter) {
124 GetOrCreateChannelProxyImpl(iter->first);
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();
136 void TransportProxy::CompleteNegotiation() {
138 for (ChannelMap::iterator iter = channels_.begin();
139 iter != channels_.end(); ++iter) {
140 SetupChannelProxy(iter->first, iter->second);
146 void TransportProxy::AddSentCandidates(const Candidates& candidates) {
147 for (Candidates::const_iterator cand = candidates.begin();
148 cand != candidates.end(); ++cand) {
149 sent_candidates_.push_back(*cand);
153 void TransportProxy::AddUnsentCandidates(const Candidates& candidates) {
154 for (Candidates::const_iterator cand = candidates.begin();
155 cand != candidates.end(); ++cand) {
156 unsent_candidates_.push_back(*cand);
160 bool TransportProxy::GetChannelNameFromComponent(
161 int component, std::string* channel_name) const {
162 const TransportChannelProxy* channel = GetChannelProxy(component);
163 if (channel == NULL) {
167 *channel_name = channel->name();
171 bool TransportProxy::GetComponentFromChannelName(
172 const std::string& channel_name, int* component) const {
173 const TransportChannelProxy* channel = GetChannelProxyByName(channel_name);
174 if (channel == NULL) {
178 *component = channel->component();
182 TransportChannelProxy* TransportProxy::GetChannelProxy(int component) const {
183 ChannelMap::const_iterator iter = channels_.find(component);
184 return (iter != channels_.end()) ? iter->second : NULL;
187 TransportChannelProxy* TransportProxy::GetChannelProxyByName(
188 const std::string& name) const {
189 for (ChannelMap::const_iterator iter = channels_.begin();
190 iter != channels_.end();
192 if (iter->second->name() == name) {
199 TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl(
201 return worker_thread_->Invoke<TransportChannelImpl*>(Bind(
202 &TransportProxy::GetOrCreateChannelProxyImpl_w, this, component));
205 TransportChannelImpl* TransportProxy::GetOrCreateChannelProxyImpl_w(
207 ASSERT(talk_base::Thread::Current() == worker_thread_);
208 TransportChannelImpl* impl = transport_->get()->GetChannel(component);
210 impl = transport_->get()->CreateChannel(component);
215 void TransportProxy::SetupChannelProxy(
216 int component, TransportChannelProxy* transproxy) {
217 worker_thread_->Invoke<void>(Bind(
218 &TransportProxy::SetupChannelProxy_w, this, component, transproxy));
221 void TransportProxy::SetupChannelProxy_w(
222 int component, TransportChannelProxy* transproxy) {
223 ASSERT(talk_base::Thread::Current() == worker_thread_);
224 TransportChannelImpl* impl = GetOrCreateChannelProxyImpl(component);
225 ASSERT(impl != NULL);
226 transproxy->SetImplementation(impl);
229 void TransportProxy::ReplaceChannelProxyImpl(TransportChannelProxy* proxy,
230 TransportChannelImpl* impl) {
231 worker_thread_->Invoke<void>(Bind(
232 &TransportProxy::ReplaceChannelProxyImpl_w, this, proxy, impl));
235 void TransportProxy::ReplaceChannelProxyImpl_w(TransportChannelProxy* proxy,
236 TransportChannelImpl* impl) {
237 ASSERT(talk_base::Thread::Current() == worker_thread_);
238 ASSERT(proxy != NULL);
239 proxy->SetImplementation(impl);
242 // 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.
245 bool TransportProxy::SetupMux(TransportProxy* target) {
246 // Bail out if there's nothing to do.
247 if (transport_ == target->transport_) {
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_|.
257 ReplaceChannelProxyImpl(iter->second, NULL);
259 // Replace the impl for all the TransportProxyChannels with the channels
260 // from |target|'s transport. Fail if there's not an exact match.
261 ReplaceChannelProxyImpl(
262 iter->second, target->transport_->get()->CreateChannel(iter->first));
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());
275 void TransportProxy::SetIceRole(IceRole role) {
276 transport_->get()->SetIceRole(role);
279 bool TransportProxy::SetLocalTransportDescription(
280 const TransportDescription& description,
281 ContentAction action,
282 std::string* error_desc) {
283 // If this is an answer, finalize the negotiation.
284 if (action == CA_ANSWER) {
285 CompleteNegotiation();
287 return transport_->get()->SetLocalTransportDescription(description,
292 bool TransportProxy::SetRemoteTransportDescription(
293 const TransportDescription& description,
294 ContentAction action,
295 std::string* error_desc) {
296 // If this is an answer, finalize the negotiation.
297 if (action == CA_ANSWER) {
298 CompleteNegotiation();
300 return transport_->get()->SetRemoteTransportDescription(description,
305 void TransportProxy::OnSignalingReady() {
306 // If we're starting a new allocation sequence, reset our state.
307 set_candidates_allocated(false);
308 transport_->get()->OnSignalingReady();
311 bool 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();
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))
322 if (!HasChannel(cand->component())) {
323 *error = "Candidate has unknown component: " + cand->ToString() +
324 " for content: " + content_name_;
328 transport_->get()->OnRemoteCandidates(candidates);
332 void TransportProxy::SetIdentity(
333 talk_base::SSLIdentity* identity) {
334 transport_->get()->SetIdentity(identity);
337 std::string BaseSession::StateToString(State state) {
339 case Session::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";
374 return "STATE_" + talk_base::ToString(state);
377 BaseSession::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,
383 : state_(STATE_INIT),
385 signaling_thread_(signaling_thread),
386 worker_thread_(worker_thread),
387 port_allocator_(port_allocator),
389 content_type_(content_type),
390 transport_type_(NS_GINGLE_P2P),
391 initiator_(initiator),
393 local_description_(NULL),
394 remote_description_(NULL),
395 ice_tiebreaker_(talk_base::CreateRandomId64()),
396 role_switch_(false) {
397 ASSERT(signaling_thread->IsCurrent());
400 BaseSession::~BaseSession() {
401 ASSERT(signaling_thread()->IsCurrent());
403 ASSERT(state_ != STATE_DEINIT);
404 LogState(state_, STATE_DEINIT);
405 state_ = STATE_DEINIT;
406 SignalState(this, state_);
408 for (TransportMap::iterator iter = transports_.begin();
409 iter != transports_.end(); ++iter) {
413 delete remote_description_;
414 delete local_description_;
417 bool BaseSession::SetIdentity(talk_base::SSLIdentity* identity) {
420 identity_ = identity;
421 for (TransportMap::iterator iter = transports_.begin();
422 iter != transports_.end(); ++iter) {
423 iter->second->SetIdentity(identity_);
428 bool BaseSession::PushdownTransportDescription(ContentSource source,
429 ContentAction action,
430 std::string* error_desc) {
431 if (source == CS_LOCAL) {
432 return PushdownLocalTransportDescription(local_description_,
436 return PushdownRemoteTransportDescription(remote_description_,
441 bool BaseSession::PushdownLocalTransportDescription(
442 const SessionDescription* sdesc,
443 ContentAction action,
444 std::string* error_desc) {
445 // Update the Transports with the right information, and trigger them to
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);
455 if (!iter->second->SetLocalTransportDescription(tdesc, action,
460 iter->second->ConnectChannels();
467 bool BaseSession::PushdownRemoteTransportDescription(
468 const SessionDescription* sdesc,
469 ContentAction action,
470 std::string* error_desc) {
471 // Update the Transports with the right information.
472 for (TransportMap::iterator iter = transports_.begin();
473 iter != transports_.end(); ++iter) {
474 TransportDescription tdesc;
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);
481 if (!iter->second->SetRemoteTransportDescription(tdesc, action,
491 TransportChannel* BaseSession::CreateChannel(const std::string& content_name,
492 const std::string& channel_name,
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);
501 TransportChannel* BaseSession::GetChannel(const std::string& content_name,
503 TransportProxy* transproxy = GetTransportProxy(content_name);
504 if (transproxy == NULL)
507 return transproxy->GetChannel(component);
510 void BaseSession::DestroyChannel(const std::string& content_name,
512 TransportProxy* transproxy = GetTransportProxy(content_name);
513 ASSERT(transproxy != NULL);
514 transproxy->DestroyChannel(component);
517 TransportProxy* BaseSession::GetOrCreateTransportProxy(
518 const std::string& content_name) {
519 TransportProxy* transproxy = GetTransportProxy(content_name);
523 Transport* transport = CreateTransport(content_name);
524 transport->SetIceRole(initiator_ ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED);
525 transport->SetIceTiebreaker(ice_tiebreaker_);
526 // 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);
543 transproxy = new TransportProxy(worker_thread_, sid_, content_name,
544 new TransportWrapper(transport));
545 transproxy->SignalCandidatesReady.connect(
546 this, &BaseSession::OnTransportProxyCandidatesReady);
547 transports_[content_name] = transproxy;
552 Transport* BaseSession::GetTransport(const std::string& content_name) {
553 TransportProxy* transproxy = GetTransportProxy(content_name);
554 if (transproxy == NULL)
556 return transproxy->impl();
559 TransportProxy* BaseSession::GetTransportProxy(
560 const std::string& content_name) {
561 TransportMap::iterator iter = transports_.find(content_name);
562 return (iter != transports_.end()) ? iter->second : NULL;
565 TransportProxy* BaseSession::GetTransportProxy(const Transport* transport) {
566 for (TransportMap::iterator iter = transports_.begin();
567 iter != transports_.end(); ++iter) {
568 TransportProxy* transproxy = iter->second;
569 if (transproxy->impl() == transport) {
576 TransportProxy* BaseSession::GetFirstTransportProxy() {
577 if (transports_.empty())
579 return transports_.begin()->second;
582 void BaseSession::DestroyTransportProxy(
583 const std::string& content_name) {
584 TransportMap::iterator iter = transports_.find(content_name);
585 if (iter != transports_.end()) {
587 transports_.erase(content_name);
591 cricket::Transport* BaseSession::CreateTransport(
592 const std::string& content_name) {
593 ASSERT(transport_type_ == NS_GINGLE_P2P);
594 return new cricket::DtlsTransport<P2PTransport>(
595 signaling_thread(), worker_thread(), content_name,
596 port_allocator(), identity_);
599 bool BaseSession::GetStats(SessionStats* stats) {
600 for (TransportMap::iterator iter = transports_.begin();
601 iter != transports_.end(); ++iter) {
602 std::string proxy_id = iter->second->content_name();
603 // We are ignoring not-yet-instantiated transports.
604 if (iter->second->impl()) {
605 std::string transport_id = iter->second->impl()->content_name();
606 stats->proxy_to_transport[proxy_id] = transport_id;
607 if (stats->transport_stats.find(transport_id)
608 == stats->transport_stats.end()) {
609 TransportStats subinfos;
610 if (!iter->second->impl()->GetStats(&subinfos)) {
613 stats->transport_stats[transport_id] = subinfos;
620 void BaseSession::SetState(State state) {
621 ASSERT(signaling_thread_->IsCurrent());
622 if (state != state_) {
623 LogState(state_, state);
625 SignalState(this, state_);
626 signaling_thread_->Post(this, MSG_STATE);
628 SignalNewDescription();
631 void BaseSession::SetError(Error error, const std::string& error_desc) {
632 ASSERT(signaling_thread_->IsCurrent());
633 if (error != error_) {
635 error_desc_ = error_desc;
636 SignalError(this, error);
640 void BaseSession::OnSignalingReady() {
641 ASSERT(signaling_thread()->IsCurrent());
642 for (TransportMap::iterator iter = transports_.begin();
643 iter != transports_.end(); ++iter) {
644 iter->second->OnSignalingReady();
648 // TODO(juberti): Since PushdownLocalTD now triggers the connection process to
649 // start, remove this method once everyone calls PushdownLocalTD.
650 void BaseSession::SpeculativelyConnectAllTransportChannels() {
651 // Put all transports into the connecting state.
652 for (TransportMap::iterator iter = transports_.begin();
653 iter != transports_.end(); ++iter) {
654 iter->second->ConnectChannels();
658 bool BaseSession::OnRemoteCandidates(const std::string& content_name,
659 const Candidates& candidates,
660 std::string* error) {
661 // Give candidates to the appropriate transport, and tell that transport
662 // to start connecting, if it's not already doing so.
663 TransportProxy* transproxy = GetTransportProxy(content_name);
665 *error = "Unknown content name " + content_name;
668 if (!transproxy->OnRemoteCandidates(candidates, error)) {
671 // TODO(juberti): Remove this call once we can be sure that we always have
672 // a local transport description (which will trigger the connection).
673 transproxy->ConnectChannels();
677 bool BaseSession::MaybeEnableMuxingSupport() {
678 // We need both a local and remote description to decide if we should mux.
679 if ((state_ == STATE_SENTINITIATE ||
680 state_ == STATE_RECEIVEDINITIATE) &&
681 ((local_description_ == NULL) ||
682 (remote_description_ == NULL))) {
686 // In order to perform the multiplexing, we need all proxies to be in the
687 // negotiated state, i.e. to have implementations underneath.
688 // Ensure that this is the case, regardless of whether we are going to mux.
689 for (TransportMap::iterator iter = transports_.begin();
690 iter != transports_.end(); ++iter) {
691 ASSERT(iter->second->negotiated());
692 if (!iter->second->negotiated())
696 // If both sides agree to BUNDLE, mux all the specified contents onto the
697 // transport belonging to the first content name in the BUNDLE group.
698 // If the contents are already muxed, this will be a no-op.
699 // TODO(juberti): Should this check that local and remote have configured
700 // BUNDLE the same way?
701 bool candidates_allocated = IsCandidateAllocationDone();
702 const ContentGroup* local_bundle_group =
703 local_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
704 const ContentGroup* remote_bundle_group =
705 remote_description()->GetGroupByName(GROUP_TYPE_BUNDLE);
706 if (local_bundle_group && remote_bundle_group &&
707 local_bundle_group->FirstContentName()) {
708 const std::string* content_name = local_bundle_group->FirstContentName();
709 const ContentInfo* content =
710 local_description_->GetContentByName(*content_name);
711 ASSERT(content != NULL);
712 if (!SetSelectedProxy(content->name, local_bundle_group)) {
713 LOG(LS_WARNING) << "Failed to set up BUNDLE";
717 // If we weren't done gathering before, we might be done now, as a result
719 LOG(LS_INFO) << "Enabling BUNDLE, bundling onto transport: "
721 if (!candidates_allocated) {
722 MaybeCandidateAllocationDone();
725 LOG(LS_INFO) << "No BUNDLE information, not bundling.";
730 bool BaseSession::SetSelectedProxy(const std::string& content_name,
731 const ContentGroup* muxed_group) {
732 TransportProxy* selected_proxy = GetTransportProxy(content_name);
733 if (!selected_proxy) {
737 ASSERT(selected_proxy->negotiated());
738 for (TransportMap::iterator iter = transports_.begin();
739 iter != transports_.end(); ++iter) {
740 // If content is part of the mux group, then repoint its proxy at the
741 // transport object that we have chosen to mux onto. If the proxy
742 // is already pointing at the right object, it will be a no-op.
743 if (muxed_group->HasContentName(iter->first) &&
744 !iter->second->SetupMux(selected_proxy)) {
751 void BaseSession::OnTransportCandidatesAllocationDone(Transport* transport) {
752 // TODO(juberti): This is a clunky way of processing the done signal. Instead,
753 // TransportProxy should receive the done signal directly, set its allocated
754 // flag internally, and then reissue the done signal to Session.
755 // Overall we should make TransportProxy receive *all* the signals from
756 // Transport, since this removes the need to manually iterate over all
757 // the transports, as is needed to make sure signals are handled properly
760 ASSERT(!IsCandidateAllocationDone());
762 for (TransportMap::iterator iter = transports_.begin();
763 iter != transports_.end(); ++iter) {
764 if (iter->second->impl() == transport) {
765 iter->second->set_candidates_allocated(true);
768 MaybeCandidateAllocationDone();
771 bool BaseSession::IsCandidateAllocationDone() const {
772 for (TransportMap::const_iterator iter = transports_.begin();
773 iter != transports_.end(); ++iter) {
774 if (!iter->second->candidates_allocated())
780 void BaseSession::MaybeCandidateAllocationDone() {
781 if (IsCandidateAllocationDone()) {
782 LOG(LS_INFO) << "Candidate gathering is complete.";
783 OnCandidatesAllocationDone();
787 void BaseSession::OnRoleConflict() {
789 LOG(LS_WARNING) << "Repeat of role conflict signal from Transport.";
794 for (TransportMap::iterator iter = transports_.begin();
795 iter != transports_.end(); ++iter) {
796 // Role will be reverse of initial role setting.
797 IceRole role = initiator_ ? ICEROLE_CONTROLLED : ICEROLE_CONTROLLING;
798 iter->second->SetIceRole(role);
802 void BaseSession::LogState(State old_state, State new_state) {
803 LOG(LS_INFO) << "Session:" << id()
804 << " Old state:" << StateToString(old_state)
805 << " New state:" << StateToString(new_state)
806 << " Type:" << content_type()
807 << " Transport:" << transport_type();
810 bool BaseSession::GetTransportDescription(const SessionDescription* description,
811 const std::string& content_name,
812 TransportDescription* tdesc) {
813 if (!description || !tdesc) {
816 const TransportInfo* transport_info =
817 description->GetTransportInfoByName(content_name);
818 if (!transport_info) {
821 *tdesc = transport_info->description;
825 void BaseSession::SignalNewDescription() {
826 ContentAction action;
827 ContentSource source;
828 if (!GetContentAction(&action, &source)) {
831 if (source == CS_LOCAL) {
832 SignalNewLocalDescription(this, action);
834 SignalNewRemoteDescription(this, action);
838 bool BaseSession::GetContentAction(ContentAction* action,
839 ContentSource* source) {
841 // new local description
842 case STATE_SENTINITIATE:
846 case STATE_SENTPRACCEPT:
847 *action = CA_PRANSWER;
850 case STATE_SENTACCEPT:
854 // new remote description
855 case STATE_RECEIVEDINITIATE:
859 case STATE_RECEIVEDPRACCEPT:
860 *action = CA_PRANSWER;
863 case STATE_RECEIVEDACCEPT:
873 void BaseSession::OnMessage(talk_base::Message *pmsg) {
874 switch (pmsg->message_id) {
876 // Session timeout has occured.
877 SetError(ERROR_TIME, "Session timeout has occured.");
882 case STATE_SENTACCEPT:
883 case STATE_RECEIVEDACCEPT:
884 SetState(STATE_INPROGRESS);
888 // Explicitly ignoring some states here.
895 Session::Session(SessionManager* session_manager,
896 const std::string& local_name,
897 const std::string& initiator_name,
898 const std::string& sid,
899 const std::string& content_type,
900 SessionClient* client)
901 : BaseSession(session_manager->signaling_thread(),
902 session_manager->worker_thread(),
903 session_manager->port_allocator(),
904 sid, content_type, initiator_name == local_name) {
905 ASSERT(client != NULL);
906 session_manager_ = session_manager;
907 local_name_ = local_name;
908 initiator_name_ = initiator_name;
909 transport_parser_ = new P2PTransportParser();
911 initiate_acked_ = false;
912 current_protocol_ = PROTOCOL_HYBRID;
915 Session::~Session() {
916 delete transport_parser_;
919 bool Session::Initiate(const std::string &to,
920 const SessionDescription* sdesc) {
921 ASSERT(signaling_thread()->IsCurrent());
924 // Only from STATE_INIT
925 if (state() != STATE_INIT)
928 // Setup for signaling.
930 set_local_description(sdesc);
931 if (!CreateTransportProxies(GetEmptyTransportInfos(sdesc->contents()),
933 LOG(LS_ERROR) << "Could not create transports: " << error.text;
937 if (!SendInitiateMessage(sdesc, &error)) {
938 LOG(LS_ERROR) << "Could not send initiate message: " << error.text;
942 // We need to connect transport proxy and impl here so that we can process
943 // the TransportDescriptions.
944 SpeculativelyConnectAllTransportChannels();
946 PushdownTransportDescription(CS_LOCAL, CA_OFFER, NULL);
947 SetState(Session::STATE_SENTINITIATE);
951 bool Session::Accept(const SessionDescription* sdesc) {
952 ASSERT(signaling_thread()->IsCurrent());
954 // Only if just received initiate
955 if (state() != STATE_RECEIVEDINITIATE)
958 // Setup for signaling.
959 set_local_description(sdesc);
962 if (!SendAcceptMessage(sdesc, &error)) {
963 LOG(LS_ERROR) << "Could not send accept message: " << error.text;
966 // TODO(juberti): Add BUNDLE support to transport-info messages.
967 PushdownTransportDescription(CS_LOCAL, CA_ANSWER, NULL);
968 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported.
969 SetState(Session::STATE_SENTACCEPT);
973 bool Session::Reject(const std::string& reason) {
974 ASSERT(signaling_thread()->IsCurrent());
976 // Reject is sent in response to an initiate or modify, to reject the
978 if (state() != STATE_RECEIVEDINITIATE && state() != STATE_RECEIVEDMODIFY)
982 if (!SendRejectMessage(reason, &error)) {
983 LOG(LS_ERROR) << "Could not send reject message: " << error.text;
987 SetState(STATE_SENTREJECT);
991 bool Session::TerminateWithReason(const std::string& reason) {
992 ASSERT(signaling_thread()->IsCurrent());
994 // Either side can terminate, at any time.
996 case STATE_SENTTERMINATE:
997 case STATE_RECEIVEDTERMINATE:
1000 case STATE_SENTREJECT:
1001 case STATE_RECEIVEDREJECT:
1002 // We don't need to send terminate if we sent or received a reject...
1008 if (!SendTerminateMessage(reason, &error)) {
1009 LOG(LS_ERROR) << "Could not send terminate message: " << error.text;
1015 SetState(STATE_SENTTERMINATE);
1019 bool Session::SendInfoMessage(const XmlElements& elems,
1020 const std::string& remote_name) {
1021 ASSERT(signaling_thread()->IsCurrent());
1023 if (!SendMessage(ACTION_SESSION_INFO, elems, remote_name, &error)) {
1024 LOG(LS_ERROR) << "Could not send info message " << error.text;
1030 bool Session::SendDescriptionInfoMessage(const ContentInfos& contents) {
1032 WriteError write_error;
1033 if (!WriteDescriptionInfo(current_protocol_,
1035 GetContentParsers(),
1036 &elems, &write_error)) {
1037 LOG(LS_ERROR) << "Could not write description info message: "
1038 << write_error.text;
1042 if (!SendMessage(ACTION_DESCRIPTION_INFO, elems, &error)) {
1043 LOG(LS_ERROR) << "Could not send description info message: "
1050 TransportInfos Session::GetEmptyTransportInfos(
1051 const ContentInfos& contents) const {
1052 TransportInfos tinfos;
1053 for (ContentInfos::const_iterator content = contents.begin();
1054 content != contents.end(); ++content) {
1055 tinfos.push_back(TransportInfo(content->name,
1056 TransportDescription(transport_type(),
1063 bool Session::OnRemoteCandidates(
1064 const TransportInfos& tinfos, ParseError* error) {
1065 for (TransportInfos::const_iterator tinfo = tinfos.begin();
1066 tinfo != tinfos.end(); ++tinfo) {
1067 std::string str_error;
1068 if (!BaseSession::OnRemoteCandidates(
1069 tinfo->content_name, tinfo->description.candidates, &str_error)) {
1070 return BadParse(str_error, error);
1076 bool Session::CreateTransportProxies(const TransportInfos& tinfos,
1077 SessionError* error) {
1078 for (TransportInfos::const_iterator tinfo = tinfos.begin();
1079 tinfo != tinfos.end(); ++tinfo) {
1080 if (tinfo->description.transport_type != transport_type()) {
1081 error->SetText("No supported transport in offer.");
1085 GetOrCreateTransportProxy(tinfo->content_name);
1090 TransportParserMap Session::GetTransportParsers() {
1091 TransportParserMap parsers;
1092 parsers[transport_type()] = transport_parser_;
1096 CandidateTranslatorMap Session::GetCandidateTranslators() {
1097 CandidateTranslatorMap translators;
1098 // NOTE: This technique makes it impossible to parse G-ICE
1099 // candidates in session-initiate messages because the channels
1100 // aren't yet created at that point. Since we don't use candidates
1101 // in session-initiate messages, we should be OK. Once we switch to
1102 // ICE, this translation shouldn't be necessary.
1103 for (TransportMap::const_iterator iter = transport_proxies().begin();
1104 iter != transport_proxies().end(); ++iter) {
1105 translators[iter->first] = iter->second;
1110 ContentParserMap Session::GetContentParsers() {
1111 ContentParserMap parsers;
1112 parsers[content_type()] = client_;
1113 // We need to be able parse both RTP-based and SCTP-based Jingle
1114 // with the same client.
1115 if (content_type() == NS_JINGLE_RTP) {
1116 parsers[NS_JINGLE_DRAFT_SCTP] = client_;
1121 void Session::OnTransportRequestSignaling(Transport* transport) {
1122 ASSERT(signaling_thread()->IsCurrent());
1123 TransportProxy* transproxy = GetTransportProxy(transport);
1124 ASSERT(transproxy != NULL);
1126 // Reset candidate allocation status for the transport proxy.
1127 transproxy->set_candidates_allocated(false);
1129 SignalRequestSignaling(this);
1132 void Session::OnTransportConnecting(Transport* transport) {
1133 // This is an indication that we should begin watching the writability
1134 // state of the transport.
1135 OnTransportWritable(transport);
1138 void Session::OnTransportWritable(Transport* transport) {
1139 ASSERT(signaling_thread()->IsCurrent());
1141 // If the transport is not writable, start a timer to make sure that it
1142 // becomes writable within a reasonable amount of time. If it does not, we
1143 // terminate since we can't actually send data. If the transport is writable,
1144 // cancel the timer. Note that writability transitions may occur repeatedly
1145 // during the lifetime of the session.
1146 signaling_thread()->Clear(this, MSG_TIMEOUT);
1147 if (transport->HasChannels() && !transport->writable()) {
1148 signaling_thread()->PostDelayed(
1149 session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT);
1153 void Session::OnTransportProxyCandidatesReady(TransportProxy* transproxy,
1154 const Candidates& candidates) {
1155 ASSERT(signaling_thread()->IsCurrent());
1156 if (transproxy != NULL) {
1157 if (initiator() && !initiate_acked_) {
1158 // TODO: This is to work around server re-ordering
1159 // messages. We send the candidates once the session-initiate
1160 // is acked. Once we have fixed the server to guarantee message
1161 // order, we can remove this case.
1162 transproxy->AddUnsentCandidates(candidates);
1164 if (!transproxy->negotiated()) {
1165 transproxy->AddSentCandidates(candidates);
1168 if (!SendTransportInfoMessage(transproxy, candidates, &error)) {
1169 LOG(LS_ERROR) << "Could not send transport info message: "
1177 void Session::OnTransportSendError(Transport* transport,
1178 const buzz::XmlElement* stanza,
1179 const buzz::QName& name,
1180 const std::string& type,
1181 const std::string& text,
1182 const buzz::XmlElement* extra_info) {
1183 ASSERT(signaling_thread()->IsCurrent());
1184 SignalErrorMessage(this, stanza, name, type, text, extra_info);
1187 void Session::OnIncomingMessage(const SessionMessage& msg) {
1188 ASSERT(signaling_thread()->IsCurrent());
1189 ASSERT(state() == STATE_INIT || msg.from == remote_name());
1191 if (current_protocol_== PROTOCOL_HYBRID) {
1192 if (msg.protocol == PROTOCOL_GINGLE) {
1193 current_protocol_ = PROTOCOL_GINGLE;
1195 current_protocol_ = PROTOCOL_JINGLE;
1202 case ACTION_SESSION_INITIATE:
1203 valid = OnInitiateMessage(msg, &error);
1205 case ACTION_SESSION_INFO:
1206 valid = OnInfoMessage(msg);
1208 case ACTION_SESSION_ACCEPT:
1209 valid = OnAcceptMessage(msg, &error);
1211 case ACTION_SESSION_REJECT:
1212 valid = OnRejectMessage(msg, &error);
1214 case ACTION_SESSION_TERMINATE:
1215 valid = OnTerminateMessage(msg, &error);
1217 case ACTION_TRANSPORT_INFO:
1218 valid = OnTransportInfoMessage(msg, &error);
1220 case ACTION_TRANSPORT_ACCEPT:
1221 valid = OnTransportAcceptMessage(msg, &error);
1223 case ACTION_DESCRIPTION_INFO:
1224 valid = OnDescriptionInfoMessage(msg, &error);
1227 valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST,
1228 "unknown session message type",
1233 SendAcknowledgementMessage(msg.stanza);
1235 SignalErrorMessage(this, msg.stanza, error.type,
1236 "modify", error.text, NULL);
1240 void Session::OnIncomingResponse(const buzz::XmlElement* orig_stanza,
1241 const buzz::XmlElement* response_stanza,
1242 const SessionMessage& msg) {
1243 ASSERT(signaling_thread()->IsCurrent());
1245 if (msg.type == ACTION_SESSION_INITIATE) {
1250 void Session::OnInitiateAcked() {
1251 // TODO: This is to work around server re-ordering
1252 // messages. We send the candidates once the session-initiate
1253 // is acked. Once we have fixed the server to guarantee message
1254 // order, we can remove this case.
1255 if (!initiate_acked_) {
1256 initiate_acked_ = true;
1258 SendAllUnsentTransportInfoMessages(&error);
1262 void Session::OnFailedSend(const buzz::XmlElement* orig_stanza,
1263 const buzz::XmlElement* error_stanza) {
1264 ASSERT(signaling_thread()->IsCurrent());
1267 ParseError parse_error;
1268 if (!ParseSessionMessage(orig_stanza, &msg, &parse_error)) {
1269 LOG(LS_ERROR) << "Error parsing failed send: " << parse_error.text
1270 << ":" << orig_stanza;
1274 // If the error is a session redirect, call OnRedirectError, which will
1275 // continue the session with a new remote JID.
1276 SessionRedirect redirect;
1277 if (FindSessionRedirect(error_stanza, &redirect)) {
1279 if (!OnRedirectError(redirect, &error)) {
1280 // TODO: Should we send a message back? The standard
1281 // says nothing about it.
1282 std::ostringstream desc;
1283 desc << "Failed to redirect: " << error.text;
1284 LOG(LS_ERROR) << desc.str();
1285 SetError(ERROR_RESPONSE, desc.str());
1290 std::string error_type = "cancel";
1292 const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR);
1294 error_type = error->Attr(buzz::QN_TYPE);
1296 LOG(LS_ERROR) << "Session error:\n" << error->Str() << "\n"
1297 << "in response to:\n" << orig_stanza->Str();
1299 // don't crash if <error> is missing
1300 LOG(LS_ERROR) << "Session error without <error/> element, ignoring";
1304 if (msg.type == ACTION_TRANSPORT_INFO) {
1305 // Transport messages frequently generate errors because they are sent right
1306 // when we detect a network failure. For that reason, we ignore such
1307 // errors, because if we do not establish writability again, we will
1308 // terminate anyway. The exceptions are transport-specific error tags,
1309 // which we pass on to the respective transport.
1310 } else if ((error_type != "continue") && (error_type != "wait")) {
1311 // We do not set an error if the other side said it is okay to continue
1312 // (possibly after waiting). These errors can be ignored.
1313 SetError(ERROR_RESPONSE, "");
1317 bool Session::OnInitiateMessage(const SessionMessage& msg,
1318 MessageError* error) {
1319 if (!CheckState(STATE_INIT, error))
1322 SessionInitiate init;
1323 if (!ParseSessionInitiate(msg.protocol, msg.action_elem,
1324 GetContentParsers(), GetTransportParsers(),
1325 GetCandidateTranslators(),
1329 SessionError session_error;
1330 if (!CreateTransportProxies(init.transports, &session_error)) {
1331 return BadMessage(buzz::QN_STANZA_NOT_ACCEPTABLE,
1332 session_error.text, error);
1335 set_remote_name(msg.from);
1336 set_initiator_name(msg.initiator);
1337 set_remote_description(new SessionDescription(init.ClearContents(),
1340 // Updating transport with TransportDescription.
1341 PushdownTransportDescription(CS_REMOTE, CA_OFFER, NULL);
1342 SetState(STATE_RECEIVEDINITIATE);
1344 // Users of Session may listen to state change and call Reject().
1345 if (state() != STATE_SENTREJECT) {
1346 if (!OnRemoteCandidates(init.transports, error))
1349 // TODO(juberti): Auto-generate and push down the local transport answer.
1350 // This is necessary for trickling to work with RFC 5245 ICE.
1355 bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) {
1356 if (!CheckState(STATE_SENTINITIATE, error))
1359 SessionAccept accept;
1360 if (!ParseSessionAccept(msg.protocol, msg.action_elem,
1361 GetContentParsers(), GetTransportParsers(),
1362 GetCandidateTranslators(),
1367 // If we get an accept, we can assume the initiate has been
1368 // received, even if we haven't gotten an IQ response.
1371 set_remote_description(new SessionDescription(accept.ClearContents(),
1374 // Updating transport with TransportDescription.
1375 PushdownTransportDescription(CS_REMOTE, CA_ANSWER, NULL);
1376 MaybeEnableMuxingSupport(); // Enable transport channel mux if supported.
1377 SetState(STATE_RECEIVEDACCEPT);
1379 if (!OnRemoteCandidates(accept.transports, error))
1385 bool Session::OnRejectMessage(const SessionMessage& msg, MessageError* error) {
1386 if (!CheckState(STATE_SENTINITIATE, error))
1389 SetState(STATE_RECEIVEDREJECT);
1393 bool Session::OnInfoMessage(const SessionMessage& msg) {
1394 SignalInfoMessage(this, msg.action_elem);
1398 bool Session::OnTerminateMessage(const SessionMessage& msg,
1399 MessageError* error) {
1400 SessionTerminate term;
1401 if (!ParseSessionTerminate(msg.protocol, msg.action_elem, &term, error))
1404 SignalReceivedTerminateReason(this, term.reason);
1405 if (term.debug_reason != buzz::STR_EMPTY) {
1406 LOG(LS_VERBOSE) << "Received error on call: " << term.debug_reason;
1409 SetState(STATE_RECEIVEDTERMINATE);
1413 bool Session::OnTransportInfoMessage(const SessionMessage& msg,
1414 MessageError* error) {
1415 TransportInfos tinfos;
1416 if (!ParseTransportInfos(msg.protocol, msg.action_elem,
1417 initiator_description()->contents(),
1418 GetTransportParsers(), GetCandidateTranslators(),
1422 if (!OnRemoteCandidates(tinfos, error))
1428 bool Session::OnTransportAcceptMessage(const SessionMessage& msg,
1429 MessageError* error) {
1430 // TODO: Currently here only for compatibility with
1431 // Gingle 1.1 clients (notably, Google Voice).
1435 bool Session::OnDescriptionInfoMessage(const SessionMessage& msg,
1436 MessageError* error) {
1437 if (!CheckState(STATE_INPROGRESS, error))
1440 DescriptionInfo description_info;
1441 if (!ParseDescriptionInfo(msg.protocol, msg.action_elem,
1442 GetContentParsers(), GetTransportParsers(),
1443 GetCandidateTranslators(),
1444 &description_info, error)) {
1448 ContentInfos& updated_contents = description_info.contents;
1450 // TODO: Currently, reflector sends back
1451 // video stream updates even for an audio-only call, which causes
1452 // this to fail. Put this back once reflector is fixed.
1454 // ContentInfos::iterator it;
1455 // First, ensure all updates are valid before modifying remote_description_.
1456 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
1457 // if (remote_description()->GetContentByName(it->name) == NULL) {
1462 // TODO: We used to replace contents from an update, but
1463 // that no longer works with partial updates. We need to figure out
1464 // a way to merge patial updates into contents. For now, users of
1465 // Session should listen to SignalRemoteDescriptionUpdate and handle
1466 // updates. They should not expect remote_description to be the
1469 // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) {
1470 // remote_description()->RemoveContentByName(it->name);
1471 // remote_description()->AddContent(it->name, it->type, it->description);
1475 SignalRemoteDescriptionUpdate(this, updated_contents);
1479 bool BareJidsEqual(const std::string& name1,
1480 const std::string& name2) {
1481 buzz::Jid jid1(name1);
1482 buzz::Jid jid2(name2);
1484 return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2);
1487 bool Session::OnRedirectError(const SessionRedirect& redirect,
1488 SessionError* error) {
1489 MessageError message_error;
1490 if (!CheckState(STATE_SENTINITIATE, &message_error)) {
1491 return BadWrite(message_error.text, error);
1494 if (!BareJidsEqual(remote_name(), redirect.target))
1495 return BadWrite("Redirection not allowed: must be the same bare jid.",
1498 // When we receive a redirect, we point the session at the new JID
1499 // and resend the candidates.
1500 set_remote_name(redirect.target);
1501 return (SendInitiateMessage(local_description(), error) &&
1502 ResendAllTransportInfoMessages(error));
1505 bool Session::CheckState(State expected, MessageError* error) {
1506 if (state() != expected) {
1507 // The server can deliver messages out of order/repeated for various
1508 // reasons. For example, if the server does not recive our iq response,
1509 // it could assume that the iq it sent was lost, and will then send
1510 // it again. Ideally, we should implement reliable messaging with
1511 // duplicate elimination.
1512 return BadMessage(buzz::QN_STANZA_NOT_ALLOWED,
1513 "message not allowed in current state",
1519 void Session::SetError(Error error, const std::string& error_desc) {
1520 BaseSession::SetError(error, error_desc);
1521 if (error != ERROR_NONE)
1522 signaling_thread()->Post(this, MSG_ERROR);
1525 void Session::OnMessage(talk_base::Message* pmsg) {
1526 // preserve this because BaseSession::OnMessage may modify it
1527 State orig_state = state();
1529 BaseSession::OnMessage(pmsg);
1531 switch (pmsg->message_id) {
1533 TerminateWithReason(STR_TERMINATE_ERROR);
1537 switch (orig_state) {
1538 case STATE_SENTREJECT:
1539 case STATE_RECEIVEDREJECT:
1540 // Assume clean termination.
1544 case STATE_SENTTERMINATE:
1545 case STATE_RECEIVEDTERMINATE:
1546 session_manager_->DestroySession(this);
1550 // Explicitly ignoring some states here.
1557 bool Session::SendInitiateMessage(const SessionDescription* sdesc,
1558 SessionError* error) {
1559 SessionInitiate init;
1560 init.contents = sdesc->contents();
1561 init.transports = GetEmptyTransportInfos(init.contents);
1562 init.groups = sdesc->groups();
1563 return SendMessage(ACTION_SESSION_INITIATE, init, error);
1566 bool Session::WriteSessionAction(
1567 SignalingProtocol protocol, const SessionInitiate& init,
1568 XmlElements* elems, WriteError* error) {
1569 return WriteSessionInitiate(protocol, init.contents, init.transports,
1570 GetContentParsers(), GetTransportParsers(),
1571 GetCandidateTranslators(), init.groups,
1575 bool Session::SendAcceptMessage(const SessionDescription* sdesc,
1576 SessionError* error) {
1578 if (!WriteSessionAccept(current_protocol_,
1580 GetEmptyTransportInfos(sdesc->contents()),
1581 GetContentParsers(), GetTransportParsers(),
1582 GetCandidateTranslators(), sdesc->groups(),
1586 return SendMessage(ACTION_SESSION_ACCEPT, elems, error);
1589 bool Session::SendRejectMessage(const std::string& reason,
1590 SessionError* error) {
1591 SessionTerminate term(reason);
1592 return SendMessage(ACTION_SESSION_REJECT, term, error);
1595 bool Session::SendTerminateMessage(const std::string& reason,
1596 SessionError* error) {
1597 SessionTerminate term(reason);
1598 return SendMessage(ACTION_SESSION_TERMINATE, term, error);
1601 bool Session::WriteSessionAction(SignalingProtocol protocol,
1602 const SessionTerminate& term,
1603 XmlElements* elems, WriteError* error) {
1604 WriteSessionTerminate(protocol, term, elems);
1608 bool Session::SendTransportInfoMessage(const TransportInfo& tinfo,
1609 SessionError* error) {
1610 return SendMessage(ACTION_TRANSPORT_INFO, tinfo, error);
1613 bool Session::SendTransportInfoMessage(const TransportProxy* transproxy,
1614 const Candidates& candidates,
1615 SessionError* error) {
1616 return SendTransportInfoMessage(TransportInfo(transproxy->content_name(),
1617 TransportDescription(transproxy->type(), std::vector<std::string>(),
1618 std::string(), std::string(), ICEMODE_FULL,
1619 CONNECTIONROLE_NONE, NULL, candidates)), error);
1622 bool Session::WriteSessionAction(SignalingProtocol protocol,
1623 const TransportInfo& tinfo,
1624 XmlElements* elems, WriteError* error) {
1625 TransportInfos tinfos;
1626 tinfos.push_back(tinfo);
1627 return WriteTransportInfos(protocol, tinfos,
1628 GetTransportParsers(), GetCandidateTranslators(),
1632 bool Session::ResendAllTransportInfoMessages(SessionError* error) {
1633 for (TransportMap::const_iterator iter = transport_proxies().begin();
1634 iter != transport_proxies().end(); ++iter) {
1635 TransportProxy* transproxy = iter->second;
1636 if (transproxy->sent_candidates().size() > 0) {
1637 if (!SendTransportInfoMessage(
1638 transproxy, transproxy->sent_candidates(), error)) {
1639 LOG(LS_ERROR) << "Could not resend transport info messages: "
1643 transproxy->ClearSentCandidates();
1649 bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) {
1650 for (TransportMap::const_iterator iter = transport_proxies().begin();
1651 iter != transport_proxies().end(); ++iter) {
1652 TransportProxy* transproxy = iter->second;
1653 if (transproxy->unsent_candidates().size() > 0) {
1654 if (!SendTransportInfoMessage(
1655 transproxy, transproxy->unsent_candidates(), error)) {
1656 LOG(LS_ERROR) << "Could not send unsent transport info messages: "
1660 transproxy->ClearUnsentCandidates();
1666 bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
1667 SessionError* error) {
1668 return SendMessage(type, action_elems, remote_name(), error);
1671 bool Session::SendMessage(ActionType type, const XmlElements& action_elems,
1672 const std::string& remote_name, SessionError* error) {
1673 talk_base::scoped_ptr<buzz::XmlElement> stanza(
1674 new buzz::XmlElement(buzz::QN_IQ));
1676 SessionMessage msg(current_protocol_, type, id(), initiator_name());
1677 msg.to = remote_name;
1678 WriteSessionMessage(msg, action_elems, stanza.get());
1680 SignalOutgoingMessage(this, stanza.get());
1684 template <typename Action>
1685 bool Session::SendMessage(ActionType type, const Action& action,
1686 SessionError* error) {
1687 talk_base::scoped_ptr<buzz::XmlElement> stanza(
1688 new buzz::XmlElement(buzz::QN_IQ));
1689 if (!WriteActionMessage(type, action, stanza.get(), error))
1692 SignalOutgoingMessage(this, stanza.get());
1696 template <typename Action>
1697 bool Session::WriteActionMessage(ActionType type, const Action& action,
1698 buzz::XmlElement* stanza,
1699 WriteError* error) {
1700 if (current_protocol_ == PROTOCOL_HYBRID) {
1701 if (!WriteActionMessage(PROTOCOL_JINGLE, type, action, stanza, error))
1703 if (!WriteActionMessage(PROTOCOL_GINGLE, type, action, stanza, error))
1706 if (!WriteActionMessage(current_protocol_, type, action, stanza, error))
1712 template <typename Action>
1713 bool Session::WriteActionMessage(SignalingProtocol protocol,
1714 ActionType type, const Action& action,
1715 buzz::XmlElement* stanza, WriteError* error) {
1716 XmlElements action_elems;
1717 if (!WriteSessionAction(protocol, action, &action_elems, error))
1720 SessionMessage msg(protocol, type, id(), initiator_name());
1721 msg.to = remote_name();
1723 WriteSessionMessage(msg, action_elems, stanza);
1727 void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) {
1728 talk_base::scoped_ptr<buzz::XmlElement> ack(
1729 new buzz::XmlElement(buzz::QN_IQ));
1730 ack->SetAttr(buzz::QN_TO, remote_name());
1731 ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID));
1732 ack->SetAttr(buzz::QN_TYPE, "result");
1734 SignalOutgoingMessage(this, ack.get());
1737 } // namespace cricket