PageRenderTime 48ms CodeModel.GetById 20ms app.highlight 21ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmessage/llsdrpcserver.h

https://bitbucket.org/lindenlab/viewer-beta/
C++ Header | 360 lines | 144 code | 33 blank | 183 comment | 0 complexity | 324970ba81c223cef1becb9286abe600 MD5 | raw file
  1/** 
  2 * @file llsdrpcserver.h
  3 * @author Phoenix
  4 * @date 2005-10-11
  5 * @brief Declaration of the structured data remote procedure call server.
  6 *
  7 * $LicenseInfo:firstyear=2005&license=viewerlgpl$
  8 * Second Life Viewer Source Code
  9 * Copyright (C) 2010, Linden Research, Inc.
 10 * 
 11 * This library is free software; you can redistribute it and/or
 12 * modify it under the terms of the GNU Lesser General Public
 13 * License as published by the Free Software Foundation;
 14 * version 2.1 of the License only.
 15 * 
 16 * This library is distributed in the hope that it will be useful,
 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 19 * Lesser General Public License for more details.
 20 * 
 21 * You should have received a copy of the GNU Lesser General Public
 22 * License along with this library; if not, write to the Free Software
 23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 24 * 
 25 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 26 * $/LicenseInfo$
 27 */
 28
 29#ifndef LL_LLSDRPCSERVER_H
 30#define LL_LLSDRPCSERVER_H
 31
 32/** 
 33 * I've set this up to be pretty easy to use when you want to make a
 34 * structured data rpc server which responds to methods by
 35 * name. Derive a class from the LLSDRPCServer, and during
 36 * construction (or initialization if you have the luxury) map method
 37 * names to pointers to member functions. This will look a lot like:
 38 *
 39 * <code>
 40 *  class LLMessageAgents : public LLSDRPCServer {<br>
 41 *  public:<br>
 42 *    typedef LLSDRPCServer<LLUsher> mem_fn_t;<br>
 43 *    LLMessageAgents() {<br>
 44 *      mMethods["message"] = new mem_fn_t(this, &LLMessageAgents::rpc_IM);<br>
 45 *      mMethods["alert"] = new mem_fn_t(this, &LLMessageAgents::rpc_Alrt);<br>
 46 *    }<br>
 47 *  protected:<br>
 48 *    rpc_IM(const LLSD& params,
 49 *		const LLChannelDescriptors& channels,
 50 *		LLBufferArray* data)
 51 *		{...}<br>
 52 *    rpc_Alert(const LLSD& params,
 53 *		const LLChannelDescriptors& channels,
 54 *		LLBufferArray* data)
 55 *		{...}<br>
 56 *  };<br>
 57 * </code>
 58 *
 59 * The params are an array where each element in the array is a single
 60 * parameter in the call.
 61 *
 62 * It is up to you to pack a valid serialized llsd response into the
 63 * data object passed into the method, but you can use the helper
 64 * methods below to help.
 65 */
 66
 67#include <map>
 68#include "lliopipe.h"
 69#include "lliohttpserver.h"
 70#include "llfiltersd2xmlrpc.h"
 71
 72class LLSD;
 73
 74/** 
 75 * @brief Enumeration for specifying server method call status. This
 76 * enumeration controls how the server class will manage the pump
 77 * process/callback mechanism.
 78 */
 79enum ESDRPCSStatus
 80{
 81 	// The call went ok, but the response is not yet ready. The
 82 	// method will arrange for the clearLock() call to be made at
 83 	// a later date, after which, once the chain is being pumped
 84	// again, deferredResponse() will be called to gather the result
 85 	ESDRPCS_DEFERRED,
 86
 87	// The LLSDRPCServer would like to handle the method on the
 88	// callback queue of the pump.
 89	ESDRPCS_CALLBACK,
 90
 91	// The method call finished and generated output.
 92	ESDRPCS_DONE,
 93
 94	// Method failed for some unspecified reason - you should avoid
 95	// this. A generic fault will be sent to the output.
 96	ESDRPCS_ERROR,
 97
 98	ESDRPCS_COUNT,
 99};
100
101/** 
102 * @class LLSDRPCMethodCallBase
103 * @brief Base class for calling a member function in an sd rpcserver
104 * implementation.
105 */
106class LLSDRPCMethodCallBase
107{
108public:
109	LLSDRPCMethodCallBase() {}
110	virtual ~LLSDRPCMethodCallBase() {}
111
112	virtual ESDRPCSStatus call(
113		const LLSD& params,
114		const LLChannelDescriptors& channels,
115		LLBufferArray* response) = 0;
116protected:
117};
118
119/** 
120 * @class LLSDRPCMethodCall
121 * @brief Class which implements member function calls.
122 */
123template<class Server>
124class LLSDRPCMethodCall : public LLSDRPCMethodCallBase
125{
126public:
127	typedef ESDRPCSStatus (Server::*mem_fn)(
128		const LLSD& params,
129		const LLChannelDescriptors& channels,
130		LLBufferArray* data);
131	LLSDRPCMethodCall(Server* s, mem_fn fn) :
132		mServer(s),
133		mMemFn(fn)
134	{
135	}
136	virtual ~LLSDRPCMethodCall() {}
137	virtual ESDRPCSStatus call(
138		const LLSD& params,
139		const LLChannelDescriptors& channels,
140		LLBufferArray* data)
141	{
142		return (*mServer.*mMemFn)(params, channels, data);
143	}
144
145protected:
146	Server* mServer;
147	mem_fn mMemFn;
148	//bool (Server::*mMemFn)(const LLSD& params, LLBufferArray& data);
149};
150
151
152/** 
153 * @class LLSDRPCServer
154 * @brief Basic implementation of a structure data rpc server
155 *
156 * The rpc server is also designed to appropriately straddle the pump
157 * <code>process()</code> and <code>callback()</code> to specify which
158 * thread you want to work on when handling a method call. The
159 * <code>mMethods</code> methods are called from
160 * <code>process()</code>, while the <code>mCallbackMethods</code> are
161 * called when a pump is in a <code>callback()</code> cycle.
162 */
163class LLSDRPCServer : public LLIOPipe
164{
165public:
166	LLSDRPCServer();
167	virtual ~LLSDRPCServer();
168
169	/**
170	 * enumeration for generic fault codes
171	 */
172	enum
173	{
174		FAULT_BAD_REQUEST = 2000,
175		FAULT_NO_RESPONSE = 2001,
176	};
177
178	/** 
179	 * @brief Call this method to return an rpc fault.
180	 *
181	 * @param channel The channel for output on the data buffer
182	 * @param data buffer which will recieve the final output 
183	 * @param code The fault code 
184	 * @param msg The fault message 
185	 */
186	static void buildFault(
187		const LLChannelDescriptors& channels,
188		LLBufferArray* data,
189		S32 code,
190		const std::string& msg);
191
192	/** 
193	 * @brief Call this method to build an rpc response.
194	 *
195	 * @param channel The channel for output on the data buffer
196	 * @param data buffer which will recieve the final output 
197	 * @param response The return value from the method call
198	 */
199	static void buildResponse(
200		const LLChannelDescriptors& channels,
201		LLBufferArray* data,
202		const LLSD& response);
203
204protected:
205	/* @name LLIOPipe virtual implementations
206	 */
207	//@{
208	/** 
209	 * @brief Process the data in buffer
210	 */
211	virtual EStatus process_impl(
212		const LLChannelDescriptors& channels,
213		buffer_ptr_t& buffer,
214		bool& eos,
215		LLSD& context,
216		LLPumpIO* pump);
217	//@}
218
219protected:
220
221	/** 
222	 * @brief Enumeration to track the state of the rpc server instance
223	 */
224	enum EState
225	{
226		STATE_NONE,
227		STATE_CALLBACK,
228		STATE_DEFERRED,
229		STATE_DONE
230	};
231
232	/** 
233	 * @brief This method is called when an http post comes in.
234	 *
235	 * The default behavior is to look at the method name, look up the
236	 * method in the method table, and call it. If the method is not
237	 * found, this function will build a fault response.  You can
238	 * implement your own version of this function if you want to hard
239	 * wire some behavior or optimize things a bit.
240	 * @param method The method name being called
241	 * @param params The parameters
242	 * @param channel The channel for output on the data buffer
243	 * @param data The http data
244	 * @return Returns the status of the method call, done/deferred/etc
245	 */
246	virtual ESDRPCSStatus callMethod(
247		const std::string& method,
248		const LLSD& params,
249		const LLChannelDescriptors& channels,
250		LLBufferArray* data);
251
252	/** 
253	 * @brief This method is called when a pump callback is processed.
254	 *
255	 * The default behavior is to look at the method name, look up the
256	 * method in the callback method table, and call it. If the method
257	 * is not found, this function will build a fault response.  You
258	 * can implement your own version of this function if you want to
259	 * hard wire some behavior or optimize things a bit.
260	 * @param method The method name being called
261	 * @param params The parameters
262	 * @param channel The channel for output on the data buffer
263	 * @param data The http data
264	 * @return Returns the status of the method call, done/deferred/etc
265	 */
266	virtual ESDRPCSStatus callbackMethod(
267		const std::string& method,
268		const LLSD& params,
269		const LLChannelDescriptors& channels,
270		LLBufferArray* data);
271
272	/**
273	 * @brief Called after a deferred service is unlocked
274	 *
275	 * If a method returns ESDRPCS_DEFERRED, then the service chain
276	 * will be locked and not processed until some other system calls
277	 * clearLock() on the service instance again.  At that point,
278	 * once the pump starts processing the chain again, this method
279	 * will be called so the service can output the final result
280	 * into the buffers.
281	 */
282	virtual ESDRPCSStatus deferredResponse(
283		const LLChannelDescriptors& channels,
284		LLBufferArray* data);
285
286	// donovan put this public here 7/27/06
287public:
288	/**
289	 * @brief unlock a service that as ESDRPCS_DEFERRED
290	 */
291	void clearLock();
292
293protected:
294	EState mState;
295	LLSD mRequest;
296	LLPumpIO* mPump;
297	S32 mLock;
298	typedef std::map<std::string, LLSDRPCMethodCallBase*> method_map_t;
299	method_map_t mMethods;
300	method_map_t mCallbackMethods;
301};
302
303/** 
304 * @name Helper Templates for making LLHTTPNodes
305 *
306 * These templates help in creating nodes for handing a service from
307 * either SDRPC or XMLRPC, given a single implementation of LLSDRPCServer.
308 *
309 * To use it:
310 * \code
311 *  class LLUsefulServer : public LLSDRPCServer { ... }
312 *
313 *  LLHTTPNode& root = LLCreateHTTPWireServer(...);
314 *  root.addNode("llsdrpc/useful", new LLSDRPCNode<LLUsefulServer>);
315 *  root.addNode("xmlrpc/useful", new LLXMLRPCNode<LLUsefulServer>);
316 * \endcode
317 */
318//@{
319
320template<class Server>
321class LLSDRPCServerFactory : public LLChainIOFactory
322{
323public:
324	virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
325	{
326		lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
327		chain.push_back(LLIOPipe::ptr_t(new Server));
328		return true;
329	}
330};
331
332template<class Server>
333class LLSDRPCNode : public LLHTTPNodeForFactory<
334								LLSDRPCServerFactory<Server> >
335{
336};
337
338template<class Server>
339class LLXMLRPCServerFactory : public LLChainIOFactory
340{
341public:
342	virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const
343	{
344		lldebugs << "LLXMLSDRPCServerFactory::build" << llendl;
345		chain.push_back(LLIOPipe::ptr_t(new LLFilterXMLRPCRequest2LLSD));
346		chain.push_back(LLIOPipe::ptr_t(new Server));
347		chain.push_back(LLIOPipe::ptr_t(new LLFilterSD2XMLRPCResponse));
348		return true;
349	}
350};
351
352template<class Server>
353class LLXMLRPCNode : public LLHTTPNodeForFactory<
354					 			LLXMLRPCServerFactory<Server> >
355{
356};
357
358//@}
359
360#endif // LL_LLSDRPCSERVER_H