lliohttpserver.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:24k
- /**
- * @file lliohttpserver.cpp
- * @author Phoenix
- * @date 2005-10-05
- * @brief Implementation of the http server classes
- *
- * $LicenseInfo:firstyear=2005&license=viewergpl$
- *
- * Copyright (c) 2005-2010, Linden Research, Inc.
- *
- * Second Life Viewer Source Code
- * The source code in this file ("Source Code") is provided by Linden Lab
- * to you under the terms of the GNU General Public License, version 2.0
- * ("GPL"), unless you have obtained a separate licensing agreement
- * ("Other License"), formally executed by you and Linden Lab. Terms of
- * the GPL can be found in doc/GPL-license.txt in this distribution, or
- * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
- *
- * There are special exceptions to the terms and conditions of the GPL as
- * it is applied to this Source Code. View the full text of the exception
- * in the file doc/FLOSS-exception.txt in this software distribution, or
- * online at
- * http://secondlifegrid.net/programs/open_source/licensing/flossexception
- *
- * By copying, modifying or distributing this software, you acknowledge
- * that you have read and understood your obligations described above,
- * and agree to abide by those obligations.
- *
- * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
- * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
- * COMPLETENESS OR PERFORMANCE.
- * $/LicenseInfo$
- */
- #include "linden_common.h"
- #include "lliohttpserver.h"
- #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 "llstat.h"
- #include "llstl.h"
- #include "lltimer.h"
- #include <sstream>
- #include <boost/tokenizer.hpp>
- static const char HTTP_VERSION_STR[] = "HTTP/1.0";
- const std::string CONTEXT_REQUEST("request");
- const std::string CONTEXT_RESPONSE("response");
- const std::string CONTEXT_VERB("verb");
- const std::string CONTEXT_HEADERS("headers");
- const std::string HTTP_VERB_GET("GET");
- const std::string HTTP_VERB_PUT("PUT");
- const std::string HTTP_VERB_POST("POST");
- const std::string HTTP_VERB_DELETE("DELETE");
- const std::string HTTP_VERB_OPTIONS("OPTIONS");
- static LLIOHTTPServer::timing_callback_t sTimingCallback = NULL;
- static void* sTimingCallbackData = NULL;
- class LLHTTPPipe : public LLIOPipe
- {
- public:
- LLHTTPPipe(const LLHTTPNode& node)
- : mNode(node),
- mResponse(NULL),
- mState(STATE_INVOKE),
- mChainLock(0),
- mLockedPump(NULL),
- 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 extendedResult(S32 code, const std::string& body, const LLSD& headers);
-
- virtual void status(S32 code, const std::string& message);
- void nullPipe();
- private:
- Response() : mPipe(NULL) {} // 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_EXTENDED_RESULT
- };
- State mState;
- S32 mChainLock;
- LLPumpIO* mLockedPump;
- void lockChain(LLPumpIO*);
- void unlockChain();
- LLSD mGoodResult;
- S32 mStatusCode;
- std::string mStatusMessage;
- LLSD mHeaders;
- };
- 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?
- // *TODO: We should look at content-type and do the right
- // thing. Phoenix 2007-12-31
- LLBufferStream istr(channels, buffer.get());
- static LLTimer timer;
- timer.reset();
- std::string verb = context[CONTEXT_REQUEST][CONTEXT_VERB];
- if(verb == HTTP_VERB_GET)
- {
- LLPerfBlock getblock("http_get");
- mNode.get(LLHTTPNode::ResponsePtr(mResponse), context);
- }
- else if(verb == HTTP_VERB_PUT)
- {
- LLPerfBlock putblock("http_put");
- LLSD input;
- if (mNode.getContentType() == LLHTTPNode::CONTENT_TYPE_LLSD)
- {
- LLSDSerialize::fromXML(input, istr);
- }
- else if (mNode.getContentType() == LLHTTPNode::CONTENT_TYPE_TEXT)
- {
- std::stringstream strstrm;
- strstrm << istr.rdbuf();
- input = strstrm.str();
- }
- mNode.put(LLHTTPNode::ResponsePtr(mResponse), context, input);
- }
- else if(verb == HTTP_VERB_POST)
- {
- LLPerfBlock postblock("http_post");
- LLSD input;
- if (mNode.getContentType() == LLHTTPNode::CONTENT_TYPE_LLSD)
- {
- LLSDSerialize::fromXML(input, istr);
- }
- else if (mNode.getContentType() == LLHTTPNode::CONTENT_TYPE_TEXT)
- {
- std::stringstream strstrm;
- strstrm << istr.rdbuf();
- input = strstrm.str();
- }
- mNode.post(LLHTTPNode::ResponsePtr(mResponse), context, input);
- }
- else if(verb == HTTP_VERB_DELETE)
- {
- LLPerfBlock delblock("http_delete");
- mNode.del(LLHTTPNode::ResponsePtr(mResponse), context);
- }
- else if(verb == HTTP_VERB_OPTIONS)
- {
- mNode.options(LLHTTPNode::ResponsePtr(mResponse), context);
- }
- else
- {
- mResponse->methodNotAllowed();
- }
- F32 delta = timer.getElapsedTimeF32();
- if (sTimingCallback)
- {
- LLHTTPNode::Description desc;
- mNode.describe(desc);
- LLSD info = desc.getInfo();
- std::string timing_name = info["description"];
- timing_name += " ";
- timing_name += verb;
- sTimingCallback(timing_name.c_str(), delta, sTimingCallbackData);
- }
- // Log all HTTP transactions.
- // TODO: Add a way to log these to their own file instead of indra.log
- // It is just too spammy to be in indra.log.
- lldebugs << verb << " " << context[CONTEXT_REQUEST]["path"].asString()
- << " " << mStatusCode << " " << mStatusMessage << " " << delta
- << "s" << llendl;
- // 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:
- {
- LLSD headers = mHeaders;
- headers["Content-Type"] = "application/llsd+xml";
- context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers;
- LLBufferStream ostr(channels, buffer.get());
- LLSDSerialize::toXML(mGoodResult, ostr);
- return STATUS_DONE;
- }
- case STATE_STATUS_RESULT:
- {
- LLSD headers = mHeaders;
- headers["Content-Type"] = "text/plain";
- context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers;
- context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode;
- context[CONTEXT_RESPONSE]["statusMessage"] = mStatusMessage;
- LLBufferStream ostr(channels, buffer.get());
- ostr << mStatusMessage;
- return STATUS_DONE;
- }
- case STATE_EXTENDED_RESULT:
- {
- context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = mHeaders;
- context[CONTEXT_RESPONSE]["statusCode"] = mStatusCode;
- LLBufferStream ostr(channels, buffer.get());
- ostr << mStatusMessage;
- 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->mHeaders = mHeaders;
- mPipe->unlockChain();
- }
- void LLHTTPPipe::Response::extendedResult(S32 code, const std::string& body, const LLSD& headers)
- {
- if(! mPipe)
- {
- llwarns << "LLHTTPPipe::Response::status: NULL pipe" << llendl;
- return;
- }
- mPipe->mStatusCode = code;
- mPipe->mStatusMessage = body;
- mPipe->mHeaders = headers;
- mPipe->mState = STATE_EXTENDED_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->mHeaders = mHeaders;
- 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() : mCode(0) {}
- 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[CONTEXT_RESPONSE]["statusMessage"];
-
- int code = context[CONTEXT_RESPONSE]["statusCode"];
- if (code < 200)
- {
- code = 200;
- message = "OK";
- }
-
- ostr << HTTP_VERSION_STR << " " << code << " " << message << "rn";
- S32 content_length = buffer->countAfter(channels.in(), NULL);
- if(0 < content_length)
- {
- ostr << "Content-Length: " << content_length << "rn";
- }
- // *NOTE: This guard can go away once the LLSD static map
- // iterator is available. Phoenix. 2008-05-09
- LLSD headers = context[CONTEXT_RESPONSE][CONTEXT_HEADERS];
- if(headers.isDefined())
- {
- LLSD::map_iterator iter = headers.beginMap();
- LLSD::map_iterator end = headers.endMap();
- for(; iter != end; ++iter)
- {
- ostr << (*iter).first << ": " << (*iter).second.asString()
- << "rn";
- }
- }
- ostr << "rn";
- 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, const LLSD& ctx);
- ~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 readHeaderLine(
- 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
- };
- LLSD mBuildContext;
- EState mState;
- U8* mLastRead;
- std::string mVerb;
- std::string mAbsPathAndQuery;
- std::string mPath;
- std::string mQuery;
- std::string mVersion;
- S32 mContentLength;
- LLSD mHeaders;
- // handle the urls
- const LLHTTPNode& mRootNode;
- };
- LLHTTPResponder::LLHTTPResponder(const LLHTTPNode& tree, const LLSD& ctx) :
- mBuildContext(ctx),
- 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::readHeaderLine(
- 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] = ' ';
- 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 = ' ';
- 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 Requestrnrn<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()] = ' ';
- llinfos << (*seg_iter).getChannel() << ": " << buf
- << llendl;
- ++seg_iter;
- }
- }
- #endif
-
- PUMP_DEBUG;
- if(readHeaderLine(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)
- || (HTTP_VERB_OPTIONS == 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;
- if (!readHeaderLine(channels, buffer, (U8*)buf, len))
- {
- // Failed to read the header line, probably too long.
- // readHeaderLine already marked the channel/buffer as bad.
- keep_parsing = false;
- break;
- }
- }
- 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);
- LLStringUtil::toLower(name);
- if("content-length" == name)
- {
- lldebugs << "Content-Length: " << value << llendl;
- mContentLength = atoi(value.c_str());
- }
- else
- {
- LLStringUtil::trimTail(value);
- mHeaders[name] = value;
- }
- }
- }
- }
- }
- 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][CONTEXT_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()] = ' ';
- 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;
- context[CONTEXT_REQUEST]["remote-host"]
- = mBuildContext["remote-host"];
- context[CONTEXT_REQUEST]["remote-port"]
- = mBuildContext["remote-port"];
- context[CONTEXT_REQUEST][CONTEXT_HEADERS] = mHeaders;
- const LLChainIOFactory* protocolHandler
- = node->getProtocolHandler();
- if (protocolHandler)
- {
- lldebugs << "HTTP context: " << context << llendl;
- 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
- {
- llwarns << "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 Foundrnrn<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;
- }
- // static
- void LLIOHTTPServer::createPipe(LLPumpIO::chain_t& chain,
- const LLHTTPNode& root, const LLSD& ctx)
- {
- chain.push_back(LLIOPipe::ptr_t(new LLHTTPResponder(root, ctx)));
- }
- class LLHTTPResponseFactory : public LLChainIOFactory
- {
- public:
- bool build(LLPumpIO::chain_t& chain, LLSD ctx) const
- {
- LLIOHTTPServer::createPipe(chain, mTree, ctx);
- return true;
- }
- LLHTTPNode& getRootNode() { return mTree; }
- private:
- LLHTTPNode mTree;
- };
- // static
- LLHTTPNode& LLIOHTTPServer::create(
- 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();
- }
- // static
- void LLIOHTTPServer::setTimingCallback(timing_callback_t callback,
- void* data)
- {
- sTimingCallback = callback;
- sTimingCallbackData = data;
- }