diff options
Diffstat (limited to 'webrtc/p2p/base/sessionmanager.cc')
-rw-r--r-- | webrtc/p2p/base/sessionmanager.cc | 309 |
1 files changed, 309 insertions, 0 deletions
diff --git a/webrtc/p2p/base/sessionmanager.cc b/webrtc/p2p/base/sessionmanager.cc new file mode 100644 index 0000000000..f375dea5cd --- /dev/null +++ b/webrtc/p2p/base/sessionmanager.cc @@ -0,0 +1,309 @@ +/* + * 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/session.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 { + +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<buzz::XmlElement> 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<buzz::XmlElement> 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 |