PageRenderTime 68ms CodeModel.GetById 14ms app.highlight 48ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/test/lliohttpserver_tut.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 345 lines | 227 code | 62 blank | 56 comment | 7 complexity | f0812fa2b6a8476c1388836f5e24c652 MD5 | raw file
  1/** 
  2 * @file lliohttpserver_tut.cpp
  3 * @date   May 2006
  4 * @brief HTTP server unit tests
  5 *
  6 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  7 * Second Life Viewer Source Code
  8 * Copyright (C) 2010, Linden Research, Inc.
  9 * 
 10 * This library is free software; you can redistribute it and/or
 11 * modify it under the terms of the GNU Lesser General Public
 12 * License as published by the Free Software Foundation;
 13 * version 2.1 of the License only.
 14 * 
 15 * This library is distributed in the hope that it will be useful,
 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 18 * Lesser General Public License for more details.
 19 * 
 20 * You should have received a copy of the GNU Lesser General Public
 21 * License along with this library; if not, write to the Free Software
 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 23 * 
 24 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 25 * $/LicenseInfo$
 26 */
 27
 28#include "linden_common.h"
 29#include "lltut.h"
 30#include "llbufferstream.h"
 31#include "lliohttpserver.h"
 32#include "llsdhttpserver.h"
 33#include "llsdserialize.h"
 34
 35#include "llpipeutil.h"
 36
 37
 38namespace tut
 39{
 40	class HTTPServiceTestData
 41	{
 42	public:
 43		class DelayedEcho : public LLHTTPNode
 44		{
 45			HTTPServiceTestData* mTester;
 46
 47		public:
 48			DelayedEcho(HTTPServiceTestData* tester) : mTester(tester) { }
 49
 50			void post(ResponsePtr response, const LLSD& context, const LLSD& input) const
 51			{
 52				ensure("response already set", mTester->mResponse == ResponsePtr(NULL));
 53				mTester->mResponse = response;
 54				mTester->mResult = input;
 55			}
 56		};
 57
 58		class WireHello : public LLIOPipe
 59		{
 60		protected:
 61			virtual EStatus process_impl(
 62				const LLChannelDescriptors& channels,
 63				buffer_ptr_t& buffer,
 64				bool& eos,
 65				LLSD& context,
 66				LLPumpIO* pump)
 67			{
 68				if(!eos) return STATUS_BREAK;
 69				LLSD sd = "yo!";
 70				LLBufferStream ostr(channels, buffer.get());
 71				ostr << LLSDXMLStreamer(sd);
 72				return STATUS_DONE;
 73			}
 74		};
 75
 76		HTTPServiceTestData()
 77			: mResponse(NULL)
 78		{
 79			LLHTTPStandardServices::useServices();
 80			LLHTTPRegistrar::buildAllServices(mRoot);
 81			mRoot.addNode("/delayed/echo", new DelayedEcho(this));
 82			mRoot.addNode("/wire/hello", new LLHTTPNodeForPipe<WireHello>);
 83		}
 84		
 85		LLHTTPNode mRoot;
 86		LLHTTPNode::ResponsePtr mResponse;
 87		LLSD mResult;
 88
 89		void pumpPipe(LLPumpIO* pump, S32 iterations)
 90		{
 91			while(iterations > 0)
 92			{
 93				pump->pump();
 94				pump->callback();
 95				--iterations;
 96			}
 97		}
 98
 99		std::string makeRequest(
100			const std::string& name,
101			const std::string& httpRequest,
102			bool timeout = false)
103		{
104			LLPipeStringInjector* injector = new LLPipeStringInjector(httpRequest);
105			LLPipeStringExtractor* extractor = new LLPipeStringExtractor();
106			
107			apr_pool_t* pool;
108			apr_pool_create(&pool, NULL);
109
110			LLPumpIO* pump;
111			pump = new LLPumpIO(pool);
112
113			LLPumpIO::chain_t chain;
114			LLSD context;
115
116			chain.push_back(LLIOPipe::ptr_t(injector));
117			LLIOHTTPServer::createPipe(chain, mRoot, LLSD());
118			chain.push_back(LLIOPipe::ptr_t(extractor));
119
120			pump->addChain(chain, DEFAULT_CHAIN_EXPIRY_SECS);
121
122			pumpPipe(pump, 10);
123			if(mResponse.notNull() && (! timeout)) 
124			{
125				mResponse->result(mResult);
126				mResponse = NULL;
127			}
128			pumpPipe(pump, 10);
129			
130			std::string httpResult = extractor->string();
131
132			chain.clear();
133			delete pump;
134			apr_pool_destroy(pool);
135
136			if(mResponse.notNull() && timeout)
137			{
138				mResponse->result(mResult);
139				mResponse = NULL;
140			}
141			
142			return httpResult;
143		}
144		
145		std::string httpGET(const std::string& uri, 
146							bool timeout = false)
147		{
148			std::string httpRequest = "GET " + uri + " HTTP/1.0\r\n\r\n";
149			return makeRequest(uri, httpRequest, timeout);
150		}
151		
152		std::string httpPOST(const std::string& uri,
153			const std::string& body,
154			bool timeout,
155			const std::string& evilExtra = "")
156		{
157			std::ostringstream httpRequest;
158			httpRequest << "POST " + uri + " HTTP/1.0\r\n";
159			httpRequest << "Content-Length: " << body.size() << "\r\n";
160			httpRequest << "\r\n";
161			httpRequest << body;
162			httpRequest << evilExtra;
163				
164			return makeRequest(uri, httpRequest.str(), timeout);
165		}
166
167		std::string httpPOST(const std::string& uri,
168			const std::string& body,
169			const std::string& evilExtra = "")
170		{
171			bool timeout = false;
172			return httpPOST(uri, body, timeout, evilExtra);
173		}
174	};
175
176	typedef test_group<HTTPServiceTestData>		HTTPServiceTestGroup;
177	typedef HTTPServiceTestGroup::object		HTTPServiceTestObject;
178	HTTPServiceTestGroup httpServiceTestGroup("http service");
179
180	template<> template<>
181	void HTTPServiceTestObject::test<1>()
182	{
183		std::string result = httpGET("web/hello");
184		
185		ensure_starts_with("web/hello status", result,
186			"HTTP/1.0 200 OK\r\n");
187						
188		ensure_contains("web/hello content type", result,
189			"Content-Type: application/llsd+xml\r\n");
190
191		ensure_contains("web/hello content length", result,
192			"Content-Length: 36\r\n");
193
194		ensure_contains("web/hello content", result,
195			"\r\n"
196			"<llsd><string>hello</string></llsd>"
197			);
198	}
199	
200	template<> template<>
201	void HTTPServiceTestObject::test<2>()
202	{
203		// test various HTTP errors
204		
205		std::string actual;
206
207		actual = httpGET("web/missing");
208		ensure_starts_with("web/missing 404", actual,
209			"HTTP/1.0 404 Not Found\r\n");
210
211		actual = httpGET("web/echo");			
212		ensure_starts_with("web/echo 405", actual,
213			"HTTP/1.0 405 Method Not Allowed\r\n");
214	}
215	
216	template<> template<>
217	void HTTPServiceTestObject::test<3>()
218	{
219		// test POST & content-length handling
220		
221		std::string result;
222		
223		result = httpPOST("web/echo",
224			"<llsd><integer>42</integer></llsd>");
225			
226		ensure_starts_with("web/echo status", result,
227			"HTTP/1.0 200 OK\r\n");
228						
229		ensure_contains("web/echo content type", result,
230			"Content-Type: application/llsd+xml\r\n");
231
232		ensure_contains("web/echo content length", result,
233			"Content-Length: 35\r\n");
234
235		ensure_contains("web/hello content", result,
236			"\r\n"
237			"<llsd><integer>42</integer></llsd>"
238			);
239
240/* TO DO: this test doesn't pass!!
241		
242		result = httpPOST("web/echo",
243			"<llsd><string>evil</string></llsd>",
244			"really!  evil!!!");
245			
246		ensure_equals("web/echo evil result", result,
247			"HTTP/1.0 200 OK\r\n"
248			"Content-Length: 34\r\n"
249			"\r\n"
250			"<llsd><string>evil</string></llsd>"
251			);
252*/
253	}
254
255	template<> template<>
256	void HTTPServiceTestObject::test<4>()
257	{
258		// test calling things based on pipes
259
260		std::string result;
261
262		result = httpGET("wire/hello");
263
264		ensure_contains("wire/hello", result, "yo!");
265	}
266
267    template<> template<>
268	void HTTPServiceTestObject::test<5>()
269    {
270		// test timeout before async response
271		std::string result;
272
273		bool timeout = true;
274		result = httpPOST("delayed/echo",
275			"<llsd><string>agent99</string></llsd>", timeout);
276
277		ensure_equals("timeout delayed/echo status", result, std::string(""));
278	}
279
280	template<> template<>
281	void HTTPServiceTestObject::test<6>()
282	{
283		// test delayed service		
284		std::string result;
285		
286		result = httpPOST("delayed/echo",
287			"<llsd><string>agent99</string></llsd>");
288			
289		ensure_starts_with("delayed/echo status", result,
290			"HTTP/1.0 200 OK\r\n");
291						
292		ensure_contains("delayed/echo content", result,
293			"\r\n"
294			"<llsd><string>agent99</string></llsd>"
295			);
296	}
297
298	template<> template<>
299	void HTTPServiceTestObject::test<7>()
300	{
301		// test large request
302		std::stringstream stream;
303
304		//U32 size = 36 * 1024 * 1024;
305		//U32 size = 36 * 1024;
306		//std::vector<char> data(size);
307		//memset(&(data[0]), '1', size);
308		//data[size - 1] = '\0';
309
310		
311		//std::string result = httpPOST("web/echo", &(data[0]));
312
313		stream << "<llsd><array>";
314		for(U32 i = 0; i < 1000000; ++i)
315		{
316			stream << "<integer>42</integer>";
317		}
318		stream << "</array></llsd>";
319		llinfos << "HTTPServiceTestObject::test<7>"
320				<< stream.str().length() << llendl;
321		std::string result = httpPOST("web/echo", stream.str());
322		ensure_starts_with("large echo status", result, "HTTP/1.0 200 OK\r\n");
323	}
324
325	template<> template<>
326	void HTTPServiceTestObject::test<8>()
327	{
328		// test the OPTIONS http method -- the default implementation
329		// should return the X-Documentation-URL
330		std::ostringstream http_request;
331		http_request << "OPTIONS /  HTTP/1.0\r\nHost: localhost\r\n\r\n";
332		bool timeout = false;
333		std::string result = makeRequest("/", http_request.str(), timeout);
334		ensure_starts_with("OPTIONS verb ok", result, "HTTP/1.0 200 OK\r\n");
335		ensure_contains(
336			"Doc url header exists",
337			result,
338			"X-Documentation-URL: http://localhost");
339	}
340
341
342	/* TO DO:
343		test generation of not found and method not allowed errors
344	*/
345}