28100cb388
BUG=N/A TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/29829004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@7472 4adac7df-926f-26a2-2b94-8c16560cd09d
327 lines
11 KiB
C++
327 lines
11 KiB
C++
/*
|
|
* libjingle
|
|
* Copyright 2004--2005, Google Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "talk/p2p/base/sessionmanager.h"
|
|
|
|
#include "talk/p2p/base/constants.h"
|
|
#include "talk/p2p/base/session.h"
|
|
#include "talk/p2p/base/sessionmessages.h"
|
|
#include "talk/xmpp/constants.h"
|
|
#include "talk/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
|