Files
SingularityViewer/indra/llmessage/llsdrpcserver.cpp
Siana Gearz 8b6f462d13 Wholesale update of llmessage to V3.2
Note that this removes message logger for now.
2012-03-06 07:31:15 +01:00

348 lines
8.3 KiB
C++

/**
* @file llsdrpcserver.cpp
* @author Phoenix
* @date 2005-10-11
* @brief Implementation of the LLSDRPCServer and related classes.
*
* $LicenseInfo:firstyear=2005&license=viewerlgpl$
* Second Life Viewer Source Code
* Copyright (C) 2010, Linden Research, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
* $/LicenseInfo$
*/
#include "linden_common.h"
#include "llsdrpcserver.h"
#include "llbuffer.h"
#include "llbufferstream.h"
#include "llfasttimer.h"
#include "llmemtype.h"
#include "llpumpio.h"
#include "llsdserialize.h"
#include "llstl.h"
static const char FAULT_PART_1[] = "{'fault':{'code':i";
static const char FAULT_PART_2[] = ", 'description':'";
static const char FAULT_PART_3[] = "'}}";
static const char RESPONSE_PART_1[] = "{'response':";
static const char RESPONSE_PART_2[] = "}";
static const S32 FAULT_GENERIC = 1000;
static const S32 FAULT_METHOD_NOT_FOUND = 1001;
static const std::string LLSDRPC_METHOD_SD_NAME("method");
static const std::string LLSDRPC_PARAMETER_SD_NAME("parameter");
/**
* LLSDRPCServer
*/
LLSDRPCServer::LLSDRPCServer() :
mState(LLSDRPCServer::STATE_NONE),
mPump(NULL),
mLock(0)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
}
LLSDRPCServer::~LLSDRPCServer()
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
std::for_each(
mMethods.begin(),
mMethods.end(),
llcompose1(
DeletePointerFunctor<LLSDRPCMethodCallBase>(),
llselect2nd<method_map_t::value_type>()));
std::for_each(
mCallbackMethods.begin(),
mCallbackMethods.end(),
llcompose1(
DeletePointerFunctor<LLSDRPCMethodCallBase>(),
llselect2nd<method_map_t::value_type>()));
}
// virtual
ESDRPCSStatus LLSDRPCServer::deferredResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data) {
// subclass should provide a sane implementation
return ESDRPCS_DONE;
}
void LLSDRPCServer::clearLock()
{
if(mLock && mPump)
{
mPump->clearLock(mLock);
mPump = NULL;
mLock = 0;
}
}
static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_SERVER("SDRPC Server");
// virtual
LLIOPipe::EStatus LLSDRPCServer::process_impl(
const LLChannelDescriptors& channels,
buffer_ptr_t& buffer,
bool& eos,
LLSD& context,
LLPumpIO* pump)
{
LLFastTimer t(FTM_PROCESS_SDRPC_SERVER);
PUMP_DEBUG;
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
// lldebugs << "LLSDRPCServer::process_impl" << llendl;
// Once we have all the data, We need to read the sd on
// the the in channel, and respond on the out channel
if(!eos) return STATUS_BREAK;
if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
std::string method_name;
LLIOPipe::EStatus status = STATUS_DONE;
switch(mState)
{
case STATE_DEFERRED:
PUMP_DEBUG;
if(ESDRPCS_DONE != deferredResponse(channels, buffer.get()))
{
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"deferred response failed.");
}
mState = STATE_DONE;
return STATUS_DONE;
case STATE_DONE:
// lldebugs << "STATE_DONE" << llendl;
break;
case STATE_CALLBACK:
// lldebugs << "STATE_CALLBACK" << llendl;
PUMP_DEBUG;
method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
{
if(ESDRPCS_DONE != callbackMethod(
method_name,
mRequest[LLSDRPC_PARAMETER_SD_NAME],
channels,
buffer.get()))
{
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Callback method call failed.");
}
}
else
{
// this should never happen, since we should not be in
// this state unless we originally found a method and
// params during the first call to process.
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Invalid LLSDRPC sever state - callback without method.");
}
pump->clearLock(mLock);
mLock = 0;
mState = STATE_DONE;
break;
case STATE_NONE:
// lldebugs << "STATE_NONE" << llendl;
default:
{
// First time we got here - process the SD request, and call
// the method.
PUMP_DEBUG;
LLBufferStream istr(channels, buffer.get());
mRequest.clear();
LLSDSerialize::fromNotation(
mRequest,
istr,
buffer->count(channels.in()));
// { 'method':'...', 'parameter': ... }
method_name = mRequest[LLSDRPC_METHOD_SD_NAME].asString();
if(!method_name.empty() && mRequest.has(LLSDRPC_PARAMETER_SD_NAME))
{
ESDRPCSStatus rv = callMethod(
method_name,
mRequest[LLSDRPC_PARAMETER_SD_NAME],
channels,
buffer.get());
switch(rv)
{
case ESDRPCS_DEFERRED:
mPump = pump;
mLock = pump->setLock();
mState = STATE_DEFERRED;
status = STATUS_BREAK;
break;
case ESDRPCS_CALLBACK:
{
mState = STATE_CALLBACK;
LLPumpIO::LLLinkInfo link;
link.mPipe = LLIOPipe::ptr_t(this);
link.mChannels = channels;
LLPumpIO::links_t links;
links.push_back(link);
pump->respond(links, buffer, context);
mLock = pump->setLock();
status = STATUS_BREAK;
break;
}
case ESDRPCS_DONE:
mState = STATE_DONE;
break;
case ESDRPCS_ERROR:
default:
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Method call failed.");
break;
}
}
else
{
// send a fault
buildFault(
channels,
buffer.get(),
FAULT_GENERIC,
"Unable to find method and parameter in request.");
}
break;
}
}
PUMP_DEBUG;
return status;
}
// virtual
ESDRPCSStatus LLSDRPCServer::callMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* response)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
// Try to find the method in the method table.
ESDRPCSStatus rv = ESDRPCS_DONE;
method_map_t::iterator it = mMethods.find(method);
if(it != mMethods.end())
{
rv = (*it).second->call(params, channels, response);
}
else
{
it = mCallbackMethods.find(method);
if(it == mCallbackMethods.end())
{
// method not found.
std::ostringstream message;
message << "rpc server unable to find method: " << method;
buildFault(
channels,
response,
FAULT_METHOD_NOT_FOUND,
message.str());
}
else
{
// we found it in the callback methods - tell the process
// to coordinate calling on the pump callback.
return ESDRPCS_CALLBACK;
}
}
return rv;
}
// virtual
ESDRPCSStatus LLSDRPCServer::callbackMethod(
const std::string& method,
const LLSD& params,
const LLChannelDescriptors& channels,
LLBufferArray* response)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
// Try to find the method in the callback method table.
ESDRPCSStatus rv = ESDRPCS_DONE;
method_map_t::iterator it = mCallbackMethods.find(method);
if(it != mCallbackMethods.end())
{
rv = (*it).second->call(params, channels, response);
}
else
{
std::ostringstream message;
message << "pcserver unable to find callback method: " << method;
buildFault(
channels,
response,
FAULT_METHOD_NOT_FOUND,
message.str());
}
return rv;
}
// static
void LLSDRPCServer::buildFault(
const LLChannelDescriptors& channels,
LLBufferArray* data,
S32 code,
const std::string& msg)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
LLBufferStream ostr(channels, data);
ostr << FAULT_PART_1 << code << FAULT_PART_2 << msg << FAULT_PART_3;
llinfos << "LLSDRPCServer::buildFault: " << code << ", " << msg << llendl;
}
// static
void LLSDRPCServer::buildResponse(
const LLChannelDescriptors& channels,
LLBufferArray* data,
const LLSD& response)
{
LLMemType m1(LLMemType::MTYPE_IO_SD_SERVER);
LLBufferStream ostr(channels, data);
ostr << RESPONSE_PART_1;
LLSDSerialize::toNotation(response, ostr);
ostr << RESPONSE_PART_2;
#if LL_DEBUG
std::ostringstream debug_ostr;
debug_ostr << "LLSDRPCServer::buildResponse: ";
LLSDSerialize::toNotation(response, debug_ostr);
llinfos << debug_ostr.str() << llendl;
#endif
}