diff --git a/talk/session/media/call.h b/talk/session/media/call.h index 382cdc5ca..f9920436f 100644 --- a/talk/session/media/call.h +++ b/talk/session/media/call.h @@ -37,12 +37,12 @@ #include "talk/media/base/screencastid.h" #include "talk/media/base/streamparams.h" #include "talk/media/base/videocommon.h" +#include "webrtc/p2p/base/session.h" #include "webrtc/p2p/client/socketmonitor.h" #include "talk/session/media/audiomonitor.h" #include "talk/session/media/currentspeakermonitor.h" #include "talk/session/media/mediamessages.h" #include "talk/session/media/mediasession.h" -#include "webrtc/libjingle/session/session.h" #include "webrtc/libjingle/xmpp/jid.h" #include "webrtc/base/messagequeue.h" @@ -160,9 +160,9 @@ class Call : public rtc::MessageHandler, public sigslot::has_slots<> { sigslot::signal0<> SignalSetupToCallVoicemail; sigslot::signal2 SignalAddSession; sigslot::signal2 SignalRemoveSession; - sigslot::signal3 + sigslot::signal3 SignalSessionState; - sigslot::signal3 + sigslot::signal3 SignalSessionError; sigslot::signal3 SignalReceivedTerminateReason; @@ -192,7 +192,7 @@ class Call : public rtc::MessageHandler, public sigslot::has_slots<> { private: void OnMessage(rtc::Message* message); void OnSessionState(BaseSession* base_session, BaseSession::State state); - void OnSessionError(BaseSession* base_session, BaseSession::Error error); + void OnSessionError(BaseSession* base_session, Session::Error error); void OnSessionInfoMessage( Session* session, const buzz::XmlElement* action_elem); void OnViewRequest( diff --git a/talk/session/media/channel_unittest.cc b/talk/session/media/channel_unittest.cc index b8428a8ae..b652b7475 100644 --- a/talk/session/media/channel_unittest.cc +++ b/talk/session/media/channel_unittest.cc @@ -1555,21 +1555,21 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { // Test failures in SetLocalContent. media_channel1_->set_fail_set_recv_codecs(true); session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); - session1_.SetState(cricket::BaseSession::STATE_SENTINITIATE); + session1_.SetState(cricket::Session::STATE_SENTINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_CONTENT, session1_.error()); media_channel1_->set_fail_set_recv_codecs(true); session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); - session1_.SetState(cricket::BaseSession::STATE_SENTACCEPT); + session1_.SetState(cricket::Session::STATE_SENTACCEPT); EXPECT_EQ(cricket::BaseSession::ERROR_CONTENT, session1_.error()); // Test failures in SetRemoteContent. media_channel1_->set_fail_set_send_codecs(true); session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); - session1_.SetState(cricket::BaseSession::STATE_RECEIVEDINITIATE); + session1_.SetState(cricket::Session::STATE_RECEIVEDINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_CONTENT, session1_.error()); media_channel1_->set_fail_set_send_codecs(true); session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); - session1_.SetState(cricket::BaseSession::STATE_RECEIVEDACCEPT); + session1_.SetState(cricket::Session::STATE_RECEIVEDACCEPT); EXPECT_EQ(cricket::BaseSession::ERROR_CONTENT, session1_.error()); } @@ -1581,7 +1581,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { session1_.set_local_description(sdesc); session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); - session1_.SetState(cricket::BaseSession::STATE_SENTINITIATE); + session1_.SetState(cricket::Session::STATE_SENTINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasSendStream(1)); @@ -1589,7 +1589,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { sdesc = CreateSessionDescriptionWithStream(2); session1_.set_local_description(sdesc); - session1_.SetState(cricket::BaseSession::STATE_SENTINITIATE); + session1_.SetState(cricket::Session::STATE_SENTINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_FALSE(media_channel1_->HasSendStream(1)); EXPECT_TRUE(media_channel1_->HasSendStream(2)); @@ -1603,13 +1603,13 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { session1_.set_remote_description(sdesc); session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); - session1_.SetState(cricket::BaseSession::STATE_RECEIVEDINITIATE); + session1_.SetState(cricket::Session::STATE_RECEIVEDINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasRecvStream(1)); sdesc = CreateSessionDescriptionWithStream(2); session1_.set_remote_description(sdesc); - session1_.SetState(cricket::BaseSession::STATE_RECEIVEDINITIATE); + session1_.SetState(cricket::Session::STATE_RECEIVEDINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_FALSE(media_channel1_->HasRecvStream(1)); EXPECT_TRUE(media_channel1_->HasRecvStream(2)); @@ -1623,7 +1623,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { session1_.set_remote_description(sdesc); session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); - session1_.SetState(cricket::BaseSession::STATE_RECEIVEDINITIATE); + session1_.SetState(cricket::Session::STATE_RECEIVEDINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasRecvStream(1)); @@ -1631,7 +1631,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { sdesc = CreateSessionDescriptionWithStream(2); session1_.set_local_description(sdesc); - session1_.SetState(cricket::BaseSession::STATE_SENTPRACCEPT); + session1_.SetState(cricket::Session::STATE_SENTPRACCEPT); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasRecvStream(1)); EXPECT_TRUE(media_channel1_->HasSendStream(2)); @@ -1640,7 +1640,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { sdesc = CreateSessionDescriptionWithStream(3); session1_.set_local_description(sdesc); - session1_.SetState(cricket::BaseSession::STATE_SENTACCEPT); + session1_.SetState(cricket::Session::STATE_SENTACCEPT); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasRecvStream(1)); EXPECT_FALSE(media_channel1_->HasSendStream(2)); @@ -1655,7 +1655,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { session1_.set_local_description(sdesc); session1_.SetError(cricket::BaseSession::ERROR_NONE, ""); - session1_.SetState(cricket::BaseSession::STATE_SENTINITIATE); + session1_.SetState(cricket::Session::STATE_SENTINITIATE); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasSendStream(1)); @@ -1663,7 +1663,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { sdesc = CreateSessionDescriptionWithStream(2); session1_.set_remote_description(sdesc); - session1_.SetState(cricket::BaseSession::STATE_RECEIVEDPRACCEPT); + session1_.SetState(cricket::Session::STATE_RECEIVEDPRACCEPT); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasSendStream(1)); EXPECT_TRUE(media_channel1_->HasRecvStream(2)); @@ -1672,7 +1672,7 @@ class ChannelTest : public testing::Test, public sigslot::has_slots<> { sdesc = CreateSessionDescriptionWithStream(3); session1_.set_remote_description(sdesc); - session1_.SetState(cricket::BaseSession::STATE_RECEIVEDACCEPT); + session1_.SetState(cricket::Session::STATE_RECEIVEDACCEPT); EXPECT_EQ(cricket::BaseSession::ERROR_NONE, session1_.error()); EXPECT_TRUE(media_channel1_->HasSendStream(1)); EXPECT_FALSE(media_channel1_->HasRecvStream(2)); diff --git a/talk/session/media/currentspeakermonitor.h b/talk/session/media/currentspeakermonitor.h index 64beb9824..0397a6d94 100644 --- a/talk/session/media/currentspeakermonitor.h +++ b/talk/session/media/currentspeakermonitor.h @@ -39,6 +39,7 @@ namespace cricket { class BaseSession; +class Session; struct AudioInfo; struct MediaStreams; diff --git a/talk/session/tunnel/pseudotcpchannel.h b/talk/session/tunnel/pseudotcpchannel.h index e216f35f9..ad6d907f5 100644 --- a/talk/session/tunnel/pseudotcpchannel.h +++ b/talk/session/tunnel/pseudotcpchannel.h @@ -29,10 +29,10 @@ #define TALK_SESSION_TUNNEL_PSEUDOTCPCHANNEL_H_ #include "webrtc/p2p/base/pseudotcp.h" +#include "webrtc/p2p/base/session.h" #include "webrtc/base/criticalsection.h" #include "webrtc/base/messagequeue.h" #include "webrtc/base/stream.h" -#include "webrtc/libjingle/session/session.h" namespace rtc { class Thread; diff --git a/webrtc/libjingle/session/session.cc b/webrtc/libjingle/session/session.cc deleted file mode 100644 index cf07c1ffe..000000000 --- a/webrtc/libjingle/session/session.cc +++ /dev/null @@ -1,859 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#include "webrtc/libjingle/session/session.h" - -#include "webrtc/libjingle/xmpp/constants.h" -#include "webrtc/libjingle/xmpp/jid.h" -#include "webrtc/p2p/base/p2ptransport.h" - -namespace cricket { - -bool BadMessage(const buzz::QName type, - const std::string& text, - MessageError* err) { - err->SetType(type); - err->SetText(text); - return false; -} - -Session::Session(SessionManager* session_manager, - const std::string& local_name, - const std::string& initiator_name, - const std::string& sid, - const std::string& content_type, - SessionClient* client) - : BaseSession(session_manager->signaling_thread(), - session_manager->worker_thread(), - session_manager->port_allocator(), - sid, content_type, initiator_name == local_name) { - ASSERT(client != NULL); - session_manager_ = session_manager; - local_name_ = local_name; - initiator_name_ = initiator_name; - transport_parser_ = new P2PTransportParser(); - client_ = client; - initiate_acked_ = false; - current_protocol_ = PROTOCOL_HYBRID; -} - -Session::~Session() { - delete transport_parser_; -} - -bool Session::Initiate(const std::string& to, - const SessionDescription* sdesc) { - ASSERT(signaling_thread()->IsCurrent()); - SessionError error; - - // Only from STATE_INIT - if (state() != STATE_INIT) - return false; - - // Setup for signaling. - set_remote_name(to); - set_local_description(sdesc); - if (!CreateTransportProxies(GetEmptyTransportInfos(sdesc->contents()), - &error)) { - LOG(LS_ERROR) << "Could not create transports: " << error.text; - return false; - } - - if (!SendInitiateMessage(sdesc, &error)) { - LOG(LS_ERROR) << "Could not send initiate message: " << error.text; - return false; - } - - // We need to connect transport proxy and impl here so that we can process - // the TransportDescriptions. - SpeculativelyConnectAllTransportChannels(); - - PushdownTransportDescription(CS_LOCAL, CA_OFFER, NULL); - SetState(Session::STATE_SENTINITIATE); - return true; -} - -bool Session::Accept(const SessionDescription* sdesc) { - ASSERT(signaling_thread()->IsCurrent()); - - // Only if just received initiate - if (state() != STATE_RECEIVEDINITIATE) - return false; - - // Setup for signaling. - set_local_description(sdesc); - - SessionError error; - if (!SendAcceptMessage(sdesc, &error)) { - LOG(LS_ERROR) << "Could not send accept message: " << error.text; - return false; - } - // TODO(juberti): Add BUNDLE support to transport-info messages. - PushdownTransportDescription(CS_LOCAL, CA_ANSWER, NULL); - MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. - SetState(Session::STATE_SENTACCEPT); - return true; -} - -bool Session::Reject(const std::string& reason) { - ASSERT(signaling_thread()->IsCurrent()); - - // Reject is sent in response to an initiate or modify, to reject the - // request - if (state() != STATE_RECEIVEDINITIATE && state() != STATE_RECEIVEDMODIFY) - return false; - - SessionError error; - if (!SendRejectMessage(reason, &error)) { - LOG(LS_ERROR) << "Could not send reject message: " << error.text; - return false; - } - - SetState(STATE_SENTREJECT); - return true; -} - -bool Session::TerminateWithReason(const std::string& reason) { - ASSERT(signaling_thread()->IsCurrent()); - - // Either side can terminate, at any time. - switch (state()) { - case STATE_SENTTERMINATE: - case STATE_RECEIVEDTERMINATE: - return false; - - case STATE_SENTREJECT: - case STATE_RECEIVEDREJECT: - // We don't need to send terminate if we sent or received a reject... - // it's implicit. - break; - - default: - SessionError error; - if (!SendTerminateMessage(reason, &error)) { - LOG(LS_ERROR) << "Could not send terminate message: " << error.text; - return false; - } - break; - } - - SetState(STATE_SENTTERMINATE); - return true; -} - -bool Session::SendInfoMessage(const XmlElements& elems, - const std::string& remote_name) { - ASSERT(signaling_thread()->IsCurrent()); - SessionError error; - if (!SendMessage(ACTION_SESSION_INFO, elems, remote_name, &error)) { - LOG(LS_ERROR) << "Could not send info message " << error.text; - return false; - } - return true; -} - -bool Session::SendDescriptionInfoMessage(const ContentInfos& contents) { - XmlElements elems; - WriteError write_error; - if (!WriteDescriptionInfo(current_protocol_, - contents, - GetContentParsers(), - &elems, &write_error)) { - LOG(LS_ERROR) << "Could not write description info message: " - << write_error.text; - return false; - } - SessionError error; - if (!SendMessage(ACTION_DESCRIPTION_INFO, elems, &error)) { - LOG(LS_ERROR) << "Could not send description info message: " - << error.text; - return false; - } - return true; -} - -TransportInfos Session::GetEmptyTransportInfos( - const ContentInfos& contents) const { - TransportInfos tinfos; - for (ContentInfos::const_iterator content = contents.begin(); - content != contents.end(); ++content) { - tinfos.push_back(TransportInfo(content->name, - TransportDescription(transport_type(), - std::string(), - std::string()))); - } - return tinfos; -} - -bool Session::OnRemoteCandidates( - const TransportInfos& tinfos, ParseError* error) { - for (TransportInfos::const_iterator tinfo = tinfos.begin(); - tinfo != tinfos.end(); ++tinfo) { - std::string str_error; - if (!BaseSession::OnRemoteCandidates( - tinfo->content_name, tinfo->description.candidates, &str_error)) { - return BadParse(str_error, error); - } - } - return true; -} - -bool Session::CreateTransportProxies(const TransportInfos& tinfos, - SessionError* error) { - for (TransportInfos::const_iterator tinfo = tinfos.begin(); - tinfo != tinfos.end(); ++tinfo) { - if (tinfo->description.transport_type != transport_type()) { - error->SetText("No supported transport in offer."); - return false; - } - - GetOrCreateTransportProxy(tinfo->content_name); - } - return true; -} - -TransportParserMap Session::GetTransportParsers() { - TransportParserMap parsers; - parsers[transport_type()] = transport_parser_; - return parsers; -} - -CandidateTranslatorMap Session::GetCandidateTranslators() { - CandidateTranslatorMap translators; - // NOTE: This technique makes it impossible to parse G-ICE - // candidates in session-initiate messages because the channels - // aren't yet created at that point. Since we don't use candidates - // in session-initiate messages, we should be OK. Once we switch to - // ICE, this translation shouldn't be necessary. - for (TransportMap::const_iterator iter = transport_proxies().begin(); - iter != transport_proxies().end(); ++iter) { - translators[iter->first] = iter->second; - } - return translators; -} - -ContentParserMap Session::GetContentParsers() { - ContentParserMap parsers; - parsers[content_type()] = client_; - // We need to be able parse both RTP-based and SCTP-based Jingle - // with the same client. - if (content_type() == NS_JINGLE_RTP) { - parsers[NS_JINGLE_DRAFT_SCTP] = client_; - } - return parsers; -} - -void Session::OnTransportRequestSignaling(Transport* transport) { - ASSERT(signaling_thread()->IsCurrent()); - TransportProxy* transproxy = GetTransportProxy(transport); - ASSERT(transproxy != NULL); - if (transproxy) { - // Reset candidate allocation status for the transport proxy. - transproxy->set_candidates_allocated(false); - } - SignalRequestSignaling(this); -} - -void Session::OnTransportConnecting(Transport* transport) { - // This is an indication that we should begin watching the writability - // state of the transport. - OnTransportWritable(transport); -} - -void Session::OnTransportWritable(Transport* transport) { - ASSERT(signaling_thread()->IsCurrent()); - - // If the transport is not writable, start a timer to make sure that it - // becomes writable within a reasonable amount of time. If it does not, we - // terminate since we can't actually send data. If the transport is writable, - // cancel the timer. Note that writability transitions may occur repeatedly - // during the lifetime of the session. - signaling_thread()->Clear(this, MSG_TIMEOUT); - if (transport->HasChannels() && !transport->writable()) { - signaling_thread()->PostDelayed( - session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); - } -} - -void Session::OnTransportProxyCandidatesReady(TransportProxy* transproxy, - const Candidates& candidates) { - ASSERT(signaling_thread()->IsCurrent()); - if (transproxy != NULL) { - if (initiator() && !initiate_acked_) { - // TODO: This is to work around server re-ordering - // messages. We send the candidates once the session-initiate - // is acked. Once we have fixed the server to guarantee message - // order, we can remove this case. - transproxy->AddUnsentCandidates(candidates); - } else { - if (!transproxy->negotiated()) { - transproxy->AddSentCandidates(candidates); - } - SessionError error; - if (!SendTransportInfoMessage(transproxy, candidates, &error)) { - LOG(LS_ERROR) << "Could not send transport info message: " - << error.text; - return; - } - } - } -} - -void Session::OnIncomingMessage(const SessionMessage& msg) { - ASSERT(signaling_thread()->IsCurrent()); - ASSERT(state() == STATE_INIT || msg.from == remote_name()); - - if (current_protocol_== PROTOCOL_HYBRID) { - if (msg.protocol == PROTOCOL_GINGLE) { - current_protocol_ = PROTOCOL_GINGLE; - } else { - current_protocol_ = PROTOCOL_JINGLE; - } - } - - bool valid = false; - MessageError error; - switch (msg.type) { - case ACTION_SESSION_INITIATE: - valid = OnInitiateMessage(msg, &error); - break; - case ACTION_SESSION_INFO: - valid = OnInfoMessage(msg); - break; - case ACTION_SESSION_ACCEPT: - valid = OnAcceptMessage(msg, &error); - break; - case ACTION_SESSION_REJECT: - valid = OnRejectMessage(msg, &error); - break; - case ACTION_SESSION_TERMINATE: - valid = OnTerminateMessage(msg, &error); - break; - case ACTION_TRANSPORT_INFO: - valid = OnTransportInfoMessage(msg, &error); - break; - case ACTION_TRANSPORT_ACCEPT: - valid = OnTransportAcceptMessage(msg, &error); - break; - case ACTION_DESCRIPTION_INFO: - valid = OnDescriptionInfoMessage(msg, &error); - break; - default: - valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST, - "unknown session message type", - &error); - } - - if (valid) { - SendAcknowledgementMessage(msg.stanza); - } else { - SignalErrorMessage(this, msg.stanza, error.type, - "modify", error.text, NULL); - } -} - -void Session::OnIncomingResponse(const buzz::XmlElement* orig_stanza, - const buzz::XmlElement* response_stanza, - const SessionMessage& msg) { - ASSERT(signaling_thread()->IsCurrent()); - - if (msg.type == ACTION_SESSION_INITIATE) { - OnInitiateAcked(); - } -} - -void Session::OnInitiateAcked() { - // TODO: This is to work around server re-ordering - // messages. We send the candidates once the session-initiate - // is acked. Once we have fixed the server to guarantee message - // order, we can remove this case. - if (!initiate_acked_) { - initiate_acked_ = true; - SessionError error; - SendAllUnsentTransportInfoMessages(&error); - } -} - -void Session::OnFailedSend(const buzz::XmlElement* orig_stanza, - const buzz::XmlElement* error_stanza) { - ASSERT(signaling_thread()->IsCurrent()); - - SessionMessage msg; - ParseError parse_error; - if (!ParseSessionMessage(orig_stanza, &msg, &parse_error)) { - LOG(LS_ERROR) << "Error parsing failed send: " << parse_error.text - << ":" << orig_stanza; - return; - } - - // If the error is a session redirect, call OnRedirectError, which will - // continue the session with a new remote JID. - SessionRedirect redirect; - if (FindSessionRedirect(error_stanza, &redirect)) { - SessionError error; - if (!OnRedirectError(redirect, &error)) { - // TODO: Should we send a message back? The standard - // says nothing about it. - std::ostringstream desc; - desc << "Failed to redirect: " << error.text; - LOG(LS_ERROR) << desc.str(); - SetError(ERROR_RESPONSE, desc.str()); - } - return; - } - - std::string error_type = "cancel"; - - const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR); - if (error) { - error_type = error->Attr(buzz::QN_TYPE); - - LOG(LS_ERROR) << "Session error:\n" << error->Str() << "\n" - << "in response to:\n" << orig_stanza->Str(); - } else { - // don't crash if is missing - LOG(LS_ERROR) << "Session error without element, ignoring"; - return; - } - - if (msg.type == ACTION_TRANSPORT_INFO) { - // Transport messages frequently generate errors because they are sent right - // when we detect a network failure. For that reason, we ignore such - // errors, because if we do not establish writability again, we will - // terminate anyway. The exceptions are transport-specific error tags, - // which we pass on to the respective transport. - } else if ((error_type != "continue") && (error_type != "wait")) { - // We do not set an error if the other side said it is okay to continue - // (possibly after waiting). These errors can be ignored. - SetError(ERROR_RESPONSE, ""); - } -} - -bool Session::OnInitiateMessage(const SessionMessage& msg, - MessageError* error) { - if (!CheckState(STATE_INIT, error)) - return false; - - SessionInitiate init; - if (!ParseSessionInitiate(msg.protocol, msg.action_elem, - GetContentParsers(), GetTransportParsers(), - GetCandidateTranslators(), - &init, error)) - return false; - - SessionError session_error; - if (!CreateTransportProxies(init.transports, &session_error)) { - return BadMessage(buzz::QN_STANZA_NOT_ACCEPTABLE, - session_error.text, error); - } - - set_remote_name(msg.from); - set_initiator_name(msg.initiator); - set_remote_description(new SessionDescription(init.ClearContents(), - init.transports, - init.groups)); - // Updating transport with TransportDescription. - PushdownTransportDescription(CS_REMOTE, CA_OFFER, NULL); - SetState(STATE_RECEIVEDINITIATE); - - // Users of Session may listen to state change and call Reject(). - if (state() != STATE_SENTREJECT) { - if (!OnRemoteCandidates(init.transports, error)) - return false; - - // TODO(juberti): Auto-generate and push down the local transport answer. - // This is necessary for trickling to work with RFC 5245 ICE. - } - return true; -} - -bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) { - if (!CheckState(STATE_SENTINITIATE, error)) - return false; - - SessionAccept accept; - if (!ParseSessionAccept(msg.protocol, msg.action_elem, - GetContentParsers(), GetTransportParsers(), - GetCandidateTranslators(), - &accept, error)) { - return false; - } - - // If we get an accept, we can assume the initiate has been - // received, even if we haven't gotten an IQ response. - OnInitiateAcked(); - - set_remote_description(new SessionDescription(accept.ClearContents(), - accept.transports, - accept.groups)); - // Updating transport with TransportDescription. - PushdownTransportDescription(CS_REMOTE, CA_ANSWER, NULL); - MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. - SetState(STATE_RECEIVEDACCEPT); - - if (!OnRemoteCandidates(accept.transports, error)) - return false; - - return true; -} - -bool Session::OnRejectMessage(const SessionMessage& msg, MessageError* error) { - if (!CheckState(STATE_SENTINITIATE, error)) - return false; - - SetState(STATE_RECEIVEDREJECT); - return true; -} - -bool Session::OnInfoMessage(const SessionMessage& msg) { - SignalInfoMessage(this, msg.action_elem); - return true; -} - -bool Session::OnTerminateMessage(const SessionMessage& msg, - MessageError* error) { - SessionTerminate term; - if (!ParseSessionTerminate(msg.protocol, msg.action_elem, &term, error)) - return false; - - SignalReceivedTerminateReason(this, term.reason); - if (term.debug_reason != buzz::STR_EMPTY) { - LOG(LS_VERBOSE) << "Received error on call: " << term.debug_reason; - } - - SetState(STATE_RECEIVEDTERMINATE); - return true; -} - -bool Session::OnTransportInfoMessage(const SessionMessage& msg, - MessageError* error) { - TransportInfos tinfos; - if (!ParseTransportInfos(msg.protocol, msg.action_elem, - initiator_description()->contents(), - GetTransportParsers(), GetCandidateTranslators(), - &tinfos, error)) - return false; - - if (!OnRemoteCandidates(tinfos, error)) - return false; - - return true; -} - -bool Session::OnTransportAcceptMessage(const SessionMessage& msg, - MessageError* error) { - // TODO: Currently here only for compatibility with - // Gingle 1.1 clients (notably, Google Voice). - return true; -} - -bool Session::OnDescriptionInfoMessage(const SessionMessage& msg, - MessageError* error) { - if (!CheckState(STATE_INPROGRESS, error)) - return false; - - DescriptionInfo description_info; - if (!ParseDescriptionInfo(msg.protocol, msg.action_elem, - GetContentParsers(), GetTransportParsers(), - GetCandidateTranslators(), - &description_info, error)) { - return false; - } - - ContentInfos& updated_contents = description_info.contents; - - // TODO: Currently, reflector sends back - // video stream updates even for an audio-only call, which causes - // this to fail. Put this back once reflector is fixed. - // - // ContentInfos::iterator it; - // First, ensure all updates are valid before modifying remote_description_. - // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) { - // if (remote_description()->GetContentByName(it->name) == NULL) { - // return false; - // } - // } - - // TODO: We used to replace contents from an update, but - // that no longer works with partial updates. We need to figure out - // a way to merge patial updates into contents. For now, users of - // Session should listen to SignalRemoteDescriptionUpdate and handle - // updates. They should not expect remote_description to be the - // latest value. - // - // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) { - // remote_description()->RemoveContentByName(it->name); - // remote_description()->AddContent(it->name, it->type, it->description); - // } - // } - - SignalRemoteDescriptionUpdate(this, updated_contents); - return true; -} - -bool BareJidsEqual(const std::string& name1, - const std::string& name2) { - buzz::Jid jid1(name1); - buzz::Jid jid2(name2); - - return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2); -} - -bool Session::OnRedirectError(const SessionRedirect& redirect, - SessionError* error) { - MessageError message_error; - if (!CheckState(STATE_SENTINITIATE, &message_error)) { - return BadWrite(message_error.text, error); - } - - if (!BareJidsEqual(remote_name(), redirect.target)) - return BadWrite("Redirection not allowed: must be the same bare jid.", - error); - - // When we receive a redirect, we point the session at the new JID - // and resend the candidates. - set_remote_name(redirect.target); - return (SendInitiateMessage(local_description(), error) && - ResendAllTransportInfoMessages(error)); -} - -bool Session::CheckState(State expected, MessageError* error) { - if (state() != expected) { - // The server can deliver messages out of order/repeated for various - // reasons. For example, if the server does not recive our iq response, - // it could assume that the iq it sent was lost, and will then send - // it again. Ideally, we should implement reliable messaging with - // duplicate elimination. - return BadMessage(buzz::QN_STANZA_NOT_ALLOWED, - "message not allowed in current state", - error); - } - return true; -} - -void Session::SetError(Error error, const std::string& error_desc) { - BaseSession::SetError(error, error_desc); - if (error != ERROR_NONE) - signaling_thread()->Post(this, MSG_ERROR); -} - -void Session::OnMessage(rtc::Message* pmsg) { - // preserve this because BaseSession::OnMessage may modify it - State orig_state = state(); - - BaseSession::OnMessage(pmsg); - - switch (pmsg->message_id) { - case MSG_ERROR: - TerminateWithReason(STR_TERMINATE_ERROR); - break; - - case MSG_STATE: - switch (orig_state) { - case STATE_SENTREJECT: - case STATE_RECEIVEDREJECT: - // Assume clean termination. - Terminate(); - break; - - case STATE_SENTTERMINATE: - case STATE_RECEIVEDTERMINATE: - session_manager_->DestroySession(this); - break; - - default: - // Explicitly ignoring some states here. - break; - } - break; - } -} - -bool Session::SendInitiateMessage(const SessionDescription* sdesc, - SessionError* error) { - SessionInitiate init; - init.contents = sdesc->contents(); - init.transports = GetEmptyTransportInfos(init.contents); - init.groups = sdesc->groups(); - return SendMessage(ACTION_SESSION_INITIATE, init, error); -} - -bool Session::WriteSessionAction( - SignalingProtocol protocol, const SessionInitiate& init, - XmlElements* elems, WriteError* error) { - return WriteSessionInitiate(protocol, init.contents, init.transports, - GetContentParsers(), GetTransportParsers(), - GetCandidateTranslators(), init.groups, - elems, error); -} - -bool Session::SendAcceptMessage(const SessionDescription* sdesc, - SessionError* error) { - XmlElements elems; - if (!WriteSessionAccept(current_protocol_, - sdesc->contents(), - GetEmptyTransportInfos(sdesc->contents()), - GetContentParsers(), GetTransportParsers(), - GetCandidateTranslators(), sdesc->groups(), - &elems, error)) { - return false; - } - return SendMessage(ACTION_SESSION_ACCEPT, elems, error); -} - -bool Session::SendRejectMessage(const std::string& reason, - SessionError* error) { - SessionTerminate term(reason); - return SendMessage(ACTION_SESSION_REJECT, term, error); -} - -bool Session::SendTerminateMessage(const std::string& reason, - SessionError* error) { - SessionTerminate term(reason); - return SendMessage(ACTION_SESSION_TERMINATE, term, error); -} - -bool Session::WriteSessionAction(SignalingProtocol protocol, - const SessionTerminate& term, - XmlElements* elems, WriteError* error) { - WriteSessionTerminate(protocol, term, elems); - return true; -} - -bool Session::SendTransportInfoMessage(const TransportInfo& tinfo, - SessionError* error) { - return SendMessage(ACTION_TRANSPORT_INFO, tinfo, error); -} - -bool Session::SendTransportInfoMessage(const TransportProxy* transproxy, - const Candidates& candidates, - SessionError* error) { - return SendTransportInfoMessage(TransportInfo(transproxy->content_name(), - TransportDescription(transproxy->type(), std::vector(), - std::string(), std::string(), ICEMODE_FULL, - CONNECTIONROLE_NONE, NULL, candidates)), error); -} - -bool Session::WriteSessionAction(SignalingProtocol protocol, - const TransportInfo& tinfo, - XmlElements* elems, WriteError* error) { - TransportInfos tinfos; - tinfos.push_back(tinfo); - return WriteTransportInfos(protocol, tinfos, - GetTransportParsers(), GetCandidateTranslators(), - elems, error); -} - -bool Session::ResendAllTransportInfoMessages(SessionError* error) { - for (TransportMap::const_iterator iter = transport_proxies().begin(); - iter != transport_proxies().end(); ++iter) { - TransportProxy* transproxy = iter->second; - if (transproxy->sent_candidates().size() > 0) { - if (!SendTransportInfoMessage( - transproxy, transproxy->sent_candidates(), error)) { - LOG(LS_ERROR) << "Could not resend transport info messages: " - << error->text; - return false; - } - transproxy->ClearSentCandidates(); - } - } - return true; -} - -bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) { - for (TransportMap::const_iterator iter = transport_proxies().begin(); - iter != transport_proxies().end(); ++iter) { - TransportProxy* transproxy = iter->second; - if (transproxy->unsent_candidates().size() > 0) { - if (!SendTransportInfoMessage( - transproxy, transproxy->unsent_candidates(), error)) { - LOG(LS_ERROR) << "Could not send unsent transport info messages: " - << error->text; - return false; - } - transproxy->ClearUnsentCandidates(); - } - } - return true; -} - -bool Session::SendMessage(ActionType type, const XmlElements& action_elems, - SessionError* error) { - return SendMessage(type, action_elems, remote_name(), error); -} - -bool Session::SendMessage(ActionType type, const XmlElements& action_elems, - const std::string& remote_name, SessionError* error) { - rtc::scoped_ptr stanza( - new buzz::XmlElement(buzz::QN_IQ)); - - SessionMessage msg(current_protocol_, type, id(), initiator_name()); - msg.to = remote_name; - WriteSessionMessage(msg, action_elems, stanza.get()); - - SignalOutgoingMessage(this, stanza.get()); - return true; -} - -template -bool Session::SendMessage(ActionType type, const Action& action, - SessionError* error) { - rtc::scoped_ptr stanza( - new buzz::XmlElement(buzz::QN_IQ)); - if (!WriteActionMessage(type, action, stanza.get(), error)) - return false; - - SignalOutgoingMessage(this, stanza.get()); - return true; -} - -template -bool Session::WriteActionMessage(ActionType type, const Action& action, - buzz::XmlElement* stanza, - WriteError* error) { - if (current_protocol_ == PROTOCOL_HYBRID) { - if (!WriteActionMessage(PROTOCOL_JINGLE, type, action, stanza, error)) - return false; - if (!WriteActionMessage(PROTOCOL_GINGLE, type, action, stanza, error)) - return false; - } else { - if (!WriteActionMessage(current_protocol_, type, action, stanza, error)) - return false; - } - return true; -} - -template -bool Session::WriteActionMessage(SignalingProtocol protocol, - ActionType type, const Action& action, - buzz::XmlElement* stanza, WriteError* error) { - XmlElements action_elems; - if (!WriteSessionAction(protocol, action, &action_elems, error)) - return false; - - SessionMessage msg(protocol, type, id(), initiator_name()); - msg.to = remote_name(); - - WriteSessionMessage(msg, action_elems, stanza); - return true; -} - -void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) { - rtc::scoped_ptr ack( - new buzz::XmlElement(buzz::QN_IQ)); - ack->SetAttr(buzz::QN_TO, remote_name()); - ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); - ack->SetAttr(buzz::QN_TYPE, "result"); - - SignalOutgoingMessage(this, ack.get()); -} - -} // namespace cricket diff --git a/webrtc/libjingle/session/session.h b/webrtc/libjingle/session/session.h deleted file mode 100644 index 4f185f1d3..000000000 --- a/webrtc/libjingle/session/session.h +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2004 The WebRTC Project Authors. All rights reserved. - * - * Use of this source code is governed by a BSD-style license - * that can be found in the LICENSE file in the root of the source - * tree. An additional intellectual property rights grant can be found - * in the file PATENTS. All contributing project authors may - * be found in the AUTHORS file in the root of the source tree. - */ - -#ifndef WEBRTC_LIBJINGLE_SESSION_SESSION_H_ -#define WEBRTC_LIBJINGLE_SESSION_SESSION_H_ - -#include - -#include "webrtc/libjingle/xmllite/xmlelement.h" -#include "webrtc/libjingle/xmpp/constants.h" -#include "webrtc/p2p/base/parsing.h" -#include "webrtc/p2p/base/session.h" -#include "webrtc/p2p/base/sessionclient.h" -#include "webrtc/p2p/base/sessionmanager.h" -#include "webrtc/p2p/base/sessionmessages.h" - - -namespace cricket { - -// Used for errors that will send back a specific error message to the -// remote peer. We add "type" to the errors because it's needed for -// SignalErrorMessage. -struct MessageError : ParseError { - buzz::QName type; - - // if unset, assume type is a parse error - MessageError() : ParseError(), type(buzz::QN_STANZA_BAD_REQUEST) {} - - void SetType(const buzz::QName type) { - this->type = type; - } -}; - -// Used for errors that may be returned by public session methods that -// can fail. -// TODO: Use this error in Session::Initiate and -// Session::Accept. -struct SessionError : WriteError { -}; - -// A specific Session created by the SessionManager, using XMPP for protocol. -class Session : public BaseSession { - public: - // Returns the manager that created and owns this session. - SessionManager* session_manager() const { return session_manager_; } - - // Returns the client that is handling the application data of this session. - SessionClient* client() const { return client_; } - - // Returns the JID of this client. - const std::string& local_name() const { return local_name_; } - - // Returns the JID of the other peer in this session. - const std::string& remote_name() const { return remote_name_; } - - // Set the JID of the other peer in this session. - // Typically the remote_name_ is set when the session is initiated. - // However, sometimes (e.g when a proxy is used) the peer name is - // known after the BaseSession has been initiated and it must be updated - // explicitly. - void set_remote_name(const std::string& name) { remote_name_ = name; } - - // Set the JID of the initiator of this session. Allows for the overriding - // of the initiator to be a third-party, eg. the MUC JID when creating p2p - // sessions. - void set_initiator_name(const std::string& name) { initiator_name_ = name; } - - // Indicates the JID of the entity who initiated this session. - // In special cases, may be different than both local_name and remote_name. - const std::string& initiator_name() const { return initiator_name_; } - - SignalingProtocol current_protocol() const { return current_protocol_; } - - void set_current_protocol(SignalingProtocol protocol) { - current_protocol_ = protocol; - } - - // Updates the error state, signaling if necessary. - virtual void SetError(Error error, const std::string& error_desc); - - // When the session needs to send signaling messages, it beings by requesting - // signaling. The client should handle this by calling OnSignalingReady once - // it is ready to send the messages. - // (These are called only by SessionManager.) - sigslot::signal1 SignalRequestSignaling; - void OnSignalingReady() { BaseSession::OnSignalingReady(); } - - // Takes ownership of session description. - // TODO: Add an error argument to pass back to the caller. - bool Initiate(const std::string& to, - const SessionDescription* sdesc); - - // When we receive an initiate, we create a session in the - // RECEIVEDINITIATE state and respond by accepting or rejecting. - // Takes ownership of session description. - // TODO: Add an error argument to pass back to the caller. - bool Accept(const SessionDescription* sdesc); - bool Reject(const std::string& reason); - bool Terminate() { - return TerminateWithReason(STR_TERMINATE_SUCCESS); - } - bool TerminateWithReason(const std::string& reason); - // Fired whenever we receive a terminate message along with a reason - sigslot::signal2 SignalReceivedTerminateReason; - - // The two clients in the session may also send one another - // arbitrary XML messages, which are called "info" messages. Sending - // takes ownership of the given elements. The signal does not; the - // parent element will be deleted after the signal. - bool SendInfoMessage(const XmlElements& elems, - const std::string& remote_name); - bool SendDescriptionInfoMessage(const ContentInfos& contents); - sigslot::signal2 SignalInfoMessage; - - private: - // Creates or destroys a session. (These are called only SessionManager.) - Session(SessionManager *session_manager, - const std::string& local_name, const std::string& initiator_name, - const std::string& sid, const std::string& content_type, - SessionClient* client); - ~Session(); - // For each transport info, create a transport proxy. Can fail for - // incompatible transport types. - bool CreateTransportProxies(const TransportInfos& tinfos, - SessionError* error); - bool OnRemoteCandidates(const TransportInfos& tinfos, - ParseError* error); - // Returns a TransportInfo without candidates for each content name. - // Uses the transport_type_ of the session. - TransportInfos GetEmptyTransportInfos(const ContentInfos& contents) const; - - // Maps passed to serialization functions. - TransportParserMap GetTransportParsers(); - ContentParserMap GetContentParsers(); - CandidateTranslatorMap GetCandidateTranslators(); - - virtual void OnTransportRequestSignaling(Transport* transport); - virtual void OnTransportConnecting(Transport* transport); - virtual void OnTransportWritable(Transport* transport); - virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy, - const Candidates& candidates); - virtual void OnMessage(rtc::Message *pmsg); - - // Send various kinds of session messages. - bool SendInitiateMessage(const SessionDescription* sdesc, - SessionError* error); - bool SendAcceptMessage(const SessionDescription* sdesc, SessionError* error); - bool SendRejectMessage(const std::string& reason, SessionError* error); - bool SendTerminateMessage(const std::string& reason, SessionError* error); - bool SendTransportInfoMessage(const TransportInfo& tinfo, - SessionError* error); - bool SendTransportInfoMessage(const TransportProxy* transproxy, - const Candidates& candidates, - SessionError* error); - - bool ResendAllTransportInfoMessages(SessionError* error); - bool SendAllUnsentTransportInfoMessages(SessionError* error); - - // All versions of SendMessage send a message of the given type to - // the other client. Can pass either a set of elements or an - // "action", which must have a WriteSessionAction method to go along - // with it. Sending with an action supports sending a "hybrid" - // message. Sending with elements must be sent as Jingle or Gingle. - - // When passing elems, must be either Jingle or Gingle protocol. - // Takes ownership of action_elems. - bool SendMessage(ActionType type, const XmlElements& action_elems, - SessionError* error); - // Sends a messge, but overrides the remote name. - bool SendMessage(ActionType type, const XmlElements& action_elems, - const std::string& remote_name, - SessionError* error); - // When passing an action, may be Hybrid protocol. - template - bool SendMessage(ActionType type, const Action& action, - SessionError* error); - - // Helper methods to write the session message stanza. - template - bool WriteActionMessage(ActionType type, const Action& action, - buzz::XmlElement* stanza, WriteError* error); - template - bool WriteActionMessage(SignalingProtocol protocol, - ActionType type, const Action& action, - buzz::XmlElement* stanza, WriteError* error); - - // Sending messages in hybrid form requires being able to write them - // on a per-protocol basis with a common method signature, which all - // of these have. - bool WriteSessionAction(SignalingProtocol protocol, - const SessionInitiate& init, - XmlElements* elems, WriteError* error); - bool WriteSessionAction(SignalingProtocol protocol, - const TransportInfo& tinfo, - XmlElements* elems, WriteError* error); - bool WriteSessionAction(SignalingProtocol protocol, - const SessionTerminate& term, - XmlElements* elems, WriteError* error); - - // Sends a message back to the other client indicating that we have received - // and accepted their message. - void SendAcknowledgementMessage(const buzz::XmlElement* stanza); - - // Once signaling is ready, the session will use this signal to request the - // sending of each message. When messages are received by the other client, - // they should be handed to OnIncomingMessage. - // (These are called only by SessionManager.) - sigslot::signal2 SignalOutgoingMessage; - void OnIncomingMessage(const SessionMessage& msg); - - void OnIncomingResponse(const buzz::XmlElement* orig_stanza, - const buzz::XmlElement* response_stanza, - const SessionMessage& msg); - void OnInitiateAcked(); - void OnFailedSend(const buzz::XmlElement* orig_stanza, - const buzz::XmlElement* error_stanza); - - // Invoked when an error is found in an incoming message. This is translated - // into the appropriate XMPP response by SessionManager. - sigslot::signal6 SignalErrorMessage; - - // Handlers for the various types of messages. These functions may take - // pointers to the whole stanza or to just the session element. - bool OnInitiateMessage(const SessionMessage& msg, MessageError* error); - bool OnAcceptMessage(const SessionMessage& msg, MessageError* error); - bool OnRejectMessage(const SessionMessage& msg, MessageError* error); - bool OnInfoMessage(const SessionMessage& msg); - bool OnTerminateMessage(const SessionMessage& msg, MessageError* error); - bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error); - bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error); - bool OnDescriptionInfoMessage(const SessionMessage& msg, MessageError* error); - bool OnRedirectError(const SessionRedirect& redirect, SessionError* error); - - // Verifies that we are in the appropriate state to receive this message. - bool CheckState(State state, MessageError* error); - - SessionManager* session_manager_; - bool initiate_acked_; - std::string local_name_; - std::string initiator_name_; - std::string remote_name_; - SessionClient* client_; - TransportParser* transport_parser_; - // Keeps track of what protocol we are speaking. - SignalingProtocol current_protocol_; - - friend class SessionManager; // For access to constructor, destructor, - // and signaling related methods. -}; - -} // namespace cricket - -#endif // WEBRTC_LIBJINGLE_SESSION_SESSION_H_ diff --git a/webrtc/p2p/base/candidate.h b/webrtc/p2p/base/candidate.h index 7645a71c1..72bc69ef2 100644 --- a/webrtc/p2p/base/candidate.h +++ b/webrtc/p2p/base/candidate.h @@ -207,18 +207,6 @@ class Candidate { std::string tcptype_; }; -// Used during parsing and writing to map component to channel name -// and back. This is primarily for converting old G-ICE candidate -// signalling to new ICE candidate classes. -class CandidateTranslator { - public: - virtual ~CandidateTranslator() {} - virtual bool GetChannelNameFromComponent( - int component, std::string* channel_name) const = 0; - virtual bool GetComponentFromChannelName( - const std::string& channel_name, int* component) const = 0; -}; - } // namespace cricket #endif // WEBRTC_P2P_BASE_CANDIDATE_H_ diff --git a/webrtc/p2p/base/session.cc b/webrtc/p2p/base/session.cc index 38eadae74..9749b14ef 100644 --- a/webrtc/p2p/base/session.cc +++ b/webrtc/p2p/base/session.cc @@ -12,15 +12,17 @@ #include "webrtc/p2p/base/dtlstransport.h" #include "webrtc/p2p/base/p2ptransport.h" +#include "webrtc/p2p/base/sessionclient.h" #include "webrtc/p2p/base/transport.h" #include "webrtc/p2p/base/transportchannelproxy.h" #include "webrtc/p2p/base/transportinfo.h" +#include "webrtc/libjingle/xmpp/constants.h" +#include "webrtc/libjingle/xmpp/jid.h" #include "webrtc/base/bind.h" #include "webrtc/base/common.h" #include "webrtc/base/helpers.h" #include "webrtc/base/logging.h" #include "webrtc/base/scoped_ptr.h" -#include "webrtc/base/stringencode.h" #include "webrtc/base/sslstreamadapter.h" #include "webrtc/p2p/base/constants.h" @@ -29,6 +31,14 @@ namespace cricket { using rtc::Bind; +bool BadMessage(const buzz::QName type, + const std::string& text, + MessageError* err) { + err->SetType(type); + err->SetText(text); + return false; +} + TransportProxy::~TransportProxy() { for (ChannelMap::iterator iter = channels_.begin(); iter != channels_.end(); ++iter) { @@ -315,37 +325,37 @@ void TransportProxy::SetIdentity( std::string BaseSession::StateToString(State state) { switch (state) { - case BaseSession::STATE_INIT: + case Session::STATE_INIT: return "STATE_INIT"; - case BaseSession::STATE_SENTINITIATE: + case Session::STATE_SENTINITIATE: return "STATE_SENTINITIATE"; - case BaseSession::STATE_RECEIVEDINITIATE: + case Session::STATE_RECEIVEDINITIATE: return "STATE_RECEIVEDINITIATE"; - case BaseSession::STATE_SENTPRACCEPT: + case Session::STATE_SENTPRACCEPT: return "STATE_SENTPRACCEPT"; - case BaseSession::STATE_SENTACCEPT: + case Session::STATE_SENTACCEPT: return "STATE_SENTACCEPT"; - case BaseSession::STATE_RECEIVEDPRACCEPT: + case Session::STATE_RECEIVEDPRACCEPT: return "STATE_RECEIVEDPRACCEPT"; - case BaseSession::STATE_RECEIVEDACCEPT: + case Session::STATE_RECEIVEDACCEPT: return "STATE_RECEIVEDACCEPT"; - case BaseSession::STATE_SENTMODIFY: + case Session::STATE_SENTMODIFY: return "STATE_SENTMODIFY"; - case BaseSession::STATE_RECEIVEDMODIFY: + case Session::STATE_RECEIVEDMODIFY: return "STATE_RECEIVEDMODIFY"; - case BaseSession::STATE_SENTREJECT: + case Session::STATE_SENTREJECT: return "STATE_SENTREJECT"; - case BaseSession::STATE_RECEIVEDREJECT: + case Session::STATE_RECEIVEDREJECT: return "STATE_RECEIVEDREJECT"; - case BaseSession::STATE_SENTREDIRECT: + case Session::STATE_SENTREDIRECT: return "STATE_SENTREDIRECT"; - case BaseSession::STATE_SENTTERMINATE: + case Session::STATE_SENTTERMINATE: return "STATE_SENTTERMINATE"; - case BaseSession::STATE_RECEIVEDTERMINATE: + case Session::STATE_RECEIVEDTERMINATE: return "STATE_RECEIVEDTERMINATE"; - case BaseSession::STATE_INPROGRESS: + case Session::STATE_INPROGRESS: return "STATE_INPROGRESS"; - case BaseSession::STATE_DEINIT: + case Session::STATE_DEINIT: return "STATE_DEINIT"; default: break; @@ -537,6 +547,8 @@ TransportProxy* BaseSession::GetOrCreateTransportProxy( this, &BaseSession::OnTransportWritable); transport->SignalRequestSignaling.connect( this, &BaseSession::OnTransportRequestSignaling); + transport->SignalTransportError.connect( + this, &BaseSession::OnTransportSendError); transport->SignalRouteChange.connect( this, &BaseSession::OnTransportRouteChange); transport->SignalCandidatesAllocationDone.connect( @@ -903,4 +915,846 @@ void BaseSession::OnMessage(rtc::Message *pmsg) { } } +Session::Session(SessionManager* session_manager, + const std::string& local_name, + const std::string& initiator_name, + const std::string& sid, + const std::string& content_type, + SessionClient* client) + : BaseSession(session_manager->signaling_thread(), + session_manager->worker_thread(), + session_manager->port_allocator(), + sid, content_type, initiator_name == local_name) { + ASSERT(client != NULL); + session_manager_ = session_manager; + local_name_ = local_name; + initiator_name_ = initiator_name; + transport_parser_ = new P2PTransportParser(); + client_ = client; + initiate_acked_ = false; + current_protocol_ = PROTOCOL_HYBRID; +} + +Session::~Session() { + delete transport_parser_; +} + +bool Session::Initiate(const std::string& to, + const SessionDescription* sdesc) { + ASSERT(signaling_thread()->IsCurrent()); + SessionError error; + + // Only from STATE_INIT + if (state() != STATE_INIT) + return false; + + // Setup for signaling. + set_remote_name(to); + set_local_description(sdesc); + if (!CreateTransportProxies(GetEmptyTransportInfos(sdesc->contents()), + &error)) { + LOG(LS_ERROR) << "Could not create transports: " << error.text; + return false; + } + + if (!SendInitiateMessage(sdesc, &error)) { + LOG(LS_ERROR) << "Could not send initiate message: " << error.text; + return false; + } + + // We need to connect transport proxy and impl here so that we can process + // the TransportDescriptions. + SpeculativelyConnectAllTransportChannels(); + + PushdownTransportDescription(CS_LOCAL, CA_OFFER, NULL); + SetState(Session::STATE_SENTINITIATE); + return true; +} + +bool Session::Accept(const SessionDescription* sdesc) { + ASSERT(signaling_thread()->IsCurrent()); + + // Only if just received initiate + if (state() != STATE_RECEIVEDINITIATE) + return false; + + // Setup for signaling. + set_local_description(sdesc); + + SessionError error; + if (!SendAcceptMessage(sdesc, &error)) { + LOG(LS_ERROR) << "Could not send accept message: " << error.text; + return false; + } + // TODO(juberti): Add BUNDLE support to transport-info messages. + PushdownTransportDescription(CS_LOCAL, CA_ANSWER, NULL); + MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. + SetState(Session::STATE_SENTACCEPT); + return true; +} + +bool Session::Reject(const std::string& reason) { + ASSERT(signaling_thread()->IsCurrent()); + + // Reject is sent in response to an initiate or modify, to reject the + // request + if (state() != STATE_RECEIVEDINITIATE && state() != STATE_RECEIVEDMODIFY) + return false; + + SessionError error; + if (!SendRejectMessage(reason, &error)) { + LOG(LS_ERROR) << "Could not send reject message: " << error.text; + return false; + } + + SetState(STATE_SENTREJECT); + return true; +} + +bool Session::TerminateWithReason(const std::string& reason) { + ASSERT(signaling_thread()->IsCurrent()); + + // Either side can terminate, at any time. + switch (state()) { + case STATE_SENTTERMINATE: + case STATE_RECEIVEDTERMINATE: + return false; + + case STATE_SENTREJECT: + case STATE_RECEIVEDREJECT: + // We don't need to send terminate if we sent or received a reject... + // it's implicit. + break; + + default: + SessionError error; + if (!SendTerminateMessage(reason, &error)) { + LOG(LS_ERROR) << "Could not send terminate message: " << error.text; + return false; + } + break; + } + + SetState(STATE_SENTTERMINATE); + return true; +} + +bool Session::SendInfoMessage(const XmlElements& elems, + const std::string& remote_name) { + ASSERT(signaling_thread()->IsCurrent()); + SessionError error; + if (!SendMessage(ACTION_SESSION_INFO, elems, remote_name, &error)) { + LOG(LS_ERROR) << "Could not send info message " << error.text; + return false; + } + return true; +} + +bool Session::SendDescriptionInfoMessage(const ContentInfos& contents) { + XmlElements elems; + WriteError write_error; + if (!WriteDescriptionInfo(current_protocol_, + contents, + GetContentParsers(), + &elems, &write_error)) { + LOG(LS_ERROR) << "Could not write description info message: " + << write_error.text; + return false; + } + SessionError error; + if (!SendMessage(ACTION_DESCRIPTION_INFO, elems, &error)) { + LOG(LS_ERROR) << "Could not send description info message: " + << error.text; + return false; + } + return true; +} + +TransportInfos Session::GetEmptyTransportInfos( + const ContentInfos& contents) const { + TransportInfos tinfos; + for (ContentInfos::const_iterator content = contents.begin(); + content != contents.end(); ++content) { + tinfos.push_back(TransportInfo(content->name, + TransportDescription(transport_type(), + std::string(), + std::string()))); + } + return tinfos; +} + +bool Session::OnRemoteCandidates( + const TransportInfos& tinfos, ParseError* error) { + for (TransportInfos::const_iterator tinfo = tinfos.begin(); + tinfo != tinfos.end(); ++tinfo) { + std::string str_error; + if (!BaseSession::OnRemoteCandidates( + tinfo->content_name, tinfo->description.candidates, &str_error)) { + return BadParse(str_error, error); + } + } + return true; +} + +bool Session::CreateTransportProxies(const TransportInfos& tinfos, + SessionError* error) { + for (TransportInfos::const_iterator tinfo = tinfos.begin(); + tinfo != tinfos.end(); ++tinfo) { + if (tinfo->description.transport_type != transport_type()) { + error->SetText("No supported transport in offer."); + return false; + } + + GetOrCreateTransportProxy(tinfo->content_name); + } + return true; +} + +TransportParserMap Session::GetTransportParsers() { + TransportParserMap parsers; + parsers[transport_type()] = transport_parser_; + return parsers; +} + +CandidateTranslatorMap Session::GetCandidateTranslators() { + CandidateTranslatorMap translators; + // NOTE: This technique makes it impossible to parse G-ICE + // candidates in session-initiate messages because the channels + // aren't yet created at that point. Since we don't use candidates + // in session-initiate messages, we should be OK. Once we switch to + // ICE, this translation shouldn't be necessary. + for (TransportMap::const_iterator iter = transport_proxies().begin(); + iter != transport_proxies().end(); ++iter) { + translators[iter->first] = iter->second; + } + return translators; +} + +ContentParserMap Session::GetContentParsers() { + ContentParserMap parsers; + parsers[content_type()] = client_; + // We need to be able parse both RTP-based and SCTP-based Jingle + // with the same client. + if (content_type() == NS_JINGLE_RTP) { + parsers[NS_JINGLE_DRAFT_SCTP] = client_; + } + return parsers; +} + +void Session::OnTransportRequestSignaling(Transport* transport) { + ASSERT(signaling_thread()->IsCurrent()); + TransportProxy* transproxy = GetTransportProxy(transport); + ASSERT(transproxy != NULL); + if (transproxy) { + // Reset candidate allocation status for the transport proxy. + transproxy->set_candidates_allocated(false); + } + SignalRequestSignaling(this); +} + +void Session::OnTransportConnecting(Transport* transport) { + // This is an indication that we should begin watching the writability + // state of the transport. + OnTransportWritable(transport); +} + +void Session::OnTransportWritable(Transport* transport) { + ASSERT(signaling_thread()->IsCurrent()); + + // If the transport is not writable, start a timer to make sure that it + // becomes writable within a reasonable amount of time. If it does not, we + // terminate since we can't actually send data. If the transport is writable, + // cancel the timer. Note that writability transitions may occur repeatedly + // during the lifetime of the session. + signaling_thread()->Clear(this, MSG_TIMEOUT); + if (transport->HasChannels() && !transport->writable()) { + signaling_thread()->PostDelayed( + session_manager_->session_timeout() * 1000, this, MSG_TIMEOUT); + } +} + +void Session::OnTransportProxyCandidatesReady(TransportProxy* transproxy, + const Candidates& candidates) { + ASSERT(signaling_thread()->IsCurrent()); + if (transproxy != NULL) { + if (initiator() && !initiate_acked_) { + // TODO: This is to work around server re-ordering + // messages. We send the candidates once the session-initiate + // is acked. Once we have fixed the server to guarantee message + // order, we can remove this case. + transproxy->AddUnsentCandidates(candidates); + } else { + if (!transproxy->negotiated()) { + transproxy->AddSentCandidates(candidates); + } + SessionError error; + if (!SendTransportInfoMessage(transproxy, candidates, &error)) { + LOG(LS_ERROR) << "Could not send transport info message: " + << error.text; + return; + } + } + } +} + +void Session::OnTransportSendError(Transport* transport, + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + ASSERT(signaling_thread()->IsCurrent()); + SignalErrorMessage(this, stanza, name, type, text, extra_info); +} + +void Session::OnIncomingMessage(const SessionMessage& msg) { + ASSERT(signaling_thread()->IsCurrent()); + ASSERT(state() == STATE_INIT || msg.from == remote_name()); + + if (current_protocol_== PROTOCOL_HYBRID) { + if (msg.protocol == PROTOCOL_GINGLE) { + current_protocol_ = PROTOCOL_GINGLE; + } else { + current_protocol_ = PROTOCOL_JINGLE; + } + } + + bool valid = false; + MessageError error; + switch (msg.type) { + case ACTION_SESSION_INITIATE: + valid = OnInitiateMessage(msg, &error); + break; + case ACTION_SESSION_INFO: + valid = OnInfoMessage(msg); + break; + case ACTION_SESSION_ACCEPT: + valid = OnAcceptMessage(msg, &error); + break; + case ACTION_SESSION_REJECT: + valid = OnRejectMessage(msg, &error); + break; + case ACTION_SESSION_TERMINATE: + valid = OnTerminateMessage(msg, &error); + break; + case ACTION_TRANSPORT_INFO: + valid = OnTransportInfoMessage(msg, &error); + break; + case ACTION_TRANSPORT_ACCEPT: + valid = OnTransportAcceptMessage(msg, &error); + break; + case ACTION_DESCRIPTION_INFO: + valid = OnDescriptionInfoMessage(msg, &error); + break; + default: + valid = BadMessage(buzz::QN_STANZA_BAD_REQUEST, + "unknown session message type", + &error); + } + + if (valid) { + SendAcknowledgementMessage(msg.stanza); + } else { + SignalErrorMessage(this, msg.stanza, error.type, + "modify", error.text, NULL); + } +} + +void Session::OnIncomingResponse(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* response_stanza, + const SessionMessage& msg) { + ASSERT(signaling_thread()->IsCurrent()); + + if (msg.type == ACTION_SESSION_INITIATE) { + OnInitiateAcked(); + } +} + +void Session::OnInitiateAcked() { + // TODO: This is to work around server re-ordering + // messages. We send the candidates once the session-initiate + // is acked. Once we have fixed the server to guarantee message + // order, we can remove this case. + if (!initiate_acked_) { + initiate_acked_ = true; + SessionError error; + SendAllUnsentTransportInfoMessages(&error); + } +} + +void Session::OnFailedSend(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* error_stanza) { + ASSERT(signaling_thread()->IsCurrent()); + + SessionMessage msg; + ParseError parse_error; + if (!ParseSessionMessage(orig_stanza, &msg, &parse_error)) { + LOG(LS_ERROR) << "Error parsing failed send: " << parse_error.text + << ":" << orig_stanza; + return; + } + + // If the error is a session redirect, call OnRedirectError, which will + // continue the session with a new remote JID. + SessionRedirect redirect; + if (FindSessionRedirect(error_stanza, &redirect)) { + SessionError error; + if (!OnRedirectError(redirect, &error)) { + // TODO: Should we send a message back? The standard + // says nothing about it. + std::ostringstream desc; + desc << "Failed to redirect: " << error.text; + LOG(LS_ERROR) << desc.str(); + SetError(ERROR_RESPONSE, desc.str()); + } + return; + } + + std::string error_type = "cancel"; + + const buzz::XmlElement* error = error_stanza->FirstNamed(buzz::QN_ERROR); + if (error) { + error_type = error->Attr(buzz::QN_TYPE); + + LOG(LS_ERROR) << "Session error:\n" << error->Str() << "\n" + << "in response to:\n" << orig_stanza->Str(); + } else { + // don't crash if is missing + LOG(LS_ERROR) << "Session error without element, ignoring"; + return; + } + + if (msg.type == ACTION_TRANSPORT_INFO) { + // Transport messages frequently generate errors because they are sent right + // when we detect a network failure. For that reason, we ignore such + // errors, because if we do not establish writability again, we will + // terminate anyway. The exceptions are transport-specific error tags, + // which we pass on to the respective transport. + } else if ((error_type != "continue") && (error_type != "wait")) { + // We do not set an error if the other side said it is okay to continue + // (possibly after waiting). These errors can be ignored. + SetError(ERROR_RESPONSE, ""); + } +} + +bool Session::OnInitiateMessage(const SessionMessage& msg, + MessageError* error) { + if (!CheckState(STATE_INIT, error)) + return false; + + SessionInitiate init; + if (!ParseSessionInitiate(msg.protocol, msg.action_elem, + GetContentParsers(), GetTransportParsers(), + GetCandidateTranslators(), + &init, error)) + return false; + + SessionError session_error; + if (!CreateTransportProxies(init.transports, &session_error)) { + return BadMessage(buzz::QN_STANZA_NOT_ACCEPTABLE, + session_error.text, error); + } + + set_remote_name(msg.from); + set_initiator_name(msg.initiator); + set_remote_description(new SessionDescription(init.ClearContents(), + init.transports, + init.groups)); + // Updating transport with TransportDescription. + PushdownTransportDescription(CS_REMOTE, CA_OFFER, NULL); + SetState(STATE_RECEIVEDINITIATE); + + // Users of Session may listen to state change and call Reject(). + if (state() != STATE_SENTREJECT) { + if (!OnRemoteCandidates(init.transports, error)) + return false; + + // TODO(juberti): Auto-generate and push down the local transport answer. + // This is necessary for trickling to work with RFC 5245 ICE. + } + return true; +} + +bool Session::OnAcceptMessage(const SessionMessage& msg, MessageError* error) { + if (!CheckState(STATE_SENTINITIATE, error)) + return false; + + SessionAccept accept; + if (!ParseSessionAccept(msg.protocol, msg.action_elem, + GetContentParsers(), GetTransportParsers(), + GetCandidateTranslators(), + &accept, error)) { + return false; + } + + // If we get an accept, we can assume the initiate has been + // received, even if we haven't gotten an IQ response. + OnInitiateAcked(); + + set_remote_description(new SessionDescription(accept.ClearContents(), + accept.transports, + accept.groups)); + // Updating transport with TransportDescription. + PushdownTransportDescription(CS_REMOTE, CA_ANSWER, NULL); + MaybeEnableMuxingSupport(); // Enable transport channel mux if supported. + SetState(STATE_RECEIVEDACCEPT); + + if (!OnRemoteCandidates(accept.transports, error)) + return false; + + return true; +} + +bool Session::OnRejectMessage(const SessionMessage& msg, MessageError* error) { + if (!CheckState(STATE_SENTINITIATE, error)) + return false; + + SetState(STATE_RECEIVEDREJECT); + return true; +} + +bool Session::OnInfoMessage(const SessionMessage& msg) { + SignalInfoMessage(this, msg.action_elem); + return true; +} + +bool Session::OnTerminateMessage(const SessionMessage& msg, + MessageError* error) { + SessionTerminate term; + if (!ParseSessionTerminate(msg.protocol, msg.action_elem, &term, error)) + return false; + + SignalReceivedTerminateReason(this, term.reason); + if (term.debug_reason != buzz::STR_EMPTY) { + LOG(LS_VERBOSE) << "Received error on call: " << term.debug_reason; + } + + SetState(STATE_RECEIVEDTERMINATE); + return true; +} + +bool Session::OnTransportInfoMessage(const SessionMessage& msg, + MessageError* error) { + TransportInfos tinfos; + if (!ParseTransportInfos(msg.protocol, msg.action_elem, + initiator_description()->contents(), + GetTransportParsers(), GetCandidateTranslators(), + &tinfos, error)) + return false; + + if (!OnRemoteCandidates(tinfos, error)) + return false; + + return true; +} + +bool Session::OnTransportAcceptMessage(const SessionMessage& msg, + MessageError* error) { + // TODO: Currently here only for compatibility with + // Gingle 1.1 clients (notably, Google Voice). + return true; +} + +bool Session::OnDescriptionInfoMessage(const SessionMessage& msg, + MessageError* error) { + if (!CheckState(STATE_INPROGRESS, error)) + return false; + + DescriptionInfo description_info; + if (!ParseDescriptionInfo(msg.protocol, msg.action_elem, + GetContentParsers(), GetTransportParsers(), + GetCandidateTranslators(), + &description_info, error)) { + return false; + } + + ContentInfos& updated_contents = description_info.contents; + + // TODO: Currently, reflector sends back + // video stream updates even for an audio-only call, which causes + // this to fail. Put this back once reflector is fixed. + // + // ContentInfos::iterator it; + // First, ensure all updates are valid before modifying remote_description_. + // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) { + // if (remote_description()->GetContentByName(it->name) == NULL) { + // return false; + // } + // } + + // TODO: We used to replace contents from an update, but + // that no longer works with partial updates. We need to figure out + // a way to merge patial updates into contents. For now, users of + // Session should listen to SignalRemoteDescriptionUpdate and handle + // updates. They should not expect remote_description to be the + // latest value. + // + // for (it = updated_contents.begin(); it != updated_contents.end(); ++it) { + // remote_description()->RemoveContentByName(it->name); + // remote_description()->AddContent(it->name, it->type, it->description); + // } + // } + + SignalRemoteDescriptionUpdate(this, updated_contents); + return true; +} + +bool BareJidsEqual(const std::string& name1, + const std::string& name2) { + buzz::Jid jid1(name1); + buzz::Jid jid2(name2); + + return jid1.IsValid() && jid2.IsValid() && jid1.BareEquals(jid2); +} + +bool Session::OnRedirectError(const SessionRedirect& redirect, + SessionError* error) { + MessageError message_error; + if (!CheckState(STATE_SENTINITIATE, &message_error)) { + return BadWrite(message_error.text, error); + } + + if (!BareJidsEqual(remote_name(), redirect.target)) + return BadWrite("Redirection not allowed: must be the same bare jid.", + error); + + // When we receive a redirect, we point the session at the new JID + // and resend the candidates. + set_remote_name(redirect.target); + return (SendInitiateMessage(local_description(), error) && + ResendAllTransportInfoMessages(error)); +} + +bool Session::CheckState(State expected, MessageError* error) { + if (state() != expected) { + // The server can deliver messages out of order/repeated for various + // reasons. For example, if the server does not recive our iq response, + // it could assume that the iq it sent was lost, and will then send + // it again. Ideally, we should implement reliable messaging with + // duplicate elimination. + return BadMessage(buzz::QN_STANZA_NOT_ALLOWED, + "message not allowed in current state", + error); + } + return true; +} + +void Session::SetError(Error error, const std::string& error_desc) { + BaseSession::SetError(error, error_desc); + if (error != ERROR_NONE) + signaling_thread()->Post(this, MSG_ERROR); +} + +void Session::OnMessage(rtc::Message* pmsg) { + // preserve this because BaseSession::OnMessage may modify it + State orig_state = state(); + + BaseSession::OnMessage(pmsg); + + switch (pmsg->message_id) { + case MSG_ERROR: + TerminateWithReason(STR_TERMINATE_ERROR); + break; + + case MSG_STATE: + switch (orig_state) { + case STATE_SENTREJECT: + case STATE_RECEIVEDREJECT: + // Assume clean termination. + Terminate(); + break; + + case STATE_SENTTERMINATE: + case STATE_RECEIVEDTERMINATE: + session_manager_->DestroySession(this); + break; + + default: + // Explicitly ignoring some states here. + break; + } + break; + } +} + +bool Session::SendInitiateMessage(const SessionDescription* sdesc, + SessionError* error) { + SessionInitiate init; + init.contents = sdesc->contents(); + init.transports = GetEmptyTransportInfos(init.contents); + init.groups = sdesc->groups(); + return SendMessage(ACTION_SESSION_INITIATE, init, error); +} + +bool Session::WriteSessionAction( + SignalingProtocol protocol, const SessionInitiate& init, + XmlElements* elems, WriteError* error) { + return WriteSessionInitiate(protocol, init.contents, init.transports, + GetContentParsers(), GetTransportParsers(), + GetCandidateTranslators(), init.groups, + elems, error); +} + +bool Session::SendAcceptMessage(const SessionDescription* sdesc, + SessionError* error) { + XmlElements elems; + if (!WriteSessionAccept(current_protocol_, + sdesc->contents(), + GetEmptyTransportInfos(sdesc->contents()), + GetContentParsers(), GetTransportParsers(), + GetCandidateTranslators(), sdesc->groups(), + &elems, error)) { + return false; + } + return SendMessage(ACTION_SESSION_ACCEPT, elems, error); +} + +bool Session::SendRejectMessage(const std::string& reason, + SessionError* error) { + SessionTerminate term(reason); + return SendMessage(ACTION_SESSION_REJECT, term, error); +} + +bool Session::SendTerminateMessage(const std::string& reason, + SessionError* error) { + SessionTerminate term(reason); + return SendMessage(ACTION_SESSION_TERMINATE, term, error); +} + +bool Session::WriteSessionAction(SignalingProtocol protocol, + const SessionTerminate& term, + XmlElements* elems, WriteError* error) { + WriteSessionTerminate(protocol, term, elems); + return true; +} + +bool Session::SendTransportInfoMessage(const TransportInfo& tinfo, + SessionError* error) { + return SendMessage(ACTION_TRANSPORT_INFO, tinfo, error); +} + +bool Session::SendTransportInfoMessage(const TransportProxy* transproxy, + const Candidates& candidates, + SessionError* error) { + return SendTransportInfoMessage(TransportInfo(transproxy->content_name(), + TransportDescription(transproxy->type(), std::vector(), + std::string(), std::string(), ICEMODE_FULL, + CONNECTIONROLE_NONE, NULL, candidates)), error); +} + +bool Session::WriteSessionAction(SignalingProtocol protocol, + const TransportInfo& tinfo, + XmlElements* elems, WriteError* error) { + TransportInfos tinfos; + tinfos.push_back(tinfo); + return WriteTransportInfos(protocol, tinfos, + GetTransportParsers(), GetCandidateTranslators(), + elems, error); +} + +bool Session::ResendAllTransportInfoMessages(SessionError* error) { + for (TransportMap::const_iterator iter = transport_proxies().begin(); + iter != transport_proxies().end(); ++iter) { + TransportProxy* transproxy = iter->second; + if (transproxy->sent_candidates().size() > 0) { + if (!SendTransportInfoMessage( + transproxy, transproxy->sent_candidates(), error)) { + LOG(LS_ERROR) << "Could not resend transport info messages: " + << error->text; + return false; + } + transproxy->ClearSentCandidates(); + } + } + return true; +} + +bool Session::SendAllUnsentTransportInfoMessages(SessionError* error) { + for (TransportMap::const_iterator iter = transport_proxies().begin(); + iter != transport_proxies().end(); ++iter) { + TransportProxy* transproxy = iter->second; + if (transproxy->unsent_candidates().size() > 0) { + if (!SendTransportInfoMessage( + transproxy, transproxy->unsent_candidates(), error)) { + LOG(LS_ERROR) << "Could not send unsent transport info messages: " + << error->text; + return false; + } + transproxy->ClearUnsentCandidates(); + } + } + return true; +} + +bool Session::SendMessage(ActionType type, const XmlElements& action_elems, + SessionError* error) { + return SendMessage(type, action_elems, remote_name(), error); +} + +bool Session::SendMessage(ActionType type, const XmlElements& action_elems, + const std::string& remote_name, SessionError* error) { + rtc::scoped_ptr stanza( + new buzz::XmlElement(buzz::QN_IQ)); + + SessionMessage msg(current_protocol_, type, id(), initiator_name()); + msg.to = remote_name; + WriteSessionMessage(msg, action_elems, stanza.get()); + + SignalOutgoingMessage(this, stanza.get()); + return true; +} + +template +bool Session::SendMessage(ActionType type, const Action& action, + SessionError* error) { + rtc::scoped_ptr stanza( + new buzz::XmlElement(buzz::QN_IQ)); + if (!WriteActionMessage(type, action, stanza.get(), error)) + return false; + + SignalOutgoingMessage(this, stanza.get()); + return true; +} + +template +bool Session::WriteActionMessage(ActionType type, const Action& action, + buzz::XmlElement* stanza, + WriteError* error) { + if (current_protocol_ == PROTOCOL_HYBRID) { + if (!WriteActionMessage(PROTOCOL_JINGLE, type, action, stanza, error)) + return false; + if (!WriteActionMessage(PROTOCOL_GINGLE, type, action, stanza, error)) + return false; + } else { + if (!WriteActionMessage(current_protocol_, type, action, stanza, error)) + return false; + } + return true; +} + +template +bool Session::WriteActionMessage(SignalingProtocol protocol, + ActionType type, const Action& action, + buzz::XmlElement* stanza, WriteError* error) { + XmlElements action_elems; + if (!WriteSessionAction(protocol, action, &action_elems, error)) + return false; + + SessionMessage msg(protocol, type, id(), initiator_name()); + msg.to = remote_name(); + + WriteSessionMessage(msg, action_elems, stanza); + return true; +} + +void Session::SendAcknowledgementMessage(const buzz::XmlElement* stanza) { + rtc::scoped_ptr ack( + new buzz::XmlElement(buzz::QN_IQ)); + ack->SetAttr(buzz::QN_TO, remote_name()); + ack->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); + ack->SetAttr(buzz::QN_TYPE, "result"); + + SignalOutgoingMessage(this, ack.get()); +} + } // namespace cricket diff --git a/webrtc/p2p/base/session.h b/webrtc/p2p/base/session.h index fc0981097..f5eaf413a 100644 --- a/webrtc/p2p/base/session.h +++ b/webrtc/p2p/base/session.h @@ -16,9 +16,14 @@ #include #include -#include "webrtc/p2p/base/candidate.h" +#include "webrtc/p2p/base/parsing.h" #include "webrtc/p2p/base/port.h" +#include "webrtc/p2p/base/sessionclient.h" +#include "webrtc/p2p/base/sessionmanager.h" +#include "webrtc/p2p/base/sessionmessages.h" #include "webrtc/p2p/base/transport.h" +#include "webrtc/libjingle/xmllite/xmlelement.h" +#include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/base/refcount.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/scoped_ref_ptr.h" @@ -36,6 +41,27 @@ class TransportChannelImpl; typedef rtc::RefCountedObject > TransportWrapper; +// Used for errors that will send back a specific error message to the +// remote peer. We add "type" to the errors because it's needed for +// SignalErrorMessage. +struct MessageError : ParseError { + buzz::QName type; + + // if unset, assume type is a parse error + MessageError() : ParseError(), type(buzz::QN_STANZA_BAD_REQUEST) {} + + void SetType(const buzz::QName type) { + this->type = type; + } +}; + +// Used for errors that may be returned by public session methods that +// can fail. +// TODO: Use this error in Session::Initiate and +// Session::Accept. +struct SessionError : WriteError { +}; + // Bundles a Transport and ChannelMap together. ChannelMap is used to // create transport channels before receiving or sending a session // initiate, and for speculatively connecting channels. Previously, a @@ -392,6 +418,16 @@ class BaseSession : public sigslot::has_slots<>, const Candidates& candidates) { } + // Called when a transport signals that it found an error in an incoming + // message. + virtual void OnTransportSendError(Transport* transport, + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + } + virtual void OnTransportRouteChange( Transport* transport, int component, @@ -468,6 +504,227 @@ class BaseSession : public sigslot::has_slots<>, TransportMap transports_; }; +// A specific Session created by the SessionManager, using XMPP for protocol. +class Session : public BaseSession { + public: + // Returns the manager that created and owns this session. + SessionManager* session_manager() const { return session_manager_; } + + // Returns the client that is handling the application data of this session. + SessionClient* client() const { return client_; } + + // Returns the JID of this client. + const std::string& local_name() const { return local_name_; } + + // Returns the JID of the other peer in this session. + const std::string& remote_name() const { return remote_name_; } + + // Set the JID of the other peer in this session. + // Typically the remote_name_ is set when the session is initiated. + // However, sometimes (e.g when a proxy is used) the peer name is + // known after the BaseSession has been initiated and it must be updated + // explicitly. + void set_remote_name(const std::string& name) { remote_name_ = name; } + + // Set the JID of the initiator of this session. Allows for the overriding + // of the initiator to be a third-party, eg. the MUC JID when creating p2p + // sessions. + void set_initiator_name(const std::string& name) { initiator_name_ = name; } + + // Indicates the JID of the entity who initiated this session. + // In special cases, may be different than both local_name and remote_name. + const std::string& initiator_name() const { return initiator_name_; } + + SignalingProtocol current_protocol() const { return current_protocol_; } + + void set_current_protocol(SignalingProtocol protocol) { + current_protocol_ = protocol; + } + + // Updates the error state, signaling if necessary. + virtual void SetError(Error error, const std::string& error_desc); + + // When the session needs to send signaling messages, it beings by requesting + // signaling. The client should handle this by calling OnSignalingReady once + // it is ready to send the messages. + // (These are called only by SessionManager.) + sigslot::signal1 SignalRequestSignaling; + void OnSignalingReady() { BaseSession::OnSignalingReady(); } + + // Takes ownership of session description. + // TODO: Add an error argument to pass back to the caller. + bool Initiate(const std::string& to, + const SessionDescription* sdesc); + + // When we receive an initiate, we create a session in the + // RECEIVEDINITIATE state and respond by accepting or rejecting. + // Takes ownership of session description. + // TODO: Add an error argument to pass back to the caller. + bool Accept(const SessionDescription* sdesc); + bool Reject(const std::string& reason); + bool Terminate() { + return TerminateWithReason(STR_TERMINATE_SUCCESS); + } + bool TerminateWithReason(const std::string& reason); + // Fired whenever we receive a terminate message along with a reason + sigslot::signal2 SignalReceivedTerminateReason; + + // The two clients in the session may also send one another + // arbitrary XML messages, which are called "info" messages. Sending + // takes ownership of the given elements. The signal does not; the + // parent element will be deleted after the signal. + bool SendInfoMessage(const XmlElements& elems, + const std::string& remote_name); + bool SendDescriptionInfoMessage(const ContentInfos& contents); + sigslot::signal2 SignalInfoMessage; + + private: + // Creates or destroys a session. (These are called only SessionManager.) + Session(SessionManager *session_manager, + const std::string& local_name, const std::string& initiator_name, + const std::string& sid, const std::string& content_type, + SessionClient* client); + ~Session(); + // For each transport info, create a transport proxy. Can fail for + // incompatible transport types. + bool CreateTransportProxies(const TransportInfos& tinfos, + SessionError* error); + bool OnRemoteCandidates(const TransportInfos& tinfos, + ParseError* error); + // Returns a TransportInfo without candidates for each content name. + // Uses the transport_type_ of the session. + TransportInfos GetEmptyTransportInfos(const ContentInfos& contents) const; + + // Maps passed to serialization functions. + TransportParserMap GetTransportParsers(); + ContentParserMap GetContentParsers(); + CandidateTranslatorMap GetCandidateTranslators(); + + virtual void OnTransportRequestSignaling(Transport* transport); + virtual void OnTransportConnecting(Transport* transport); + virtual void OnTransportWritable(Transport* transport); + virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy, + const Candidates& candidates); + virtual void OnTransportSendError(Transport* transport, + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info); + virtual void OnMessage(rtc::Message *pmsg); + + // Send various kinds of session messages. + bool SendInitiateMessage(const SessionDescription* sdesc, + SessionError* error); + bool SendAcceptMessage(const SessionDescription* sdesc, SessionError* error); + bool SendRejectMessage(const std::string& reason, SessionError* error); + bool SendTerminateMessage(const std::string& reason, SessionError* error); + bool SendTransportInfoMessage(const TransportInfo& tinfo, + SessionError* error); + bool SendTransportInfoMessage(const TransportProxy* transproxy, + const Candidates& candidates, + SessionError* error); + + bool ResendAllTransportInfoMessages(SessionError* error); + bool SendAllUnsentTransportInfoMessages(SessionError* error); + + // All versions of SendMessage send a message of the given type to + // the other client. Can pass either a set of elements or an + // "action", which must have a WriteSessionAction method to go along + // with it. Sending with an action supports sending a "hybrid" + // message. Sending with elements must be sent as Jingle or Gingle. + + // When passing elems, must be either Jingle or Gingle protocol. + // Takes ownership of action_elems. + bool SendMessage(ActionType type, const XmlElements& action_elems, + SessionError* error); + // Sends a messge, but overrides the remote name. + bool SendMessage(ActionType type, const XmlElements& action_elems, + const std::string& remote_name, + SessionError* error); + // When passing an action, may be Hybrid protocol. + template + bool SendMessage(ActionType type, const Action& action, + SessionError* error); + + // Helper methods to write the session message stanza. + template + bool WriteActionMessage(ActionType type, const Action& action, + buzz::XmlElement* stanza, WriteError* error); + template + bool WriteActionMessage(SignalingProtocol protocol, + ActionType type, const Action& action, + buzz::XmlElement* stanza, WriteError* error); + + // Sending messages in hybrid form requires being able to write them + // on a per-protocol basis with a common method signature, which all + // of these have. + bool WriteSessionAction(SignalingProtocol protocol, + const SessionInitiate& init, + XmlElements* elems, WriteError* error); + bool WriteSessionAction(SignalingProtocol protocol, + const TransportInfo& tinfo, + XmlElements* elems, WriteError* error); + bool WriteSessionAction(SignalingProtocol protocol, + const SessionTerminate& term, + XmlElements* elems, WriteError* error); + + // Sends a message back to the other client indicating that we have received + // and accepted their message. + void SendAcknowledgementMessage(const buzz::XmlElement* stanza); + + // Once signaling is ready, the session will use this signal to request the + // sending of each message. When messages are received by the other client, + // they should be handed to OnIncomingMessage. + // (These are called only by SessionManager.) + sigslot::signal2 SignalOutgoingMessage; + void OnIncomingMessage(const SessionMessage& msg); + + void OnIncomingResponse(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* response_stanza, + const SessionMessage& msg); + void OnInitiateAcked(); + void OnFailedSend(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* error_stanza); + + // Invoked when an error is found in an incoming message. This is translated + // into the appropriate XMPP response by SessionManager. + sigslot::signal6 SignalErrorMessage; + + // Handlers for the various types of messages. These functions may take + // pointers to the whole stanza or to just the session element. + bool OnInitiateMessage(const SessionMessage& msg, MessageError* error); + bool OnAcceptMessage(const SessionMessage& msg, MessageError* error); + bool OnRejectMessage(const SessionMessage& msg, MessageError* error); + bool OnInfoMessage(const SessionMessage& msg); + bool OnTerminateMessage(const SessionMessage& msg, MessageError* error); + bool OnTransportInfoMessage(const SessionMessage& msg, MessageError* error); + bool OnTransportAcceptMessage(const SessionMessage& msg, MessageError* error); + bool OnDescriptionInfoMessage(const SessionMessage& msg, MessageError* error); + bool OnRedirectError(const SessionRedirect& redirect, SessionError* error); + + // Verifies that we are in the appropriate state to receive this message. + bool CheckState(State state, MessageError* error); + + SessionManager* session_manager_; + bool initiate_acked_; + std::string local_name_; + std::string initiator_name_; + std::string remote_name_; + SessionClient* client_; + TransportParser* transport_parser_; + // Keeps track of what protocol we are speaking. + SignalingProtocol current_protocol_; + + friend class SessionManager; // For access to constructor, destructor, + // and signaling related methods. +}; + } // namespace cricket #endif // WEBRTC_P2P_BASE_SESSION_H_ diff --git a/webrtc/p2p/base/session_unittest.cc b/webrtc/p2p/base/session_unittest.cc index fdd0bdc62..d6f94b29d 100644 --- a/webrtc/p2p/base/session_unittest.cc +++ b/webrtc/p2p/base/session_unittest.cc @@ -14,6 +14,22 @@ #include #include +#include "webrtc/p2p/base/basicpacketsocketfactory.h" +#include "webrtc/p2p/base/constants.h" +#include "webrtc/p2p/base/p2ptransport.h" +#include "webrtc/p2p/base/parsing.h" +#include "webrtc/p2p/base/portallocator.h" +#include "webrtc/p2p/base/relayport.h" +#include "webrtc/p2p/base/relayserver.h" +#include "webrtc/p2p/base/session.h" +#include "webrtc/p2p/base/sessionclient.h" +#include "webrtc/p2p/base/sessionmanager.h" +#include "webrtc/p2p/base/stunport.h" +#include "webrtc/p2p/base/stunserver.h" +#include "webrtc/p2p/base/transportchannel.h" +#include "webrtc/p2p/base/transportchannelproxy.h" +#include "webrtc/p2p/base/udpport.h" +#include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/base/base64.h" #include "webrtc/base/common.h" #include "webrtc/base/gunit.h" @@ -22,22 +38,6 @@ #include "webrtc/base/natserver.h" #include "webrtc/base/natsocketfactory.h" #include "webrtc/base/stringencode.h" -#include "webrtc/p2p/base/basicpacketsocketfactory.h" -#include "webrtc/p2p/base/constants.h" -#include "webrtc/p2p/base/p2ptransport.h" -#include "webrtc/p2p/base/parsing.h" -#include "webrtc/p2p/base/portallocator.h" -#include "webrtc/p2p/base/relayport.h" -#include "webrtc/p2p/base/relayserver.h" -#include "webrtc/p2p/base/sessionclient.h" -#include "webrtc/p2p/base/sessionmanager.h" -#include "webrtc/p2p/base/stunport.h" -#include "webrtc/p2p/base/stunserver.h" -#include "webrtc/p2p/base/transportchannel.h" -#include "webrtc/p2p/base/transportchannelproxy.h" -#include "webrtc/p2p/base/udpport.h" -#include "webrtc/libjingle/session/session.h" -#include "webrtc/libjingle/xmpp/constants.h" using cricket::SignalingProtocol; using cricket::PROTOCOL_HYBRID; diff --git a/webrtc/p2p/base/sessionmanager.cc b/webrtc/p2p/base/sessionmanager.cc index 816c61383..f375dea5c 100644 --- a/webrtc/p2p/base/sessionmanager.cc +++ b/webrtc/p2p/base/sessionmanager.cc @@ -11,8 +11,8 @@ #include "webrtc/p2p/base/sessionmanager.h" #include "webrtc/p2p/base/constants.h" +#include "webrtc/p2p/base/session.h" #include "webrtc/p2p/base/sessionmessages.h" -#include "webrtc/libjingle/session/session.h" #include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/libjingle/xmpp/jid.h" #include "webrtc/base/common.h" diff --git a/webrtc/p2p/base/sessionmessages.h b/webrtc/p2p/base/sessionmessages.h index 3437a9ac6..7b156d49d 100644 --- a/webrtc/p2p/base/sessionmessages.h +++ b/webrtc/p2p/base/sessionmessages.h @@ -15,11 +15,9 @@ #include #include -#include "webrtc/p2p/base/candidate.h" #include "webrtc/p2p/base/constants.h" #include "webrtc/p2p/base/parsing.h" #include "webrtc/p2p/base/sessiondescription.h" // Needed to delete contents. -#include "webrtc/p2p/base/transport.h" #include "webrtc/p2p/base/transportinfo.h" #include "webrtc/libjingle/xmllite/xmlelement.h" #include "webrtc/base/basictypes.h" @@ -127,6 +125,17 @@ struct SessionRedirect { std::string target; }; +// Used during parsing and writing to map component to channel name +// and back. This is primarily for converting old G-ICE candidate +// signalling to new ICE candidate classes. +class CandidateTranslator { + public: + virtual bool GetChannelNameFromComponent( + int component, std::string* channel_name) const = 0; + virtual bool GetComponentFromChannelName( + const std::string& channel_name, int* component) const = 0; +}; + // Content name => translator typedef std::map CandidateTranslatorMap; diff --git a/webrtc/p2p/base/transport.h b/webrtc/p2p/base/transport.h index 5fe445cad..1091cb037 100644 --- a/webrtc/p2p/base/transport.h +++ b/webrtc/p2p/base/transport.h @@ -56,6 +56,8 @@ struct ParseError; struct WriteError; class CandidateTranslator; class PortAllocator; +class SessionManager; +class Session; class TransportChannel; class TransportChannelImpl; diff --git a/webrtc/p2p/p2p.gyp b/webrtc/p2p/p2p.gyp index ae02d65ee..102e75b58 100644 --- a/webrtc/p2p/p2p.gyp +++ b/webrtc/p2p/p2p.gyp @@ -108,8 +108,6 @@ 'client/sessionsendtask.h', 'client/socketmonitor.cc', 'client/socketmonitor.h', - '<(webrtc_root)/libjingle/session/session.cc', - '<(webrtc_root)/libjingle/session/session.h', ], 'direct_dependent_settings': { 'cflags_cc!': [