PageRenderTime 38ms CodeModel.GetById 8ms app.highlight 26ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llmessage/llhttpnode.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 494 lines | 355 code | 93 blank | 46 comment | 35 complexity | 44c9e089cd35268a66719f0c436d93f7 MD5 | raw file
  1/** 
  2 * @file llhttpnode.cpp
  3 * @brief Implementation of classes for generic HTTP/LSL/REST handling.
  4 *
  5 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  6 * Second Life Viewer Source Code
  7 * Copyright (C) 2010, Linden Research, Inc.
  8 * 
  9 * This library is free software; you can redistribute it and/or
 10 * modify it under the terms of the GNU Lesser General Public
 11 * License as published by the Free Software Foundation;
 12 * version 2.1 of the License only.
 13 * 
 14 * This library is distributed in the hope that it will be useful,
 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 17 * Lesser General Public License for more details.
 18 * 
 19 * You should have received a copy of the GNU Lesser General Public
 20 * License along with this library; if not, write to the Free Software
 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 22 * 
 23 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 24 * $/LicenseInfo$
 25 */
 26
 27#include "linden_common.h"
 28#include "llhttpnode.h"
 29
 30#include <boost/tokenizer.hpp>
 31
 32#include "llstl.h"
 33#include "lliohttpserver.h" // for string constants
 34
 35static const std::string CONTEXT_WILDCARD("wildcard");
 36
 37/**
 38 * LLHTTPNode
 39 */
 40 
 41class LLHTTPNode::Impl
 42{
 43public:
 44	typedef std::map<std::string, LLHTTPNode*> ChildMap;
 45	
 46	ChildMap mNamedChildren;
 47	LLHTTPNode* mWildcardChild;
 48	std::string mWildcardName;
 49	std::string mWildcardKey;
 50	LLHTTPNode* mParentNode;
 51	
 52	Impl() : mWildcardChild(NULL), mParentNode(NULL) { }
 53	
 54	LLHTTPNode* findNamedChild(const std::string& name) const;
 55};
 56
 57
 58LLHTTPNode* LLHTTPNode::Impl::findNamedChild(const std::string& name) const
 59{
 60	LLHTTPNode* child = get_ptr_in_map(mNamedChildren, name);
 61
 62	if (!child  &&  ((name[0] == '*') || (name == mWildcardName)))
 63	{
 64		child = mWildcardChild;
 65	}
 66	
 67	return child;
 68}
 69
 70
 71LLHTTPNode::LLHTTPNode()
 72	: impl(* new Impl)
 73{
 74}
 75
 76// virtual
 77LLHTTPNode::~LLHTTPNode()
 78{
 79	std::for_each(impl.mNamedChildren.begin(), impl.mNamedChildren.end(),
 80		DeletePairedPointer());
 81
 82	delete impl.mWildcardChild;
 83	
 84	delete &impl;
 85}
 86
 87
 88namespace {
 89	class NotImplemented
 90	{
 91	};
 92}
 93
 94// virtual
 95LLSD LLHTTPNode::simpleGet() const
 96{
 97	throw NotImplemented();
 98}
 99
100// virtual
101LLSD LLHTTPNode::simplePut(const LLSD& input) const
102{
103	throw NotImplemented();
104}
105
106// virtual
107LLSD LLHTTPNode::simplePost(const LLSD& input) const
108{
109	throw NotImplemented();
110}
111
112
113// virtual
114void LLHTTPNode::get(LLHTTPNode::ResponsePtr response, const LLSD& context) const
115{
116	try
117	{
118		response->result(simpleGet());
119	}
120	catch (NotImplemented)
121	{
122		response->methodNotAllowed();
123	}
124}
125
126// virtual
127void LLHTTPNode::put(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const
128{
129	try
130	{
131		response->result(simplePut(input));
132	}
133	catch (NotImplemented)
134	{
135		response->methodNotAllowed();
136	}
137}
138
139// virtual
140void LLHTTPNode::post(LLHTTPNode::ResponsePtr response, const LLSD& context, const LLSD& input) const
141{
142	try
143	{
144		response->result(simplePost(input));
145	}
146	catch (NotImplemented)
147	{
148		response->methodNotAllowed();
149	}
150}
151
152// virtual
153void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) const
154{
155    try
156    {
157	response->result(simpleDel(context));
158    }
159    catch (NotImplemented)
160    {
161	response->methodNotAllowed();
162    }
163
164}
165
166// virtual
167LLSD LLHTTPNode::simpleDel(const LLSD&) const
168{
169	throw NotImplemented();
170}
171
172// virtual
173void  LLHTTPNode::options(ResponsePtr response, const LLSD& context) const
174{
175	//llinfos << "options context: " << context << llendl;
176
177	// default implementation constructs an url to the documentation.
178	std::string host(
179		context[CONTEXT_REQUEST][CONTEXT_HEADERS]["host"].asString());
180	if(host.empty())
181	{
182		response->status(400, "Bad Request -- need Host header");
183		return;
184	}
185	std::ostringstream ostr;
186	ostr << "http://" << host << "/web/server/api";
187	ostr << context[CONTEXT_REQUEST]["path"].asString();
188	static const std::string DOC_HEADER("X-Documentation-URL");
189	response->addHeader(DOC_HEADER, ostr.str());
190	response->status(200, "OK");
191}
192
193
194// virtual
195LLHTTPNode* LLHTTPNode::getChild(const std::string& name, LLSD& context) const
196{
197	LLHTTPNode* namedChild = get_ptr_in_map(impl.mNamedChildren, name);
198	if (namedChild)
199	{
200		return namedChild;
201	}
202	
203	if (impl.mWildcardChild
204	&&  impl.mWildcardChild->validate(name, context))
205	{
206		context[CONTEXT_REQUEST][CONTEXT_WILDCARD][impl.mWildcardKey] = name;
207		return impl.mWildcardChild;
208	}
209	
210	return NULL;
211}
212
213
214// virtual
215bool LLHTTPNode::handles(const LLSD& remainder, LLSD& context) const
216{
217	return remainder.size() == 0;
218}
219
220// virtual
221bool LLHTTPNode::validate(const std::string& name, LLSD& context) const
222{
223	return false;
224}
225
226const LLHTTPNode* LLHTTPNode::traverse(
227	const std::string& path, LLSD& context) const
228{
229	typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
230	boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
231	tokenizer tokens(path, sep);
232	tokenizer::iterator iter = tokens.begin();
233	tokenizer::iterator end = tokens.end();
234
235	const LLHTTPNode* node = this;
236	for(; iter != end; ++iter)
237	{
238		LLHTTPNode* child = node->getChild(*iter, context);
239		if(!child) 
240		{
241			lldebugs << "LLHTTPNode::traverse: Couldn't find '" << *iter << "'" << llendl;
242			break; 
243		}
244		lldebugs << "LLHTTPNode::traverse: Found '" << *iter << "'" << llendl;
245
246		node = child;
247	}
248
249	LLSD& remainder = context[CONTEXT_REQUEST]["remainder"];
250	for(; iter != end; ++iter)
251	{
252		remainder.append(*iter);
253	}
254
255	return node->handles(remainder, context) ? node : NULL;
256}
257
258
259
260void LLHTTPNode::addNode(const std::string& path, LLHTTPNode* nodeToAdd)
261{
262	typedef boost::tokenizer< boost::char_separator<char> > tokenizer;
263	boost::char_separator<char> sep("/", "", boost::drop_empty_tokens);
264	tokenizer tokens(path, sep);
265	tokenizer::iterator iter = tokens.begin();
266	tokenizer::iterator end = tokens.end();
267
268	LLHTTPNode* node = this;
269	for(; iter != end; ++iter)
270	{
271		LLHTTPNode* child = node->impl.findNamedChild(*iter);
272		if (!child) { break; }
273		node = child;
274	}
275	
276	if (iter == end)
277	{
278		llwarns << "LLHTTPNode::addNode: already a node that handles "
279			<< path << llendl;
280		return;
281	}
282	
283	while (true)
284	{
285		std::string pathPart = *iter;
286		
287		++iter;
288		bool lastOne = iter == end;
289		
290		LLHTTPNode* nextNode = lastOne ? nodeToAdd : new LLHTTPNode();
291		
292		switch (pathPart[0])
293		{
294			case '<':
295				// *NOTE: This should really validate that it is of
296				// the proper form: <wildcardkey> so that the substr()
297				// generates the correct key name.
298				node->impl.mWildcardChild = nextNode;
299				node->impl.mWildcardName = pathPart;
300				if(node->impl.mWildcardKey.empty())
301				{
302					node->impl.mWildcardKey = pathPart.substr(
303						1,
304						pathPart.size() - 2);
305				}
306				break;
307			case '*':
308				node->impl.mWildcardChild = nextNode;
309				if(node->impl.mWildcardName.empty())
310				{
311					node->impl.mWildcardName = pathPart;
312				}
313				break;
314			
315			default:
316				node->impl.mNamedChildren[pathPart] = nextNode;
317		}
318		nextNode->impl.mParentNode = node;
319
320		if (lastOne) break;
321		node = nextNode;
322	}
323}
324
325static void append_node_paths(LLSD& result,
326	const std::string& name, const LLHTTPNode* node)
327{
328	result.append(name);
329	
330	LLSD paths = node->allNodePaths();
331	LLSD::array_const_iterator i = paths.beginArray();
332	LLSD::array_const_iterator end = paths.endArray();
333	
334	for (; i != end; ++i)
335	{
336		result.append(name + "/" + (*i).asString());
337	}
338}
339
340LLSD LLHTTPNode::allNodePaths() const
341{
342	LLSD result;
343	
344	Impl::ChildMap::const_iterator i = impl.mNamedChildren.begin();
345	Impl::ChildMap::const_iterator end = impl.mNamedChildren.end();
346	for (; i != end; ++i)
347	{
348		append_node_paths(result, i->first, i->second);
349	}
350	
351	if (impl.mWildcardChild)
352	{
353		append_node_paths(result, impl.mWildcardName, impl.mWildcardChild);
354	}
355	
356	return result;
357}
358
359
360const LLHTTPNode* LLHTTPNode::rootNode() const
361{
362	const LLHTTPNode* node = this;
363	
364	while (true)
365	{
366		const LLHTTPNode* next = node->impl.mParentNode;
367		if (!next)
368		{
369			return node;
370		}
371		node = next;
372	}
373}
374
375
376const LLHTTPNode* LLHTTPNode::findNode(const std::string& name) const
377{
378	return impl.findNamedChild(name);
379}
380
381LLHTTPNode::Response::~Response()
382{
383}
384
385void LLHTTPNode::Response::statusUnknownError(S32 code)
386{
387	status(code, "Unknown Error");
388}
389
390void LLHTTPNode::Response::notFound(const std::string& message)
391{
392	status(404, message);
393}
394
395void LLHTTPNode::Response::notFound()
396{
397	status(404, "Not Found");
398}
399
400void LLHTTPNode::Response::methodNotAllowed()
401{
402	status(405, "Method Not Allowed");
403}
404
405void LLHTTPNode::Response::addHeader(
406	const std::string& name,
407	const std::string& value)
408{
409	mHeaders[name] = value;
410}
411
412void LLHTTPNode::describe(Description& desc) const
413{
414	desc.shortInfo("unknown service (missing describe() method)");
415}
416
417
418const LLChainIOFactory* LLHTTPNode::getProtocolHandler() const
419{
420	return NULL;
421}
422
423
424
425namespace
426{
427    typedef std::map<std::string, LLHTTPRegistrar::NodeFactory*>  FactoryMap;
428    
429    FactoryMap& factoryMap()
430    {
431        static FactoryMap theMap;
432        return theMap;
433    }
434}
435
436LLHTTPRegistrar::NodeFactory::~NodeFactory() { }
437
438void LLHTTPRegistrar::registerFactory(
439    const std::string& path, NodeFactory& factory)
440{
441	factoryMap()[path] = &factory;
442}
443
444void LLHTTPRegistrar::buildAllServices(LLHTTPNode& root)
445{
446    const FactoryMap& map = factoryMap();
447    
448    FactoryMap::const_iterator i = map.begin();
449    FactoryMap::const_iterator end = map.end();
450    for (; i != end; ++i)
451    {
452		LL_DEBUGS("AppInit") << "LLHTTPRegistrar::buildAllServices adding node for path "
453			<< i->first << LL_ENDL;
454		
455        root.addNode(i->first, i->second->build());
456    }
457}
458
459LLPointer<LLSimpleResponse> LLSimpleResponse::create()
460{
461	return new LLSimpleResponse();
462}
463
464LLSimpleResponse::~LLSimpleResponse()
465{
466}
467
468void LLSimpleResponse::result(const LLSD& result)
469{
470	status(200, "OK");
471}
472
473void LLSimpleResponse::extendedResult(S32 code, const std::string& body, const LLSD& headers)
474{
475	status(code,body);
476}
477
478void LLSimpleResponse::status(S32 code, const std::string& message)
479{
480	mCode = code;
481	mMessage = message;
482}
483
484void LLSimpleResponse::print(std::ostream& out) const
485{
486	out << mCode << " " << mMessage;
487}
488
489
490std::ostream& operator<<(std::ostream& out, const LLSimpleResponse& resp)
491{
492	resp.print(out);
493	return out;
494}