Merge branch 'curlthreading2' of git://github.com/AlericInglewood/SingularityViewer into curlthreading
This commit is contained in:
@@ -264,7 +264,7 @@ bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_co
|
||||
{
|
||||
done = true; // request does not exist
|
||||
}
|
||||
else if (req->getStatus() == STATUS_COMPLETE)
|
||||
else if (req->getStatus() == STATUS_COMPLETE && !(req->getFlags() & FLAG_LOCKED))
|
||||
{
|
||||
res = true;
|
||||
if (auto_complete)
|
||||
@@ -372,9 +372,17 @@ bool LLQueuedThread::completeRequest(handle_t handle)
|
||||
#if _DEBUG
|
||||
// llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl;
|
||||
#endif
|
||||
mRequestHash.erase(handle);
|
||||
req->deleteRequest();
|
||||
// check();
|
||||
if (!(req->getFlags() & FLAG_LOCKED))
|
||||
{
|
||||
mRequestHash.erase(handle);
|
||||
req->deleteRequest();
|
||||
// check();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cause deletion immediately after FLAG_LOCKED is released.
|
||||
req->setFlags(FLAG_AUTO_COMPLETE);
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
unlockData();
|
||||
@@ -421,12 +429,44 @@ S32 LLQueuedThread::processNextRequest()
|
||||
if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING))
|
||||
{
|
||||
req->setStatus(STATUS_ABORTED);
|
||||
// Unlock, because we can't call finishRequest() while keeping this lock:
|
||||
// generateHandle() takes this lock too and is called while holding a lock
|
||||
// (ie LLTextureFetchWorker::mWorkMutex) that finishRequest will lock too,
|
||||
// causing a dead lock.
|
||||
// Although a complete rewrite of LLQueuedThread is in order because it's
|
||||
// far from robust the way it handles it's locking; the following rationale
|
||||
// at least makes plausible that releasing the lock here SHOULD work if
|
||||
// the original coder didn't completely fuck up: if before the QueuedRequest
|
||||
// req was only accessed while keeping the lock, then it still should
|
||||
// never happen that another thread is waiting for this lock in order to
|
||||
// access the QueuedRequest: a few lines lower we delete it.
|
||||
// In other words, if directly after releasing this lock another thread
|
||||
// would access req, then that can only happen by finding it again in
|
||||
// either mRequestQueue or mRequestHash. We already deleted it from the
|
||||
// first, so this access would have to happen by finding it in mRequestHash.
|
||||
// Such access happens in the following functions:
|
||||
// 1) LLQueuedThread::shutdown -- but it does that anyway, as it doesn't use any locking.
|
||||
// 2) LLQueuedThread::generateHandle -- might skip our handle while before it would block until we deleted it. Skipping it is actually better.
|
||||
// 3) LLQueuedThread::waitForResult -- this now doesn't touch the req when it has the flag FLAG_LOCKED set.
|
||||
// 4) LLQueuedThread::getRequest -- whereever this is used, FLAG_LOCKED is tested before using the req.
|
||||
// 5) LLQueuedThread::getRequestStatus -- this is a read-only operation on the status, which should never be changed from finishRequest().
|
||||
// 6) LLQueuedThread::abortRequest -- it doesn't seem to hurt to add flags (if this happens at all), while calling finishRequest().
|
||||
// 7) LLQueuedThread::setFlags -- same.
|
||||
// 8) LLQueuedThread::setPriority -- doesn't access req with status STATUS_ABORTED, STATUS_COMPLETE or STATUS_INPROGRESS.
|
||||
// 9) LLQueuedThread::completeRequest -- now sets FLAG_AUTO_COMPLETE instead of deleting the req, if FLAG_LOCKED is set, so that deletion happens here when finishRequest returns.
|
||||
req->setFlags(FLAG_LOCKED);
|
||||
unlockData();
|
||||
req->finishRequest(false);
|
||||
if (req->getFlags() & FLAG_AUTO_COMPLETE)
|
||||
lockData();
|
||||
req->resetFlags(FLAG_LOCKED);
|
||||
if ((req->getFlags() & FLAG_AUTO_COMPLETE))
|
||||
{
|
||||
req->resetFlags(FLAG_AUTO_COMPLETE);
|
||||
mRequestHash.erase(req);
|
||||
req->deleteRequest();
|
||||
// check();
|
||||
unlockData();
|
||||
req->deleteRequest();
|
||||
lockData();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -453,14 +493,23 @@ S32 LLQueuedThread::processNextRequest()
|
||||
{
|
||||
lockData();
|
||||
req->setStatus(STATUS_COMPLETE);
|
||||
req->finishRequest(true);
|
||||
if (req->getFlags() & FLAG_AUTO_COMPLETE)
|
||||
{
|
||||
mRequestHash.erase(req);
|
||||
req->deleteRequest();
|
||||
// check();
|
||||
}
|
||||
req->setFlags(FLAG_LOCKED);
|
||||
unlockData();
|
||||
req->finishRequest(true);
|
||||
if ((req->getFlags() & FLAG_AUTO_COMPLETE))
|
||||
{
|
||||
lockData();
|
||||
req->resetFlags(FLAG_AUTO_COMPLETE);
|
||||
mRequestHash.erase(req);
|
||||
// check();
|
||||
req->resetFlags(FLAG_LOCKED);
|
||||
unlockData();
|
||||
req->deleteRequest();
|
||||
}
|
||||
else
|
||||
{
|
||||
req->resetFlags(FLAG_LOCKED);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -72,7 +72,8 @@ public:
|
||||
enum flags_t {
|
||||
FLAG_AUTO_COMPLETE = 1,
|
||||
FLAG_AUTO_DELETE = 2, // child-class dependent
|
||||
FLAG_ABORT = 4
|
||||
FLAG_ABORT = 4,
|
||||
FLAG_LOCKED = 8
|
||||
};
|
||||
|
||||
typedef U32 handle_t;
|
||||
@@ -124,6 +125,10 @@ public:
|
||||
// NOTE: flags are |'d
|
||||
mFlags |= flags;
|
||||
}
|
||||
void resetFlags(U32 flags)
|
||||
{
|
||||
mFlags &= ~flags;
|
||||
}
|
||||
|
||||
virtual bool processRequest() = 0; // Return true when request has completed
|
||||
virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted
|
||||
|
||||
@@ -226,7 +226,8 @@ LLWorkerClass::~LLWorkerClass()
|
||||
llerrs << "LLWorkerClass destroyed with stale work handle" << llendl;
|
||||
}
|
||||
if (workreq->getStatus() != LLWorkerThread::STATUS_ABORTED &&
|
||||
workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE)
|
||||
workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE &&
|
||||
!(workreq->getFlags() & LLWorkerThread::FLAG_LOCKED))
|
||||
{
|
||||
llerrs << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << llendl;
|
||||
}
|
||||
@@ -350,14 +351,10 @@ bool LLWorkerClass::checkWork(bool aborting)
|
||||
}
|
||||
|
||||
LLQueuedThread::status_t status = workreq->getStatus();
|
||||
if (status == LLWorkerThread::STATUS_ABORTED)
|
||||
if (status == LLWorkerThread::STATUS_COMPLETE || status == LLWorkerThread::STATUS_ABORTED)
|
||||
{
|
||||
complete = true;
|
||||
abort = true;
|
||||
}
|
||||
else if (status == LLWorkerThread::STATUS_COMPLETE)
|
||||
{
|
||||
complete = true;
|
||||
complete = !(workreq->getFlags() & LLWorkerThread::FLAG_LOCKED);
|
||||
abort = status == LLWorkerThread::STATUS_ABORTED;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -67,8 +67,6 @@ set(llmessage_SOURCE_FILES
|
||||
llsdmessage.cpp
|
||||
llsdmessagebuilder.cpp
|
||||
llsdmessagereader.cpp
|
||||
llsdrpcclient.cpp
|
||||
llsdrpcserver.cpp
|
||||
llservicebuilder.cpp
|
||||
llservice.cpp
|
||||
llstoredmessage.cpp
|
||||
@@ -168,8 +166,6 @@ set(llmessage_HEADER_FILES
|
||||
llsdmessage.h
|
||||
llsdmessagebuilder.h
|
||||
llsdmessagereader.h
|
||||
llsdrpcclient.h
|
||||
llsdrpcserver.h
|
||||
llservice.h
|
||||
llservicebuilder.h
|
||||
llstoredmessage.h
|
||||
|
||||
@@ -887,6 +887,8 @@ static int curl_debug_callback(CURL*, curl_infotype infotype, char* buf, size_t
|
||||
marker << (void*)request->get_lockobj();
|
||||
libcw_do.push_marker();
|
||||
libcw_do.marker().assign(marker.str().data(), marker.str().size());
|
||||
if (!debug::channels::dc::curlio.is_on())
|
||||
debug::channels::dc::curlio.on();
|
||||
LibcwDoutScopeBegin(LIBCWD_DEBUGCHANNELS, libcw_do, dc::curlio|cond_nonewline_cf(infotype == CURLINFO_TEXT))
|
||||
switch (infotype)
|
||||
{
|
||||
@@ -1116,7 +1118,7 @@ CurlResponderBuffer::CurlResponderBuffer()
|
||||
curl_easy_request_w->send_events_to(this);
|
||||
}
|
||||
|
||||
#define llmaybeerrs lllog(LLApp::isExiting() ? LLError::LEVEL_WARN : LLError::LEVEL_ERROR, NULL, NULL, false)
|
||||
#define llmaybeerrs lllog(LLApp::isRunning ? LLError::LEVEL_ERROR : LLError::LEVEL_WARN, NULL, NULL, false)
|
||||
|
||||
// The callbacks need to be revoked when the CurlResponderBuffer is destructed (because that is what the callbacks use).
|
||||
// The AIThreadSafeSimple<CurlResponderBuffer> is destructed first (right to left), so when we get here then the
|
||||
|
||||
@@ -1,255 +0,0 @@
|
||||
/**
|
||||
* @file llsdrpcclient.cpp
|
||||
* @author Phoenix
|
||||
* @date 2005-11-05
|
||||
* @brief Implementation of the llsd client 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 "llsdrpcclient.h"
|
||||
|
||||
#include "llbufferstream.h"
|
||||
#include "llfasttimer.h"
|
||||
#include "llfiltersd2xmlrpc.h"
|
||||
#include "llmemtype.h"
|
||||
#include "llpumpio.h"
|
||||
#include "llsd.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llurlrequest.h"
|
||||
|
||||
/**
|
||||
* String constants
|
||||
*/
|
||||
static std::string LLSDRPC_RESPONSE_NAME("response");
|
||||
static std::string LLSDRPC_FAULT_NAME("fault");
|
||||
|
||||
/**
|
||||
* LLSDRPCResponse
|
||||
*/
|
||||
LLSDRPCResponse::LLSDRPCResponse() :
|
||||
mIsError(false),
|
||||
mIsFault(false)
|
||||
{
|
||||
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
|
||||
}
|
||||
|
||||
// virtual
|
||||
LLSDRPCResponse::~LLSDRPCResponse()
|
||||
{
|
||||
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
|
||||
}
|
||||
|
||||
bool LLSDRPCResponse::extractResponse(const LLSD& sd)
|
||||
{
|
||||
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
|
||||
bool rv = true;
|
||||
if(sd.has(LLSDRPC_RESPONSE_NAME))
|
||||
{
|
||||
mReturnValue = sd[LLSDRPC_RESPONSE_NAME];
|
||||
mIsFault = false;
|
||||
}
|
||||
else if(sd.has(LLSDRPC_FAULT_NAME))
|
||||
{
|
||||
mReturnValue = sd[LLSDRPC_FAULT_NAME];
|
||||
mIsFault = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
mReturnValue.clear();
|
||||
mIsError = true;
|
||||
rv = false;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
static LLFastTimer::DeclareTimer FTM_SDRPC_RESPONSE("SDRPC Response");
|
||||
|
||||
// virtual
|
||||
LLIOPipe::EStatus LLSDRPCResponse::process_impl(
|
||||
const LLChannelDescriptors& channels,
|
||||
buffer_ptr_t& buffer,
|
||||
bool& eos,
|
||||
LLSD& context,
|
||||
LLPumpIO* pump)
|
||||
{
|
||||
LLFastTimer t(FTM_SDRPC_RESPONSE);
|
||||
PUMP_DEBUG;
|
||||
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
|
||||
if(mIsError)
|
||||
{
|
||||
error(pump);
|
||||
}
|
||||
else if(mIsFault)
|
||||
{
|
||||
fault(pump);
|
||||
}
|
||||
else
|
||||
{
|
||||
response(pump);
|
||||
}
|
||||
PUMP_DEBUG;
|
||||
return STATUS_DONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* LLSDRPCClient
|
||||
*/
|
||||
|
||||
LLSDRPCClient::LLSDRPCClient() :
|
||||
mState(STATE_NONE),
|
||||
mQueue(EPBQ_PROCESS)
|
||||
{
|
||||
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
|
||||
}
|
||||
|
||||
// virtual
|
||||
LLSDRPCClient::~LLSDRPCClient()
|
||||
{
|
||||
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
|
||||
}
|
||||
|
||||
bool LLSDRPCClient::call(
|
||||
const std::string& uri,
|
||||
const std::string& method,
|
||||
const LLSD& parameter,
|
||||
LLSDRPCResponse* response,
|
||||
EPassBackQueue queue)
|
||||
{
|
||||
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
|
||||
//llinfos << "RPC: " << uri << "." << method << "(" << *parameter << ")"
|
||||
// << llendl;
|
||||
if(method.empty() || !response)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
mState = STATE_READY;
|
||||
mURI.assign(uri);
|
||||
std::stringstream req;
|
||||
req << LLSDRPC_REQUEST_HEADER_1 << method
|
||||
<< LLSDRPC_REQUEST_HEADER_2;
|
||||
LLSDSerialize::toNotation(parameter, req);
|
||||
req << LLSDRPC_REQUEST_FOOTER;
|
||||
mRequest = req.str();
|
||||
mQueue = queue;
|
||||
mResponse = response;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LLSDRPCClient::call(
|
||||
const std::string& uri,
|
||||
const std::string& method,
|
||||
const std::string& parameter,
|
||||
LLSDRPCResponse* response,
|
||||
EPassBackQueue queue)
|
||||
{
|
||||
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
|
||||
//llinfos << "RPC: " << uri << "." << method << "(" << parameter << ")"
|
||||
// << llendl;
|
||||
if(method.empty() || parameter.empty() || !response)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
mState = STATE_READY;
|
||||
mURI.assign(uri);
|
||||
std::stringstream req;
|
||||
req << LLSDRPC_REQUEST_HEADER_1 << method
|
||||
<< LLSDRPC_REQUEST_HEADER_2 << parameter
|
||||
<< LLSDRPC_REQUEST_FOOTER;
|
||||
mRequest = req.str();
|
||||
mQueue = queue;
|
||||
mResponse = response;
|
||||
return true;
|
||||
}
|
||||
|
||||
static LLFastTimer::DeclareTimer FTM_PROCESS_SDRPC_CLIENT("SDRPC Client");
|
||||
|
||||
// virtual
|
||||
LLIOPipe::EStatus LLSDRPCClient::process_impl(
|
||||
const LLChannelDescriptors& channels,
|
||||
buffer_ptr_t& buffer,
|
||||
bool& eos,
|
||||
LLSD& context,
|
||||
LLPumpIO* pump)
|
||||
{
|
||||
LLFastTimer t(FTM_PROCESS_SDRPC_CLIENT);
|
||||
PUMP_DEBUG;
|
||||
LLMemType m1(LLMemType::MTYPE_IO_SD_CLIENT);
|
||||
if((STATE_NONE == mState) || (!pump))
|
||||
{
|
||||
// You should have called the call() method already.
|
||||
return STATUS_PRECONDITION_NOT_MET;
|
||||
}
|
||||
EStatus rv = STATUS_DONE;
|
||||
switch(mState)
|
||||
{
|
||||
case STATE_READY:
|
||||
{
|
||||
PUMP_DEBUG;
|
||||
// lldebugs << "LLSDRPCClient::process_impl STATE_READY" << llendl;
|
||||
buffer->append(
|
||||
channels.out(),
|
||||
(U8*)mRequest.c_str(),
|
||||
mRequest.length());
|
||||
context[CONTEXT_DEST_URI_SD_LABEL] = mURI;
|
||||
mState = STATE_WAITING_FOR_RESPONSE;
|
||||
break;
|
||||
}
|
||||
case STATE_WAITING_FOR_RESPONSE:
|
||||
{
|
||||
PUMP_DEBUG;
|
||||
// The input channel has the sd response in it.
|
||||
//lldebugs << "LLSDRPCClient::process_impl STATE_WAITING_FOR_RESPONSE"
|
||||
// << llendl;
|
||||
LLBufferStream resp(channels, buffer.get());
|
||||
LLSD sd;
|
||||
LLSDSerialize::fromNotation(sd, resp, buffer->count(channels.in()));
|
||||
LLSDRPCResponse* response = (LLSDRPCResponse*)mResponse.get();
|
||||
if (!response)
|
||||
{
|
||||
mState = STATE_DONE;
|
||||
break;
|
||||
}
|
||||
response->extractResponse(sd);
|
||||
if(EPBQ_PROCESS == mQueue)
|
||||
{
|
||||
LLPumpIO::chain_t chain;
|
||||
chain.push_back(mResponse);
|
||||
pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS);
|
||||
}
|
||||
else
|
||||
{
|
||||
pump->respond(mResponse.get());
|
||||
}
|
||||
mState = STATE_DONE;
|
||||
break;
|
||||
}
|
||||
case STATE_DONE:
|
||||
default:
|
||||
PUMP_DEBUG;
|
||||
llinfos << "invalid state to process" << llendl;
|
||||
rv = STATUS_ERROR;
|
||||
break;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
/**
|
||||
* @file llsdrpcclient.h
|
||||
* @author Phoenix
|
||||
* @date 2005-11-05
|
||||
* @brief Implementation and helpers for structure data RPC clients.
|
||||
*
|
||||
* $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$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLSDRPCCLIENT_H
|
||||
#define LL_LLSDRPCCLIENT_H
|
||||
|
||||
/**
|
||||
* This file declares classes to encapsulate a basic structured data
|
||||
* remote procedure client.
|
||||
*/
|
||||
|
||||
#include "llchainio.h"
|
||||
#include "llfiltersd2xmlrpc.h"
|
||||
#include "lliopipe.h"
|
||||
#include "llurlrequest.h"
|
||||
|
||||
/**
|
||||
* @class LLSDRPCClientResponse
|
||||
* @brief Abstract base class to represent a response from an SD server.
|
||||
*
|
||||
* This is used as a base class for callbacks generated from an
|
||||
* structured data remote procedure call. The
|
||||
* <code>extractResponse</code> method will deal with the llsdrpc method
|
||||
* call overhead, and keep track of what to call during the next call
|
||||
* into <code>process</code>. If you use this as a base class, you
|
||||
* need to implement <code>response</code>, <code>fault</code>, and
|
||||
* <code>error</code> to do something useful. When in those methods,
|
||||
* you can parse and utilize the mReturnValue member data.
|
||||
*/
|
||||
class LLSDRPCResponse : public LLIOPipe
|
||||
{
|
||||
public:
|
||||
LLSDRPCResponse();
|
||||
virtual ~LLSDRPCResponse();
|
||||
|
||||
/**
|
||||
* @brief This method extracts the response out of the sd passed in
|
||||
*
|
||||
* Any appropriate data found in the sd passed in will be
|
||||
* extracted and managed by this object - not copied or cloned. It
|
||||
* will still be up to the caller to delete the pointer passed in.
|
||||
* @param sd The raw structured data response from the remote server.
|
||||
* @return Returns true if this was able to parse the structured data.
|
||||
*/
|
||||
bool extractResponse(const LLSD& sd);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Method called when the response is ready.
|
||||
*/
|
||||
virtual bool response(LLPumpIO* pump) = 0;
|
||||
|
||||
/**
|
||||
* @brief Method called when a fault is generated by the remote server.
|
||||
*/
|
||||
virtual bool fault(LLPumpIO* pump) = 0;
|
||||
|
||||
/**
|
||||
* @brief Method called when there was an error
|
||||
*/
|
||||
virtual bool error(LLPumpIO* pump) = 0;
|
||||
|
||||
protected:
|
||||
/* @name LLIOPipe virtual implementations
|
||||
*/
|
||||
//@{
|
||||
/**
|
||||
* @brief Process the data in buffer
|
||||
*/
|
||||
virtual EStatus process_impl(
|
||||
const LLChannelDescriptors& channels,
|
||||
buffer_ptr_t& buffer,
|
||||
bool& eos,
|
||||
LLSD& context,
|
||||
LLPumpIO* pump);
|
||||
//@}
|
||||
|
||||
protected:
|
||||
LLSD mReturnValue;
|
||||
bool mIsError;
|
||||
bool mIsFault;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLSDRPCClient
|
||||
* @brief Client class for a structured data remote procedure call.
|
||||
*
|
||||
* This class helps deal with making structured data calls to a remote
|
||||
* server. You can visualize the calls as:
|
||||
* <code>
|
||||
* response = uri.method(parameter)
|
||||
* </code>
|
||||
* where you pass in everything to <code>call</code> and this class
|
||||
* takes care of the rest of the details.
|
||||
* In typical usage, you will derive a class from this class and
|
||||
* provide an API more useful for the specific application at
|
||||
* hand. For example, if you were writing a service to send an instant
|
||||
* message, you could create an API for it to send the messsage, and
|
||||
* that class would do the work of translating it into the method and
|
||||
* parameter, find the destination, and invoke <code>call</call> with
|
||||
* a useful implementation of LLSDRPCResponse passed in to handle the
|
||||
* response from the network.
|
||||
*/
|
||||
class LLSDRPCClient : public LLIOPipe
|
||||
{
|
||||
public:
|
||||
LLSDRPCClient();
|
||||
virtual ~LLSDRPCClient();
|
||||
|
||||
/**
|
||||
* @brief Enumeration for tracking which queue to process the
|
||||
* response.
|
||||
*/
|
||||
enum EPassBackQueue
|
||||
{
|
||||
EPBQ_PROCESS,
|
||||
EPBQ_CALLBACK,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Call a method on a remote LLSDRPCServer
|
||||
*
|
||||
* @param uri The remote object to call, eg,
|
||||
* http://localhost/usher. If you are using a factory with a fixed
|
||||
* url, the uri passed in will probably be ignored.
|
||||
* @param method The method to call on the remote object
|
||||
* @param parameter The parameter to pass into the remote
|
||||
* object. It is up to the caller to delete the value passed in.
|
||||
* @param response The object which gets the response.
|
||||
* @param queue Specifies to call the response on the process or
|
||||
* callback queue.
|
||||
* @return Returns true if this object will be able to make the RPC call.
|
||||
*/
|
||||
bool call(
|
||||
const std::string& uri,
|
||||
const std::string& method,
|
||||
const LLSD& parameter,
|
||||
LLSDRPCResponse* response,
|
||||
EPassBackQueue queue);
|
||||
|
||||
/**
|
||||
* @brief Call a method on a remote LLSDRPCServer
|
||||
*
|
||||
* @param uri The remote object to call, eg,
|
||||
* http://localhost/usher. If you are using a factory with a fixed
|
||||
* url, the uri passed in will probably be ignored.
|
||||
* @param method The method to call on the remote object
|
||||
* @param parameter The seriailized parameter to pass into the
|
||||
* remote object.
|
||||
* @param response The object which gets the response.
|
||||
* @param queue Specifies to call the response on the process or
|
||||
* callback queue.
|
||||
* @return Returns true if this object will be able to make the RPC call.
|
||||
*/
|
||||
bool call(
|
||||
const std::string& uri,
|
||||
const std::string& method,
|
||||
const std::string& parameter,
|
||||
LLSDRPCResponse* response,
|
||||
EPassBackQueue queue);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Enumeration for tracking client state.
|
||||
*/
|
||||
enum EState
|
||||
{
|
||||
STATE_NONE,
|
||||
STATE_READY,
|
||||
STATE_WAITING_FOR_RESPONSE,
|
||||
STATE_DONE
|
||||
};
|
||||
|
||||
/* @name LLIOPipe virtual implementations
|
||||
*/
|
||||
//@{
|
||||
/**
|
||||
* @brief Process the data in buffer
|
||||
*/
|
||||
virtual EStatus process_impl(
|
||||
const LLChannelDescriptors& channels,
|
||||
buffer_ptr_t& buffer,
|
||||
bool& eos,
|
||||
LLSD& context,
|
||||
LLPumpIO* pump);
|
||||
//@}
|
||||
|
||||
protected:
|
||||
EState mState;
|
||||
std::string mURI;
|
||||
std::string mRequest;
|
||||
EPassBackQueue mQueue;
|
||||
LLIOPipe::ptr_t mResponse;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLSDRPCClientFactory
|
||||
* @brief Basic implementation for making an SD RPC client factory
|
||||
*
|
||||
* This class eases construction of a basic sd rpc client. Here is an
|
||||
* example of it's use:
|
||||
* <code>
|
||||
* class LLUsefulService : public LLService { ... }
|
||||
* LLService::registerCreator(
|
||||
* "useful",
|
||||
* LLService::creator_t(new LLSDRPCClientFactory<LLUsefulService>))
|
||||
* </code>
|
||||
*/
|
||||
template<class Client>
|
||||
class LLSDRPCClientFactory : public LLChainIOFactory
|
||||
{
|
||||
public:
|
||||
LLSDRPCClientFactory() {}
|
||||
LLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
|
||||
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
|
||||
{
|
||||
lldebugs << "LLSDRPCClientFactory::build" << llendl;
|
||||
LLURLRequest* http;
|
||||
try
|
||||
{
|
||||
http = new LLURLRequest(LLURLRequest::HTTP_POST);
|
||||
}
|
||||
catch(AICurlNoEasyHandle const& error)
|
||||
{
|
||||
llwarns << "Creating LLURLRequest failed: " << error.what() << llendl ;
|
||||
return false;
|
||||
}
|
||||
|
||||
LLIOPipe::ptr_t service(new Client);
|
||||
chain.push_back(service);
|
||||
LLIOPipe::ptr_t http_pipe(http);
|
||||
http->addHeader("Content-Type: text/llsd");
|
||||
if(mURL.empty())
|
||||
{
|
||||
chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
|
||||
}
|
||||
else
|
||||
{
|
||||
http->setURL(mURL);
|
||||
}
|
||||
chain.push_back(http_pipe);
|
||||
chain.push_back(service);
|
||||
return true;
|
||||
}
|
||||
protected:
|
||||
std::string mURL;
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLXMLSDRPCClientFactory
|
||||
* @brief Basic implementation for making an XMLRPC to SD RPC client factory
|
||||
*
|
||||
* This class eases construction of a basic sd rpc client which uses
|
||||
* xmlrpc as a serialization grammar. Here is an example of it's use:
|
||||
* <code>
|
||||
* class LLUsefulService : public LLService { ... }
|
||||
* LLService::registerCreator(
|
||||
* "useful",
|
||||
* LLService::creator_t(new LLXMLSDRPCClientFactory<LLUsefulService>))
|
||||
* </code>
|
||||
*/
|
||||
template<class Client>
|
||||
class LLXMLSDRPCClientFactory : public LLChainIOFactory
|
||||
{
|
||||
public:
|
||||
LLXMLSDRPCClientFactory() {}
|
||||
LLXMLSDRPCClientFactory(const std::string& fixed_url) : mURL(fixed_url) {}
|
||||
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
|
||||
{
|
||||
lldebugs << "LLXMLSDRPCClientFactory::build" << llendl;
|
||||
|
||||
LLURLRequest* http;
|
||||
try
|
||||
{
|
||||
http = new LLURLRequest(LLURLRequest::HTTP_POST);
|
||||
}
|
||||
catch(AICurlNoEasyHandle const& error)
|
||||
{
|
||||
llwarns << "Creating LLURLRequest failed: " << error.what() << llendl ;
|
||||
return false ;
|
||||
}
|
||||
LLIOPipe::ptr_t service(new Client);
|
||||
chain.push_back(service);
|
||||
LLIOPipe::ptr_t http_pipe(http);
|
||||
http->addHeader("Content-Type: text/xml");
|
||||
if(mURL.empty())
|
||||
{
|
||||
chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http)));
|
||||
}
|
||||
else
|
||||
{
|
||||
http->setURL(mURL);
|
||||
}
|
||||
chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCRequest(NULL)));
|
||||
chain.push_back(http_pipe);
|
||||
chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCResponse2LLSD));
|
||||
chain.push_back(service);
|
||||
return true;
|
||||
}
|
||||
protected:
|
||||
std::string mURL;
|
||||
};
|
||||
|
||||
#endif // LL_LLSDRPCCLIENT_H
|
||||
@@ -1,347 +0,0 @@
|
||||
/**
|
||||
* @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
|
||||
}
|
||||
@@ -1,360 +0,0 @@
|
||||
/**
|
||||
* @file llsdrpcserver.h
|
||||
* @author Phoenix
|
||||
* @date 2005-10-11
|
||||
* @brief Declaration of the structured data remote procedure call server.
|
||||
*
|
||||
* $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$
|
||||
*/
|
||||
|
||||
#ifndef LL_LLSDRPCSERVER_H
|
||||
#define LL_LLSDRPCSERVER_H
|
||||
|
||||
/**
|
||||
* I've set this up to be pretty easy to use when you want to make a
|
||||
* structured data rpc server which responds to methods by
|
||||
* name. Derive a class from the LLSDRPCServer, and during
|
||||
* construction (or initialization if you have the luxury) map method
|
||||
* names to pointers to member functions. This will look a lot like:
|
||||
*
|
||||
* <code>
|
||||
* class LLMessageAgents : public LLSDRPCServer {<br>
|
||||
* public:<br>
|
||||
* typedef LLSDRPCServer<LLUsher> mem_fn_t;<br>
|
||||
* LLMessageAgents() {<br>
|
||||
* mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);<br>
|
||||
* mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);<br>
|
||||
* }<br>
|
||||
* protected:<br>
|
||||
* rpc_IM(const LLSD& params,
|
||||
* const LLChannelDescriptors& channels,
|
||||
* LLBufferArray* data)
|
||||
* {...}<br>
|
||||
* rpc_Alert(const LLSD& params,
|
||||
* const LLChannelDescriptors& channels,
|
||||
* LLBufferArray* data)
|
||||
* {...}<br>
|
||||
* };<br>
|
||||
* </code>
|
||||
*
|
||||
* The params are an array where each element in the array is a single
|
||||
* parameter in the call.
|
||||
*
|
||||
* It is up to you to pack a valid serialized llsd response into the
|
||||
* data object passed into the method, but you can use the helper
|
||||
* methods below to help.
|
||||
*/
|
||||
|
||||
#include <map>
|
||||
#include "lliopipe.h"
|
||||
#include "lliohttpserver.h"
|
||||
#include "llfiltersd2xmlrpc.h"
|
||||
|
||||
class LLSD;
|
||||
|
||||
/**
|
||||
* @brief Enumeration for specifying server method call status. This
|
||||
* enumeration controls how the server class will manage the pump
|
||||
* process/callback mechanism.
|
||||
*/
|
||||
enum ESDRPCSStatus
|
||||
{
|
||||
// The call went ok, but the response is not yet ready. The
|
||||
// method will arrange for the clearLock() call to be made at
|
||||
// a later date, after which, once the chain is being pumped
|
||||
// again, deferredResponse() will be called to gather the result
|
||||
ESDRPCS_DEFERRED,
|
||||
|
||||
// The LLSDRPCServer would like to handle the method on the
|
||||
// callback queue of the pump.
|
||||
ESDRPCS_CALLBACK,
|
||||
|
||||
// The method call finished and generated output.
|
||||
ESDRPCS_DONE,
|
||||
|
||||
// Method failed for some unspecified reason - you should avoid
|
||||
// this. A generic fault will be sent to the output.
|
||||
ESDRPCS_ERROR,
|
||||
|
||||
ESDRPCS_COUNT,
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLSDRPCMethodCallBase
|
||||
* @brief Base class for calling a member function in an sd rpcserver
|
||||
* implementation.
|
||||
*/
|
||||
class LLSDRPCMethodCallBase
|
||||
{
|
||||
public:
|
||||
LLSDRPCMethodCallBase() {}
|
||||
virtual ~LLSDRPCMethodCallBase() {}
|
||||
|
||||
virtual ESDRPCSStatus call(
|
||||
const LLSD& params,
|
||||
const LLChannelDescriptors& channels,
|
||||
LLBufferArray* response) = 0;
|
||||
protected:
|
||||
};
|
||||
|
||||
/**
|
||||
* @class LLSDRPCMethodCall
|
||||
* @brief Class which implements member function calls.
|
||||
*/
|
||||
template<class Server>
|
||||
class LLSDRPCMethodCall : public LLSDRPCMethodCallBase
|
||||
{
|
||||
public:
|
||||
typedef ESDRPCSStatus (Server::*mem_fn)(
|
||||
const LLSD& params,
|
||||
const LLChannelDescriptors& channels,
|
||||
LLBufferArray* data);
|
||||
LLSDRPCMethodCall(Server* s, mem_fn fn) :
|
||||
mServer(s),
|
||||
mMemFn(fn)
|
||||
{
|
||||
}
|
||||
virtual ~LLSDRPCMethodCall() {}
|
||||
virtual ESDRPCSStatus call(
|
||||
const LLSD& params,
|
||||
const LLChannelDescriptors& channels,
|
||||
LLBufferArray* data)
|
||||
{
|
||||
return (*mServer.*mMemFn)(params, channels, data);
|
||||
}
|
||||
|
||||
protected:
|
||||
Server* mServer;
|
||||
mem_fn mMemFn;
|
||||
//bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class LLSDRPCServer
|
||||
* @brief Basic implementation of a structure data rpc server
|
||||
*
|
||||
* The rpc server is also designed to appropriately straddle the pump
|
||||
* <code>process()</code> and <code>callback()</code> to specify which
|
||||
* thread you want to work on when handling a method call. The
|
||||
* <code>mMethods</code> methods are called from
|
||||
* <code>process()</code>, while the <code>mCallbackMethods</code> are
|
||||
* called when a pump is in a <code>callback()</code> cycle.
|
||||
*/
|
||||
class LLSDRPCServer : public LLIOPipe
|
||||
{
|
||||
public:
|
||||
LLSDRPCServer();
|
||||
virtual ~LLSDRPCServer();
|
||||
|
||||
/**
|
||||
* enumeration for generic fault codes
|
||||
*/
|
||||
enum
|
||||
{
|
||||
FAULT_BAD_REQUEST = 2000,
|
||||
FAULT_NO_RESPONSE = 2001,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Call this method to return an rpc fault.
|
||||
*
|
||||
* @param channel The channel for output on the data buffer
|
||||
* @param data buffer which will recieve the final output
|
||||
* @param code The fault code
|
||||
* @param msg The fault message
|
||||
*/
|
||||
static void buildFault(
|
||||
const LLChannelDescriptors& channels,
|
||||
LLBufferArray* data,
|
||||
S32 code,
|
||||
const std::string& msg);
|
||||
|
||||
/**
|
||||
* @brief Call this method to build an rpc response.
|
||||
*
|
||||
* @param channel The channel for output on the data buffer
|
||||
* @param data buffer which will recieve the final output
|
||||
* @param response The return value from the method call
|
||||
*/
|
||||
static void buildResponse(
|
||||
const LLChannelDescriptors& channels,
|
||||
LLBufferArray* data,
|
||||
const LLSD& response);
|
||||
|
||||
protected:
|
||||
/* @name LLIOPipe virtual implementations
|
||||
*/
|
||||
//@{
|
||||
/**
|
||||
* @brief Process the data in buffer
|
||||
*/
|
||||
virtual EStatus process_impl(
|
||||
const LLChannelDescriptors& channels,
|
||||
buffer_ptr_t& buffer,
|
||||
bool& eos,
|
||||
LLSD& context,
|
||||
LLPumpIO* pump);
|
||||
//@}
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief Enumeration to track the state of the rpc server instance
|
||||
*/
|
||||
enum EState
|
||||
{
|
||||
STATE_NONE,
|
||||
STATE_CALLBACK,
|
||||
STATE_DEFERRED,
|
||||
STATE_DONE
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This method is called when an http post comes in.
|
||||
*
|
||||
* The default behavior is to look at the method name, look up the
|
||||
* method in the method table, and call it. If the method is not
|
||||
* found, this function will build a fault response. You can
|
||||
* implement your own version of this function if you want to hard
|
||||
* wire some behavior or optimize things a bit.
|
||||
* @param method The method name being called
|
||||
* @param params The parameters
|
||||
* @param channel The channel for output on the data buffer
|
||||
* @param data The http data
|
||||
* @return Returns the status of the method call, done/deferred/etc
|
||||
*/
|
||||
virtual ESDRPCSStatus callMethod(
|
||||
const std::string& method,
|
||||
const LLSD& params,
|
||||
const LLChannelDescriptors& channels,
|
||||
LLBufferArray* data);
|
||||
|
||||
/**
|
||||
* @brief This method is called when a pump callback is processed.
|
||||
*
|
||||
* The default behavior is to look at the method name, look up the
|
||||
* method in the callback method table, and call it. If the method
|
||||
* is not found, this function will build a fault response. You
|
||||
* can implement your own version of this function if you want to
|
||||
* hard wire some behavior or optimize things a bit.
|
||||
* @param method The method name being called
|
||||
* @param params The parameters
|
||||
* @param channel The channel for output on the data buffer
|
||||
* @param data The http data
|
||||
* @return Returns the status of the method call, done/deferred/etc
|
||||
*/
|
||||
virtual ESDRPCSStatus callbackMethod(
|
||||
const std::string& method,
|
||||
const LLSD& params,
|
||||
const LLChannelDescriptors& channels,
|
||||
LLBufferArray* data);
|
||||
|
||||
/**
|
||||
* @brief Called after a deferred service is unlocked
|
||||
*
|
||||
* If a method returns ESDRPCS_DEFERRED, then the service chain
|
||||
* will be locked and not processed until some other system calls
|
||||
* clearLock() on the service instance again. At that point,
|
||||
* once the pump starts processing the chain again, this method
|
||||
* will be called so the service can output the final result
|
||||
* into the buffers.
|
||||
*/
|
||||
virtual ESDRPCSStatus deferredResponse(
|
||||
const LLChannelDescriptors& channels,
|
||||
LLBufferArray* data);
|
||||
|
||||
// donovan put this public here 7/27/06
|
||||
public:
|
||||
/**
|
||||
* @brief unlock a service that as ESDRPCS_DEFERRED
|
||||
*/
|
||||
void clearLock();
|
||||
|
||||
protected:
|
||||
EState mState;
|
||||
LLSD mRequest;
|
||||
LLPumpIO* mPump;
|
||||
S32 mLock;
|
||||
typedef std::map<std::string, LLSDRPCMethodCallBase*> method_map_t;
|
||||
method_map_t mMethods;
|
||||
method_map_t mCallbackMethods;
|
||||
};
|
||||
|
||||
/**
|
||||
* @name Helper Templates for making LLHTTPNodes
|
||||
*
|
||||
* These templates help in creating nodes for handing a service from
|
||||
* either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer.
|
||||
*
|
||||
* To use it:
|
||||
* \code
|
||||
* class LLUsefulServer : public LLSDRPCServer { ... }
|
||||
*
|
||||
* LLHTTPNode& root = LLCreateHTTPWireServer(...);
|
||||
* root.addNode("llsdrpc/useful", new LLSDRPCNode<LLUsefulServer>);
|
||||
* root.addNode("xmlrpc/useful", new LLXMLRPCNode<LLUsefulServer>);
|
||||
* \endcode
|
||||
*/
|
||||
//@{
|
||||
|
||||
template<class Server>
|
||||
class LLSDRPCServerFactory : public LLChainIOFactory
|
||||
{
|
||||
public:
|
||||
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
|
||||
{
|
||||
lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
|
||||
chain.push_back(LLIOPipe::ptr_t(new Server));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Server>
|
||||
class LLSDRPCNode : public LLHTTPNodeForFactory<
|
||||
LLSDRPCServerFactory<Server> >
|
||||
{
|
||||
};
|
||||
|
||||
template<class Server>
|
||||
class LLXMLRPCServerFactory : public LLChainIOFactory
|
||||
{
|
||||
public:
|
||||
virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
|
||||
{
|
||||
lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
|
||||
chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD));
|
||||
chain.push_back(LLIOPipe::ptr_t(new Server));
|
||||
chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<class Server>
|
||||
class LLXMLRPCNode : public LLHTTPNodeForFactory<
|
||||
LLXMLRPCServerFactory<Server> >
|
||||
{
|
||||
};
|
||||
|
||||
//@}
|
||||
|
||||
#endif // LL_LLSDRPCSERVER_H
|
||||
@@ -52,7 +52,6 @@ static const U32 HTTP_STATUS_PIPE_ERROR = 499;
|
||||
/**
|
||||
* String constants
|
||||
*/
|
||||
const std::string CONTEXT_DEST_URI_SD_LABEL("dest_uri");
|
||||
const std::string CONTEXT_TRANSFERED_BYTES("transfered_bytes");
|
||||
|
||||
|
||||
@@ -715,42 +714,6 @@ static size_t headerCallback(char* header_line, size_t size, size_t nmemb, void*
|
||||
return header_len;
|
||||
}
|
||||
|
||||
static LLFastTimer::DeclareTimer FTM_PROCESS_URL_EXTRACTOR("URL Extractor");
|
||||
/**
|
||||
* LLContextURLExtractor
|
||||
*/
|
||||
// virtual
|
||||
LLIOPipe::EStatus LLContextURLExtractor::process_impl(
|
||||
const LLChannelDescriptors& channels,
|
||||
buffer_ptr_t& buffer,
|
||||
bool& eos,
|
||||
LLSD& context,
|
||||
LLPumpIO* pump)
|
||||
{
|
||||
LLFastTimer t(FTM_PROCESS_URL_EXTRACTOR);
|
||||
PUMP_DEBUG;
|
||||
LLMemType m1(LLMemType::MTYPE_IO_URL_REQUEST);
|
||||
// The destination host is in the context.
|
||||
if(context.isUndefined() || !mRequest)
|
||||
{
|
||||
return STATUS_PRECONDITION_NOT_MET;
|
||||
}
|
||||
|
||||
// copy in to out, since this just extract the URL and does not
|
||||
// actually change the data.
|
||||
LLChangeChannel change(channels.in(), channels.out());
|
||||
std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
|
||||
|
||||
// find the context url
|
||||
if(context.has(CONTEXT_DEST_URI_SD_LABEL))
|
||||
{
|
||||
mRequest->setURL(context[CONTEXT_DEST_URI_SD_LABEL].asString());
|
||||
return STATUS_DONE;
|
||||
}
|
||||
return STATUS_ERROR;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* LLURLRequestComplete
|
||||
*/
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
|
||||
|
||||
extern const std::string CONTEXT_REQUEST;
|
||||
extern const std::string CONTEXT_DEST_URI_SD_LABEL;
|
||||
extern const std::string CONTEXT_RESPONSE;
|
||||
extern const std::string CONTEXT_TRANSFERED_BYTES;
|
||||
|
||||
@@ -276,42 +275,6 @@ private:
|
||||
LLURLRequest(const LLURLRequest&);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class LLContextURLExtractor
|
||||
* @brief This class unpacks the url out of a agent usher service so
|
||||
* it can be packed into a LLURLRequest object.
|
||||
* @see LLIOPipe
|
||||
*
|
||||
* This class assumes that the context is a map that contains an entry
|
||||
* named CONTEXT_DEST_URI_SD_LABEL.
|
||||
*/
|
||||
class LLContextURLExtractor : public LLIOPipe
|
||||
{
|
||||
public:
|
||||
LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {}
|
||||
~LLContextURLExtractor() {}
|
||||
|
||||
protected:
|
||||
/* @name LLIOPipe virtual implementations
|
||||
*/
|
||||
//@{
|
||||
/**
|
||||
* @brief Process the data in buffer
|
||||
*/
|
||||
virtual EStatus process_impl(
|
||||
const LLChannelDescriptors& channels,
|
||||
buffer_ptr_t& buffer,
|
||||
bool& eos,
|
||||
LLSD& context,
|
||||
LLPumpIO* pump);
|
||||
//@}
|
||||
|
||||
protected:
|
||||
LLURLRequest* mRequest;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @class LLURLRequestComplete
|
||||
* @brief Class which can optionally be used with an LLURLRequest to
|
||||
@@ -384,11 +347,4 @@ protected:
|
||||
EStatus mRequestStatus;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* External constants
|
||||
*/
|
||||
extern const std::string CONTEXT_DEST_URI_SD_LABEL;
|
||||
|
||||
#endif // LL_LLURLREQUEST_H
|
||||
|
||||
@@ -175,7 +175,7 @@ BOOL LLVFile::isReadComplete()
|
||||
{
|
||||
LLVFSThread::Request* req = (LLVFSThread::Request*)sVFSThread->getRequest(mHandle);
|
||||
LLVFSThread::status_t status = req->getStatus();
|
||||
if (status == LLVFSThread::STATUS_COMPLETE)
|
||||
if (status == LLVFSThread::STATUS_COMPLETE && !(req->getFlags() & LLVFSThread::FLAG_LOCKED))
|
||||
{
|
||||
mBytesRead = req->getBytesRead();
|
||||
mPosition += mBytesRead;
|
||||
|
||||
@@ -253,7 +253,9 @@ extern S32 gStartImageHeight;
|
||||
// local globals
|
||||
//
|
||||
|
||||
|
||||
#ifdef CWDEBUG
|
||||
static bool gCurlIo;
|
||||
#endif
|
||||
|
||||
static LLHost gAgentSimHost;
|
||||
static BOOL gSkipOptionalUpdate = FALSE;
|
||||
@@ -1334,6 +1336,9 @@ bool idle_startup()
|
||||
|
||||
llinfos << "Authenticating with " << grid_uri << llendl;
|
||||
|
||||
// Always write curl I/O debug info for the login attempt.
|
||||
Debug(gCurlIo = dc::curl.is_on() && !dc::curlio.is_on(); if (gCurlIo) dc::curlio.on());
|
||||
|
||||
// TODO if statement here to use web_login_key
|
||||
// OGPX : which routine would this end up in? the LLSD or XMLRPC, or ....?
|
||||
LLUserAuth::getInstance()->authenticate(
|
||||
@@ -1408,6 +1413,7 @@ bool idle_startup()
|
||||
LL_DEBUGS("AppInit") << "downloading..." << LL_ENDL;
|
||||
return FALSE;
|
||||
}
|
||||
Debug(if (gCurlIo) dc::curlio.off()); // Login succeeded: restore dc::curlio to original state.
|
||||
LLStartUp::setStartupState( STATE_LOGIN_PROCESS_RESPONSE );
|
||||
progress += 0.01f;
|
||||
set_startup_status(progress, LLTrans::getString("LoginProcessingResponse"), auth_message);
|
||||
|
||||
@@ -28,7 +28,6 @@ include_directories(
|
||||
set(test_SOURCE_FILES
|
||||
common.cpp
|
||||
inventory.cpp
|
||||
io.cpp
|
||||
# llapp_tut.cpp # Temporarily removed until thread issues can be solved
|
||||
llbase64_tut.cpp
|
||||
llblowfish_tut.cpp
|
||||
|
||||
1604
indra/test/io.cpp
1604
indra/test/io.cpp
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user