summaryrefslogtreecommitdiff
path: root/indra/llmessage/llsdrpcserver.h
blob: 930732336b6e9331431e1aae0631b96f7b241b93 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
/** 
 * @file llsdrpcserver.h
 * @author Phoenix
 * @date 2005-10-11
 * @brief Declaration of the structured data remote procedure call server.
 *
 * $LicenseInfo:firstyear=2005&license=viewergpl$
 * 
 * Copyright (c) 2005-2009, Linden Research, Inc.
 * 
 * Second Life Viewer Source Code
 * The source code in this file ("Source Code") is provided by Linden Lab
 * to you under the terms of the GNU General Public License, version 2.0
 * ("GPL"), unless you have obtained a separate licensing agreement
 * ("Other License"), formally executed by you and Linden Lab.  Terms of
 * the GPL can be found in doc/GPL-license.txt in this distribution, or
 * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
 * 
 * There are special exceptions to the terms and conditions of the GPL as
 * it is applied to this Source Code. View the full text of the exception
 * in the file doc/FLOSS-exception.txt in this software distribution, or
 * online at
 * http://secondlifegrid.net/programs/open_source/licensing/flossexception
 * 
 * By copying, modifying or distributing this software, you acknowledge
 * that you have read and understood your obligations described above,
 * and agree to abide by those obligations.
 * 
 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
 * COMPLETENESS OR PERFORMANCE.
 * $/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