summaryrefslogtreecommitdiff
path: root/indra/llmessage/lliohttpserver.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/llmessage/lliohttpserver.cpp
Print done when done.
Diffstat (limited to 'indra/llmessage/lliohttpserver.cpp')
-rw-r--r--indra/llmessage/lliohttpserver.cpp833
1 files changed, 833 insertions, 0 deletions
diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp
new file mode 100644
index 0000000000..c1a9bc442e
--- /dev/null
+++ b/indra/llmessage/lliohttpserver.cpp
@@ -0,0 +1,833 @@
+/**
+ * @file lliohttpserver.cpp
+ * @author Phoenix
+ * @date 2005-10-05
+ * @brief Implementation of the http server classes
+ *
+ * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#include "linden_common.h"
+#include "lliohttpserver.h"
+
+#include "boost/tokenizer.hpp"
+
+#include "llapr.h"
+#include "llbuffer.h"
+#include "llbufferstream.h"
+#include "llhttpnode.h"
+#include "lliopipe.h"
+#include "lliosocket.h"
+#include "llioutil.h"
+#include "llmemtype.h"
+#include "llmemorystream.h"
+#include "llpumpio.h"
+#include "llsd.h"
+#include "llsdserialize_xml.h"
+#include "llstl.h"
+
+static const char HTTP_VERSION_STR[] = "HTTP/1.0";
+static const std::string CONTEXT_REQUEST("request");
+static const std::string HTTP_VERB_GET("GET");
+static const std::string HTTP_VERB_PUT("PUT");
+static const std::string HTTP_VERB_POST("POST");
+static const std::string HTTP_VERB_DELETE("DELETE");
+
+
+class LLHTTPPipe : public LLIOPipe
+{
+public:
+ LLHTTPPipe(const LLHTTPNode& node)
+ : mNode(node), mResponse(NULL), mState(STATE_INVOKE), mChainLock(0), mStatusCode(0)
+ { }
+ virtual ~LLHTTPPipe()
+ {
+ if (mResponse.notNull())
+ {
+ mResponse->nullPipe();
+ }
+ }
+
+private:
+ // LLIOPipe API implementation.
+ virtual EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ LLIOPipe::buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+
+ const LLHTTPNode& mNode;
+
+ class Response : public LLHTTPNode::Response
+ {
+ public:
+
+ static LLPointer<Response> create(LLHTTPPipe* pipe);
+ virtual ~Response();
+
+ // from LLHTTPNode::Response
+ virtual void result(const LLSD&);
+ virtual void status(S32 code, const std::string& message);
+
+ void nullPipe();
+
+ private:
+ Response() {;} // Must be accessed through LLPointer.
+ LLHTTPPipe* mPipe;
+ };
+ friend class Response;
+
+ LLPointer<Response> mResponse;
+
+ enum State
+ {
+ STATE_INVOKE,
+ STATE_DELAYED,
+ STATE_LOCKED,
+ STATE_GOOD_RESULT,
+ STATE_STATUS_RESULT
+ };
+ State mState;
+
+ S32 mChainLock;
+ LLPumpIO* mLockedPump;
+
+ void lockChain(LLPumpIO*);
+ void unlockChain();
+
+ LLSD mGoodResult;
+ S32 mStatusCode;
+ std::string mStatusMessage;
+};
+
+LLIOPipe::EStatus LLHTTPPipe::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ lldebugs << "LLSDHTTPServer::process_impl" << llendl;
+
+ // Once we have all the data, We need to read the sd on
+ // the the in channel, and respond on the out channel
+
+ if(!eos) return STATUS_BREAK;
+ if(!pump || !buffer) return STATUS_PRECONDITION_NOT_MET;
+
+ PUMP_DEBUG;
+ if (mState == STATE_INVOKE)
+ {
+ PUMP_DEBUG;
+ mState = STATE_DELAYED;
+ // assume deferred unless mResponse does otherwise
+ mResponse = Response::create(this);
+
+ // TODO: Babbage: Parameterize parser?
+ LLBufferStream istr(channels, buffer.get());
+
+ std::string verb = context[CONTEXT_REQUEST]["verb"];
+ if(verb == HTTP_VERB_GET)
+ {
+ mNode.get(LLHTTPNode::ResponsePtr(mResponse), context);
+ }
+ else if(verb == HTTP_VERB_PUT)
+ {
+ LLSD input;
+ LLSDSerialize::fromXML(input, istr);
+
+ mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input);
+ }
+ else if(verb == HTTP_VERB_POST)
+ {
+ LLSD input;
+ LLSDSerialize::fromXML(input, istr);
+
+ mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input);
+ }
+ else if(verb == HTTP_VERB_DELETE)
+ {
+ mNode.del(LLHTTPNode::ResponsePtr(mResponse), context);
+ }
+ else
+ {
+ mResponse->methodNotAllowed();
+ }
+
+ // Log Internal Server Errors
+ if(mStatusCode == 500)
+ {
+ llwarns << "LLHTTPPipe::process_impl:500:Internal Server Error"
+ << llendl;
+ }
+ }
+
+ PUMP_DEBUG;
+ switch (mState)
+ {
+ case STATE_DELAYED:
+ lockChain(pump);
+ mState = STATE_LOCKED;
+ return STATUS_BREAK;
+
+ case STATE_LOCKED:
+ // should never ever happen!
+ return STATUS_ERROR;
+
+ case STATE_GOOD_RESULT:
+ {
+ context["response"]["contentType"] = "application/xml";
+ LLBufferStream ostr(channels, buffer.get());
+ LLSDSerialize::toXML(mGoodResult, ostr);
+
+ return STATUS_DONE;
+ }
+
+ case STATE_STATUS_RESULT:
+ {
+ context["response"]["contentType"] = "text/plain";
+ context["response"]["statusCode"] = mStatusCode;
+ context["response"]["statusMessage"] = mStatusMessage;
+ LLBufferStream ostr(channels, buffer.get());
+ ostr << mStatusMessage << std::ends;
+
+ return STATUS_DONE;
+ }
+ default:
+ llwarns << "LLHTTPPipe::process_impl: unexpected state "
+ << mState << llendl;
+
+ return STATUS_BREAK;
+ }
+// PUMP_DEBUG; // unreachable
+}
+
+LLPointer<LLHTTPPipe::Response> LLHTTPPipe::Response::create(LLHTTPPipe* pipe)
+{
+ LLPointer<Response> result = new Response();
+ result->mPipe = pipe;
+ return result;
+}
+
+// virtual
+LLHTTPPipe::Response::~Response()
+{
+}
+
+void LLHTTPPipe::Response::nullPipe()
+{
+ mPipe = NULL;
+}
+
+// virtual
+void LLHTTPPipe::Response::result(const LLSD& r)
+{
+ if(! mPipe)
+ {
+ llwarns << "LLHTTPPipe::Response::result: NULL pipe" << llendl;
+ return;
+ }
+
+ mPipe->mStatusCode = 200;
+ mPipe->mStatusMessage = "OK";
+ mPipe->mGoodResult = r;
+ mPipe->mState = STATE_GOOD_RESULT;
+ mPipe->unlockChain();
+}
+
+// virtual
+void LLHTTPPipe::Response::status(S32 code, const std::string& message)
+{
+ if(! mPipe)
+ {
+ llwarns << "LLHTTPPipe::Response::status: NULL pipe" << llendl;
+ return;
+ }
+
+ mPipe->mStatusCode = code;
+ mPipe->mStatusMessage = message;
+ mPipe->mState = STATE_STATUS_RESULT;
+ mPipe->unlockChain();
+}
+
+void LLHTTPPipe::lockChain(LLPumpIO* pump)
+{
+ if (mChainLock != 0) { return; }
+
+ mLockedPump = pump;
+ mChainLock = pump->setLock();
+}
+
+void LLHTTPPipe::unlockChain()
+{
+ if (mChainLock == 0) { return; }
+
+ mLockedPump->clearLock(mChainLock);
+ mLockedPump = NULL;
+ mChainLock = 0;
+}
+
+
+
+/**
+ * @class LLHTTPResponseHeader
+ * @brief Class which correctly builds HTTP headers on a pipe
+ * @see LLIOPipe
+ *
+ * An instance of this class can be placed in a chain where it will
+ * wait for an end of stream. Once it gets that, it will count the
+ * bytes on CHANNEL_OUT (or the size of the buffer in io pipe versions
+ * prior to 2) prepend that data to the request in an HTTP format, and
+ * supply all normal HTTP response headers.
+ */
+class LLHTTPResponseHeader : public LLIOPipe
+{
+public:
+ LLHTTPResponseHeader() {}
+ virtual ~LLHTTPResponseHeader() {}
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ S32 mCode;
+};
+
+
+/**
+ * LLHTTPResponseHeader
+ */
+// virtual
+LLIOPipe::EStatus LLHTTPResponseHeader::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ if(eos)
+ {
+ PUMP_DEBUG;
+ //mGotEOS = true;
+ std::ostringstream ostr;
+ std::string message = context["response"]["statusMessage"];
+
+ int code = context["response"]["statusCode"];
+ if (code < 200)
+ {
+ code = 200;
+ message = "OK";
+ }
+
+ ostr << HTTP_VERSION_STR << " " << code << " " << message << "\r\n";
+
+ std::string type = context["response"]["contentType"].asString();
+ if (!type.empty())
+ {
+ ostr << "Content-Type: " << type << "\r\n";
+ }
+ S32 content_length = buffer->countAfter(channels.in(), NULL);
+ if(0 < content_length)
+ {
+ ostr << "Content-Length: " << content_length << "\r\n";
+ }
+ ostr << "\r\n";
+
+ LLChangeChannel change(channels.in(), channels.out());
+ std::for_each(buffer->beginSegment(), buffer->endSegment(), change);
+ std::string header = ostr.str();
+ buffer->prepend(channels.out(), (U8*)header.c_str(), header.size());
+ PUMP_DEBUG;
+ return STATUS_DONE;
+ }
+ PUMP_DEBUG;
+ return STATUS_OK;
+}
+
+
+
+/**
+ * @class LLHTTPResponder
+ * @brief This class
+ * @see LLIOPipe
+ *
+ * <b>NOTE:</b> You should not need to create or use one of these, the
+ * details are handled by the HTTPResponseFactory.
+ */
+class LLHTTPResponder : public LLIOPipe
+{
+public:
+ LLHTTPResponder(const LLHTTPNode& tree);
+ ~LLHTTPResponder();
+
+protected:
+ /**
+ * @brief Read data off of CHANNEL_IN keeping track of last read position.
+ *
+ * This is a quick little hack to read headers. It is not IO
+ * optimal, but it makes it easier for me to implement the header
+ * parsing. Plus, there should never be more than a few headers.
+ * This method will tend to read more than necessary, find the
+ * newline, make the front part of dest look like a c string, and
+ * move the read head back to where the newline was found. Thus,
+ * the next read will pick up on the next line.
+ * @param channel The channel to read in the buffer
+ * @param buffer The heap array of processed data
+ * @param dest Destination for the data to be read
+ * @param[in,out] len <b>in</b> The size of the buffer. <b>out</b> how
+ * much was read. This value is not useful for determining where to
+ * seek orfor string assignment.
+ * @returns Returns true if a line was found.
+ */
+ bool readLine(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t buffer,
+ U8* dest,
+ S32& len);
+
+ /**
+ * @brief Mark the request as bad, and handle appropriately
+ *
+ * @param channels The channels to use in the buffer.
+ * @param buffer The heap array of processed data.
+ */
+ void markBad(const LLChannelDescriptors& channels, buffer_ptr_t buffer);
+
+protected:
+ /* @name LLIOPipe virtual implementations
+ */
+ //@{
+ /**
+ * @brief Process the data in buffer
+ */
+ EStatus process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump);
+ //@}
+
+protected:
+ enum EState
+ {
+ STATE_NOTHING,
+ STATE_READING_HEADERS,
+ STATE_LOOKING_FOR_EOS,
+ STATE_DONE,
+ STATE_SHORT_CIRCUIT
+ };
+
+ EState mState;
+ U8* mLastRead;
+ std::string mVerb;
+ std::string mAbsPathAndQuery;
+ std::string mPath;
+ std::string mQuery;
+ std::string mVersion;
+ S32 mContentLength;
+
+ // handle the urls
+ const LLHTTPNode& mRootNode;
+};
+
+LLHTTPResponder::LLHTTPResponder(const LLHTTPNode& tree) :
+ mState(STATE_NOTHING),
+ mLastRead(NULL),
+ mContentLength(0),
+ mRootNode(tree)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+}
+
+// virtual
+LLHTTPResponder::~LLHTTPResponder()
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ //lldebugs << "destroying LLHTTPResponder" << llendl;
+}
+
+bool LLHTTPResponder::readLine(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t buffer,
+ U8* dest,
+ S32& len)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ --len;
+ U8* last = buffer->readAfter(channels.in(), mLastRead, dest, len);
+ dest[len] = '\0';
+ U8* newline = (U8*)strchr((char*)dest, '\n');
+ if(!newline)
+ {
+ if(len)
+ {
+ lldebugs << "readLine failed - too long maybe?" << llendl;
+ markBad(channels, buffer);
+ }
+ return false;
+ }
+ S32 offset = -((len - 1) - (newline - dest));
+ ++newline;
+ *newline = '\0';
+ mLastRead = buffer->seek(channels.in(), last, offset);
+ return true;
+}
+
+void LLHTTPResponder::markBad(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t buffer)
+{
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ mState = STATE_SHORT_CIRCUIT;
+ LLBufferStream out(channels, buffer.get());
+ out << HTTP_VERSION_STR << " 400 Bad Request\r\n\r\n<html>\n"
+ << "<title>Bad Request</title>\n<body>\nBad Request.\n"
+ << "</body>\n</html>\n";
+}
+
+// virtual
+LLIOPipe::EStatus LLHTTPResponder::process_impl(
+ const LLChannelDescriptors& channels,
+ buffer_ptr_t& buffer,
+ bool& eos,
+ LLSD& context,
+ LLPumpIO* pump)
+{
+ PUMP_DEBUG;
+ LLMemType m1(LLMemType::MTYPE_IO_HTTP_SERVER);
+ LLIOPipe::EStatus status = STATUS_OK;
+
+ // parsing headers
+ if((STATE_NOTHING == mState) || (STATE_READING_HEADERS == mState))
+ {
+ PUMP_DEBUG;
+ status = STATUS_BREAK;
+ mState = STATE_READING_HEADERS;
+ const S32 HEADER_BUFFER_SIZE = 1024;
+ char buf[HEADER_BUFFER_SIZE + 1]; /*Flawfinder: ignore*/
+ S32 len = HEADER_BUFFER_SIZE;
+
+#if 0
+ if(true)
+ {
+ LLBufferArray::segment_iterator_t seg_iter = buffer->beginSegment();
+ char buf[1024]; /*Flawfinder: ignore*/
+ while(seg_iter != buffer->endSegment())
+ {
+ memcpy(buf, (*seg_iter).data(), (*seg_iter).size()); /*Flawfinder: ignore*/
+ buf[(*seg_iter).size()] = '\0';
+ llinfos << (*seg_iter).getChannel() << ": " << buf
+ << llendl;
+ ++seg_iter;
+ }
+ }
+#endif
+
+ PUMP_DEBUG;
+ if(readLine(channels, buffer, (U8*)buf, len))
+ {
+ bool read_next_line = false;
+ bool parse_all = true;
+ if(mVerb.empty())
+ {
+ read_next_line = true;
+ LLMemoryStream header((U8*)buf, len);
+ header >> mVerb;
+
+ if((HTTP_VERB_GET == mVerb)
+ || (HTTP_VERB_POST == mVerb)
+ || (HTTP_VERB_PUT == mVerb)
+ || (HTTP_VERB_DELETE == mVerb))
+ {
+ header >> mAbsPathAndQuery;
+ header >> mVersion;
+
+ lldebugs << "http request: "
+ << mVerb
+ << " " << mAbsPathAndQuery
+ << " " << mVersion << llendl;
+
+ std::string::size_type delimiter
+ = mAbsPathAndQuery.find('?');
+ if (delimiter == std::string::npos)
+ {
+ mPath = mAbsPathAndQuery;
+ mQuery = "";
+ }
+ else
+ {
+ mPath = mAbsPathAndQuery.substr(0, delimiter);
+ mQuery = mAbsPathAndQuery.substr(delimiter+1);
+ }
+
+ if(!mAbsPathAndQuery.empty())
+ {
+ if(mVersion.empty())
+ {
+ // simple request.
+ parse_all = false;
+ mState = STATE_DONE;
+ mVersion.assign("HTTP/1.0");
+ }
+ }
+ }
+ else
+ {
+ read_next_line = false;
+ parse_all = false;
+ lldebugs << "unknown http verb: " << mVerb << llendl;
+ markBad(channels, buffer);
+ }
+ }
+ if(parse_all)
+ {
+ bool keep_parsing = true;
+ while(keep_parsing)
+ {
+ if(read_next_line)
+ {
+ len = HEADER_BUFFER_SIZE;
+ readLine(channels, buffer, (U8*)buf, len);
+ }
+ if(0 == len)
+ {
+ return status;
+ }
+ if(buf[0] == '\r' && buf[1] == '\n')
+ {
+ // end-o-headers
+ keep_parsing = false;
+ mState = STATE_LOOKING_FOR_EOS;
+ break;
+ }
+ char* pos_colon = strchr(buf, ':');
+ if(NULL == pos_colon)
+ {
+ keep_parsing = false;
+ lldebugs << "bad header: " << buf << llendl;
+ markBad(channels, buffer);
+ break;
+ }
+ // we've found a header
+ read_next_line = true;
+ std::string name(buf, pos_colon - buf);
+ std::string value(pos_colon + 2);
+ LLString::toLower(name);
+ if("content-length" == name)
+ {
+ lldebugs << "Content-Length: " << value << llendl;
+ mContentLength = atoi(value.c_str());
+ }
+ }
+ }
+ }
+ }
+
+ PUMP_DEBUG;
+ // look for the end of stream based on
+ if(STATE_LOOKING_FOR_EOS == mState)
+ {
+ if(0 == mContentLength)
+ {
+ mState = STATE_DONE;
+ }
+ else if(buffer->countAfter(channels.in(), mLastRead) >= mContentLength)
+ {
+ mState = STATE_DONE;
+ }
+ // else more bytes should be coming.
+ }
+
+ PUMP_DEBUG;
+ if(STATE_DONE == mState)
+ {
+ // hey, hey, we should have everything now, so we pass it to
+ // a content handler.
+ context[CONTEXT_REQUEST]["verb"] = mVerb;
+ const LLHTTPNode* node = mRootNode.traverse(mPath, context);
+ if(node)
+ {
+ llinfos << "LLHTTPResponder::process_impl found node for "
+ << mAbsPathAndQuery << llendl;
+
+ // Copy everything after mLast read to the out.
+ LLBufferArray::segment_iterator_t seg_iter;
+ seg_iter = buffer->splitAfter(mLastRead);
+ if(seg_iter != buffer->endSegment())
+ {
+ LLChangeChannel change(channels.in(), channels.out());
+ ++seg_iter;
+ std::for_each(seg_iter, buffer->endSegment(), change);
+
+#if 0
+ seg_iter = buffer->beginSegment();
+ char buf[1024]; /*Flawfinder: ignore*/
+ while(seg_iter != buffer->endSegment())
+ {
+ memcpy(buf, (*seg_iter).data(), (*seg_iter).size()); /*Flawfinder: ignore*/
+ buf[(*seg_iter).size()] = '\0';
+ llinfos << (*seg_iter).getChannel() << ": " << buf
+ << llendl;
+ ++seg_iter;
+ }
+#endif
+ }
+
+ //
+ // *FIX: get rid of extra bytes off the end
+ //
+
+ // Set up a chain which will prepend a content length and
+ // HTTP headers.
+ LLPumpIO::chain_t chain;
+ chain.push_back(LLIOPipe::ptr_t(new LLIOFlush));
+ context[CONTEXT_REQUEST]["path"] = mPath;
+ context[CONTEXT_REQUEST]["query-string"] = mQuery;
+
+ const LLChainIOFactory* protocolHandler
+ = node->getProtocolHandler();
+ if (protocolHandler)
+ {
+ protocolHandler->build(chain, context);
+ }
+ else
+ {
+ // this is a simple LLHTTPNode, so use LLHTTPPipe
+ chain.push_back(LLIOPipe::ptr_t(new LLHTTPPipe(*node)));
+ }
+
+ // Add the header - which needs to have the same
+ // channel information as the link before it since it
+ // is part of the response.
+ LLIOPipe* header = new LLHTTPResponseHeader;
+ chain.push_back(LLIOPipe::ptr_t(header));
+
+ // We need to copy all of the pipes _after_ this so
+ // that the response goes out correctly.
+ LLPumpIO::links_t current_links;
+ pump->copyCurrentLinkInfo(current_links);
+ LLPumpIO::links_t::iterator link_iter = current_links.begin();
+ LLPumpIO::links_t::iterator links_end = current_links.end();
+ bool after_this = false;
+ for(; link_iter < links_end; ++link_iter)
+ {
+ if(after_this)
+ {
+ chain.push_back((*link_iter).mPipe);
+ }
+ else if(this == (*link_iter).mPipe.get())
+ {
+ after_this = true;
+ }
+ }
+
+ // Do the final build of the chain, and send it on
+ // it's way.
+ LLChannelDescriptors chnl = channels;
+ LLPumpIO::LLLinkInfo link;
+ LLPumpIO::links_t links;
+ LLPumpIO::chain_t::iterator it = chain.begin();
+ LLPumpIO::chain_t::iterator end = chain.end();
+ while(it != end)
+ {
+ link.mPipe = *it;
+ link.mChannels = chnl;
+ links.push_back(link);
+ chnl = LLBufferArray::makeChannelConsumer(chnl);
+ ++it;
+ }
+ pump->addChain(
+ links,
+ buffer,
+ context,
+ DEFAULT_CHAIN_EXPIRY_SECS);
+
+ status = STATUS_STOP;
+ }
+ else
+ {
+ llinfos << "LLHTTPResponder::process_impl didn't find a node for "
+ << mAbsPathAndQuery << llendl;
+ LLBufferStream str(channels, buffer.get());
+ mState = STATE_SHORT_CIRCUIT;
+ str << HTTP_VERSION_STR << " 404 Not Found\r\n\r\n<html>\n"
+ << "<title>Not Found</title>\n<body>\nNode '" << mAbsPathAndQuery
+ << "' not found.\n</body>\n</html>\n";
+ }
+ }
+
+ if(STATE_SHORT_CIRCUIT == mState)
+ {
+ //status = mNext->process(buffer, true, pump, context);
+ status = STATUS_DONE;
+ }
+ PUMP_DEBUG;
+ return status;
+}
+
+
+
+void LLCreateHTTPPipe(LLPumpIO::chain_t& chain, const LLHTTPNode& root)
+{
+ chain.push_back(LLIOPipe::ptr_t(new LLHTTPResponder(root)));
+}
+
+
+class LLHTTPResponseFactory : public LLChainIOFactory
+{
+public:
+ bool build(LLPumpIO::chain_t& chain, LLSD ctx) const
+ {
+ LLCreateHTTPPipe(chain, mTree);
+ return true;
+ }
+
+ LLHTTPNode& getRootNode() { return mTree; }
+
+private:
+ LLHTTPNode mTree;
+};
+
+
+LLHTTPNode& LLCreateHTTPServer(
+ apr_pool_t* pool, LLPumpIO& pump, U16 port)
+{
+ LLSocket::ptr_t socket = LLSocket::create(
+ pool,
+ LLSocket::STREAM_TCP,
+ port);
+ if(!socket)
+ {
+ llerrs << "Unable to initialize socket" << llendl;
+ }
+
+ LLHTTPResponseFactory* factory = new LLHTTPResponseFactory;
+ boost::shared_ptr<LLChainIOFactory> factory_ptr(factory);
+
+ LLIOServerSocket* server = new LLIOServerSocket(pool, socket, factory_ptr);
+
+ LLPumpIO::chain_t chain;
+ chain.push_back(LLIOPipe::ptr_t(server));
+ pump.addChain(chain, NEVER_CHAIN_EXPIRY_SECS);
+
+ return factory->getRootNode();
+}
+