From 5647877b2d7b299837ed7ab8e8270d593fe5aa79 Mon Sep 17 00:00:00 2001 From: "pthatcher@webrtc.org" Date: Fri, 19 Dec 2014 03:32:59 +0000 Subject: [PATCH] Breakup Transports and TransportParsers and move TransportParsers into webrtc/libjingle. This is part of an ongoing effort to move Jingle-specific code out of WebRTC and into its own repository. R=juberti@webrtc.org Review URL: https://webrtc-codereview.appspot.com/33679004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7959 4adac7df-926f-26a2-2b94-8c16560cd09d --- talk/libjingle.gyp | 1 + talk/libjingle_examples.gyp | 13 - talk/session/media/call.h | 6 +- talk/session/media/mediasessionclient.h | 8 +- webrtc/libjingle/libjingle.gyp | 20 + .../libjingle/session/p2ptransportparser.cc | 215 +++ webrtc/libjingle/session/p2ptransportparser.h | 67 + .../libjingle/session/rawtransportparser.cc | 92 ++ webrtc/libjingle/session/rawtransportparser.h | 48 + .../session}/session_unittest.cc | 4 +- webrtc/libjingle/session/sessionmanager.cc | 1150 ++++++++++++++++ webrtc/libjingle/session/sessionmanager.h | 436 +++++++ webrtc/libjingle/session/transportparser.cc | 38 + webrtc/libjingle/session/transportparser.h | 78 ++ .../session/transportparser_unittest.cc | 136 ++ .../session/tunnel/pseudotcpchannel.h | 8 +- .../tunnel/securetunnelsessionclient.h | 6 +- .../session/tunnel/tunnelsessionclient.h | 10 +- .../tunnel/tunnelsessionclient_unittest.cc | 8 +- webrtc/p2p/base/constants.cc | 1 + webrtc/p2p/base/constants.h | 1 + webrtc/p2p/base/p2ptransport.cc | 199 --- webrtc/p2p/base/p2ptransport.h | 47 - webrtc/p2p/base/rawtransport.cc | 74 +- webrtc/p2p/base/rawtransport.h | 20 +- webrtc/p2p/base/rawtransportchannel.cc | 4 - webrtc/p2p/base/sessionclient.h | 1 + webrtc/p2p/base/sessionmanager.cc | 1151 +---------------- webrtc/p2p/base/sessionmanager.h | 437 +------ webrtc/p2p/base/sessionmessages.h | 3 +- webrtc/p2p/base/transport.cc | 23 - webrtc/p2p/base/transport.h | 69 - webrtc/p2p/base/transport_unittest.cc | 120 +- webrtc/p2p/client/sessionmanagertask.h | 4 +- webrtc/p2p/client/sessionsendtask.h | 2 +- webrtc/p2p/p2p.gyp | 2 - webrtc/p2p/p2p_tests.gypi | 1 - 37 files changed, 2320 insertions(+), 2183 deletions(-) create mode 100644 webrtc/libjingle/session/p2ptransportparser.cc create mode 100644 webrtc/libjingle/session/p2ptransportparser.h create mode 100644 webrtc/libjingle/session/rawtransportparser.cc create mode 100644 webrtc/libjingle/session/rawtransportparser.h rename webrtc/{p2p/base => libjingle/session}/session_unittest.cc (99%) create mode 100644 webrtc/libjingle/session/sessionmanager.cc create mode 100644 webrtc/libjingle/session/sessionmanager.h create mode 100644 webrtc/libjingle/session/transportparser.cc create mode 100644 webrtc/libjingle/session/transportparser.h create mode 100644 webrtc/libjingle/session/transportparser_unittest.cc diff --git a/talk/libjingle.gyp b/talk/libjingle.gyp index 7f1746d40..d12affbf4 100755 --- a/talk/libjingle.gyp +++ b/talk/libjingle.gyp @@ -575,6 +575,7 @@ '<(DEPTH)/third_party/libsrtp/libsrtp.gyp:libsrtp', 'libjingle', 'libjingle_media', + '<(webrtc_root)/libjingle/libjingle.gyp:jingle_session', ], 'include_dirs': [ '<(DEPTH)/testing/gtest/include', diff --git a/talk/libjingle_examples.gyp b/talk/libjingle_examples.gyp index 1a573443e..63aa5f111 100755 --- a/talk/libjingle_examples.gyp +++ b/talk/libjingle_examples.gyp @@ -30,19 +30,6 @@ 'build/common.gypi', ], 'targets': [ - { - 'target_name': 'libjingle_xmpphelp', - 'type': 'static_library', - 'dependencies': [ - '<(DEPTH)/third_party/expat/expat.gyp:expat', - 'libjingle.gyp:libjingle', - 'libjingle.gyp:libjingle_p2p', - ], - 'sources': [ - '<(webrtc_root)/libjingle/xmpp/jingleinfotask.cc', - '<(webrtc_root)/libjingle/xmpp/jingleinfotask.h', - ], - }, # target libjingle_xmpphelp { 'target_name': 'relayserver', 'type': 'executable', diff --git a/talk/session/media/call.h b/talk/session/media/call.h index d756c340d..6f682b2e8 100644 --- a/talk/session/media/call.h +++ b/talk/session/media/call.h @@ -37,14 +37,14 @@ #include "talk/media/base/screencastid.h" #include "talk/media/base/streamparams.h" #include "talk/media/base/videocommon.h" -#include "webrtc/p2p/base/sessionmanager.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/xmpp/jid.h" #include "webrtc/base/messagequeue.h" +#include "webrtc/libjingle/session/sessionmanager.h" +#include "webrtc/libjingle/xmpp/jid.h" +#include "webrtc/p2p/client/socketmonitor.h" namespace cricket { diff --git a/talk/session/media/mediasessionclient.h b/talk/session/media/mediasessionclient.h index c66c221e4..4d29987a1 100644 --- a/talk/session/media/mediasessionclient.h +++ b/talk/session/media/mediasessionclient.h @@ -33,10 +33,6 @@ #include #include #include "talk/media/base/cryptoparams.h" -#include "webrtc/p2p/base/session.h" -#include "webrtc/p2p/base/sessionclient.h" -#include "webrtc/p2p/base/sessiondescription.h" -#include "webrtc/p2p/base/sessionmanager.h" #include "talk/session/media/call.h" #include "talk/session/media/channelmanager.h" #include "talk/session/media/mediasession.h" @@ -44,6 +40,10 @@ #include "webrtc/base/sigslot.h" #include "webrtc/base/sigslotrepeater.h" #include "webrtc/base/thread.h" +#include "webrtc/libjingle/session/sessionmanager.h" +#include "webrtc/p2p/base/session.h" +#include "webrtc/p2p/base/sessionclient.h" +#include "webrtc/p2p/base/sessiondescription.h" namespace cricket { diff --git a/webrtc/libjingle/libjingle.gyp b/webrtc/libjingle/libjingle.gyp index ab25bc587..8f58d2c20 100644 --- a/webrtc/libjingle/libjingle.gyp +++ b/webrtc/libjingle/libjingle.gyp @@ -40,6 +40,14 @@ '<(DEPTH)/third_party/expat/expat.gyp:expat', ], 'sources': [ + 'session/p2ptransportparser.cc', + 'session/p2ptransportparser.h', + 'session/rawtransportparser.cc', + 'session/rawtransportparser.h', + 'session/sessionmanager.cc', + 'session/sessionmanager.h', + 'session/transportparser.cc', + 'session/transportparser.h', 'session/tunnel/pseudotcpchannel.cc', 'session/tunnel/pseudotcpchannel.h', 'session/tunnel/tunnelsessionclient.cc', @@ -64,6 +72,18 @@ }], ], }, + { + 'target_name': 'jingle_session_unittest', + 'type': 'executable', + 'dependencies': [ + 'jingle_session', + '<(webrtc_root)/base/base_tests.gyp:rtc_base_tests_utils', + ], + 'sources': [ + 'session/session_unittest.cc', + 'session/transportparser_unittest.cc', + ], + }, # target jingle_session_unittest { 'target_name': 'login', 'type': 'executable', diff --git a/webrtc/libjingle/session/p2ptransportparser.cc b/webrtc/libjingle/session/p2ptransportparser.cc new file mode 100644 index 000000000..75d81c7bc --- /dev/null +++ b/webrtc/libjingle/session/p2ptransportparser.cc @@ -0,0 +1,215 @@ +/* + * 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/p2ptransportparser.h" + +#include + +#include "webrtc/p2p/base/constants.h" +#include "webrtc/p2p/base/parsing.h" +#include "webrtc/libjingle/session/sessionmanager.h" +#include "webrtc/p2p/base/sessionmessages.h" +#include "webrtc/libjingle/xmllite/qname.h" +#include "webrtc/libjingle/xmllite/xmlelement.h" +#include "webrtc/libjingle/xmpp/constants.h" +#include "webrtc/base/base64.h" +#include "webrtc/base/common.h" +#include "webrtc/base/stringencode.h" +#include "webrtc/base/stringutils.h" + +namespace cricket { + +static buzz::XmlElement* NewTransportElement(const std::string& name) { + return new buzz::XmlElement(buzz::QName(name, LN_TRANSPORT), true); +} + +bool P2PTransportParser::ParseTransportDescription( + const buzz::XmlElement* elem, + const CandidateTranslator* translator, + TransportDescription* desc, + ParseError* error) { + ASSERT(elem->Name().LocalPart() == LN_TRANSPORT); + desc->transport_type = elem->Name().Namespace(); + if (desc->transport_type != NS_GINGLE_P2P) + return BadParse("Unsupported transport type", error); + + for (const buzz::XmlElement* candidate_elem = elem->FirstElement(); + candidate_elem != NULL; + candidate_elem = candidate_elem->NextElement()) { + // Only look at local part because the namespace might (eventually) + // be NS_GINGLE_P2P or NS_JINGLE_ICE_UDP. + if (candidate_elem->Name().LocalPart() == LN_CANDIDATE) { + Candidate candidate; + if (!ParseCandidate(ICEPROTO_GOOGLE, candidate_elem, translator, + &candidate, error)) { + return false; + } + + desc->candidates.push_back(candidate); + } + } + return true; +} + +bool P2PTransportParser::WriteTransportDescription( + const TransportDescription& desc, + const CandidateTranslator* translator, + buzz::XmlElement** out_elem, + WriteError* error) { + TransportProtocol proto = TransportProtocolFromDescription(&desc); + rtc::scoped_ptr trans_elem( + NewTransportElement(desc.transport_type)); + + // Fail if we get HYBRID or ICE right now. + // TODO(juberti): Add ICE and HYBRID serialization. + if (proto != ICEPROTO_GOOGLE) { + LOG(LS_ERROR) << "Failed to serialize non-GICE TransportDescription"; + return false; + } + + for (std::vector::const_iterator iter = desc.candidates.begin(); + iter != desc.candidates.end(); ++iter) { + rtc::scoped_ptr cand_elem( + new buzz::XmlElement(QN_GINGLE_P2P_CANDIDATE)); + if (!WriteCandidate(proto, *iter, translator, cand_elem.get(), error)) { + return false; + } + trans_elem->AddElement(cand_elem.release()); + } + + *out_elem = trans_elem.release(); + return true; +} + +bool P2PTransportParser::ParseGingleCandidate( + const buzz::XmlElement* elem, + const CandidateTranslator* translator, + Candidate* candidate, + ParseError* error) { + return ParseCandidate(ICEPROTO_GOOGLE, elem, translator, candidate, error); +} + +bool P2PTransportParser::WriteGingleCandidate( + const Candidate& candidate, + const CandidateTranslator* translator, + buzz::XmlElement** out_elem, + WriteError* error) { + rtc::scoped_ptr elem( + new buzz::XmlElement(QN_GINGLE_CANDIDATE)); + bool ret = WriteCandidate(ICEPROTO_GOOGLE, candidate, translator, elem.get(), + error); + if (ret) { + *out_elem = elem.release(); + } + return ret; +} + +bool P2PTransportParser::VerifyUsernameFormat(TransportProtocol proto, + const std::string& username, + ParseError* error) { + if (proto == ICEPROTO_GOOGLE || proto == ICEPROTO_HYBRID) { + if (username.size() > GICE_UFRAG_MAX_LENGTH) + return BadParse("candidate username is too long", error); + if (!rtc::Base64::IsBase64Encoded(username)) + return BadParse("candidate username has non-base64 encoded characters", + error); + } else if (proto == ICEPROTO_RFC5245) { + if (username.size() > ICE_UFRAG_MAX_LENGTH) + return BadParse("candidate username is too long", error); + } + return true; +} + +bool P2PTransportParser::ParseCandidate(TransportProtocol proto, + const buzz::XmlElement* elem, + const CandidateTranslator* translator, + Candidate* candidate, + ParseError* error) { + ASSERT(proto == ICEPROTO_GOOGLE); + ASSERT(translator != NULL); + + if (!elem->HasAttr(buzz::QN_NAME) || + !elem->HasAttr(QN_ADDRESS) || + !elem->HasAttr(QN_PORT) || + !elem->HasAttr(QN_USERNAME) || + !elem->HasAttr(QN_PROTOCOL) || + !elem->HasAttr(QN_GENERATION)) { + return BadParse("candidate missing required attribute", error); + } + + rtc::SocketAddress address; + if (!ParseAddress(elem, QN_ADDRESS, QN_PORT, &address, error)) + return false; + + std::string channel_name = elem->Attr(buzz::QN_NAME); + int component = 0; + if (!translator || + !translator->GetComponentFromChannelName(channel_name, &component)) { + return BadParse("candidate has unknown channel name " + channel_name, + error); + } + + float preference = 0.0; + if (!GetXmlAttr(elem, QN_PREFERENCE, 0.0f, &preference)) { + return BadParse("candidate has unknown preference", error); + } + + candidate->set_component(component); + candidate->set_address(address); + candidate->set_username(elem->Attr(QN_USERNAME)); + candidate->set_preference(preference); + candidate->set_protocol(elem->Attr(QN_PROTOCOL)); + candidate->set_generation_str(elem->Attr(QN_GENERATION)); + if (elem->HasAttr(QN_PASSWORD)) + candidate->set_password(elem->Attr(QN_PASSWORD)); + if (elem->HasAttr(buzz::QN_TYPE)) + candidate->set_type(elem->Attr(buzz::QN_TYPE)); + if (elem->HasAttr(QN_NETWORK)) + candidate->set_network_name(elem->Attr(QN_NETWORK)); + + if (!VerifyUsernameFormat(proto, candidate->username(), error)) + return false; + + return true; +} + +bool P2PTransportParser::WriteCandidate(TransportProtocol proto, + const Candidate& candidate, + const CandidateTranslator* translator, + buzz::XmlElement* elem, + WriteError* error) { + ASSERT(proto == ICEPROTO_GOOGLE); + ASSERT(translator != NULL); + + std::string channel_name; + if (!translator || + !translator->GetChannelNameFromComponent( + candidate.component(), &channel_name)) { + return BadWrite("Cannot write candidate because of unknown component.", + error); + } + + elem->SetAttr(buzz::QN_NAME, channel_name); + elem->SetAttr(QN_ADDRESS, candidate.address().ipaddr().ToString()); + elem->SetAttr(QN_PORT, candidate.address().PortAsString()); + AddXmlAttr(elem, QN_PREFERENCE, candidate.preference()); + elem->SetAttr(QN_USERNAME, candidate.username()); + elem->SetAttr(QN_PROTOCOL, candidate.protocol()); + elem->SetAttr(QN_GENERATION, candidate.generation_str()); + if (!candidate.password().empty()) + elem->SetAttr(QN_PASSWORD, candidate.password()); + elem->SetAttr(buzz::QN_TYPE, candidate.type()); + if (!candidate.network_name().empty()) + elem->SetAttr(QN_NETWORK, candidate.network_name()); + + return true; +} + +} // namespace cricket diff --git a/webrtc/libjingle/session/p2ptransportparser.h b/webrtc/libjingle/session/p2ptransportparser.h new file mode 100644 index 000000000..8fadbb8f7 --- /dev/null +++ b/webrtc/libjingle/session/p2ptransportparser.h @@ -0,0 +1,67 @@ +/* + * 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_P2P_BASE_P2PTRANSPORTPARSER_H_ +#define WEBRTC_P2P_BASE_P2PTRANSPORTPARSER_H_ + +#include +#include "webrtc/libjingle/session/transportparser.h" + +namespace cricket { + +class P2PTransportParser : public TransportParser { + public: + P2PTransportParser() {} + // Translator may be null, in which case ParseCandidates should + // return false if there are candidates to parse. We can't not call + // ParseCandidates because there's no way to know ahead of time if + // there are candidates or not. + + // Jingle-specific functions; can be used with either ICE, GICE, or HYBRID. + virtual bool ParseTransportDescription(const buzz::XmlElement* elem, + const CandidateTranslator* translator, + TransportDescription* desc, + ParseError* error); + virtual bool WriteTransportDescription(const TransportDescription& desc, + const CandidateTranslator* translator, + buzz::XmlElement** elem, + WriteError* error); + + // Legacy Gingle functions; only can be used with GICE. + virtual bool ParseGingleCandidate(const buzz::XmlElement* elem, + const CandidateTranslator* translator, + Candidate* candidate, + ParseError* error); + virtual bool WriteGingleCandidate(const Candidate& candidate, + const CandidateTranslator* translator, + buzz::XmlElement** elem, + WriteError* error); + + private: + bool ParseCandidate(TransportProtocol proto, + const buzz::XmlElement* elem, + const CandidateTranslator* translator, + Candidate* candidate, + ParseError* error); + bool WriteCandidate(TransportProtocol proto, + const Candidate& candidate, + const CandidateTranslator* translator, + buzz::XmlElement* elem, + WriteError* error); + bool VerifyUsernameFormat(TransportProtocol proto, + const std::string& username, + ParseError* error); + + DISALLOW_EVIL_CONSTRUCTORS(P2PTransportParser); +}; + +} // namespace cricket + +#endif // WEBRTC_P2P_BASE_P2PTRANSPORTPARSER_H_ diff --git a/webrtc/libjingle/session/rawtransportparser.cc b/webrtc/libjingle/session/rawtransportparser.cc new file mode 100644 index 000000000..1fa762a9f --- /dev/null +++ b/webrtc/libjingle/session/rawtransportparser.cc @@ -0,0 +1,92 @@ +/* + * 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/rawtransportparser.h" + +#include +#include + +#include "webrtc/p2p/base/parsing.h" +#include "webrtc/libjingle/xmllite/qname.h" +#include "webrtc/libjingle/xmllite/xmlelement.h" +#include "webrtc/libjingle/xmpp/constants.h" +#include "webrtc/p2p/base/constants.h" + +#if defined(FEATURE_ENABLE_PSTN) +namespace cricket { + +bool RawTransportParser::ParseCandidates(SignalingProtocol protocol, + const buzz::XmlElement* elem, + const CandidateTranslator* translator, + Candidates* candidates, + ParseError* error) { + for (const buzz::XmlElement* cand_elem = elem->FirstElement(); + cand_elem != NULL; + cand_elem = cand_elem->NextElement()) { + if (cand_elem->Name() == QN_GINGLE_RAW_CHANNEL) { + if (!cand_elem->HasAttr(buzz::QN_NAME)) { + return BadParse("no channel name given", error); + } + if (NS_GINGLE_RAW != cand_elem->Attr(buzz::QN_NAME)) { + return BadParse("channel named does not exist", error); + } + rtc::SocketAddress addr; + if (!ParseRawAddress(cand_elem, &addr, error)) + return false; + + Candidate candidate; + candidate.set_component(1); + candidate.set_address(addr); + candidates->push_back(candidate); + } + } + return true; +} + +bool RawTransportParser::WriteCandidates(SignalingProtocol protocol, + const Candidates& candidates, + const CandidateTranslator* translator, + XmlElements* candidate_elems, + WriteError* error) { + for (std::vector::const_iterator + cand = candidates.begin(); + cand != candidates.end(); + ++cand) { + ASSERT(cand->component() == 1); + ASSERT(cand->protocol() == "udp"); + rtc::SocketAddress addr = cand->address(); + + buzz::XmlElement* elem = new buzz::XmlElement(QN_GINGLE_RAW_CHANNEL); + elem->SetAttr(buzz::QN_NAME, NS_GINGLE_RAW); + elem->SetAttr(QN_ADDRESS, addr.ipaddr().ToString()); + elem->SetAttr(QN_PORT, addr.PortAsString()); + candidate_elems->push_back(elem); + } + return true; +} + +bool RawTransportParser::ParseRawAddress(const buzz::XmlElement* elem, + rtc::SocketAddress* addr, + ParseError* error) { + // Make sure the required attributes exist + if (!elem->HasAttr(QN_ADDRESS) || + !elem->HasAttr(QN_PORT)) { + return BadParse("channel missing required attribute", error); + } + + // Parse the address. + if (!ParseAddress(elem, QN_ADDRESS, QN_PORT, addr, error)) + return false; + + return true; +} + +} // namespace cricket +#endif // defined(FEATURE_ENABLE_PSTN) diff --git a/webrtc/libjingle/session/rawtransportparser.h b/webrtc/libjingle/session/rawtransportparser.h new file mode 100644 index 000000000..f3783607c --- /dev/null +++ b/webrtc/libjingle/session/rawtransportparser.h @@ -0,0 +1,48 @@ +/* + * 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_P2P_BASE_RAWTRANSPORTPARSER_H_ +#define WEBRTC_P2P_BASE_RAWTRANSPORTPARSER_H_ + +#include + +#include "webrtc/p2p/base/constants.h" +#include "webrtc/libjingle/session/transportparser.h" + +namespace cricket { + +class RawTransportParser : public TransportParser { + public: + RawTransportParser() {} + + virtual bool ParseCandidates(SignalingProtocol protocol, + const buzz::XmlElement* elem, + const CandidateTranslator* translator, + Candidates* candidates, + ParseError* error); + virtual bool WriteCandidates(SignalingProtocol protocol, + const Candidates& candidates, + const CandidateTranslator* translator, + XmlElements* candidate_elems, + WriteError* error); + private: + // Parses the given element, which should describe the address to use for a + // given channel. This will return false and signal an error if the address + // or channel name is bad. + bool ParseRawAddress(const buzz::XmlElement* elem, + rtc::SocketAddress* addr, + ParseError* error); + + DISALLOW_EVIL_CONSTRUCTORS(RawTransportParser); +}; + +} // namespace cricket + +#endif // WEBRTC_P2P_BASE_RAWTRANSPORTPARSER_H_ diff --git a/webrtc/p2p/base/session_unittest.cc b/webrtc/libjingle/session/session_unittest.cc similarity index 99% rename from webrtc/p2p/base/session_unittest.cc rename to webrtc/libjingle/session/session_unittest.cc index e3faeff31..8b28d2d89 100644 --- a/webrtc/p2p/base/session_unittest.cc +++ b/webrtc/libjingle/session/session_unittest.cc @@ -22,6 +22,8 @@ #include "webrtc/base/natserver.h" #include "webrtc/base/natsocketfactory.h" #include "webrtc/base/stringencode.h" +#include "webrtc/libjingle/session/sessionmanager.h" +#include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/p2p/base/basicpacketsocketfactory.h" #include "webrtc/p2p/base/constants.h" #include "webrtc/p2p/base/p2ptransport.h" @@ -30,13 +32,11 @@ #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/xmpp/constants.h" using cricket::SignalingProtocol; using cricket::PROTOCOL_HYBRID; diff --git a/webrtc/libjingle/session/sessionmanager.cc b/webrtc/libjingle/session/sessionmanager.cc new file mode 100644 index 000000000..abe42999a --- /dev/null +++ b/webrtc/libjingle/session/sessionmanager.cc @@ -0,0 +1,1150 @@ +/* + * 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/sessionmanager.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/libjingle/session/p2ptransportparser.h" +#include "webrtc/libjingle/xmpp/constants.h" +#include "webrtc/libjingle/xmpp/jid.h" +#include "webrtc/p2p/base/constants.h" +#include "webrtc/p2p/base/sessionmessages.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()); +} + +SessionManager::SessionManager(PortAllocator *allocator, + rtc::Thread *worker) { + allocator_ = allocator; + signaling_thread_ = rtc::Thread::Current(); + if (worker == NULL) { + worker_thread_ = rtc::Thread::Current(); + } else { + worker_thread_ = worker; + } + timeout_ = 50; +} + +SessionManager::~SessionManager() { + // Note: Session::Terminate occurs asynchronously, so it's too late to + // delete them now. They better be all gone. + ASSERT(session_map_.empty()); + // TerminateAll(); + SignalDestroyed(); +} + +void SessionManager::AddClient(const std::string& content_type, + SessionClient* client) { + ASSERT(client_map_.find(content_type) == client_map_.end()); + client_map_[content_type] = client; +} + +void SessionManager::RemoveClient(const std::string& content_type) { + ClientMap::iterator iter = client_map_.find(content_type); + ASSERT(iter != client_map_.end()); + client_map_.erase(iter); +} + +SessionClient* SessionManager::GetClient(const std::string& content_type) { + ClientMap::iterator iter = client_map_.find(content_type); + return (iter != client_map_.end()) ? iter->second : NULL; +} + +Session* SessionManager::CreateSession(const std::string& local_name, + const std::string& content_type) { + std::string id; + return CreateSession(id, local_name, content_type); +} + +Session* SessionManager::CreateSession(const std::string& id, + const std::string& local_name, + const std::string& content_type) { + std::string sid = + id.empty() ? rtc::ToString(rtc::CreateRandomId64()) : id; + return CreateSession(local_name, local_name, sid, content_type, false); +} + +Session* SessionManager::CreateSession( + const std::string& local_name, const std::string& initiator_name, + const std::string& sid, const std::string& content_type, + bool received_initiate) { + SessionClient* client = GetClient(content_type); + ASSERT(client != NULL); + + Session* session = new Session(this, local_name, initiator_name, + sid, content_type, client); + session->SetIdentity(transport_desc_factory_.identity()); + session_map_[session->id()] = session; + session->SignalRequestSignaling.connect( + this, &SessionManager::OnRequestSignaling); + session->SignalOutgoingMessage.connect( + this, &SessionManager::OnOutgoingMessage); + session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage); + SignalSessionCreate(session, received_initiate); + session->client()->OnSessionCreate(session, received_initiate); + return session; +} + +void SessionManager::DestroySession(Session* session) { + if (session != NULL) { + SessionMap::iterator it = session_map_.find(session->id()); + if (it != session_map_.end()) { + SignalSessionDestroy(session); + session->client()->OnSessionDestroy(session); + session_map_.erase(it); + delete session; + } + } +} + +Session* SessionManager::GetSession(const std::string& sid) { + SessionMap::iterator it = session_map_.find(sid); + if (it != session_map_.end()) + return it->second; + return NULL; +} + +void SessionManager::TerminateAll() { + while (session_map_.begin() != session_map_.end()) { + Session* session = session_map_.begin()->second; + session->Terminate(); + } +} + +bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) { + return cricket::IsSessionMessage(stanza); +} + +Session* SessionManager::FindSession(const std::string& sid, + const std::string& remote_name) { + SessionMap::iterator iter = session_map_.find(sid); + if (iter == session_map_.end()) + return NULL; + + Session* session = iter->second; + if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name())) + return NULL; + + return session; +} + +void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) { + SessionMessage msg; + ParseError error; + + if (!ParseSessionMessage(stanza, &msg, &error)) { + SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + error.text, NULL); + return; + } + + Session* session = FindSession(msg.sid, msg.from); + if (session) { + session->OnIncomingMessage(msg); + return; + } + if (msg.type != ACTION_SESSION_INITIATE) { + SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + "unknown session", NULL); + return; + } + + std::string content_type; + if (!ParseContentType(msg.protocol, msg.action_elem, + &content_type, &error)) { + SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + error.text, NULL); + return; + } + + if (!GetClient(content_type)) { + SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", + "unknown content type: " + content_type, NULL); + return; + } + + session = CreateSession(msg.to, msg.initiator, msg.sid, + content_type, true); + session->OnIncomingMessage(msg); +} + +void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* response_stanza) { + if (orig_stanza == NULL || response_stanza == NULL) { + return; + } + + SessionMessage msg; + ParseError error; + if (!ParseSessionMessage(orig_stanza, &msg, &error)) { + LOG(LS_WARNING) << "Error parsing incoming response: " << error.text + << ":" << orig_stanza; + return; + } + + Session* session = FindSession(msg.sid, msg.to); + if (!session) { + // Also try the QN_FROM in the response stanza, in case we sent the request + // to a bare JID but got the response from a full JID. + std::string ack_from = response_stanza->Attr(buzz::QN_FROM); + session = FindSession(msg.sid, ack_from); + } + if (session) { + session->OnIncomingResponse(orig_stanza, response_stanza, msg); + } +} + +void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* error_stanza) { + SessionMessage msg; + ParseError error; + if (!ParseSessionMessage(orig_stanza, &msg, &error)) { + return; // TODO: log somewhere? + } + + Session* session = FindSession(msg.sid, msg.to); + if (session) { + rtc::scoped_ptr synthetic_error; + if (!error_stanza) { + // A failed send is semantically equivalent to an error response, so we + // can just turn the former into the latter. + synthetic_error.reset( + CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND, + "cancel", "Recipient did not respond", NULL)); + error_stanza = synthetic_error.get(); + } + + session->OnFailedSend(orig_stanza, error_stanza); + } +} + +void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + rtc::scoped_ptr msg( + CreateErrorMessage(stanza, name, type, text, extra_info)); + SignalOutgoingMessage(this, msg.get()); +} + +buzz::XmlElement* SessionManager::CreateErrorMessage( + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ); + iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM)); + iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); + iq->SetAttr(buzz::QN_TYPE, "error"); + + CopyXmlChildren(stanza, iq); + + buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR); + error->SetAttr(buzz::QN_TYPE, type); + iq->AddElement(error); + + // If the error name is not in the standard namespace, we have to first add + // some error from that namespace. + if (name.Namespace() != buzz::NS_STANZA) { + error->AddElement( + new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION)); + } + error->AddElement(new buzz::XmlElement(name)); + + if (extra_info) + error->AddElement(new buzz::XmlElement(*extra_info)); + + if (text.size() > 0) { + // It's okay to always use English here. This text is for debugging + // purposes only. + buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT); + text_elem->SetAttr(buzz::QN_XML_LANG, "en"); + text_elem->SetBodyText(text); + error->AddElement(text_elem); + } + + // TODO: Should we include error codes as well for SIP compatibility? + + return iq; +} + +void SessionManager::OnOutgoingMessage(Session* session, + const buzz::XmlElement* stanza) { + SignalOutgoingMessage(this, stanza); +} + +void SessionManager::OnErrorMessage(BaseSession* session, + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info) { + SendErrorMessage(stanza, name, type, text, extra_info); +} + +void SessionManager::OnSignalingReady() { + for (SessionMap::iterator it = session_map_.begin(); + it != session_map_.end(); + ++it) { + it->second->OnSignalingReady(); + } +} + +void SessionManager::OnRequestSignaling(Session* session) { + SignalRequestSignaling(); +} + +} // namespace cricket diff --git a/webrtc/libjingle/session/sessionmanager.h b/webrtc/libjingle/session/sessionmanager.h new file mode 100644 index 000000000..2d2c121b8 --- /dev/null +++ b/webrtc/libjingle/session/sessionmanager.h @@ -0,0 +1,436 @@ +/* + * 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_P2P_BASE_SESSIONMANAGER_H_ +#define WEBRTC_P2P_BASE_SESSIONMANAGER_H_ + +#include +#include +#include +#include + +#include "webrtc/libjingle/xmllite/xmlelement.h" +#include "webrtc/libjingle/xmpp/constants.h" +#include "webrtc/p2p/base/parsing.h" +#include "webrtc/p2p/base/portallocator.h" +#include "webrtc/p2p/base/session.h" +#include "webrtc/p2p/base/sessionclient.h" +#include "webrtc/p2p/base/sessionmessages.h" +#include "webrtc/p2p/base/transportdescriptionfactory.h" +#include "webrtc/base/sigslot.h" +#include "webrtc/base/thread.h" + +namespace buzz { +class QName; +class XmlElement; +} + +namespace cricket { + +class BaseSession; +class SessionClient; +class SessionManager; + +// 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. +}; + +// SessionManager manages session instances. +class SessionManager : public sigslot::has_slots<> { + public: + SessionManager(PortAllocator *allocator, + rtc::Thread *worker_thread = NULL); + virtual ~SessionManager(); + + PortAllocator *port_allocator() const { return allocator_; } + rtc::Thread *worker_thread() const { return worker_thread_; } + rtc::Thread *signaling_thread() const { return signaling_thread_; } + + int session_timeout() const { return timeout_; } + void set_session_timeout(int timeout) { timeout_ = timeout; } + + // Set what transport protocol we want to default to. + void set_transport_protocol(TransportProtocol proto) { + transport_desc_factory_.set_protocol(proto); + } + + // Control use of DTLS. An identity must be supplied if DTLS is enabled. + void set_secure(SecurePolicy policy) { + transport_desc_factory_.set_secure(policy); + } + void set_identity(rtc::SSLIdentity* identity) { + transport_desc_factory_.set_identity(identity); + } + const TransportDescriptionFactory* transport_desc_factory() const { + return &transport_desc_factory_; + } + + // Registers support for the given client. If we receive an initiate + // describing a session of the given type, we will automatically create a + // Session object and notify this client. The client may then accept or + // reject the session. + void AddClient(const std::string& content_type, SessionClient* client); + void RemoveClient(const std::string& content_type); + SessionClient* GetClient(const std::string& content_type); + + // Creates a new session. The given name is the JID of the client on whose + // behalf we initiate the session. + Session *CreateSession(const std::string& local_name, + const std::string& content_type); + + Session *CreateSession(const std::string& id, + const std::string& local_name, + const std::string& content_type); + + // Destroys the given session. + void DestroySession(Session *session); + + // Returns the session with the given ID or NULL if none exists. + Session *GetSession(const std::string& sid); + + // Terminates all of the sessions created by this manager. + void TerminateAll(); + + // These are signaled whenever the set of existing sessions changes. + sigslot::signal2 SignalSessionCreate; + sigslot::signal1 SignalSessionDestroy; + + // Determines whether the given stanza is intended for some session. + bool IsSessionMessage(const buzz::XmlElement* stanza); + + // Given a sid, initiator, and remote_name, this finds the matching Session + Session* FindSession(const std::string& sid, + const std::string& remote_name); + + // Called when we receive a stanza for which IsSessionMessage is true. + void OnIncomingMessage(const buzz::XmlElement* stanza); + + // Called when we get a response to a message that we sent. + void OnIncomingResponse(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* response_stanza); + + // Called if an attempted to send times out or an error is returned. In the + // timeout case error_stanza will be NULL + void OnFailedSend(const buzz::XmlElement* orig_stanza, + const buzz::XmlElement* error_stanza); + + // Signalled each time a session generates a signaling message to send. + // Also signalled on errors, but with a NULL session. + sigslot::signal2 SignalOutgoingMessage; + + // Signaled before sessions try to send certain signaling messages. The + // client should call OnSignalingReady once it is safe to send them. These + // steps are taken so that we don't send signaling messages trying to + // re-establish the connectivity of a session when the client cannot send + // the messages (and would probably just drop them on the floor). + // + // Note: you can connect this directly to OnSignalingReady(), if a signalling + // check is not supported. + sigslot::signal0<> SignalRequestSignaling; + void OnSignalingReady(); + + // Signaled when this SessionManager is deleted. + sigslot::signal0<> SignalDestroyed; + + private: + typedef std::map SessionMap; + typedef std::map ClientMap; + + // Helper function for CreateSession. This is also invoked when we receive + // a message attempting to initiate a session with this client. + Session *CreateSession(const std::string& local_name, + const std::string& initiator, + const std::string& sid, + const std::string& content_type, + bool received_initiate); + + // Attempts to find a registered session type whose description appears as + // a child of the session element. Such a child should be present indicating + // the application they hope to initiate. + std::string FindClient(const buzz::XmlElement* session); + + // Sends a message back to the other client indicating that we found an error + // in the stanza they sent. name identifies the error, type is one of the + // standard XMPP types (cancel, continue, modify, auth, wait), and text is a + // description for debugging purposes. + void SendErrorMessage(const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info); + + // Creates and returns an error message from the given components. The + // caller is responsible for deleting this. + buzz::XmlElement* CreateErrorMessage( + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info); + + // Called each time a session requests signaling. + void OnRequestSignaling(Session* session); + + // Called each time a session has an outgoing message. + void OnOutgoingMessage(Session* session, const buzz::XmlElement* stanza); + + // Called each time a session has an error to send. + void OnErrorMessage(BaseSession* session, + const buzz::XmlElement* stanza, + const buzz::QName& name, + const std::string& type, + const std::string& text, + const buzz::XmlElement* extra_info); + + PortAllocator *allocator_; + rtc::Thread *signaling_thread_; + rtc::Thread *worker_thread_; + int timeout_; + TransportDescriptionFactory transport_desc_factory_; + SessionMap session_map_; + ClientMap client_map_; +}; + +} // namespace cricket + +#endif // WEBRTC_P2P_BASE_SESSIONMANAGER_H_ diff --git a/webrtc/libjingle/session/transportparser.cc b/webrtc/libjingle/session/transportparser.cc new file mode 100644 index 000000000..3dda481ea --- /dev/null +++ b/webrtc/libjingle/session/transportparser.cc @@ -0,0 +1,38 @@ +/* + * 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/transportparser.h" + +#include "webrtc/p2p/base/parsing.h" +#include "webrtc/libjingle/xmllite/xmlelement.h" +#include "webrtc/libjingle/xmpp/constants.h" + +namespace cricket { + +bool TransportParser::ParseAddress(const buzz::XmlElement* elem, + const buzz::QName& address_name, + const buzz::QName& port_name, + rtc::SocketAddress* address, + ParseError* error) { + if (!elem->HasAttr(address_name)) + return BadParse("address does not have " + address_name.LocalPart(), error); + if (!elem->HasAttr(port_name)) + return BadParse("address does not have " + port_name.LocalPart(), error); + + address->SetIP(elem->Attr(address_name)); + std::istringstream ist(elem->Attr(port_name)); + int port = 0; + ist >> port; + address->SetPort(port); + + return true; +} + +} // namespace cricket diff --git a/webrtc/libjingle/session/transportparser.h b/webrtc/libjingle/session/transportparser.h new file mode 100644 index 000000000..92c121cf2 --- /dev/null +++ b/webrtc/libjingle/session/transportparser.h @@ -0,0 +1,78 @@ +/* + * 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_P2P_BASE_TRANSPORTPARSER_H_ +#define WEBRTC_P2P_BASE_TRANSPORTPARSER_H_ + +#include +#include + +#include "webrtc/p2p/base/transportinfo.h" + +namespace buzz { +class QName; +class XmlElement; +} + +namespace cricket { + +struct ParseError; +struct WriteError; +class CandidateTranslator; + +typedef std::vector XmlElements; + +class TransportParser { + public: + // The incoming Translator value may be null, in which case + // ParseCandidates should return false if there are candidates to + // parse (indicating a failure to parse). If the Translator is null + // and there are no candidates to parse, then return true, + // indicating a successful parse of 0 candidates. + + // Parse or write a transport description, including ICE credentials and + // any DTLS fingerprint. Since only Jingle has transport descriptions, these + // functions are only used when serializing to Jingle. + virtual bool ParseTransportDescription(const buzz::XmlElement* elem, + const CandidateTranslator* translator, + TransportDescription* tdesc, + ParseError* error) = 0; + virtual bool WriteTransportDescription(const TransportDescription& tdesc, + const CandidateTranslator* translator, + buzz::XmlElement** tdesc_elem, + WriteError* error) = 0; + + + // Parse a single candidate. This must be used when parsing Gingle + // candidates, since there is no enclosing transport description. + virtual bool ParseGingleCandidate(const buzz::XmlElement* elem, + const CandidateTranslator* translator, + Candidate* candidates, + ParseError* error) = 0; + virtual bool WriteGingleCandidate(const Candidate& candidate, + const CandidateTranslator* translator, + buzz::XmlElement** candidate_elem, + WriteError* error) = 0; + + // Helper function to parse an element describing an address. This + // retrieves the IP and port from the given element and verifies + // that they look like plausible values. + bool ParseAddress(const buzz::XmlElement* elem, + const buzz::QName& address_name, + const buzz::QName& port_name, + rtc::SocketAddress* address, + ParseError* error); + + virtual ~TransportParser() {} +}; + +} // namespace cricket + +#endif // WEBRTC_P2P_BASE_TRANSPORTPARSER_H_ diff --git a/webrtc/libjingle/session/transportparser_unittest.cc b/webrtc/libjingle/session/transportparser_unittest.cc new file mode 100644 index 000000000..81db39804 --- /dev/null +++ b/webrtc/libjingle/session/transportparser_unittest.cc @@ -0,0 +1,136 @@ +/* + * Copyright 2011 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/base/gunit.h" +#include "webrtc/libjingle/session/p2ptransportparser.h" +#include "webrtc/p2p/base/parsing.h" +#include "webrtc/p2p/base/sessionmessages.h" +#include "webrtc/libjingle/xmllite/xmlelement.h" +#include "webrtc/libjingle/xmpp/constants.h" +#include "webrtc/p2p/base/constants.h" + +using cricket::Candidate; +using cricket::Candidates; +using cricket::ParseError; +using cricket::WriteError; + +class TransportParserTest : public testing::Test { +}; + +class FakeCandidateTranslator : public cricket::CandidateTranslator { + public: + void AddMapping(int component, const std::string& channel_name) { + name_to_component[channel_name] = component; + component_to_name[component] = channel_name; + } + + bool GetChannelNameFromComponent( + int component, std::string* channel_name) const { + if (component_to_name.find(component) == component_to_name.end()) { + return false; + } + *channel_name = component_to_name.find(component)->second; + return true; + } + bool GetComponentFromChannelName( + const std::string& channel_name, int* component) const { + if (name_to_component.find(channel_name) == name_to_component.end()) { + return false; + } + *component = name_to_component.find(channel_name)->second; + return true; + } + + std::map name_to_component; + std::map component_to_name; +}; + +// Tests that we can properly serialize/deserialize candidates. +TEST_F(TransportParserTest, TestP2PTransportWriteAndParseCandidate) { + Candidate test_candidate( + "", 1, "udp", + rtc::SocketAddress("2001:db8:fefe::1", 9999), + 738197504, "abcdef", "ghijkl", "foo", 50, ""); + test_candidate.set_network_name("testnet"); + Candidate test_candidate2( + "", 2, "tcp", + rtc::SocketAddress("192.168.7.1", 9999), + 1107296256, "mnopqr", "stuvwx", "bar", 100, ""); + test_candidate2.set_network_name("testnet2"); + rtc::SocketAddress host_address("www.google.com", 24601); + host_address.SetResolvedIP(rtc::IPAddress(0x0A000001)); + Candidate test_candidate3( + "", 3, "spdy", host_address, 1476395008, "yzabcd", + "efghij", "baz", 150, ""); + test_candidate3.set_network_name("testnet3"); + WriteError write_error; + ParseError parse_error; + rtc::scoped_ptr elem; + cricket::Candidate parsed_candidate; + cricket::P2PTransportParser parser; + + FakeCandidateTranslator translator; + translator.AddMapping(1, "test"); + translator.AddMapping(2, "test2"); + translator.AddMapping(3, "test3"); + + EXPECT_TRUE(parser.WriteGingleCandidate(test_candidate, &translator, + elem.accept(), &write_error)); + EXPECT_EQ("", write_error.text); + EXPECT_EQ("test", elem->Attr(buzz::QN_NAME)); + EXPECT_EQ("udp", elem->Attr(cricket::QN_PROTOCOL)); + EXPECT_EQ("2001:db8:fefe::1", elem->Attr(cricket::QN_ADDRESS)); + EXPECT_EQ("9999", elem->Attr(cricket::QN_PORT)); + EXPECT_EQ("0.34", elem->Attr(cricket::QN_PREFERENCE)); + EXPECT_EQ("abcdef", elem->Attr(cricket::QN_USERNAME)); + EXPECT_EQ("ghijkl", elem->Attr(cricket::QN_PASSWORD)); + EXPECT_EQ("foo", elem->Attr(cricket::QN_TYPE)); + EXPECT_EQ("testnet", elem->Attr(cricket::QN_NETWORK)); + EXPECT_EQ("50", elem->Attr(cricket::QN_GENERATION)); + + EXPECT_TRUE(parser.ParseGingleCandidate(elem.get(), &translator, + &parsed_candidate, &parse_error)); + EXPECT_TRUE(test_candidate.IsEquivalent(parsed_candidate)); + + EXPECT_TRUE(parser.WriteGingleCandidate(test_candidate2, &translator, + elem.accept(), &write_error)); + EXPECT_EQ("test2", elem->Attr(buzz::QN_NAME)); + EXPECT_EQ("tcp", elem->Attr(cricket::QN_PROTOCOL)); + EXPECT_EQ("192.168.7.1", elem->Attr(cricket::QN_ADDRESS)); + EXPECT_EQ("9999", elem->Attr(cricket::QN_PORT)); + EXPECT_EQ("0.51", elem->Attr(cricket::QN_PREFERENCE)); + EXPECT_EQ("mnopqr", elem->Attr(cricket::QN_USERNAME)); + EXPECT_EQ("stuvwx", elem->Attr(cricket::QN_PASSWORD)); + EXPECT_EQ("bar", elem->Attr(cricket::QN_TYPE)); + EXPECT_EQ("testnet2", elem->Attr(cricket::QN_NETWORK)); + EXPECT_EQ("100", elem->Attr(cricket::QN_GENERATION)); + + EXPECT_TRUE(parser.ParseGingleCandidate(elem.get(), &translator, + &parsed_candidate, &parse_error)); + EXPECT_TRUE(test_candidate2.IsEquivalent(parsed_candidate)); + + // Check that an ip is preferred over hostname. + EXPECT_TRUE(parser.WriteGingleCandidate(test_candidate3, &translator, + elem.accept(), &write_error)); + EXPECT_EQ("test3", elem->Attr(cricket::QN_NAME)); + EXPECT_EQ("spdy", elem->Attr(cricket::QN_PROTOCOL)); + EXPECT_EQ("10.0.0.1", elem->Attr(cricket::QN_ADDRESS)); + EXPECT_EQ("24601", elem->Attr(cricket::QN_PORT)); + EXPECT_EQ("0.69", elem->Attr(cricket::QN_PREFERENCE)); + EXPECT_EQ("yzabcd", elem->Attr(cricket::QN_USERNAME)); + EXPECT_EQ("efghij", elem->Attr(cricket::QN_PASSWORD)); + EXPECT_EQ("baz", elem->Attr(cricket::QN_TYPE)); + EXPECT_EQ("testnet3", elem->Attr(cricket::QN_NETWORK)); + EXPECT_EQ("150", elem->Attr(cricket::QN_GENERATION)); + + EXPECT_TRUE(parser.ParseGingleCandidate(elem.get(), &translator, + &parsed_candidate, &parse_error)); + EXPECT_TRUE(test_candidate3.IsEquivalent(parsed_candidate)); +} diff --git a/webrtc/libjingle/session/tunnel/pseudotcpchannel.h b/webrtc/libjingle/session/tunnel/pseudotcpchannel.h index ef74275a4..7397a0682 100644 --- a/webrtc/libjingle/session/tunnel/pseudotcpchannel.h +++ b/webrtc/libjingle/session/tunnel/pseudotcpchannel.h @@ -25,14 +25,14 @@ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef TALK_SESSION_TUNNEL_PSEUDOTCPCHANNEL_H_ -#define TALK_SESSION_TUNNEL_PSEUDOTCPCHANNEL_H_ +#ifndef WEBRTC_LIBJINGLE_SESSION_TUNNEL_PSEUDOTCPCHANNEL_H_ +#define WEBRTC_LIBJINGLE_SESSION_TUNNEL_PSEUDOTCPCHANNEL_H_ #include "webrtc/base/criticalsection.h" #include "webrtc/base/messagequeue.h" #include "webrtc/base/stream.h" +#include "webrtc/libjingle/session/sessionmanager.h" #include "webrtc/p2p/base/pseudotcp.h" -#include "webrtc/p2p/base/sessionmanager.h" namespace rtc { class Thread; @@ -137,4 +137,4 @@ class PseudoTcpChannel } // namespace cricket -#endif // TALK_SESSION_TUNNEL_PSEUDOTCPCHANNEL_H_ +#endif // WEBRTC_LIBJINGLE_SESSION_TUNNEL_PSEUDOTCPCHANNEL_H_ diff --git a/webrtc/libjingle/session/tunnel/securetunnelsessionclient.h b/webrtc/libjingle/session/tunnel/securetunnelsessionclient.h index 98127f393..705c8fed7 100644 --- a/webrtc/libjingle/session/tunnel/securetunnelsessionclient.h +++ b/webrtc/libjingle/session/tunnel/securetunnelsessionclient.h @@ -31,8 +31,8 @@ // SecureTunnelSession is a TunnelSession that wraps the underlying // tunnel stream into an SSLStreamAdapter. -#ifndef TALK_SESSION_TUNNEL_SECURETUNNELSESSIONCLIENT_H_ -#define TALK_SESSION_TUNNEL_SECURETUNNELSESSIONCLIENT_H_ +#ifndef WEBRTC_LIBJINGLE_SESSION_TUNNEL_SECURETUNNELSESSIONCLIENT_H_ +#define WEBRTC_LIBJINGLE_SESSION_TUNNEL_SECURETUNNELSESSIONCLIENT_H_ #include @@ -162,4 +162,4 @@ class SecureTunnelSession : public TunnelSession { } // namespace cricket -#endif // TALK_SESSION_TUNNEL_SECURETUNNELSESSIONCLIENT_H_ +#endif // WEBRTC_LIBJINGLE_SESSION_TUNNEL_SECURETUNNELSESSIONCLIENT_H_ diff --git a/webrtc/libjingle/session/tunnel/tunnelsessionclient.h b/webrtc/libjingle/session/tunnel/tunnelsessionclient.h index 87beaed8b..63d1c382a 100644 --- a/webrtc/libjingle/session/tunnel/tunnelsessionclient.h +++ b/webrtc/libjingle/session/tunnel/tunnelsessionclient.h @@ -30,16 +30,16 @@ #include +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/stream.h" +#include "webrtc/libjingle/session/sessionmanager.h" +#include "webrtc/libjingle/xmllite/qname.h" +#include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/p2p/base/constants.h" #include "webrtc/p2p/base/pseudotcp.h" #include "webrtc/p2p/base/session.h" #include "webrtc/p2p/base/sessionclient.h" #include "webrtc/p2p/base/sessiondescription.h" -#include "webrtc/p2p/base/sessionmanager.h" -#include "webrtc/libjingle/xmllite/qname.h" -#include "webrtc/libjingle/xmpp/constants.h" -#include "webrtc/base/criticalsection.h" -#include "webrtc/base/stream.h" namespace cricket { diff --git a/webrtc/libjingle/session/tunnel/tunnelsessionclient_unittest.cc b/webrtc/libjingle/session/tunnel/tunnelsessionclient_unittest.cc index f40da9380..97bfd848d 100644 --- a/webrtc/libjingle/session/tunnel/tunnelsessionclient_unittest.cc +++ b/webrtc/libjingle/session/tunnel/tunnelsessionclient_unittest.cc @@ -26,16 +26,16 @@ */ #include -#include "webrtc/p2p/base/sessionmanager.h" -#include "webrtc/p2p/base/transport.h" -#include "webrtc/p2p/client/fakeportallocator.h" -#include "webrtc/libjingle/session/tunnel/tunnelsessionclient.h" #include "webrtc/base/gunit.h" #include "webrtc/base/messagehandler.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/stream.h" #include "webrtc/base/thread.h" #include "webrtc/base/timeutils.h" +#include "webrtc/libjingle/session/sessionmanager.h" +#include "webrtc/libjingle/session/tunnel/tunnelsessionclient.h" +#include "webrtc/p2p/base/transport.h" +#include "webrtc/p2p/client/fakeportallocator.h" static const int kTimeoutMs = 10000; static const int kBlockSize = 4096; diff --git a/webrtc/p2p/base/constants.cc b/webrtc/p2p/base/constants.cc index 84c9ea260..ef7487bb7 100644 --- a/webrtc/p2p/base/constants.cc +++ b/webrtc/p2p/base/constants.cc @@ -163,6 +163,7 @@ const size_t ICE_UFRAG_MIN_LENGTH = 4; const size_t ICE_PWD_MIN_LENGTH = 22; const size_t ICE_UFRAG_MAX_LENGTH = 255; const size_t ICE_PWD_MAX_LENGTH = 256; +const size_t GICE_UFRAG_MAX_LENGTH = 16; // TODO: This is media-specific, so might belong // somewhere like media/base/constants.h const int ICE_CANDIDATE_COMPONENT_RTP = 1; diff --git a/webrtc/p2p/base/constants.h b/webrtc/p2p/base/constants.h index 57423f5b3..edfe07c30 100644 --- a/webrtc/p2p/base/constants.h +++ b/webrtc/p2p/base/constants.h @@ -165,6 +165,7 @@ extern const size_t ICE_UFRAG_MIN_LENGTH; extern const size_t ICE_PWD_MIN_LENGTH; extern const size_t ICE_UFRAG_MAX_LENGTH; extern const size_t ICE_PWD_MAX_LENGTH; +extern const size_t GICE_UFRAG_MAX_LENGTH; extern const int ICE_CANDIDATE_COMPONENT_RTP; extern const int ICE_CANDIDATE_COMPONENT_RTCP; extern const int ICE_CANDIDATE_COMPONENT_DEFAULT; diff --git a/webrtc/p2p/base/p2ptransport.cc b/webrtc/p2p/base/p2ptransport.cc index e873756ff..c4cea5fb1 100644 --- a/webrtc/p2p/base/p2ptransport.cc +++ b/webrtc/p2p/base/p2ptransport.cc @@ -11,13 +11,8 @@ #include "webrtc/p2p/base/p2ptransport.h" #include -#include -#include "webrtc/p2p/base/constants.h" #include "webrtc/p2p/base/p2ptransportchannel.h" -#include "webrtc/p2p/base/parsing.h" -#include "webrtc/p2p/base/sessionmanager.h" -#include "webrtc/p2p/base/sessionmessages.h" #include "webrtc/libjingle/xmllite/qname.h" #include "webrtc/libjingle/xmllite/xmlelement.h" #include "webrtc/libjingle/xmpp/constants.h" @@ -26,20 +21,8 @@ #include "webrtc/base/stringencode.h" #include "webrtc/base/stringutils.h" -namespace { - -// Limits for GICE and ICE username sizes. -const size_t kMaxGiceUsernameSize = 16; -const size_t kMaxIceUsernameSize = 512; - -} // namespace - namespace cricket { -static buzz::XmlElement* NewTransportElement(const std::string& name) { - return new buzz::XmlElement(buzz::QName(name, LN_TRANSPORT), true); -} - P2PTransport::P2PTransport(rtc::Thread* signaling_thread, rtc::Thread* worker_thread, const std::string& content_name, @@ -61,186 +44,4 @@ void P2PTransport::DestroyTransportChannel(TransportChannelImpl* channel) { delete channel; } -bool P2PTransportParser::ParseTransportDescription( - const buzz::XmlElement* elem, - const CandidateTranslator* translator, - TransportDescription* desc, - ParseError* error) { - ASSERT(elem->Name().LocalPart() == LN_TRANSPORT); - desc->transport_type = elem->Name().Namespace(); - if (desc->transport_type != NS_GINGLE_P2P) - return BadParse("Unsupported transport type", error); - - for (const buzz::XmlElement* candidate_elem = elem->FirstElement(); - candidate_elem != NULL; - candidate_elem = candidate_elem->NextElement()) { - // Only look at local part because the namespace might (eventually) - // be NS_GINGLE_P2P or NS_JINGLE_ICE_UDP. - if (candidate_elem->Name().LocalPart() == LN_CANDIDATE) { - Candidate candidate; - if (!ParseCandidate(ICEPROTO_GOOGLE, candidate_elem, translator, - &candidate, error)) { - return false; - } - - desc->candidates.push_back(candidate); - } - } - return true; -} - -bool P2PTransportParser::WriteTransportDescription( - const TransportDescription& desc, - const CandidateTranslator* translator, - buzz::XmlElement** out_elem, - WriteError* error) { - TransportProtocol proto = TransportProtocolFromDescription(&desc); - rtc::scoped_ptr trans_elem( - NewTransportElement(desc.transport_type)); - - // Fail if we get HYBRID or ICE right now. - // TODO(juberti): Add ICE and HYBRID serialization. - if (proto != ICEPROTO_GOOGLE) { - LOG(LS_ERROR) << "Failed to serialize non-GICE TransportDescription"; - return false; - } - - for (std::vector::const_iterator iter = desc.candidates.begin(); - iter != desc.candidates.end(); ++iter) { - rtc::scoped_ptr cand_elem( - new buzz::XmlElement(QN_GINGLE_P2P_CANDIDATE)); - if (!WriteCandidate(proto, *iter, translator, cand_elem.get(), error)) { - return false; - } - trans_elem->AddElement(cand_elem.release()); - } - - *out_elem = trans_elem.release(); - return true; -} - -bool P2PTransportParser::ParseGingleCandidate( - const buzz::XmlElement* elem, - const CandidateTranslator* translator, - Candidate* candidate, - ParseError* error) { - return ParseCandidate(ICEPROTO_GOOGLE, elem, translator, candidate, error); -} - -bool P2PTransportParser::WriteGingleCandidate( - const Candidate& candidate, - const CandidateTranslator* translator, - buzz::XmlElement** out_elem, - WriteError* error) { - rtc::scoped_ptr elem( - new buzz::XmlElement(QN_GINGLE_CANDIDATE)); - bool ret = WriteCandidate(ICEPROTO_GOOGLE, candidate, translator, elem.get(), - error); - if (ret) { - *out_elem = elem.release(); - } - return ret; -} - -bool P2PTransportParser::VerifyUsernameFormat(TransportProtocol proto, - const std::string& username, - ParseError* error) { - if (proto == ICEPROTO_GOOGLE || proto == ICEPROTO_HYBRID) { - if (username.size() > kMaxGiceUsernameSize) - return BadParse("candidate username is too long", error); - if (!rtc::Base64::IsBase64Encoded(username)) - return BadParse("candidate username has non-base64 encoded characters", - error); - } else if (proto == ICEPROTO_RFC5245) { - if (username.size() > kMaxIceUsernameSize) - return BadParse("candidate username is too long", error); - } - return true; -} - -bool P2PTransportParser::ParseCandidate(TransportProtocol proto, - const buzz::XmlElement* elem, - const CandidateTranslator* translator, - Candidate* candidate, - ParseError* error) { - ASSERT(proto == ICEPROTO_GOOGLE); - ASSERT(translator != NULL); - - if (!elem->HasAttr(buzz::QN_NAME) || - !elem->HasAttr(QN_ADDRESS) || - !elem->HasAttr(QN_PORT) || - !elem->HasAttr(QN_USERNAME) || - !elem->HasAttr(QN_PROTOCOL) || - !elem->HasAttr(QN_GENERATION)) { - return BadParse("candidate missing required attribute", error); - } - - rtc::SocketAddress address; - if (!ParseAddress(elem, QN_ADDRESS, QN_PORT, &address, error)) - return false; - - std::string channel_name = elem->Attr(buzz::QN_NAME); - int component = 0; - if (!translator || - !translator->GetComponentFromChannelName(channel_name, &component)) { - return BadParse("candidate has unknown channel name " + channel_name, - error); - } - - float preference = 0.0; - if (!GetXmlAttr(elem, QN_PREFERENCE, 0.0f, &preference)) { - return BadParse("candidate has unknown preference", error); - } - - candidate->set_component(component); - candidate->set_address(address); - candidate->set_username(elem->Attr(QN_USERNAME)); - candidate->set_preference(preference); - candidate->set_protocol(elem->Attr(QN_PROTOCOL)); - candidate->set_generation_str(elem->Attr(QN_GENERATION)); - if (elem->HasAttr(QN_PASSWORD)) - candidate->set_password(elem->Attr(QN_PASSWORD)); - if (elem->HasAttr(buzz::QN_TYPE)) - candidate->set_type(elem->Attr(buzz::QN_TYPE)); - if (elem->HasAttr(QN_NETWORK)) - candidate->set_network_name(elem->Attr(QN_NETWORK)); - - if (!VerifyUsernameFormat(proto, candidate->username(), error)) - return false; - - return true; -} - -bool P2PTransportParser::WriteCandidate(TransportProtocol proto, - const Candidate& candidate, - const CandidateTranslator* translator, - buzz::XmlElement* elem, - WriteError* error) { - ASSERT(proto == ICEPROTO_GOOGLE); - ASSERT(translator != NULL); - - std::string channel_name; - if (!translator || - !translator->GetChannelNameFromComponent( - candidate.component(), &channel_name)) { - return BadWrite("Cannot write candidate because of unknown component.", - error); - } - - elem->SetAttr(buzz::QN_NAME, channel_name); - elem->SetAttr(QN_ADDRESS, candidate.address().ipaddr().ToString()); - elem->SetAttr(QN_PORT, candidate.address().PortAsString()); - AddXmlAttr(elem, QN_PREFERENCE, candidate.preference()); - elem->SetAttr(QN_USERNAME, candidate.username()); - elem->SetAttr(QN_PROTOCOL, candidate.protocol()); - elem->SetAttr(QN_GENERATION, candidate.generation_str()); - if (!candidate.password().empty()) - elem->SetAttr(QN_PASSWORD, candidate.password()); - elem->SetAttr(buzz::QN_TYPE, candidate.type()); - if (!candidate.network_name().empty()) - elem->SetAttr(QN_NETWORK, candidate.network_name()); - - return true; -} - } // namespace cricket diff --git a/webrtc/p2p/base/p2ptransport.h b/webrtc/p2p/base/p2ptransport.h index efc659911..c0e3952d8 100644 --- a/webrtc/p2p/base/p2ptransport.h +++ b/webrtc/p2p/base/p2ptransport.h @@ -12,7 +12,6 @@ #define WEBRTC_P2P_BASE_P2PTRANSPORT_H_ #include -#include #include "webrtc/p2p/base/transport.h" namespace cricket { @@ -35,52 +34,6 @@ class P2PTransport : public Transport { DISALLOW_EVIL_CONSTRUCTORS(P2PTransport); }; -class P2PTransportParser : public TransportParser { - public: - P2PTransportParser() {} - // Translator may be null, in which case ParseCandidates should - // return false if there are candidates to parse. We can't not call - // ParseCandidates because there's no way to know ahead of time if - // there are candidates or not. - - // Jingle-specific functions; can be used with either ICE, GICE, or HYBRID. - virtual bool ParseTransportDescription(const buzz::XmlElement* elem, - const CandidateTranslator* translator, - TransportDescription* desc, - ParseError* error); - virtual bool WriteTransportDescription(const TransportDescription& desc, - const CandidateTranslator* translator, - buzz::XmlElement** elem, - WriteError* error); - - // Legacy Gingle functions; only can be used with GICE. - virtual bool ParseGingleCandidate(const buzz::XmlElement* elem, - const CandidateTranslator* translator, - Candidate* candidate, - ParseError* error); - virtual bool WriteGingleCandidate(const Candidate& candidate, - const CandidateTranslator* translator, - buzz::XmlElement** elem, - WriteError* error); - - private: - bool ParseCandidate(TransportProtocol proto, - const buzz::XmlElement* elem, - const CandidateTranslator* translator, - Candidate* candidate, - ParseError* error); - bool WriteCandidate(TransportProtocol proto, - const Candidate& candidate, - const CandidateTranslator* translator, - buzz::XmlElement* elem, - WriteError* error); - bool VerifyUsernameFormat(TransportProtocol proto, - const std::string& username, - ParseError* error); - - DISALLOW_EVIL_CONSTRUCTORS(P2PTransportParser); -}; - } // namespace cricket #endif // WEBRTC_P2P_BASE_P2PTRANSPORT_H_ diff --git a/webrtc/p2p/base/rawtransport.cc b/webrtc/p2p/base/rawtransport.cc index 374ed984e..8ff00cd3e 100644 --- a/webrtc/p2p/base/rawtransport.cc +++ b/webrtc/p2p/base/rawtransport.cc @@ -9,15 +9,9 @@ */ #include -#include -#include "webrtc/p2p/base/constants.h" -#include "webrtc/p2p/base/parsing.h" + #include "webrtc/p2p/base/rawtransport.h" #include "webrtc/p2p/base/rawtransportchannel.h" -#include "webrtc/p2p/base/sessionmanager.h" -#include "webrtc/libjingle/xmllite/qname.h" -#include "webrtc/libjingle/xmllite/xmlelement.h" -#include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/base/common.h" #if defined(FEATURE_ENABLE_PSTN) @@ -35,72 +29,6 @@ RawTransport::~RawTransport() { DestroyAllChannels(); } -bool RawTransport::ParseCandidates(SignalingProtocol protocol, - const buzz::XmlElement* elem, - const CandidateTranslator* translator, - Candidates* candidates, - ParseError* error) { - for (const buzz::XmlElement* cand_elem = elem->FirstElement(); - cand_elem != NULL; - cand_elem = cand_elem->NextElement()) { - if (cand_elem->Name() == QN_GINGLE_RAW_CHANNEL) { - if (!cand_elem->HasAttr(buzz::QN_NAME)) { - return BadParse("no channel name given", error); - } - if (type() != cand_elem->Attr(buzz::QN_NAME)) { - return BadParse("channel named does not exist", error); - } - rtc::SocketAddress addr; - if (!ParseRawAddress(cand_elem, &addr, error)) - return false; - - Candidate candidate; - candidate.set_component(1); - candidate.set_address(addr); - candidates->push_back(candidate); - } - } - return true; -} - -bool RawTransport::WriteCandidates(SignalingProtocol protocol, - const Candidates& candidates, - const CandidateTranslator* translator, - XmlElements* candidate_elems, - WriteError* error) { - for (std::vector::const_iterator - cand = candidates.begin(); - cand != candidates.end(); - ++cand) { - ASSERT(cand->component() == 1); - ASSERT(cand->protocol() == "udp"); - rtc::SocketAddress addr = cand->address(); - - buzz::XmlElement* elem = new buzz::XmlElement(QN_GINGLE_RAW_CHANNEL); - elem->SetAttr(buzz::QN_NAME, type()); - elem->SetAttr(QN_ADDRESS, addr.ipaddr().ToString()); - elem->SetAttr(QN_PORT, addr.PortAsString()); - candidate_elems->push_back(elem); - } - return true; -} - -bool RawTransport::ParseRawAddress(const buzz::XmlElement* elem, - rtc::SocketAddress* addr, - ParseError* error) { - // Make sure the required attributes exist - if (!elem->HasAttr(QN_ADDRESS) || - !elem->HasAttr(QN_PORT)) { - return BadParse("channel missing required attribute", error); - } - - // Parse the address. - if (!ParseAddress(elem, QN_ADDRESS, QN_PORT, addr, error)) - return false; - - return true; -} - TransportChannelImpl* RawTransport::CreateTransportChannel(int component) { return new RawTransportChannel(content_name(), component, this, worker_thread(), diff --git a/webrtc/p2p/base/rawtransport.h b/webrtc/p2p/base/rawtransport.h index dbe8f9868..bf0560b17 100644 --- a/webrtc/p2p/base/rawtransport.h +++ b/webrtc/p2p/base/rawtransport.h @@ -20,7 +20,7 @@ namespace cricket { // Implements a transport that only sends raw packets, no STUN. As a result, // it cannot do pings to determine connectivity, so it only uses a single port // that it thinks will work. -class RawTransport : public Transport, public TransportParser { +class RawTransport : public Transport { public: RawTransport(rtc::Thread* signaling_thread, rtc::Thread* worker_thread, @@ -28,30 +28,12 @@ class RawTransport : public Transport, public TransportParser { PortAllocator* allocator); virtual ~RawTransport(); - virtual bool ParseCandidates(SignalingProtocol protocol, - const buzz::XmlElement* elem, - const CandidateTranslator* translator, - Candidates* candidates, - ParseError* error); - virtual bool WriteCandidates(SignalingProtocol protocol, - const Candidates& candidates, - const CandidateTranslator* translator, - XmlElements* candidate_elems, - WriteError* error); - protected: // Creates and destroys raw channels. virtual TransportChannelImpl* CreateTransportChannel(int component); virtual void DestroyTransportChannel(TransportChannelImpl* channel); private: - // Parses the given element, which should describe the address to use for a - // given channel. This will return false and signal an error if the address - // or channel name is bad. - bool ParseRawAddress(const buzz::XmlElement* elem, - rtc::SocketAddress* addr, - ParseError* error); - friend class RawTransportChannel; // For ParseAddress. DISALLOW_EVIL_CONSTRUCTORS(RawTransport); diff --git a/webrtc/p2p/base/rawtransportchannel.cc b/webrtc/p2p/base/rawtransportchannel.cc index 5779c6e56..f0d7d5d9f 100644 --- a/webrtc/p2p/base/rawtransportchannel.cc +++ b/webrtc/p2p/base/rawtransportchannel.cc @@ -17,11 +17,7 @@ #include "webrtc/p2p/base/portinterface.h" #include "webrtc/p2p/base/rawtransport.h" #include "webrtc/p2p/base/relayport.h" -#include "webrtc/p2p/base/sessionmanager.h" #include "webrtc/p2p/base/stunport.h" -#include "webrtc/libjingle/xmllite/qname.h" -#include "webrtc/libjingle/xmllite/xmlelement.h" -#include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/base/common.h" #if defined(FEATURE_ENABLE_PSTN) diff --git a/webrtc/p2p/base/sessionclient.h b/webrtc/p2p/base/sessionclient.h index 896878858..8cbd3be31 100644 --- a/webrtc/p2p/base/sessionclient.h +++ b/webrtc/p2p/base/sessionclient.h @@ -20,6 +20,7 @@ class XmlElement; namespace cricket { struct ParseError; +struct WriteError; class Session; class ContentDescription; diff --git a/webrtc/p2p/base/sessionmanager.cc b/webrtc/p2p/base/sessionmanager.cc index 69c1e1955..a7cf322eb 100644 --- a/webrtc/p2p/base/sessionmanager.cc +++ b/webrtc/p2p/base/sessionmanager.cc @@ -1,1150 +1 @@ -/* - * 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/p2p/base/sessionmanager.h" - -#include "webrtc/p2p/base/constants.h" -#include "webrtc/p2p/base/p2ptransport.h" -#include "webrtc/p2p/base/sessionmessages.h" -#include "webrtc/libjingle/xmpp/constants.h" -#include "webrtc/libjingle/xmpp/jid.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" - -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()); -} - -SessionManager::SessionManager(PortAllocator *allocator, - rtc::Thread *worker) { - allocator_ = allocator; - signaling_thread_ = rtc::Thread::Current(); - if (worker == NULL) { - worker_thread_ = rtc::Thread::Current(); - } else { - worker_thread_ = worker; - } - timeout_ = 50; -} - -SessionManager::~SessionManager() { - // Note: Session::Terminate occurs asynchronously, so it's too late to - // delete them now. They better be all gone. - ASSERT(session_map_.empty()); - // TerminateAll(); - SignalDestroyed(); -} - -void SessionManager::AddClient(const std::string& content_type, - SessionClient* client) { - ASSERT(client_map_.find(content_type) == client_map_.end()); - client_map_[content_type] = client; -} - -void SessionManager::RemoveClient(const std::string& content_type) { - ClientMap::iterator iter = client_map_.find(content_type); - ASSERT(iter != client_map_.end()); - client_map_.erase(iter); -} - -SessionClient* SessionManager::GetClient(const std::string& content_type) { - ClientMap::iterator iter = client_map_.find(content_type); - return (iter != client_map_.end()) ? iter->second : NULL; -} - -Session* SessionManager::CreateSession(const std::string& local_name, - const std::string& content_type) { - std::string id; - return CreateSession(id, local_name, content_type); -} - -Session* SessionManager::CreateSession(const std::string& id, - const std::string& local_name, - const std::string& content_type) { - std::string sid = - id.empty() ? rtc::ToString(rtc::CreateRandomId64()) : id; - return CreateSession(local_name, local_name, sid, content_type, false); -} - -Session* SessionManager::CreateSession( - const std::string& local_name, const std::string& initiator_name, - const std::string& sid, const std::string& content_type, - bool received_initiate) { - SessionClient* client = GetClient(content_type); - ASSERT(client != NULL); - - Session* session = new Session(this, local_name, initiator_name, - sid, content_type, client); - session->SetIdentity(transport_desc_factory_.identity()); - session_map_[session->id()] = session; - session->SignalRequestSignaling.connect( - this, &SessionManager::OnRequestSignaling); - session->SignalOutgoingMessage.connect( - this, &SessionManager::OnOutgoingMessage); - session->SignalErrorMessage.connect(this, &SessionManager::OnErrorMessage); - SignalSessionCreate(session, received_initiate); - session->client()->OnSessionCreate(session, received_initiate); - return session; -} - -void SessionManager::DestroySession(Session* session) { - if (session != NULL) { - SessionMap::iterator it = session_map_.find(session->id()); - if (it != session_map_.end()) { - SignalSessionDestroy(session); - session->client()->OnSessionDestroy(session); - session_map_.erase(it); - delete session; - } - } -} - -Session* SessionManager::GetSession(const std::string& sid) { - SessionMap::iterator it = session_map_.find(sid); - if (it != session_map_.end()) - return it->second; - return NULL; -} - -void SessionManager::TerminateAll() { - while (session_map_.begin() != session_map_.end()) { - Session* session = session_map_.begin()->second; - session->Terminate(); - } -} - -bool SessionManager::IsSessionMessage(const buzz::XmlElement* stanza) { - return cricket::IsSessionMessage(stanza); -} - -Session* SessionManager::FindSession(const std::string& sid, - const std::string& remote_name) { - SessionMap::iterator iter = session_map_.find(sid); - if (iter == session_map_.end()) - return NULL; - - Session* session = iter->second; - if (buzz::Jid(remote_name) != buzz::Jid(session->remote_name())) - return NULL; - - return session; -} - -void SessionManager::OnIncomingMessage(const buzz::XmlElement* stanza) { - SessionMessage msg; - ParseError error; - - if (!ParseSessionMessage(stanza, &msg, &error)) { - SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", - error.text, NULL); - return; - } - - Session* session = FindSession(msg.sid, msg.from); - if (session) { - session->OnIncomingMessage(msg); - return; - } - if (msg.type != ACTION_SESSION_INITIATE) { - SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", - "unknown session", NULL); - return; - } - - std::string content_type; - if (!ParseContentType(msg.protocol, msg.action_elem, - &content_type, &error)) { - SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", - error.text, NULL); - return; - } - - if (!GetClient(content_type)) { - SendErrorMessage(stanza, buzz::QN_STANZA_BAD_REQUEST, "modify", - "unknown content type: " + content_type, NULL); - return; - } - - session = CreateSession(msg.to, msg.initiator, msg.sid, - content_type, true); - session->OnIncomingMessage(msg); -} - -void SessionManager::OnIncomingResponse(const buzz::XmlElement* orig_stanza, - const buzz::XmlElement* response_stanza) { - if (orig_stanza == NULL || response_stanza == NULL) { - return; - } - - SessionMessage msg; - ParseError error; - if (!ParseSessionMessage(orig_stanza, &msg, &error)) { - LOG(LS_WARNING) << "Error parsing incoming response: " << error.text - << ":" << orig_stanza; - return; - } - - Session* session = FindSession(msg.sid, msg.to); - if (!session) { - // Also try the QN_FROM in the response stanza, in case we sent the request - // to a bare JID but got the response from a full JID. - std::string ack_from = response_stanza->Attr(buzz::QN_FROM); - session = FindSession(msg.sid, ack_from); - } - if (session) { - session->OnIncomingResponse(orig_stanza, response_stanza, msg); - } -} - -void SessionManager::OnFailedSend(const buzz::XmlElement* orig_stanza, - const buzz::XmlElement* error_stanza) { - SessionMessage msg; - ParseError error; - if (!ParseSessionMessage(orig_stanza, &msg, &error)) { - return; // TODO: log somewhere? - } - - Session* session = FindSession(msg.sid, msg.to); - if (session) { - rtc::scoped_ptr synthetic_error; - if (!error_stanza) { - // A failed send is semantically equivalent to an error response, so we - // can just turn the former into the latter. - synthetic_error.reset( - CreateErrorMessage(orig_stanza, buzz::QN_STANZA_ITEM_NOT_FOUND, - "cancel", "Recipient did not respond", NULL)); - error_stanza = synthetic_error.get(); - } - - session->OnFailedSend(orig_stanza, error_stanza); - } -} - -void SessionManager::SendErrorMessage(const buzz::XmlElement* stanza, - const buzz::QName& name, - const std::string& type, - const std::string& text, - const buzz::XmlElement* extra_info) { - rtc::scoped_ptr msg( - CreateErrorMessage(stanza, name, type, text, extra_info)); - SignalOutgoingMessage(this, msg.get()); -} - -buzz::XmlElement* SessionManager::CreateErrorMessage( - const buzz::XmlElement* stanza, - const buzz::QName& name, - const std::string& type, - const std::string& text, - const buzz::XmlElement* extra_info) { - buzz::XmlElement* iq = new buzz::XmlElement(buzz::QN_IQ); - iq->SetAttr(buzz::QN_TO, stanza->Attr(buzz::QN_FROM)); - iq->SetAttr(buzz::QN_ID, stanza->Attr(buzz::QN_ID)); - iq->SetAttr(buzz::QN_TYPE, "error"); - - CopyXmlChildren(stanza, iq); - - buzz::XmlElement* error = new buzz::XmlElement(buzz::QN_ERROR); - error->SetAttr(buzz::QN_TYPE, type); - iq->AddElement(error); - - // If the error name is not in the standard namespace, we have to first add - // some error from that namespace. - if (name.Namespace() != buzz::NS_STANZA) { - error->AddElement( - new buzz::XmlElement(buzz::QN_STANZA_UNDEFINED_CONDITION)); - } - error->AddElement(new buzz::XmlElement(name)); - - if (extra_info) - error->AddElement(new buzz::XmlElement(*extra_info)); - - if (text.size() > 0) { - // It's okay to always use English here. This text is for debugging - // purposes only. - buzz::XmlElement* text_elem = new buzz::XmlElement(buzz::QN_STANZA_TEXT); - text_elem->SetAttr(buzz::QN_XML_LANG, "en"); - text_elem->SetBodyText(text); - error->AddElement(text_elem); - } - - // TODO: Should we include error codes as well for SIP compatibility? - - return iq; -} - -void SessionManager::OnOutgoingMessage(Session* session, - const buzz::XmlElement* stanza) { - SignalOutgoingMessage(this, stanza); -} - -void SessionManager::OnErrorMessage(BaseSession* session, - const buzz::XmlElement* stanza, - const buzz::QName& name, - const std::string& type, - const std::string& text, - const buzz::XmlElement* extra_info) { - SendErrorMessage(stanza, name, type, text, extra_info); -} - -void SessionManager::OnSignalingReady() { - for (SessionMap::iterator it = session_map_.begin(); - it != session_map_.end(); - ++it) { - it->second->OnSignalingReady(); - } -} - -void SessionManager::OnRequestSignaling(Session* session) { - SignalRequestSignaling(); -} - -} // namespace cricket +// TODO(pthatcher): Delete this file once Chromium no longer trys to build it. diff --git a/webrtc/p2p/base/sessionmanager.h b/webrtc/p2p/base/sessionmanager.h index 2d2c121b8..a7cf322eb 100644 --- a/webrtc/p2p/base/sessionmanager.h +++ b/webrtc/p2p/base/sessionmanager.h @@ -1,436 +1 @@ -/* - * 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_P2P_BASE_SESSIONMANAGER_H_ -#define WEBRTC_P2P_BASE_SESSIONMANAGER_H_ - -#include -#include -#include -#include - -#include "webrtc/libjingle/xmllite/xmlelement.h" -#include "webrtc/libjingle/xmpp/constants.h" -#include "webrtc/p2p/base/parsing.h" -#include "webrtc/p2p/base/portallocator.h" -#include "webrtc/p2p/base/session.h" -#include "webrtc/p2p/base/sessionclient.h" -#include "webrtc/p2p/base/sessionmessages.h" -#include "webrtc/p2p/base/transportdescriptionfactory.h" -#include "webrtc/base/sigslot.h" -#include "webrtc/base/thread.h" - -namespace buzz { -class QName; -class XmlElement; -} - -namespace cricket { - -class BaseSession; -class SessionClient; -class SessionManager; - -// 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. -}; - -// SessionManager manages session instances. -class SessionManager : public sigslot::has_slots<> { - public: - SessionManager(PortAllocator *allocator, - rtc::Thread *worker_thread = NULL); - virtual ~SessionManager(); - - PortAllocator *port_allocator() const { return allocator_; } - rtc::Thread *worker_thread() const { return worker_thread_; } - rtc::Thread *signaling_thread() const { return signaling_thread_; } - - int session_timeout() const { return timeout_; } - void set_session_timeout(int timeout) { timeout_ = timeout; } - - // Set what transport protocol we want to default to. - void set_transport_protocol(TransportProtocol proto) { - transport_desc_factory_.set_protocol(proto); - } - - // Control use of DTLS. An identity must be supplied if DTLS is enabled. - void set_secure(SecurePolicy policy) { - transport_desc_factory_.set_secure(policy); - } - void set_identity(rtc::SSLIdentity* identity) { - transport_desc_factory_.set_identity(identity); - } - const TransportDescriptionFactory* transport_desc_factory() const { - return &transport_desc_factory_; - } - - // Registers support for the given client. If we receive an initiate - // describing a session of the given type, we will automatically create a - // Session object and notify this client. The client may then accept or - // reject the session. - void AddClient(const std::string& content_type, SessionClient* client); - void RemoveClient(const std::string& content_type); - SessionClient* GetClient(const std::string& content_type); - - // Creates a new session. The given name is the JID of the client on whose - // behalf we initiate the session. - Session *CreateSession(const std::string& local_name, - const std::string& content_type); - - Session *CreateSession(const std::string& id, - const std::string& local_name, - const std::string& content_type); - - // Destroys the given session. - void DestroySession(Session *session); - - // Returns the session with the given ID or NULL if none exists. - Session *GetSession(const std::string& sid); - - // Terminates all of the sessions created by this manager. - void TerminateAll(); - - // These are signaled whenever the set of existing sessions changes. - sigslot::signal2 SignalSessionCreate; - sigslot::signal1 SignalSessionDestroy; - - // Determines whether the given stanza is intended for some session. - bool IsSessionMessage(const buzz::XmlElement* stanza); - - // Given a sid, initiator, and remote_name, this finds the matching Session - Session* FindSession(const std::string& sid, - const std::string& remote_name); - - // Called when we receive a stanza for which IsSessionMessage is true. - void OnIncomingMessage(const buzz::XmlElement* stanza); - - // Called when we get a response to a message that we sent. - void OnIncomingResponse(const buzz::XmlElement* orig_stanza, - const buzz::XmlElement* response_stanza); - - // Called if an attempted to send times out or an error is returned. In the - // timeout case error_stanza will be NULL - void OnFailedSend(const buzz::XmlElement* orig_stanza, - const buzz::XmlElement* error_stanza); - - // Signalled each time a session generates a signaling message to send. - // Also signalled on errors, but with a NULL session. - sigslot::signal2 SignalOutgoingMessage; - - // Signaled before sessions try to send certain signaling messages. The - // client should call OnSignalingReady once it is safe to send them. These - // steps are taken so that we don't send signaling messages trying to - // re-establish the connectivity of a session when the client cannot send - // the messages (and would probably just drop them on the floor). - // - // Note: you can connect this directly to OnSignalingReady(), if a signalling - // check is not supported. - sigslot::signal0<> SignalRequestSignaling; - void OnSignalingReady(); - - // Signaled when this SessionManager is deleted. - sigslot::signal0<> SignalDestroyed; - - private: - typedef std::map SessionMap; - typedef std::map ClientMap; - - // Helper function for CreateSession. This is also invoked when we receive - // a message attempting to initiate a session with this client. - Session *CreateSession(const std::string& local_name, - const std::string& initiator, - const std::string& sid, - const std::string& content_type, - bool received_initiate); - - // Attempts to find a registered session type whose description appears as - // a child of the session element. Such a child should be present indicating - // the application they hope to initiate. - std::string FindClient(const buzz::XmlElement* session); - - // Sends a message back to the other client indicating that we found an error - // in the stanza they sent. name identifies the error, type is one of the - // standard XMPP types (cancel, continue, modify, auth, wait), and text is a - // description for debugging purposes. - void SendErrorMessage(const buzz::XmlElement* stanza, - const buzz::QName& name, - const std::string& type, - const std::string& text, - const buzz::XmlElement* extra_info); - - // Creates and returns an error message from the given components. The - // caller is responsible for deleting this. - buzz::XmlElement* CreateErrorMessage( - const buzz::XmlElement* stanza, - const buzz::QName& name, - const std::string& type, - const std::string& text, - const buzz::XmlElement* extra_info); - - // Called each time a session requests signaling. - void OnRequestSignaling(Session* session); - - // Called each time a session has an outgoing message. - void OnOutgoingMessage(Session* session, const buzz::XmlElement* stanza); - - // Called each time a session has an error to send. - void OnErrorMessage(BaseSession* session, - const buzz::XmlElement* stanza, - const buzz::QName& name, - const std::string& type, - const std::string& text, - const buzz::XmlElement* extra_info); - - PortAllocator *allocator_; - rtc::Thread *signaling_thread_; - rtc::Thread *worker_thread_; - int timeout_; - TransportDescriptionFactory transport_desc_factory_; - SessionMap session_map_; - ClientMap client_map_; -}; - -} // namespace cricket - -#endif // WEBRTC_P2P_BASE_SESSIONMANAGER_H_ +// TODO(pthatcher): Delete this file once Chromium no longer trys to build it. diff --git a/webrtc/p2p/base/sessionmessages.h b/webrtc/p2p/base/sessionmessages.h index 3437a9ac6..95a5727f9 100644 --- a/webrtc/p2p/base/sessionmessages.h +++ b/webrtc/p2p/base/sessionmessages.h @@ -15,14 +15,15 @@ #include #include +#include "webrtc/base/basictypes.h" #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/session/transportparser.h" #include "webrtc/libjingle/xmllite/xmlelement.h" -#include "webrtc/base/basictypes.h" namespace cricket { diff --git a/webrtc/p2p/base/transport.cc b/webrtc/p2p/base/transport.cc index 07b204cf7..b03e2d5f7 100644 --- a/webrtc/p2p/base/transport.cc +++ b/webrtc/p2p/base/transport.cc @@ -12,12 +12,8 @@ #include "webrtc/p2p/base/candidate.h" #include "webrtc/p2p/base/constants.h" -#include "webrtc/p2p/base/parsing.h" #include "webrtc/p2p/base/port.h" -#include "webrtc/p2p/base/sessionmanager.h" #include "webrtc/p2p/base/transportchannelimpl.h" -#include "webrtc/libjingle/xmllite/xmlelement.h" -#include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/base/bind.h" #include "webrtc/base/common.h" #include "webrtc/base/logging.h" @@ -931,25 +927,6 @@ void Transport::OnMessage(rtc::Message* msg) { } } -bool TransportParser::ParseAddress(const buzz::XmlElement* elem, - const buzz::QName& address_name, - const buzz::QName& port_name, - rtc::SocketAddress* address, - ParseError* error) { - if (!elem->HasAttr(address_name)) - return BadParse("address does not have " + address_name.LocalPart(), error); - if (!elem->HasAttr(port_name)) - return BadParse("address does not have " + port_name.LocalPart(), error); - - address->SetIP(elem->Attr(address_name)); - std::istringstream ist(elem->Attr(port_name)); - int port = 0; - ist >> port; - address->SetPort(port); - - return true; -} - // We're GICE if the namespace is NS_GOOGLE_P2P, or if NS_JINGLE_ICE_UDP is // used and the GICE ice-option is set. TransportProtocol TransportProtocolFromDescription( diff --git a/webrtc/p2p/base/transport.h b/webrtc/p2p/base/transport.h index 5fe445cad..cfbd2ec68 100644 --- a/webrtc/p2p/base/transport.h +++ b/webrtc/p2p/base/transport.h @@ -45,71 +45,14 @@ namespace rtc { class Thread; } -namespace buzz { -class QName; -class XmlElement; -} - namespace cricket { -struct ParseError; -struct WriteError; -class CandidateTranslator; class PortAllocator; class TransportChannel; class TransportChannelImpl; -typedef std::vector XmlElements; typedef std::vector Candidates; -// Used to parse and serialize (write) transport candidates. For -// convenience of old code, Transports will implement TransportParser. -// Parse/Write seems better than Serialize/Deserialize or -// Create/Translate. -class TransportParser { - public: - // The incoming Translator value may be null, in which case - // ParseCandidates should return false if there are candidates to - // parse (indicating a failure to parse). If the Translator is null - // and there are no candidates to parse, then return true, - // indicating a successful parse of 0 candidates. - - // Parse or write a transport description, including ICE credentials and - // any DTLS fingerprint. Since only Jingle has transport descriptions, these - // functions are only used when serializing to Jingle. - virtual bool ParseTransportDescription(const buzz::XmlElement* elem, - const CandidateTranslator* translator, - TransportDescription* tdesc, - ParseError* error) = 0; - virtual bool WriteTransportDescription(const TransportDescription& tdesc, - const CandidateTranslator* translator, - buzz::XmlElement** tdesc_elem, - WriteError* error) = 0; - - - // Parse a single candidate. This must be used when parsing Gingle - // candidates, since there is no enclosing transport description. - virtual bool ParseGingleCandidate(const buzz::XmlElement* elem, - const CandidateTranslator* translator, - Candidate* candidates, - ParseError* error) = 0; - virtual bool WriteGingleCandidate(const Candidate& candidate, - const CandidateTranslator* translator, - buzz::XmlElement** candidate_elem, - WriteError* error) = 0; - - // Helper function to parse an element describing an address. This - // retrieves the IP and port from the given element and verifies - // that they look like plausible values. - bool ParseAddress(const buzz::XmlElement* elem, - const buzz::QName& address_name, - const buzz::QName& port_name, - rtc::SocketAddress* address, - ParseError* error); - - virtual ~TransportParser() {} -}; - // For "writable" and "readable", we need to differentiate between // none, all, and some. enum TransportState { @@ -314,18 +257,6 @@ class Transport : public rtc::MessageHandler, int, // component const Candidate&> SignalRouteChange; - // A transport message has generated an transport-specific error. The - // stanza that caused the error is available in session_msg. If false is - // returned, the error is considered unrecoverable, and the session is - // terminated. - // TODO(juberti): Remove these obsolete functions once Session no longer - // references them. - virtual void OnTransportError(const buzz::XmlElement* error) {} - sigslot::signal6 - SignalTransportError; - // Forwards the signal from TransportChannel to BaseSession. sigslot::signal0<> SignalRoleConflict; diff --git a/webrtc/p2p/base/transport_unittest.cc b/webrtc/p2p/base/transport_unittest.cc index e5d7acafa..ba11678ec 100644 --- a/webrtc/p2p/base/transport_unittest.cc +++ b/webrtc/p2p/base/transport_unittest.cc @@ -8,18 +8,12 @@ * be found in the AUTHORS file in the root of the source tree. */ -#include "webrtc/p2p/base/constants.h" -#include "webrtc/p2p/base/fakesession.h" -#include "webrtc/p2p/base/p2ptransport.h" -#include "webrtc/p2p/base/parsing.h" -#include "webrtc/p2p/base/rawtransport.h" -#include "webrtc/p2p/base/sessionmessages.h" -#include "webrtc/libjingle/xmllite/xmlelement.h" -#include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/base/fakesslidentity.h" #include "webrtc/base/gunit.h" #include "webrtc/base/network.h" #include "webrtc/base/thread.h" +#include "webrtc/p2p/base/fakesession.h" +#include "webrtc/p2p/base/p2ptransport.h" using cricket::Candidate; using cricket::Candidates; @@ -29,8 +23,6 @@ using cricket::TransportChannel; using cricket::FakeTransportChannel; using cricket::IceRole; using cricket::TransportDescription; -using cricket::WriteError; -using cricket::ParseError; using rtc::SocketAddress; static const char kIceUfrag1[] = "TESTICEUFRAG0001"; @@ -89,34 +81,6 @@ class TransportTest : public testing::Test, bool failed_; }; -class FakeCandidateTranslator : public cricket::CandidateTranslator { - public: - void AddMapping(int component, const std::string& channel_name) { - name_to_component[channel_name] = component; - component_to_name[component] = channel_name; - } - - bool GetChannelNameFromComponent( - int component, std::string* channel_name) const { - if (component_to_name.find(component) == component_to_name.end()) { - return false; - } - *channel_name = component_to_name.find(component)->second; - return true; - } - bool GetComponentFromChannelName( - const std::string& channel_name, int* component) const { - if (name_to_component.find(channel_name) == name_to_component.end()) { - return false; - } - *component = name_to_component.find(channel_name)->second; - return true; - } - - std::map name_to_component; - std::map component_to_name; -}; - // Test that calling ConnectChannels triggers an OnConnecting signal. TEST_F(TransportTest, TestConnectChannelsDoesSignal) { EXPECT_TRUE(SetupChannel()); @@ -345,86 +309,6 @@ TEST_F(TransportTest, TestSetRemoteIceLiteInAnswer) { EXPECT_EQ(cricket::ICEMODE_LITE, channel_->remote_ice_mode()); } -// Tests that we can properly serialize/deserialize candidates. -TEST_F(TransportTest, TestP2PTransportWriteAndParseCandidate) { - Candidate test_candidate("", 1, "udp", - rtc::SocketAddress("2001:db8:fefe::1", 9999), - 738197504, "abcdef", "ghijkl", "foo", 50, ""); - test_candidate.set_network_name("testnet"); - Candidate test_candidate2("", 2, "tcp", - rtc::SocketAddress("192.168.7.1", 9999), 1107296256, - "mnopqr", "stuvwx", "bar", 100, ""); - test_candidate2.set_network_name("testnet2"); - rtc::SocketAddress host_address("www.google.com", 24601); - host_address.SetResolvedIP(rtc::IPAddress(0x0A000001)); - Candidate test_candidate3("", 3, "spdy", host_address, 1476395008, "yzabcd", - "efghij", "baz", 150, ""); - test_candidate3.set_network_name("testnet3"); - WriteError write_error; - ParseError parse_error; - rtc::scoped_ptr elem; - cricket::Candidate parsed_candidate; - cricket::P2PTransportParser parser; - - FakeCandidateTranslator translator; - translator.AddMapping(1, "test"); - translator.AddMapping(2, "test2"); - translator.AddMapping(3, "test3"); - - EXPECT_TRUE(parser.WriteGingleCandidate(test_candidate, &translator, - elem.accept(), &write_error)); - EXPECT_EQ("", write_error.text); - EXPECT_EQ("test", elem->Attr(buzz::QN_NAME)); - EXPECT_EQ("udp", elem->Attr(cricket::QN_PROTOCOL)); - EXPECT_EQ("2001:db8:fefe::1", elem->Attr(cricket::QN_ADDRESS)); - EXPECT_EQ("9999", elem->Attr(cricket::QN_PORT)); - EXPECT_EQ("0.34", elem->Attr(cricket::QN_PREFERENCE)); - EXPECT_EQ("abcdef", elem->Attr(cricket::QN_USERNAME)); - EXPECT_EQ("ghijkl", elem->Attr(cricket::QN_PASSWORD)); - EXPECT_EQ("foo", elem->Attr(cricket::QN_TYPE)); - EXPECT_EQ("testnet", elem->Attr(cricket::QN_NETWORK)); - EXPECT_EQ("50", elem->Attr(cricket::QN_GENERATION)); - - EXPECT_TRUE(parser.ParseGingleCandidate(elem.get(), &translator, - &parsed_candidate, &parse_error)); - EXPECT_TRUE(test_candidate.IsEquivalent(parsed_candidate)); - - EXPECT_TRUE(parser.WriteGingleCandidate(test_candidate2, &translator, - elem.accept(), &write_error)); - EXPECT_EQ("test2", elem->Attr(buzz::QN_NAME)); - EXPECT_EQ("tcp", elem->Attr(cricket::QN_PROTOCOL)); - EXPECT_EQ("192.168.7.1", elem->Attr(cricket::QN_ADDRESS)); - EXPECT_EQ("9999", elem->Attr(cricket::QN_PORT)); - EXPECT_EQ("0.51", elem->Attr(cricket::QN_PREFERENCE)); - EXPECT_EQ("mnopqr", elem->Attr(cricket::QN_USERNAME)); - EXPECT_EQ("stuvwx", elem->Attr(cricket::QN_PASSWORD)); - EXPECT_EQ("bar", elem->Attr(cricket::QN_TYPE)); - EXPECT_EQ("testnet2", elem->Attr(cricket::QN_NETWORK)); - EXPECT_EQ("100", elem->Attr(cricket::QN_GENERATION)); - - EXPECT_TRUE(parser.ParseGingleCandidate(elem.get(), &translator, - &parsed_candidate, &parse_error)); - EXPECT_TRUE(test_candidate2.IsEquivalent(parsed_candidate)); - - // Check that an ip is preferred over hostname. - EXPECT_TRUE(parser.WriteGingleCandidate(test_candidate3, &translator, - elem.accept(), &write_error)); - EXPECT_EQ("test3", elem->Attr(cricket::QN_NAME)); - EXPECT_EQ("spdy", elem->Attr(cricket::QN_PROTOCOL)); - EXPECT_EQ("10.0.0.1", elem->Attr(cricket::QN_ADDRESS)); - EXPECT_EQ("24601", elem->Attr(cricket::QN_PORT)); - EXPECT_EQ("0.69", elem->Attr(cricket::QN_PREFERENCE)); - EXPECT_EQ("yzabcd", elem->Attr(cricket::QN_USERNAME)); - EXPECT_EQ("efghij", elem->Attr(cricket::QN_PASSWORD)); - EXPECT_EQ("baz", elem->Attr(cricket::QN_TYPE)); - EXPECT_EQ("testnet3", elem->Attr(cricket::QN_NETWORK)); - EXPECT_EQ("150", elem->Attr(cricket::QN_GENERATION)); - - EXPECT_TRUE(parser.ParseGingleCandidate(elem.get(), &translator, - &parsed_candidate, &parse_error)); - EXPECT_TRUE(test_candidate3.IsEquivalent(parsed_candidate)); -} - TEST_F(TransportTest, TestGetStats) { EXPECT_TRUE(SetupChannel()); cricket::TransportStats stats; diff --git a/webrtc/p2p/client/sessionmanagertask.h b/webrtc/p2p/client/sessionmanagertask.h index 04d79d48c..e5b32b3b9 100644 --- a/webrtc/p2p/client/sessionmanagertask.h +++ b/webrtc/p2p/client/sessionmanagertask.h @@ -11,10 +11,10 @@ #ifndef WEBRTC_P2P_CLIENT_SESSIONMANAGERTASK_H_ #define WEBRTC_P2P_CLIENT_SESSIONMANAGERTASK_H_ -#include "webrtc/p2p/base/sessionmanager.h" -#include "webrtc/p2p/client/sessionsendtask.h" +#include "webrtc/libjingle/session/sessionmanager.h" #include "webrtc/libjingle/xmpp/xmppengine.h" #include "webrtc/libjingle/xmpp/xmpptask.h" +#include "webrtc/p2p/client/sessionsendtask.h" namespace cricket { diff --git a/webrtc/p2p/client/sessionsendtask.h b/webrtc/p2p/client/sessionsendtask.h index 818aa1adc..77cea08d9 100644 --- a/webrtc/p2p/client/sessionsendtask.h +++ b/webrtc/p2p/client/sessionsendtask.h @@ -11,7 +11,7 @@ #ifndef WEBRTC_P2P_CLIENT_SESSIONSENDTASK_H_ #define WEBRTC_P2P_CLIENT_SESSIONSENDTASK_H_ -#include "webrtc/p2p/base/sessionmanager.h" +#include "webrtc/libjingle/session/sessionmanager.h" #include "webrtc/libjingle/xmpp/constants.h" #include "webrtc/libjingle/xmpp/xmppclient.h" #include "webrtc/libjingle/xmpp/xmppengine.h" diff --git a/webrtc/p2p/p2p.gyp b/webrtc/p2p/p2p.gyp index 102e75b58..eaae62529 100644 --- a/webrtc/p2p/p2p.gyp +++ b/webrtc/p2p/p2p.gyp @@ -66,8 +66,6 @@ 'base/sessiondescription.cc', 'base/sessiondescription.h', 'base/sessionid.h', - 'base/sessionmanager.cc', - 'base/sessionmanager.h', 'base/sessionmessages.cc', 'base/sessionmessages.h', 'base/stun.cc', diff --git a/webrtc/p2p/p2p_tests.gypi b/webrtc/p2p/p2p_tests.gypi index f9e695943..afab33bfa 100644 --- a/webrtc/p2p/p2p_tests.gypi +++ b/webrtc/p2p/p2p_tests.gypi @@ -22,7 +22,6 @@ 'base/pseudotcp_unittest.cc', 'base/relayport_unittest.cc', 'base/relayserver_unittest.cc', - 'base/session_unittest.cc', 'base/stun_unittest.cc', 'base/stunport_unittest.cc', 'base/stunrequest_unittest.cc',