diff options
author | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
---|---|---|
committer | Jacek Antonelli | 2008-08-15 23:44:46 -0500 |
commit | 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch) | |
tree | adca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llmessage/lliohttpserver.cpp | |
parent | README.txt (diff) | |
download | meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2 meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz |
Second Life viewer sources 1.13.2.12
Diffstat (limited to 'linden/indra/llmessage/lliohttpserver.cpp')
-rw-r--r-- | linden/indra/llmessage/lliohttpserver.cpp | 852 |
1 files changed, 852 insertions, 0 deletions
diff --git a/linden/indra/llmessage/lliohttpserver.cpp b/linden/indra/llmessage/lliohttpserver.cpp new file mode 100644 index 0000000..25c1aef --- /dev/null +++ b/linden/indra/llmessage/lliohttpserver.cpp | |||
@@ -0,0 +1,852 @@ | |||
1 | /** | ||
2 | * @file lliohttpserver.cpp | ||
3 | * @author Phoenix | ||
4 | * @date 2005-10-05 | ||
5 | * @brief Implementation of the http server classes | ||
6 | * | ||
7 | * Copyright (c) 2005-2007, Linden Research, Inc. | ||
8 | * | ||
9 | * The source code in this file ("Source Code") is provided by Linden Lab | ||
10 | * to you under the terms of the GNU General Public License, version 2.0 | ||
11 | * ("GPL"), unless you have obtained a separate licensing agreement | ||
12 | * ("Other License"), formally executed by you and Linden Lab. Terms of | ||
13 | * the GPL can be found in doc/GPL-license.txt in this distribution, or | ||
14 | * online at http://secondlife.com/developers/opensource/gplv2 | ||
15 | * | ||
16 | * There are special exceptions to the terms and conditions of the GPL as | ||
17 | * it is applied to this Source Code. View the full text of the exception | ||
18 | * in the file doc/FLOSS-exception.txt in this software distribution, or | ||
19 | * online at http://secondlife.com/developers/opensource/flossexception | ||
20 | * | ||
21 | * By copying, modifying or distributing this software, you acknowledge | ||
22 | * that you have read and understood your obligations described above, | ||
23 | * and agree to abide by those obligations. | ||
24 | * | ||
25 | * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO | ||
26 | * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, | ||
27 | * COMPLETENESS OR PERFORMANCE. | ||
28 | */ | ||
29 | |||
30 | #include "linden_common.h" | ||
31 | #include "lliohttpserver.h" | ||
32 | |||
33 | #include "boost/tokenizer.hpp" | ||
34 | |||
35 | #include "llapr.h" | ||
36 | #include "llbuffer.h" | ||
37 | #include "llbufferstream.h" | ||
38 | #include "llhttpnode.h" | ||
39 | #include "lliopipe.h" | ||
40 | #include "lliosocket.h" | ||
41 | #include "llioutil.h" | ||
42 | #include "llmemtype.h" | ||
43 | #include "llmemorystream.h" | ||
44 | #include "llpumpio.h" | ||
45 | #include "llsd.h" | ||
46 | #include "llsdserialize_xml.h" | ||
47 | #include "llstl.h" | ||
48 | |||
49 | static const char HTTP_VERSION_STR[] = "HTTP/1.0"; | ||
50 | static const std::string CONTEXT_REQUEST("request"); | ||
51 | static const std::string HTTP_VERB_GET("GET"); | ||
52 | static const std::string HTTP_VERB_PUT("PUT"); | ||
53 | static const std::string HTTP_VERB_POST("POST"); | ||
54 | static const std::string HTTP_VERB_DELETE("DELETE"); | ||
55 | |||
56 | |||
57 | class LLHTTPPipe : public LLIOPipe | ||
58 | { | ||
59 | public: | ||
60 | LLHTTPPipe(const LLHTTPNode& node) | ||
61 | : mNode(node), mResponse(NULL), mState(STATE_INVOKE), mChainLock(0), mStatusCode(0) | ||
62 | { } | ||
63 | virtual ~LLHTTPPipe() | ||
64 | { | ||
65 | if (mResponse.notNull()) | ||
66 | { | ||
67 | mResponse->nullPipe(); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | private: | ||
72 | // LLIOPipe API implementation. | ||
73 | virtual EStatus process_impl( | ||
74 | const LLChannelDescriptors& channels, | ||
75 | LLIOPipe::buffer_ptr_t& buffer, | ||
76 | bool& eos, | ||
77 | LLSD& context, | ||
78 | LLPumpIO* pump); | ||
79 | |||
80 | const LLHTTPNode& mNode; | ||
81 | |||
82 | class Response : public LLHTTPNode::Response | ||
83 | { | ||
84 | public: | ||
85 | |||
86 | static LLPointer<Response> create(LLHTTPPipe* pipe); | ||
87 | virtual ~Response(); | ||
88 | |||
89 | // from LLHTTPNode::Response | ||
90 | virtual void result(const LLSD&); | ||
91 | virtual void status(S32 code, const std::string& message); | ||
92 | |||
93 | void nullPipe(); | ||
94 | |||
95 | private: | ||
96 | Response() {;} // Must be accessed through LLPointer. | ||
97 | LLHTTPPipe* mPipe; | ||
98 | }; | ||
99 | friend class Response; | ||
100 | |||
101 | LLPointer<Response> mResponse; | ||
102 | |||
103 | enum State | ||
104 | { | ||
105 | STATE_INVOKE, | ||
106 | STATE_DELAYED, | ||
107 | STATE_LOCKED, | ||
108 | STATE_GOOD_RESULT, | ||
109 | STATE_STATUS_RESULT | ||
110 | }; | ||
111 | State mState; | ||
112 | |||
113 | S32 mChainLock; | ||
114 | LLPumpIO* mLockedPump; | ||
115 | |||
116 | void lockChain(LLPumpIO*); | ||
117 | void unlockChain(); | ||
118 | |||
119 | LLSD mGoodResult; | ||
120 | S32 mStatusCode; | ||
121 | std::string mStatusMessage; | ||
122 | }; | ||
123 | |||
124 | LLIOPipe::EStatus LLHTTPPipe::process_impl( | ||
125 | const LLChannelDescriptors& channels, | ||
126 | buffer_ptr_t& buffer, | ||
127 | bool& eos, | ||
128 | LLSD& context, | ||
129 | LLPumpIO* pump) | ||
130 | { | ||
131 | PUMP_DEBUG; | ||
132 | lldebugs << "LLSDHTTPServer::process_impl" << llendl; | ||
133 | |||
134 | // Once we have all the data, We need to read the sd on | ||
135 | // the the in channel, and respond on the out channel | ||
136 | |||
137 | if(!eos) return STATUS_BREAK; | ||
138 | if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET; | ||
139 | |||
140 | PUMP_DEBUG; | ||
141 | if (mState == STATE_INVOKE) | ||
142 | { | ||
143 | PUMP_DEBUG; | ||
144 | mState = STATE_DELAYED; | ||
145 | // assume deferred unless mResponse does otherwise | ||
146 | mResponse = Response::create(this); | ||
147 | |||
148 | // TODO: Babbage: Parameterize parser? | ||
149 | LLBufferStream istr(channels, buffer.get()); | ||
150 | |||
151 | std::string verb = context[CONTEXT_REQUEST]["verb"]; | ||
152 | if(verb == HTTP_VERB_GET) | ||
153 | { | ||
154 | mNode.get(LLHTTPNode::ResponsePtr(mResponse), context); | ||
155 | } | ||
156 | else if(verb == HTTP_VERB_PUT) | ||
157 | { | ||
158 | LLSD input; | ||
159 | LLSDSerialize::fromXML(input, istr); | ||
160 | |||
161 | mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input); | ||
162 | } | ||
163 | else if(verb == HTTP_VERB_POST) | ||
164 | { | ||
165 | LLSD input; | ||
166 | LLSDSerialize::fromXML(input, istr); | ||
167 | |||
168 | mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input); | ||
169 | } | ||
170 | else if(verb == HTTP_VERB_DELETE) | ||
171 | { | ||
172 | mNode.del(LLHTTPNode::ResponsePtr(mResponse), context); | ||
173 | } | ||
174 | else | ||
175 | { | ||
176 | mResponse->methodNotAllowed(); | ||
177 | } | ||
178 | |||
179 | // Log Internal Server Errors | ||
180 | if(mStatusCode == 500) | ||
181 | { | ||
182 | llwarns << "LLHTTPPipe::process_impl:500:Internal Server Error" | ||
183 | << llendl; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | PUMP_DEBUG; | ||
188 | switch (mState) | ||
189 | { | ||
190 | case STATE_DELAYED: | ||
191 | lockChain(pump); | ||
192 | mState = STATE_LOCKED; | ||
193 | return STATUS_BREAK; | ||
194 | |||
195 | case STATE_LOCKED: | ||
196 | // should never ever happen! | ||
197 | return STATUS_ERROR; | ||
198 | |||
199 | case STATE_GOOD_RESULT: | ||
200 | { | ||
201 | context["response"]["contentType"] = "application/xml"; | ||
202 | LLBufferStream ostr(channels, buffer.get()); | ||
203 | LLSDSerialize::toXML(mGoodResult, ostr); | ||
204 | |||
205 | return STATUS_DONE; | ||
206 | } | ||
207 | |||
208 | case STATE_STATUS_RESULT: | ||
209 | { | ||
210 | context["response"]["contentType"] = "text/plain"; | ||
211 | context["response"]["statusCode"] = mStatusCode; | ||
212 | context["response"]["statusMessage"] = mStatusMessage; | ||
213 | LLBufferStream ostr(channels, buffer.get()); | ||
214 | ostr << mStatusMessage << std::ends; | ||
215 | |||
216 | return STATUS_DONE; | ||
217 | } | ||
218 | default: | ||
219 | llwarns << "LLHTTPPipe::process_impl: unexpected state " | ||
220 | << mState << llendl; | ||
221 | |||
222 | return STATUS_BREAK; | ||
223 | } | ||
224 | // PUMP_DEBUG; // unreachable | ||
225 | } | ||
226 | |||
227 | LLPointer<LLHTTPPipe::Response> LLHTTPPipe::Response::create(LLHTTPPipe* pipe) | ||
228 | { | ||
229 | LLPointer<Response> result = new Response(); | ||
230 | result->mPipe = pipe; | ||
231 | return result; | ||
232 | } | ||
233 | |||
234 | // virtual | ||
235 | LLHTTPPipe::Response::~Response() | ||
236 | { | ||
237 | } | ||
238 | |||
239 | void LLHTTPPipe::Response::nullPipe() | ||
240 | { | ||
241 | mPipe = NULL; | ||
242 | } | ||
243 | |||
244 | // virtual | ||
245 | void LLHTTPPipe::Response::result(const LLSD& r) | ||
246 | { | ||
247 | if(! mPipe) | ||
248 | { | ||
249 | llwarns << "LLHTTPPipe::Response::result: NULL pipe" << llendl; | ||
250 | return; | ||
251 | } | ||
252 | |||
253 | mPipe->mStatusCode = 200; | ||
254 | mPipe->mStatusMessage = "OK"; | ||
255 | mPipe->mGoodResult = r; | ||
256 | mPipe->mState = STATE_GOOD_RESULT; | ||
257 | mPipe->unlockChain(); | ||
258 | } | ||
259 | |||
260 | // virtual | ||
261 | void LLHTTPPipe::Response::status(S32 code, const std::string& message) | ||
262 | { | ||
263 | if(! mPipe) | ||
264 | { | ||
265 | llwarns << "LLHTTPPipe::Response::status: NULL pipe" << llendl; | ||
266 | return; | ||
267 | } | ||
268 | |||
269 | mPipe->mStatusCode = code; | ||
270 | mPipe->mStatusMessage = message; | ||
271 | mPipe->mState = STATE_STATUS_RESULT; | ||
272 | mPipe->unlockChain(); | ||
273 | } | ||
274 | |||
275 | void LLHTTPPipe::lockChain(LLPumpIO* pump) | ||
276 | { | ||
277 | if (mChainLock != 0) { return; } | ||
278 | |||
279 | mLockedPump = pump; | ||
280 | mChainLock = pump->setLock(); | ||
281 | } | ||
282 | |||
283 | void LLHTTPPipe::unlockChain() | ||
284 | { | ||
285 | if (mChainLock == 0) { return; } | ||
286 | |||
287 | mLockedPump->clearLock(mChainLock); | ||
288 | mLockedPump = NULL; | ||
289 | mChainLock = 0; | ||
290 | } | ||
291 | |||
292 | |||
293 | |||
294 | /** | ||
295 | * @class LLHTTPResponseHeader | ||
296 | * @brief Class which correctly builds HTTP headers on a pipe | ||
297 | * @see LLIOPipe | ||
298 | * | ||
299 | * An instance of this class can be placed in a chain where it will | ||
300 | * wait for an end of stream. Once it gets that, it will count the | ||
301 | * bytes on CHANNEL_OUT (or the size of the buffer in io pipe versions | ||
302 | * prior to 2) prepend that data to the request in an HTTP format, and | ||
303 | * supply all normal HTTP response headers. | ||
304 | */ | ||
305 | class LLHTTPResponseHeader : public LLIOPipe | ||
306 | { | ||
307 | public: | ||
308 | LLHTTPResponseHeader() {} | ||
309 | virtual ~LLHTTPResponseHeader() {} | ||
310 | |||
311 | protected: | ||
312 | /* @name LLIOPipe virtual implementations | ||
313 | */ | ||
314 | //@{ | ||
315 | /** | ||
316 | * @brief Process the data in buffer | ||
317 | */ | ||
318 | EStatus process_impl( | ||
319 | const LLChannelDescriptors& channels, | ||
320 | buffer_ptr_t& buffer, | ||
321 | bool& eos, | ||
322 | LLSD& context, | ||
323 | LLPumpIO* pump); | ||
324 | //@} | ||
325 | |||
326 | protected: | ||
327 | S32 mCode; | ||
328 | }; | ||
329 | |||
330 | |||
331 | /** | ||
332 | * LLHTTPResponseHeader | ||
333 | */ | ||
334 | // virtual | ||
335 | LLIOPipe::EStatus LLHTTPResponseHeader::process_impl( | ||
336 | const LLChannelDescriptors& channels, | ||
337 | buffer_ptr_t& buffer, | ||
338 | bool& eos, | ||
339 | LLSD& context, | ||
340 | LLPumpIO* pump) | ||
341 | { | ||
342 | PUMP_DEBUG; | ||
343 | LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER); | ||
344 | if(eos) | ||
345 | { | ||
346 | PUMP_DEBUG; | ||
347 | //mGotEOS = true; | ||
348 | std::ostringstream ostr; | ||
349 | std::string message = context["response"]["statusMessage"]; | ||
350 | |||
351 | int code = context["response"]["statusCode"]; | ||
352 | if (code < 200) | ||
353 | { | ||
354 | code = 200; | ||
355 | message = "OK"; | ||
356 | } | ||
357 | |||
358 | ostr << HTTP_VERSION_STR << " " << code << " " << message << "\r\n"; | ||
359 | |||
360 | std::string type = context["response"]["contentType"].asString(); | ||
361 | if (!type.empty()) | ||
362 | { | ||
363 | ostr << "Content-Type: " << type << "\r\n"; | ||
364 | } | ||
365 | S32 content_length = buffer->countAfter(channels.in(), NULL); | ||
366 | if(0 < content_length) | ||
367 | { | ||
368 | ostr << "Content-Length: " << content_length << "\r\n"; | ||
369 | } | ||
370 | ostr << "\r\n"; | ||
371 | |||
372 | LLChangeChannel change(channels.in(), channels.out()); | ||
373 | std::for_each(buffer->beginSegment(), buffer->endSegment(), change); | ||
374 | std::string header = ostr.str(); | ||
375 | buffer->prepend(channels.out(), (U8*)header.c_str(), header.size()); | ||
376 | PUMP_DEBUG; | ||
377 | return STATUS_DONE; | ||
378 | } | ||
379 | PUMP_DEBUG; | ||
380 | return STATUS_OK; | ||
381 | } | ||
382 | |||
383 | |||
384 | |||
385 | /** | ||
386 | * @class LLHTTPResponder | ||
387 | * @brief This class | ||
388 | * @see LLIOPipe | ||
389 | * | ||
390 | * <b>NOTE:</b> You should not need to create or use one of these, the | ||
391 | * details are handled by the HTTPResponseFactory. | ||
392 | */ | ||
393 | class LLHTTPResponder : public LLIOPipe | ||
394 | { | ||
395 | public: | ||
396 | LLHTTPResponder(const LLHTTPNode& tree); | ||
397 | ~LLHTTPResponder(); | ||
398 | |||
399 | protected: | ||
400 | /** | ||
401 | * @brief Read data off of CHANNEL_IN keeping track of last read position. | ||
402 | * | ||
403 | * This is a quick little hack to read headers. It is not IO | ||
404 | * optimal, but it makes it easier for me to implement the header | ||
405 | * parsing. Plus, there should never be more than a few headers. | ||
406 | * This method will tend to read more than necessary, find the | ||
407 | * newline, make the front part of dest look like a c string, and | ||
408 | * move the read head back to where the newline was found. Thus, | ||
409 | * the next read will pick up on the next line. | ||
410 | * @param channel The channel to read in the buffer | ||
411 | * @param buffer The heap array of processed data | ||
412 | * @param dest Destination for the data to be read | ||
413 | * @param[in,out] len <b>in</b> The size of the buffer. <b>out</b> how | ||
414 | * much was read. This value is not useful for determining where to | ||
415 | * seek orfor string assignment. | ||
416 | * @returns Returns true if a line was found. | ||
417 | */ | ||
418 | bool readLine( | ||
419 | const LLChannelDescriptors& channels, | ||
420 | buffer_ptr_t buffer, | ||
421 | U8* dest, | ||
422 | S32& len); | ||
423 | |||
424 | /** | ||
425 | * @brief Mark the request as bad, and handle appropriately | ||
426 | * | ||
427 | * @param channels The channels to use in the buffer. | ||
428 | * @param buffer The heap array of processed data. | ||
429 | */ | ||
430 | void markBad(const LLChannelDescriptors& channels, buffer_ptr_t buffer); | ||
431 | |||
432 | protected: | ||
433 | /* @name LLIOPipe virtual implementations | ||
434 | */ | ||
435 | //@{ | ||
436 | /** | ||
437 | * @brief Process the data in buffer | ||
438 | */ | ||
439 | EStatus process_impl( | ||
440 | const LLChannelDescriptors& channels, | ||
441 | buffer_ptr_t& buffer, | ||
442 | bool& eos, | ||
443 | LLSD& context, | ||
444 | LLPumpIO* pump); | ||
445 | //@} | ||
446 | |||
447 | protected: | ||
448 | enum EState | ||
449 | { | ||
450 | STATE_NOTHING, | ||
451 | STATE_READING_HEADERS, | ||
452 | STATE_LOOKING_FOR_EOS, | ||
453 | STATE_DONE, | ||
454 | STATE_SHORT_CIRCUIT | ||
455 | }; | ||
456 | |||
457 | EState mState; | ||
458 | U8* mLastRead; | ||
459 | std::string mVerb; | ||
460 | std::string mAbsPathAndQuery; | ||
461 | std::string mPath; | ||
462 | std::string mQuery; | ||
463 | std::string mVersion; | ||
464 | S32 mContentLength; | ||
465 | |||
466 | // handle the urls | ||
467 | const LLHTTPNode& mRootNode; | ||
468 | }; | ||
469 | |||
470 | LLHTTPResponder::LLHTTPResponder(const LLHTTPNode& tree) : | ||
471 | mState(STATE_NOTHING), | ||
472 | mLastRead(NULL), | ||
473 | mContentLength(0), | ||
474 | mRootNode(tree) | ||
475 | { | ||
476 | LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER); | ||
477 | } | ||
478 | |||
479 | // virtual | ||
480 | LLHTTPResponder::~LLHTTPResponder() | ||
481 | { | ||
482 | LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER); | ||
483 | //lldebugs << "destroying LLHTTPResponder" << llendl; | ||
484 | } | ||
485 | |||
486 | bool LLHTTPResponder::readLine( | ||
487 | const LLChannelDescriptors& channels, | ||
488 | buffer_ptr_t buffer, | ||
489 | U8* dest, | ||
490 | S32& len) | ||
491 | { | ||
492 | LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER); | ||
493 | --len; | ||
494 | U8* last = buffer->readAfter(channels.in(), mLastRead, dest, len); | ||
495 | dest[len] = '\0'; | ||
496 | U8* newline = (U8*)strchr((char*)dest, '\n'); | ||
497 | if(!newline) | ||
498 | { | ||
499 | if(len) | ||
500 | { | ||
501 | lldebugs << "readLine failed - too long maybe?" << llendl; | ||
502 | markBad(channels, buffer); | ||
503 | } | ||
504 | return false; | ||
505 | } | ||
506 | S32 offset = -((len - 1) - (newline - dest)); | ||
507 | ++newline; | ||
508 | *newline = '\0'; | ||
509 | mLastRead = buffer->seek(channels.in(), last, offset); | ||
510 | return true; | ||
511 | } | ||
512 | |||
513 | void LLHTTPResponder::markBad( | ||
514 | const LLChannelDescriptors& channels, | ||
515 | buffer_ptr_t buffer) | ||
516 | { | ||
517 | LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER); | ||
518 | mState = STATE_SHORT_CIRCUIT; | ||
519 | LLBufferStream out(channels, buffer.get()); | ||
520 | out << HTTP_VERSION_STR << " 400 Bad Request\r\n\r\n<html>\n" | ||
521 | << "<title>Bad Request</title>\n<body>\nBad Request.\n" | ||
522 | << "</body>\n</html>\n"; | ||
523 | } | ||
524 | |||
525 | // virtual | ||
526 | LLIOPipe::EStatus LLHTTPResponder::process_impl( | ||
527 | const LLChannelDescriptors& channels, | ||
528 | buffer_ptr_t& buffer, | ||
529 | bool& eos, | ||
530 | LLSD& context, | ||
531 | LLPumpIO* pump) | ||
532 | { | ||
533 | PUMP_DEBUG; | ||
534 | LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER); | ||
535 | LLIOPipe::EStatus status = STATUS_OK; | ||
536 | |||
537 | // parsing headers | ||
538 | if((STATE_NOTHING == mState) || (STATE_READING_HEADERS == mState)) | ||
539 | { | ||
540 | PUMP_DEBUG; | ||
541 | status = STATUS_BREAK; | ||
542 | mState = STATE_READING_HEADERS; | ||
543 | const S32 HEADER_BUFFER_SIZE = 1024; | ||
544 | char buf[HEADER_BUFFER_SIZE + 1]; /*Flawfinder: ignore*/ | ||
545 | S32 len = HEADER_BUFFER_SIZE; | ||
546 | |||
547 | #if 0 | ||
548 | if(true) | ||
549 | { | ||
550 | LLBufferArray::segment_iterator_t seg_iter = buffer->beginSegment(); | ||
551 | char buf[1024]; /*Flawfinder: ignore*/ | ||
552 | while(seg_iter != buffer->endSegment()) | ||
553 | { | ||
554 | memcpy(buf, (*seg_iter).data(), (*seg_iter).size()); /*Flawfinder: ignore*/ | ||
555 | buf[(*seg_iter).size()] = '\0'; | ||
556 | llinfos << (*seg_iter).getChannel() << ": " << buf | ||
557 | << llendl; | ||
558 | ++seg_iter; | ||
559 | } | ||
560 | } | ||
561 | #endif | ||
562 | |||
563 | PUMP_DEBUG; | ||
564 | if(readLine(channels, buffer, (U8*)buf, len)) | ||
565 | { | ||
566 | bool read_next_line = false; | ||
567 | bool parse_all = true; | ||
568 | if(mVerb.empty()) | ||
569 | { | ||
570 | read_next_line = true; | ||
571 | LLMemoryStream header((U8*)buf, len); | ||
572 | header >> mVerb; | ||
573 | |||
574 | if((HTTP_VERB_GET == mVerb) | ||
575 | || (HTTP_VERB_POST == mVerb) | ||
576 | || (HTTP_VERB_PUT == mVerb) | ||
577 | || (HTTP_VERB_DELETE == mVerb)) | ||
578 | { | ||
579 | header >> mAbsPathAndQuery; | ||
580 | header >> mVersion; | ||
581 | |||
582 | lldebugs << "http request: " | ||
583 | << mVerb | ||
584 | << " " << mAbsPathAndQuery | ||
585 | << " " << mVersion << llendl; | ||
586 | |||
587 | std::string::size_type delimiter | ||
588 | = mAbsPathAndQuery.find('?'); | ||
589 | if (delimiter == std::string::npos) | ||
590 | { | ||
591 | mPath = mAbsPathAndQuery; | ||
592 | mQuery = ""; | ||
593 | } | ||
594 | else | ||
595 | { | ||
596 | mPath = mAbsPathAndQuery.substr(0, delimiter); | ||
597 | mQuery = mAbsPathAndQuery.substr(delimiter+1); | ||
598 | } | ||
599 | |||
600 | if(!mAbsPathAndQuery.empty()) | ||
601 | { | ||
602 | if(mVersion.empty()) | ||
603 | { | ||
604 | // simple request. | ||
605 | parse_all = false; | ||
606 | mState = STATE_DONE; | ||
607 | mVersion.assign("HTTP/1.0"); | ||
608 | } | ||
609 | } | ||
610 | } | ||
611 | else | ||
612 | { | ||
613 | read_next_line = false; | ||
614 | parse_all = false; | ||
615 | lldebugs << "unknown http verb: " << mVerb << llendl; | ||
616 | markBad(channels, buffer); | ||
617 | } | ||
618 | } | ||
619 | if(parse_all) | ||
620 | { | ||
621 | bool keep_parsing = true; | ||
622 | while(keep_parsing) | ||
623 | { | ||
624 | if(read_next_line) | ||
625 | { | ||
626 | len = HEADER_BUFFER_SIZE; | ||
627 | readLine(channels, buffer, (U8*)buf, len); | ||
628 | } | ||
629 | if(0 == len) | ||
630 | { | ||
631 | return status; | ||
632 | } | ||
633 | if(buf[0] == '\r' && buf[1] == '\n') | ||
634 | { | ||
635 | // end-o-headers | ||
636 | keep_parsing = false; | ||
637 | mState = STATE_LOOKING_FOR_EOS; | ||
638 | break; | ||
639 | } | ||
640 | char* pos_colon = strchr(buf, ':'); | ||
641 | if(NULL == pos_colon) | ||
642 | { | ||
643 | keep_parsing = false; | ||
644 | lldebugs << "bad header: " << buf << llendl; | ||
645 | markBad(channels, buffer); | ||
646 | break; | ||
647 | } | ||
648 | // we've found a header | ||
649 | read_next_line = true; | ||
650 | std::string name(buf, pos_colon - buf); | ||
651 | std::string value(pos_colon + 2); | ||
652 | LLString::toLower(name); | ||
653 | if("content-length" == name) | ||
654 | { | ||
655 | lldebugs << "Content-Length: " << value << llendl; | ||
656 | mContentLength = atoi(value.c_str()); | ||
657 | } | ||
658 | } | ||
659 | } | ||
660 | } | ||
661 | } | ||
662 | |||
663 | PUMP_DEBUG; | ||
664 | // look for the end of stream based on | ||
665 | if(STATE_LOOKING_FOR_EOS == mState) | ||
666 | { | ||
667 | if(0 == mContentLength) | ||
668 | { | ||
669 | mState = STATE_DONE; | ||
670 | } | ||
671 | else if(buffer->countAfter(channels.in(), mLastRead) >= mContentLength) | ||
672 | { | ||
673 | mState = STATE_DONE; | ||
674 | } | ||
675 | // else more bytes should be coming. | ||
676 | } | ||
677 | |||
678 | PUMP_DEBUG; | ||
679 | if(STATE_DONE == mState) | ||
680 | { | ||
681 | // hey, hey, we should have everything now, so we pass it to | ||
682 | // a content handler. | ||
683 | context[CONTEXT_REQUEST]["verb"] = mVerb; | ||
684 | const LLHTTPNode* node = mRootNode.traverse(mPath, context); | ||
685 | if(node) | ||
686 | { | ||
687 | llinfos << "LLHTTPResponder::process_impl found node for " | ||
688 | << mAbsPathAndQuery << llendl; | ||
689 | |||
690 | // Copy everything after mLast read to the out. | ||
691 | LLBufferArray::segment_iterator_t seg_iter; | ||
692 | seg_iter = buffer->splitAfter(mLastRead); | ||
693 | if(seg_iter != buffer->endSegment()) | ||
694 | { | ||
695 | LLChangeChannel change(channels.in(), channels.out()); | ||
696 | ++seg_iter; | ||
697 | std::for_each(seg_iter, buffer->endSegment(), change); | ||
698 | |||
699 | #if 0 | ||
700 | seg_iter = buffer->beginSegment(); | ||
701 | char buf[1024]; /*Flawfinder: ignore*/ | ||
702 | while(seg_iter != buffer->endSegment()) | ||
703 | { | ||
704 | memcpy(buf, (*seg_iter).data(), (*seg_iter).size()); /*Flawfinder: ignore*/ | ||
705 | buf[(*seg_iter).size()] = '\0'; | ||
706 | llinfos << (*seg_iter).getChannel() << ": " << buf | ||
707 | << llendl; | ||
708 | ++seg_iter; | ||
709 | } | ||
710 | #endif | ||
711 | } | ||
712 | |||
713 | // | ||
714 | // *FIX: get rid of extra bytes off the end | ||
715 | // | ||
716 | |||
717 | // Set up a chain which will prepend a content length and | ||
718 | // HTTP headers. | ||
719 | LLPumpIO::chain_t chain; | ||
720 | chain.push_back(LLIOPipe::ptr_t(new LLIOFlush)); | ||
721 | context[CONTEXT_REQUEST]["path"] = mPath; | ||
722 | context[CONTEXT_REQUEST]["query-string"] = mQuery; | ||
723 | |||
724 | const LLChainIOFactory* protocolHandler | ||
725 | = node->getProtocolHandler(); | ||
726 | if (protocolHandler) | ||
727 | { | ||
728 | protocolHandler->build(chain, context); | ||
729 | } | ||
730 | else | ||
731 | { | ||
732 | // this is a simple LLHTTPNode, so use LLHTTPPipe | ||
733 | chain.push_back(LLIOPipe::ptr_t(new LLHTTPPipe(*node))); | ||
734 | } | ||
735 | |||
736 | // Add the header - which needs to have the same | ||
737 | // channel information as the link before it since it | ||
738 | // is part of the response. | ||
739 | LLIOPipe* header = new LLHTTPResponseHeader; | ||
740 | chain.push_back(LLIOPipe::ptr_t(header)); | ||
741 | |||
742 | // We need to copy all of the pipes _after_ this so | ||
743 | // that the response goes out correctly. | ||
744 | LLPumpIO::links_t current_links; | ||
745 | pump->copyCurrentLinkInfo(current_links); | ||
746 | LLPumpIO::links_t::iterator link_iter = current_links.begin(); | ||
747 | LLPumpIO::links_t::iterator links_end = current_links.end(); | ||
748 | bool after_this = false; | ||
749 | for(; link_iter < links_end; ++link_iter) | ||
750 | { | ||
751 | if(after_this) | ||
752 | { | ||
753 | chain.push_back((*link_iter).mPipe); | ||
754 | } | ||
755 | else if(this == (*link_iter).mPipe.get()) | ||
756 | { | ||
757 | after_this = true; | ||
758 | } | ||
759 | } | ||
760 | |||
761 | // Do the final build of the chain, and send it on | ||
762 | // it's way. | ||
763 | LLChannelDescriptors chnl = channels; | ||
764 | LLPumpIO::LLLinkInfo link; | ||
765 | LLPumpIO::links_t links; | ||
766 | LLPumpIO::chain_t::iterator it = chain.begin(); | ||
767 | LLPumpIO::chain_t::iterator end = chain.end(); | ||
768 | while(it != end) | ||
769 | { | ||
770 | link.mPipe = *it; | ||
771 | link.mChannels = chnl; | ||
772 | links.push_back(link); | ||
773 | chnl = LLBufferArray::makeChannelConsumer(chnl); | ||
774 | ++it; | ||
775 | } | ||
776 | pump->addChain( | ||
777 | links, | ||
778 | buffer, | ||
779 | context, | ||
780 | DEFAULT_CHAIN_EXPIRY_SECS); | ||
781 | |||
782 | status = STATUS_STOP; | ||
783 | } | ||
784 | else | ||
785 | { | ||
786 | llinfos << "LLHTTPResponder::process_impl didn't find a node for " | ||
787 | << mAbsPathAndQuery << llendl; | ||
788 | LLBufferStream str(channels, buffer.get()); | ||
789 | mState = STATE_SHORT_CIRCUIT; | ||
790 | str << HTTP_VERSION_STR << " 404 Not Found\r\n\r\n<html>\n" | ||
791 | << "<title>Not Found</title>\n<body>\nNode '" << mAbsPathAndQuery | ||
792 | << "' not found.\n</body>\n</html>\n"; | ||
793 | } | ||
794 | } | ||
795 | |||
796 | if(STATE_SHORT_CIRCUIT == mState) | ||
797 | { | ||
798 | //status = mNext->process(buffer, true, pump, context); | ||
799 | status = STATUS_DONE; | ||
800 | } | ||
801 | PUMP_DEBUG; | ||
802 | return status; | ||
803 | } | ||
804 | |||
805 | |||
806 | |||
807 | void LLCreateHTTPPipe(LLPumpIO::chain_t& chain, const LLHTTPNode& root) | ||
808 | { | ||
809 | chain.push_back(LLIOPipe::ptr_t(new LLHTTPResponder(root))); | ||
810 | } | ||
811 | |||
812 | |||
813 | class LLHTTPResponseFactory : public LLChainIOFactory | ||
814 | { | ||
815 | public: | ||
816 | bool build(LLPumpIO::chain_t& chain, LLSD ctx) const | ||
817 | { | ||
818 | LLCreateHTTPPipe(chain, mTree); | ||
819 | return true; | ||
820 | } | ||
821 | |||
822 | LLHTTPNode& getRootNode() { return mTree; } | ||
823 | |||
824 | private: | ||
825 | LLHTTPNode mTree; | ||
826 | }; | ||
827 | |||
828 | |||
829 | LLHTTPNode& LLCreateHTTPServer( | ||
830 | apr_pool_t* pool, LLPumpIO& pump, U16 port) | ||
831 | { | ||
832 | LLSocket::ptr_t socket = LLSocket::create( | ||
833 | pool, | ||
834 | LLSocket::STREAM_TCP, | ||
835 | port); | ||
836 | if(!socket) | ||
837 | { | ||
838 | llerrs << "Unable to initialize socket" << llendl; | ||
839 | } | ||
840 | |||
841 | LLHTTPResponseFactory* factory = new LLHTTPResponseFactory; | ||
842 | boost::shared_ptr<LLChainIOFactory> factory_ptr(factory); | ||
843 | |||
844 | LLIOServerSocket* server = new LLIOServerSocket(pool, socket, factory_ptr); | ||
845 | |||
846 | LLPumpIO::chain_t chain; | ||
847 | chain.push_back(LLIOPipe::ptr_t(server)); | ||
848 | pump.addChain(chain, NEVER_CHAIN_EXPIRY_SECS); | ||
849 | |||
850 | return factory->getRootNode(); | ||
851 | } | ||
852 | |||