aboutsummaryrefslogtreecommitdiff
path: root/webrtc/p2p/base/sessionmanager.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webrtc/p2p/base/sessionmanager.cc')
-rw-r--r--webrtc/p2p/base/sessionmanager.cc309
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