diff options
Diffstat (limited to 'indra/llmessage/lliohttpserver.cpp')
-rw-r--r-- | indra/llmessage/lliohttpserver.cpp | 184 |
1 files changed, 132 insertions, 52 deletions
diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp index ec5cb93d69..3b18a9177c 100644 --- a/indra/llmessage/lliohttpserver.cpp +++ b/indra/llmessage/lliohttpserver.cpp @@ -4,30 +4,25 @@ * @date 2005-10-05 * @brief Implementation of the http server classes * - * $LicenseInfo:firstyear=2005&license=viewergpl$ - * - * Copyright (c) 2005-2007, Linden Research, Inc. - * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ * 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://secondlife.com/developers/opensource/gplv2 + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. * - * 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://secondlife.com/developers/opensource/flossexception + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. * - * 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. + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO - * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, - * COMPLETENESS OR PERFORMANCE. + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ @@ -47,6 +42,7 @@ #include "llpumpio.h" #include "llsd.h" #include "llsdserialize_xml.h" +#include "llstat.h" #include "llstl.h" #include "lltimer.h" @@ -55,12 +51,15 @@ #include <boost/tokenizer.hpp> static const char HTTP_VERSION_STR[] = "HTTP/1.0"; -static const std::string CONTEXT_REQUEST("request"); -static const std::string CONTEXT_RESPONSE("response"); -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"); +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; @@ -69,7 +68,12 @@ class LLHTTPPipe : public LLIOPipe { public: LLHTTPPipe(const LLHTTPNode& node) - : mNode(node), mResponse(NULL), mState(STATE_INVOKE), mChainLock(0), mStatusCode(0) + : mNode(node), + mResponse(NULL), + mState(STATE_INVOKE), + mChainLock(0), + mLockedPump(NULL), + mStatusCode(0) { } virtual ~LLHTTPPipe() { @@ -99,12 +103,14 @@ private: // 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() {;} // Must be accessed through LLPointer. + Response() : mPipe(NULL) {} // Must be accessed through LLPointer. LLHTTPPipe* mPipe; }; friend class Response; @@ -117,7 +123,8 @@ private: STATE_DELAYED, STATE_LOCKED, STATE_GOOD_RESULT, - STATE_STATUS_RESULT + STATE_STATUS_RESULT, + STATE_EXTENDED_RESULT }; State mState; @@ -130,6 +137,7 @@ private: LLSD mGoodResult; S32 mStatusCode; std::string mStatusMessage; + LLSD mHeaders; }; LLIOPipe::EStatus LLHTTPPipe::process_impl( @@ -164,27 +172,53 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( static LLTimer timer; timer.reset(); - std::string verb = context[CONTEXT_REQUEST]["verb"]; + 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; - LLSDSerialize::fromXML(input, istr); + 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; - LLSDSerialize::fromXML(input, istr); + 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(); @@ -231,7 +265,9 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( case STATE_GOOD_RESULT: { - context[CONTEXT_RESPONSE]["contentType"] = "application/xml"; + LLSD headers = mHeaders; + headers["Content-Type"] = "application/llsd+xml"; + context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers; LLBufferStream ostr(channels, buffer.get()); LLSDSerialize::toXML(mGoodResult, ostr); @@ -240,11 +276,22 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( case STATE_STATUS_RESULT: { - context[CONTEXT_RESPONSE]["contentType"] = "text/plain"; + 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 << std::ends; + 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; } @@ -287,9 +334,25 @@ void LLHTTPPipe::Response::result(const LLSD& r) 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) { @@ -302,6 +365,7 @@ void LLHTTPPipe::Response::status(S32 code, const std::string& message) mPipe->mStatusCode = code; mPipe->mStatusMessage = message; mPipe->mState = STATE_STATUS_RESULT; + mPipe->mHeaders = mHeaders; mPipe->unlockChain(); } @@ -338,7 +402,7 @@ void LLHTTPPipe::unlockChain() class LLHTTPResponseHeader : public LLIOPipe { public: - LLHTTPResponseHeader() {} + LLHTTPResponseHeader() : mCode(0) {} virtual ~LLHTTPResponseHeader() {} protected: @@ -389,17 +453,25 @@ LLIOPipe::EStatus LLHTTPResponseHeader::process_impl( } ostr << HTTP_VERSION_STR << " " << code << " " << message << "\r\n"; - - std::string type = context[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"; } + // *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() + << "\r\n"; + } + } ostr << "\r\n"; LLChangeChannel change(channels.in(), channels.out()); @@ -448,7 +520,7 @@ protected: * seek orfor string assignment. * @returns Returns true if a line was found. */ - bool readLine( + bool readHeaderLine( const LLChannelDescriptors& channels, buffer_ptr_t buffer, U8* dest, @@ -519,7 +591,7 @@ LLHTTPResponder::~LLHTTPResponder() //lldebugs << "destroying LLHTTPResponder" << llendl; } -bool LLHTTPResponder::readLine( +bool LLHTTPResponder::readHeaderLine( const LLChannelDescriptors& channels, buffer_ptr_t buffer, U8* dest, @@ -597,7 +669,7 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( #endif PUMP_DEBUG; - if(readLine(channels, buffer, (U8*)buf, len)) + if(readHeaderLine(channels, buffer, (U8*)buf, len)) { bool read_next_line = false; bool parse_all = true; @@ -606,11 +678,12 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( 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_DELETE == mVerb) + || (HTTP_VERB_OPTIONS == mVerb)) { header >> mAbsPathAndQuery; header >> mVersion; @@ -660,7 +733,13 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( if(read_next_line) { len = HEADER_BUFFER_SIZE; - readLine(channels, buffer, (U8*)buf, len); + 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) { @@ -685,7 +764,7 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( read_next_line = true; std::string name(buf, pos_colon - buf); std::string value(pos_colon + 2); - LLString::toLower(name); + LLStringUtil::toLower(name); if("content-length" == name) { lldebugs << "Content-Length: " << value << llendl; @@ -693,7 +772,7 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( } else { - LLString::trimTail(value); + LLStringUtil::trimTail(value); mHeaders[name] = value; } } @@ -721,7 +800,7 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( { // hey, hey, we should have everything now, so we pass it to // a content handler. - context[CONTEXT_REQUEST]["verb"] = mVerb; + context[CONTEXT_REQUEST][CONTEXT_VERB] = mVerb; const LLHTTPNode* node = mRootNode.traverse(mPath, context); if(node) { @@ -765,12 +844,13 @@ LLIOPipe::EStatus LLHTTPResponder::process_impl( = mBuildContext["remote-host"]; context[CONTEXT_REQUEST]["remote-port"] = mBuildContext["remote-port"]; - context[CONTEXT_REQUEST]["headers"] = mHeaders; + context[CONTEXT_REQUEST][CONTEXT_HEADERS] = mHeaders; const LLChainIOFactory* protocolHandler = node->getProtocolHandler(); if (protocolHandler) { + lldebugs << "HTTP context: " << context << llendl; protocolHandler->build(chain, context); } else |