00001
00034 #include "linden_common.h"
00035
00036 #include "lliohttpserver.h"
00037
00038 #include "llapr.h"
00039 #include "llbuffer.h"
00040 #include "llbufferstream.h"
00041 #include "llhttpnode.h"
00042 #include "lliopipe.h"
00043 #include "lliosocket.h"
00044 #include "llioutil.h"
00045 #include "llmemtype.h"
00046 #include "llmemorystream.h"
00047 #include "llpumpio.h"
00048 #include "llsd.h"
00049 #include "llsdserialize_xml.h"
00050 #include "llstl.h"
00051 #include "lltimer.h"
00052
00053 #include <sstream>
00054
00055 #include <boost/tokenizer.hpp>
00056
00057 static const char HTTP_VERSION_STR[] = "HTTP/1.0";
00058 static const std::string CONTEXT_REQUEST("request");
00059 static const std::string CONTEXT_RESPONSE("response");
00060 static const std::string HTTP_VERB_GET("GET");
00061 static const std::string HTTP_VERB_PUT("PUT");
00062 static const std::string HTTP_VERB_POST("POST");
00063 static const std::string HTTP_VERB_DELETE("DELETE");
00064
00065 static LLIOHTTPServer::timing_callback_t sTimingCallback = NULL;
00066 static void* sTimingCallbackData = NULL;
00067
00068 class LLHTTPPipe : public LLIOPipe
00069 {
00070 public:
00071 LLHTTPPipe(const LLHTTPNode& node)
00072 : mNode(node), mResponse(NULL), mState(STATE_INVOKE), mChainLock(0), mStatusCode(0)
00073 { }
00074 virtual ~LLHTTPPipe()
00075 {
00076 if (mResponse.notNull())
00077 {
00078 mResponse->nullPipe();
00079 }
00080 }
00081
00082 private:
00083
00084 virtual EStatus process_impl(
00085 const LLChannelDescriptors& channels,
00086 LLIOPipe::buffer_ptr_t& buffer,
00087 bool& eos,
00088 LLSD& context,
00089 LLPumpIO* pump);
00090
00091 const LLHTTPNode& mNode;
00092
00093 class Response : public LLHTTPNode::Response
00094 {
00095 public:
00096
00097 static LLPointer<Response> create(LLHTTPPipe* pipe);
00098 virtual ~Response();
00099
00100
00101 virtual void result(const LLSD&);
00102 virtual void status(S32 code, const std::string& message);
00103
00104 void nullPipe();
00105
00106 private:
00107 Response() {;}
00108 LLHTTPPipe* mPipe;
00109 };
00110 friend class Response;
00111
00112 LLPointer<Response> mResponse;
00113
00114 enum State
00115 {
00116 STATE_INVOKE,
00117 STATE_DELAYED,
00118 STATE_LOCKED,
00119 STATE_GOOD_RESULT,
00120 STATE_STATUS_RESULT
00121 };
00122 State mState;
00123
00124 S32 mChainLock;
00125 LLPumpIO* mLockedPump;
00126
00127 void lockChain(LLPumpIO*);
00128 void unlockChain();
00129
00130 LLSD mGoodResult;
00131 S32 mStatusCode;
00132 std::string mStatusMessage;
00133 };
00134
00135 LLIOPipe::EStatus LLHTTPPipe::process_impl(
00136 const LLChannelDescriptors& channels,
00137 buffer_ptr_t& buffer,
00138 bool& eos,
00139 LLSD& context,
00140 LLPumpIO* pump)
00141 {
00142 PUMP_DEBUG;
00143 lldebugs << "LLSDHTTPServer::process_impl" << llendl;
00144
00145
00146
00147
00148 if(!eos) return STATUS_BREAK;
00149 if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
00150
00151 PUMP_DEBUG;
00152 if (mState == STATE_INVOKE)
00153 {
00154 PUMP_DEBUG;
00155 mState = STATE_DELAYED;
00156
00157 mResponse = Response::create(this);
00158
00159
00160
00161
00162 LLBufferStream istr(channels, buffer.get());
00163
00164 static LLTimer timer;
00165 timer.reset();
00166
00167 std::string verb = context[CONTEXT_REQUEST]["verb"];
00168 if(verb == HTTP_VERB_GET)
00169 {
00170 mNode.get(LLHTTPNode::ResponsePtr(mResponse), context);
00171 }
00172 else if(verb == HTTP_VERB_PUT)
00173 {
00174 LLSD input;
00175 LLSDSerialize::fromXML(input, istr);
00176 mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input);
00177 }
00178 else if(verb == HTTP_VERB_POST)
00179 {
00180 LLSD input;
00181 LLSDSerialize::fromXML(input, istr);
00182 mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input);
00183 }
00184 else if(verb == HTTP_VERB_DELETE)
00185 {
00186 mNode.del(LLHTTPNode::ResponsePtr(mResponse), context);
00187 }
00188 else
00189 {
00190 mResponse->methodNotAllowed();
00191 }
00192
00193 F32 delta = timer.getElapsedTimeF32();
00194 if (sTimingCallback)
00195 {
00196 LLHTTPNode::Description desc;
00197 mNode.describe(desc);
00198 LLSD info = desc.getInfo();
00199 std::string timing_name = info["description"];
00200 timing_name += " ";
00201 timing_name += verb;
00202 sTimingCallback(timing_name.c_str(), delta, sTimingCallbackData);
00203 }
00204
00205
00206
00207
00208 lldebugs << verb << " " << context[CONTEXT_REQUEST]["path"].asString()
00209 << " " << mStatusCode << " " << mStatusMessage << " " << delta
00210 << "s" << llendl;
00211
00212
00213
00214
00215
00216
00217
00218 }
00219
00220 PUMP_DEBUG;
00221 switch (mState)
00222 {
00223 case STATE_DELAYED:
00224 lockChain(pump);
00225 mState = STATE_LOCKED;
00226 return STATUS_BREAK;
00227
00228 case STATE_LOCKED:
00229
00230 return STATUS_ERROR;
00231
00232 case STATE_GOOD_RESULT:
00233 {
00234 context[CONTEXT_RESPONSE]["contentType"] = "application/xml";
00235 LLBufferStream ostr(channels, buffer.get());
00236 LLSDSerialize::toXML(mGoodResult, ostr);
00237
00238 return STATUS_DONE;
00239 }
00240
00241 case STATE_STATUS_RESULT:
00242 {
00243 context[CONTEXT_RESPONSE]["contentType"] = "text/plain";
00244 context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode;
00245 context[CONTEXT_RESPONSE]["statusMessage"] = mStatusMessage;
00246 LLBufferStream ostr(channels, buffer.get());
00247 ostr << mStatusMessage << std::ends;
00248
00249 return STATUS_DONE;
00250 }
00251 default:
00252 llwarns << "LLHTTPPipe::process_impl: unexpected state "
00253 << mState << llendl;
00254
00255 return STATUS_BREAK;
00256 }
00257
00258 }
00259
00260 LLPointer<LLHTTPPipe::Response> LLHTTPPipe::Response::create(LLHTTPPipe* pipe)
00261 {
00262 LLPointer<Response> result = new Response();
00263 result->mPipe = pipe;
00264 return result;
00265 }
00266
00267
00268 LLHTTPPipe::Response::~Response()
00269 {
00270 }
00271
00272 void LLHTTPPipe::Response::nullPipe()
00273 {
00274 mPipe = NULL;
00275 }
00276
00277
00278 void LLHTTPPipe::Response::result(const LLSD& r)
00279 {
00280 if(! mPipe)
00281 {
00282 llwarns << "LLHTTPPipe::Response::result: NULL pipe" << llendl;
00283 return;
00284 }
00285
00286 mPipe->mStatusCode = 200;
00287 mPipe->mStatusMessage = "OK";
00288 mPipe->mGoodResult = r;
00289 mPipe->mState = STATE_GOOD_RESULT;
00290 mPipe->unlockChain();
00291 }
00292
00293
00294 void LLHTTPPipe::Response::status(S32 code, const std::string& message)
00295 {
00296 if(! mPipe)
00297 {
00298 llwarns << "LLHTTPPipe::Response::status: NULL pipe" << llendl;
00299 return;
00300 }
00301
00302 mPipe->mStatusCode = code;
00303 mPipe->mStatusMessage = message;
00304 mPipe->mState = STATE_STATUS_RESULT;
00305 mPipe->unlockChain();
00306 }
00307
00308 void LLHTTPPipe::lockChain(LLPumpIO* pump)
00309 {
00310 if (mChainLock != 0) { return; }
00311
00312 mLockedPump = pump;
00313 mChainLock = pump->setLock();
00314 }
00315
00316 void LLHTTPPipe::unlockChain()
00317 {
00318 if (mChainLock == 0) { return; }
00319
00320 mLockedPump->clearLock(mChainLock);
00321 mLockedPump = NULL;
00322 mChainLock = 0;
00323 }
00324
00325
00326
00338 class LLHTTPResponseHeader : public LLIOPipe
00339 {
00340 public:
00341 LLHTTPResponseHeader() {}
00342 virtual ~LLHTTPResponseHeader() {}
00343
00344 protected:
00345
00346
00348
00351 EStatus process_impl(
00352 const LLChannelDescriptors& channels,
00353 buffer_ptr_t& buffer,
00354 bool& eos,
00355 LLSD& context,
00356 LLPumpIO* pump);
00358
00359 protected:
00360 S32 mCode;
00361 };
00362
00363
00367
00368 LLIOPipe::EStatus LLHTTPResponseHeader::process_impl(
00369 const LLChannelDescriptors& channels,
00370 buffer_ptr_t& buffer,
00371 bool& eos,
00372 LLSD& context,
00373 LLPumpIO* pump)
00374 {
00375 PUMP_DEBUG;
00376 LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
00377 if(eos)
00378 {
00379 PUMP_DEBUG;
00380
00381 std::ostringstream ostr;
00382 std::string message = context[CONTEXT_RESPONSE]["statusMessage"];
00383
00384 int code = context[CONTEXT_RESPONSE]["statusCode"];
00385 if (code < 200)
00386 {
00387 code = 200;
00388 message = "OK";
00389 }
00390
00391 ostr << HTTP_VERSION_STR << " " << code << " " << message << "\r\n";
00392
00393 std::string type = context[CONTEXT_RESPONSE]["contentType"].asString();
00394 if (!type.empty())
00395 {
00396 ostr << "Content-Type: " << type << "\r\n";
00397 }
00398 S32 content_length = buffer->countAfter(channels.in(), NULL);
00399 if(0 < content_length)
00400 {
00401 ostr << "Content-Length: " << content_length << "\r\n";
00402 }
00403 ostr << "\r\n";
00404
00405 LLChangeChannel change(channels.in(), channels.out());
00406 std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
00407 std::string header = ostr.str();
00408 buffer->prepend(channels.out(), (U8*)header.c_str(), header.size());
00409 PUMP_DEBUG;
00410 return STATUS_DONE;
00411 }
00412 PUMP_DEBUG;
00413 return STATUS_OK;
00414 }
00415
00416
00417
00426 class LLHTTPResponder : public LLIOPipe
00427 {
00428 public:
00429 LLHTTPResponder(const LLHTTPNode& tree, const LLSD& ctx);
00430 ~LLHTTPResponder();
00431
00432 protected:
00451 bool readLine(
00452 const LLChannelDescriptors& channels,
00453 buffer_ptr_t buffer,
00454 U8* dest,
00455 S32& len);
00456
00463 void markBad(const LLChannelDescriptors& channels, buffer_ptr_t buffer);
00464
00465 protected:
00466
00467
00469
00472 EStatus process_impl(
00473 const LLChannelDescriptors& channels,
00474 buffer_ptr_t& buffer,
00475 bool& eos,
00476 LLSD& context,
00477 LLPumpIO* pump);
00479
00480 protected:
00481 enum EState
00482 {
00483 STATE_NOTHING,
00484 STATE_READING_HEADERS,
00485 STATE_LOOKING_FOR_EOS,
00486 STATE_DONE,
00487 STATE_SHORT_CIRCUIT
00488 };
00489
00490 LLSD mBuildContext;
00491 EState mState;
00492 U8* mLastRead;
00493 std::string mVerb;
00494 std::string mAbsPathAndQuery;
00495 std::string mPath;
00496 std::string mQuery;
00497 std::string mVersion;
00498 S32 mContentLength;
00499 LLSD mHeaders;
00500
00501
00502 const LLHTTPNode& mRootNode;
00503 };
00504
00505 LLHTTPResponder::LLHTTPResponder(const LLHTTPNode& tree, const LLSD& ctx) :
00506 mBuildContext(ctx),
00507 mState(STATE_NOTHING),
00508 mLastRead(NULL),
00509 mContentLength(0),
00510 mRootNode(tree)
00511 {
00512 LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
00513 }
00514
00515
00516 LLHTTPResponder::~LLHTTPResponder()
00517 {
00518 LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
00519
00520 }
00521
00522 bool LLHTTPResponder::readLine(
00523 const LLChannelDescriptors& channels,
00524 buffer_ptr_t buffer,
00525 U8* dest,
00526 S32& len)
00527 {
00528 LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
00529 --len;
00530 U8* last = buffer->readAfter(channels.in(), mLastRead, dest, len);
00531 dest[len] = '\0';
00532 U8* newline = (U8*)strchr((char*)dest, '\n');
00533 if(!newline)
00534 {
00535 if(len)
00536 {
00537 lldebugs << "readLine failed - too long maybe?" << llendl;
00538 markBad(channels, buffer);
00539 }
00540 return false;
00541 }
00542 S32 offset = -((len - 1) - (newline - dest));
00543 ++newline;
00544 *newline = '\0';
00545 mLastRead = buffer->seek(channels.in(), last, offset);
00546 return true;
00547 }
00548
00549 void LLHTTPResponder::markBad(
00550 const LLChannelDescriptors& channels,
00551 buffer_ptr_t buffer)
00552 {
00553 LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
00554 mState = STATE_SHORT_CIRCUIT;
00555 LLBufferStream out(channels, buffer.get());
00556 out << HTTP_VERSION_STR << " 400 Bad Request\r\n\r\n<html>\n"
00557 << "<title>Bad Request</title>\n<body>\nBad Request.\n"
00558 << "</body>\n</html>\n";
00559 }
00560
00561
00562 LLIOPipe::EStatus LLHTTPResponder::process_impl(
00563 const LLChannelDescriptors& channels,
00564 buffer_ptr_t& buffer,
00565 bool& eos,
00566 LLSD& context,
00567 LLPumpIO* pump)
00568 {
00569 PUMP_DEBUG;
00570 LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
00571 LLIOPipe::EStatus status = STATUS_OK;
00572
00573
00574 if((STATE_NOTHING == mState) || (STATE_READING_HEADERS == mState))
00575 {
00576 PUMP_DEBUG;
00577 status = STATUS_BREAK;
00578 mState = STATE_READING_HEADERS;
00579 const S32 HEADER_BUFFER_SIZE = 1024;
00580 char buf[HEADER_BUFFER_SIZE + 1];
00581 S32 len = HEADER_BUFFER_SIZE;
00582
00583 #if 0
00584 if(true)
00585 {
00586 LLBufferArray::segment_iterator_t seg_iter = buffer->beginSegment();
00587 char buf[1024];
00588 while(seg_iter != buffer->endSegment())
00589 {
00590 memcpy(buf, (*seg_iter).data(), (*seg_iter).size());
00591 buf[(*seg_iter).size()] = '\0';
00592 llinfos << (*seg_iter).getChannel() << ": " << buf
00593 << llendl;
00594 ++seg_iter;
00595 }
00596 }
00597 #endif
00598
00599 PUMP_DEBUG;
00600 if(readLine(channels, buffer, (U8*)buf, len))
00601 {
00602 bool read_next_line = false;
00603 bool parse_all = true;
00604 if(mVerb.empty())
00605 {
00606 read_next_line = true;
00607 LLMemoryStream header((U8*)buf, len);
00608 header >> mVerb;
00609
00610 if((HTTP_VERB_GET == mVerb)
00611 || (HTTP_VERB_POST == mVerb)
00612 || (HTTP_VERB_PUT == mVerb)
00613 || (HTTP_VERB_DELETE == mVerb))
00614 {
00615 header >> mAbsPathAndQuery;
00616 header >> mVersion;
00617
00618 lldebugs << "http request: "
00619 << mVerb
00620 << " " << mAbsPathAndQuery
00621 << " " << mVersion << llendl;
00622
00623 std::string::size_type delimiter
00624 = mAbsPathAndQuery.find('?');
00625 if (delimiter == std::string::npos)
00626 {
00627 mPath = mAbsPathAndQuery;
00628 mQuery = "";
00629 }
00630 else
00631 {
00632 mPath = mAbsPathAndQuery.substr(0, delimiter);
00633 mQuery = mAbsPathAndQuery.substr(delimiter+1);
00634 }
00635
00636 if(!mAbsPathAndQuery.empty())
00637 {
00638 if(mVersion.empty())
00639 {
00640
00641 parse_all = false;
00642 mState = STATE_DONE;
00643 mVersion.assign("HTTP/1.0");
00644 }
00645 }
00646 }
00647 else
00648 {
00649 read_next_line = false;
00650 parse_all = false;
00651 lldebugs << "unknown http verb: " << mVerb << llendl;
00652 markBad(channels, buffer);
00653 }
00654 }
00655 if(parse_all)
00656 {
00657 bool keep_parsing = true;
00658 while(keep_parsing)
00659 {
00660 if(read_next_line)
00661 {
00662 len = HEADER_BUFFER_SIZE;
00663 readLine(channels, buffer, (U8*)buf, len);
00664 }
00665 if(0 == len)
00666 {
00667 return status;
00668 }
00669 if(buf[0] == '\r' && buf[1] == '\n')
00670 {
00671
00672 keep_parsing = false;
00673 mState = STATE_LOOKING_FOR_EOS;
00674 break;
00675 }
00676 char* pos_colon = strchr(buf, ':');
00677 if(NULL == pos_colon)
00678 {
00679 keep_parsing = false;
00680 lldebugs << "bad header: " << buf << llendl;
00681 markBad(channels, buffer);
00682 break;
00683 }
00684
00685 read_next_line = true;
00686 std::string name(buf, pos_colon - buf);
00687 std::string value(pos_colon + 2);
00688 LLString::toLower(name);
00689 if("content-length" == name)
00690 {
00691 lldebugs << "Content-Length: " << value << llendl;
00692 mContentLength = atoi(value.c_str());
00693 }
00694 else
00695 {
00696 LLString::trimTail(value);
00697 mHeaders[name] = value;
00698 }
00699 }
00700 }
00701 }
00702 }
00703
00704 PUMP_DEBUG;
00705
00706 if(STATE_LOOKING_FOR_EOS == mState)
00707 {
00708 if(0 == mContentLength)
00709 {
00710 mState = STATE_DONE;
00711 }
00712 else if(buffer->countAfter(channels.in(), mLastRead) >= mContentLength)
00713 {
00714 mState = STATE_DONE;
00715 }
00716
00717 }
00718
00719 PUMP_DEBUG;
00720 if(STATE_DONE == mState)
00721 {
00722
00723
00724 context[CONTEXT_REQUEST]["verb"] = mVerb;
00725 const LLHTTPNode* node = mRootNode.traverse(mPath, context);
00726 if(node)
00727 {
00728
00729
00730
00731
00732 LLBufferArray::segment_iterator_t seg_iter;
00733 seg_iter = buffer->splitAfter(mLastRead);
00734 if(seg_iter != buffer->endSegment())
00735 {
00736 LLChangeChannel change(channels.in(), channels.out());
00737 ++seg_iter;
00738 std::for_each(seg_iter, buffer->endSegment(), change);
00739
00740 #if 0
00741 seg_iter = buffer->beginSegment();
00742 char buf[1024];
00743 while(seg_iter != buffer->endSegment())
00744 {
00745 memcpy(buf, (*seg_iter).data(), (*seg_iter).size());
00746 buf[(*seg_iter).size()] = '\0';
00747 llinfos << (*seg_iter).getChannel() << ": " << buf
00748 << llendl;
00749 ++seg_iter;
00750 }
00751 #endif
00752 }
00753
00754
00755
00756
00757
00758
00759
00760 LLPumpIO::chain_t chain;
00761 chain.push_back(LLIOPipe::ptr_t(new LLIOFlush));
00762 context[CONTEXT_REQUEST]["path"] = mPath;
00763 context[CONTEXT_REQUEST]["query-string"] = mQuery;
00764 context[CONTEXT_REQUEST]["remote-host"]
00765 = mBuildContext["remote-host"];
00766 context[CONTEXT_REQUEST]["remote-port"]
00767 = mBuildContext["remote-port"];
00768 context[CONTEXT_REQUEST]["headers"] = mHeaders;
00769
00770 const LLChainIOFactory* protocolHandler
00771 = node->getProtocolHandler();
00772 if (protocolHandler)
00773 {
00774 protocolHandler->build(chain, context);
00775 }
00776 else
00777 {
00778
00779 chain.push_back(LLIOPipe::ptr_t(new LLHTTPPipe(*node)));
00780 }
00781
00782
00783
00784
00785 LLIOPipe* header = new LLHTTPResponseHeader;
00786 chain.push_back(LLIOPipe::ptr_t(header));
00787
00788
00789
00790 LLPumpIO::links_t current_links;
00791 pump->copyCurrentLinkInfo(current_links);
00792 LLPumpIO::links_t::iterator link_iter = current_links.begin();
00793 LLPumpIO::links_t::iterator links_end = current_links.end();
00794 bool after_this = false;
00795 for(; link_iter < links_end; ++link_iter)
00796 {
00797 if(after_this)
00798 {
00799 chain.push_back((*link_iter).mPipe);
00800 }
00801 else if(this == (*link_iter).mPipe.get())
00802 {
00803 after_this = true;
00804 }
00805 }
00806
00807
00808
00809 LLChannelDescriptors chnl = channels;
00810 LLPumpIO::LLLinkInfo link;
00811 LLPumpIO::links_t links;
00812 LLPumpIO::chain_t::iterator it = chain.begin();
00813 LLPumpIO::chain_t::iterator end = chain.end();
00814 while(it != end)
00815 {
00816 link.mPipe = *it;
00817 link.mChannels = chnl;
00818 links.push_back(link);
00819 chnl = LLBufferArray::makeChannelConsumer(chnl);
00820 ++it;
00821 }
00822 pump->addChain(
00823 links,
00824 buffer,
00825 context,
00826 DEFAULT_CHAIN_EXPIRY_SECS);
00827
00828 status = STATUS_STOP;
00829 }
00830 else
00831 {
00832 llwarns << "LLHTTPResponder::process_impl didn't find a node for "
00833 << mAbsPathAndQuery << llendl;
00834 LLBufferStream str(channels, buffer.get());
00835 mState = STATE_SHORT_CIRCUIT;
00836 str << HTTP_VERSION_STR << " 404 Not Found\r\n\r\n<html>\n"
00837 << "<title>Not Found</title>\n<body>\nNode '" << mAbsPathAndQuery
00838 << "' not found.\n</body>\n</html>\n";
00839 }
00840 }
00841
00842 if(STATE_SHORT_CIRCUIT == mState)
00843 {
00844
00845 status = STATUS_DONE;
00846 }
00847 PUMP_DEBUG;
00848 return status;
00849 }
00850
00851
00852
00853 void LLIOHTTPServer::createPipe(LLPumpIO::chain_t& chain,
00854 const LLHTTPNode& root, const LLSD& ctx)
00855 {
00856 chain.push_back(LLIOPipe::ptr_t(new LLHTTPResponder(root, ctx)));
00857 }
00858
00859
00860 class LLHTTPResponseFactory : public LLChainIOFactory
00861 {
00862 public:
00863 bool build(LLPumpIO::chain_t& chain, LLSD ctx) const
00864 {
00865 LLIOHTTPServer::createPipe(chain, mTree, ctx);
00866 return true;
00867 }
00868
00869 LLHTTPNode& getRootNode() { return mTree; }
00870
00871 private:
00872 LLHTTPNode mTree;
00873 };
00874
00875
00876
00877 LLHTTPNode& LLIOHTTPServer::create(
00878 apr_pool_t* pool, LLPumpIO& pump, U16 port)
00879 {
00880 LLSocket::ptr_t socket = LLSocket::create(
00881 pool,
00882 LLSocket::STREAM_TCP,
00883 port);
00884 if(!socket)
00885 {
00886 llerrs << "Unable to initialize socket" << llendl;
00887 }
00888
00889 LLHTTPResponseFactory* factory = new LLHTTPResponseFactory;
00890 boost::shared_ptr<LLChainIOFactory> factory_ptr(factory);
00891
00892 LLIOServerSocket* server = new LLIOServerSocket(pool, socket, factory_ptr);
00893
00894 LLPumpIO::chain_t chain;
00895 chain.push_back(LLIOPipe::ptr_t(server));
00896 pump.addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
00897
00898 return factory->getRootNode();
00899 }
00900
00901
00902 void LLIOHTTPServer::setTimingCallback(timing_callback_t callback,
00903 void* data)
00904 {
00905 sTimingCallback = callback;
00906 sTimingCallbackData = data;
00907 }