/** * @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