diff options
Diffstat (limited to 'indra')
-rw-r--r-- | indra/cmake/CMakeLists.txt | 1 | ||||
-rw-r--r-- | indra/llcommon/llkeythrottle.h | 35 | ||||
-rw-r--r-- | indra/llmessage/llcurl.cpp | 54 | ||||
-rw-r--r-- | indra/llmessage/llcurl.h | 28 | ||||
-rw-r--r-- | indra/llmessage/llhttpclient.cpp | 43 | ||||
-rw-r--r-- | indra/llmessage/llhttpnode.cpp | 27 | ||||
-rw-r--r-- | indra/llmessage/llhttpnode.h | 88 | ||||
-rw-r--r-- | indra/llmessage/lliohttpserver.cpp | 48 | ||||
-rw-r--r-- | indra/newview/CMakeLists.txt | 1 | ||||
-rw-r--r-- | indra/test/lliohttpserver_tut.cpp | 17 |
10 files changed, 223 insertions, 119 deletions
diff --git a/indra/cmake/CMakeLists.txt b/indra/cmake/CMakeLists.txt index 9cc34ea159..f0bb241961 100644 --- a/indra/cmake/CMakeLists.txt +++ b/indra/cmake/CMakeLists.txt @@ -63,7 +63,6 @@ set(cmake_SOURCE_FILES PNG.cmake Python.cmake QuickTime.cmake - Smartheap.cmake TemplateCheck.cmake UI.cmake UnixInstall.cmake diff --git a/indra/llcommon/llkeythrottle.h b/indra/llcommon/llkeythrottle.h index 61a43b2dad..ac2f4254f2 100644 --- a/indra/llcommon/llkeythrottle.h +++ b/indra/llcommon/llkeythrottle.h @@ -55,28 +55,31 @@ class LLKeyThrottleImpl protected: struct Entry { U32 count; - BOOL blocked; + bool blocked; - Entry() : count(0), blocked(FALSE) { } + Entry() : count(0), blocked(false) { } }; typedef std::map<T, Entry> EntryMap; - EntryMap * prevMap; - EntryMap * currMap; + EntryMap* prevMap; + EntryMap* currMap; U32 countLimit; // maximum number of keys allowed per interval U64 intervalLength; // each map covers this time period (usec or frame number) U64 startTime; // start of the time period (usec or frame number) - // currMap started counting at this time // prevMap covers the previous interval - LLKeyThrottleImpl() : prevMap(0), currMap(0), - countLimit(0), intervalLength(1), - startTime(0) { }; + LLKeyThrottleImpl() : + prevMap(NULL), + currMap(NULL), + countLimit(0), + intervalLength(1), + startTime(0) + {} static U64 getTime() { @@ -93,7 +96,9 @@ template< class T > class LLKeyThrottle { public: - LLKeyThrottle(U32 limit, F32 interval, BOOL realtime = TRUE) // realtime = FALSE for frame-based throttle, TRUE for usec real-time throttle + // @param realtime = FALSE for frame-based throttle, TRUE for usec + // real-time throttle + LLKeyThrottle(U32 limit, F32 interval, BOOL realtime = TRUE) : m(* new LLKeyThrottleImpl<T>) { setParameters( limit, interval, realtime ); @@ -149,7 +154,7 @@ public: } U32 prevCount = 0; - BOOL prevBlocked = FALSE; + bool prevBlocked = false; typename LLKeyThrottleImpl<T>::EntryMap::const_iterator prev = m.prevMap->find(id); if (prev != m.prevMap->end()) @@ -198,17 +203,17 @@ public: noteAction(id); typename LLKeyThrottleImpl<T>::Entry& curr = (*m.currMap)[id]; curr.count = llmax(m.countLimit, curr.count); - curr.blocked = TRUE; + curr.blocked = true; } - // returns TRUE if key is blocked - BOOL isThrottled(const T& id) const + // returns true if key is blocked + bool isThrottled(const T& id) const { if (m.currMap->empty() && m.prevMap->empty()) { // most of the time we'll fall in here - return FALSE; + return false; } // NOTE, we ignore the case where id is in the map but the map is stale. @@ -226,7 +231,7 @@ public: { return entry->second.blocked; } - return FALSE; + return false; } // Get the throttling parameters diff --git a/indra/llmessage/llcurl.cpp b/indra/llmessage/llcurl.cpp index e282f49438..5a426c7238 100644 --- a/indra/llmessage/llcurl.cpp +++ b/indra/llmessage/llcurl.cpp @@ -113,6 +113,15 @@ LLCurl::Responder::~Responder() } // virtual +void LLCurl::Responder::error( + U32 status, + const std::string& reason, + const LLSD&) +{ + error(status, reason); +} + +// virtual void LLCurl::Responder::error(U32 status, const std::string& reason) { llinfos << status << ": " << reason << llendl; @@ -124,38 +133,16 @@ void LLCurl::Responder::result(const LLSD& content) } // virtual -void LLCurl::Responder::completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer) +void LLCurl::Responder::completedRaw( + U32 status, + const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer) { - if (isGoodStatus(status)) - { - LLSD content; - LLBufferStream istr(channels, buffer.get()); - LLSDSerialize::fromXML(content, istr); -/* - const S32 parseError = -1; - if(LLSDSerialize::fromXML(content, istr) == parseError) - { - mStatus = 498; - mReason = "Client Parse Error"; - } -*/ - completed(status, reason, content); - } - else if (status == 400) - { - // Get reason from buffer - char tbuf[4096]; - S32 len = 4096; - buffer->readAfter(channels.in(), NULL, (U8*)tbuf, len); - tbuf[len] = 0; - completed(status, std::string(tbuf), LLSD()); - } - else - { - completed(status, reason, LLSD()); - } + LLSD content; + LLBufferStream istr(channels, buffer.get()); + LLSDSerialize::fromXML(content, istr); + completed(status, reason, content); } // virtual @@ -167,10 +154,7 @@ void LLCurl::Responder::completed(U32 status, const std::string& reason, const L } else { - // *NOTE: This is kind of messed up. This should probably call - // the full error method which then provides a default impl - // which calls the thinner method. - error(status, reason); + error(status, reason, content); } } diff --git a/indra/llmessage/llcurl.h b/indra/llmessage/llcurl.h index 1b3d3f6266..b7634d420f 100644 --- a/indra/llmessage/llcurl.h +++ b/indra/llmessage/llcurl.h @@ -84,17 +84,31 @@ public: return((200 <= status) && (status < 300)); } + virtual void error( + U32 status, + const std::string& reason, + const LLSD& content); + //< called by completed() on bad status + virtual void error(U32 status, const std::string& reason); - // called with non-200 status codes + //< called by default error(status, reason, content) virtual void result(const LLSD& content); - - // Override point for clients that may want to use this class when the response is some other format besides LLSD - virtual void completedRaw(U32 status, const std::string& reason, - const LLChannelDescriptors& channels, - const LLIOPipe::buffer_ptr_t& buffer); + //< called by completed for good status codes. + + virtual void completedRaw( + U32 status, + const std::string& reason, + const LLChannelDescriptors& channels, + const LLIOPipe::buffer_ptr_t& buffer); + /**< Override point for clients that may want to use this + class when the response is some other format besides LLSD + */ - virtual void completed(U32 status, const std::string& reason, const LLSD& content); + virtual void completed( + U32 status, + const std::string& reason, + const LLSD& content); /**< The default implemetnation calls either: * result(), or diff --git a/indra/llmessage/llhttpclient.cpp b/indra/llmessage/llhttpclient.cpp index 859f3c1536..52cb8fe1c3 100644 --- a/indra/llmessage/llhttpclient.cpp +++ b/indra/llmessage/llhttpclient.cpp @@ -205,9 +205,7 @@ static void request( Injector* body_injector, LLCurl::ResponderPtr responder, const F32 timeout = HTTP_REQUEST_EXPIRY_SECS, - const LLSD& headers = LLSD(), - S32 offset = 0, - S32 bytes = 0) + const LLSD& headers = LLSD()) { if (!LLHTTPClient::hasPump()) { @@ -216,7 +214,7 @@ static void request( } LLPumpIO::chain_t chain; - LLURLRequest *req = new LLURLRequest(method, url); + LLURLRequest* req = new LLURLRequest(method, url); req->checkRootCertificate(true); // Insert custom headers is the caller sent any @@ -235,7 +233,7 @@ static void request( //to not use the proxy (read: llurlrequest.cpp) if ((iter->first == "Pragma") && (iter->second.asString() == "")) { - req->useProxy(FALSE); + req->useProxy(false); } header << iter->first << ": " << iter->second.asString() ; lldebugs << "header = " << header.str() << llendl; @@ -258,34 +256,27 @@ static void request( chain.push_back(LLIOPipe::ptr_t(body_injector)); } - if (method == LLURLRequest::HTTP_GET && (offset > 0 || bytes > 0)) - { - std::string range = llformat("Range: bytes=%d-%d", offset,offset+bytes-1); - req->addHeader(range.c_str()); - } - chain.push_back(LLIOPipe::ptr_t(req)); theClientPump->addChain(chain, timeout); } -void LLHTTPClient::getByteRange(const std::string& url, - S32 offset, S32 bytes, - ResponderPtr responder, - const LLSD& headers, - const F32 timeout) +void LLHTTPClient::getByteRange( + const std::string& url, + S32 offset, + S32 bytes, + ResponderPtr responder, + const LLSD& hdrs, + const F32 timeout) { - // *FIX: Why is the headers argument ignored? Phoenix 2008-04-28 - request( - url, - LLURLRequest::HTTP_GET, - NULL, - responder, - timeout, - LLSD(), // WTF? Shouldn't this be used? - offset, - bytes); + LLSD headers = hdrs; + if(offset > 0 || bytes > 0) + { + std::string range = llformat("bytes=%d-%d", offset, offset+bytes-1); + headers["Range"] = range; + } + request(url,LLURLRequest::HTTP_GET, NULL, responder, timeout, headers); } void LLHTTPClient::head(const std::string& url, ResponderPtr responder, const F32 timeout) diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp index 196aa5f8cf..91605b4008 100644 --- a/indra/llmessage/llhttpnode.cpp +++ b/indra/llmessage/llhttpnode.cpp @@ -174,6 +174,26 @@ LLSD LLHTTPNode::del(const LLSD&) const throw NotImplemented(); } +// virtual +void LLHTTPNode::options(ResponsePtr response, const LLSD& context) const +{ + //llinfos << "options context: " << context << llendl; + + // default implementation constructs an url to the documentation. + std::string host = context[CONTEXT_REQUEST]["headers"]["host"].asString(); + if(host.empty()) + { + response->status(400, "Bad Request -- need Host header"); + return; + } + std::ostringstream ostr; + ostr << "http://" << host << "/web/server/api"; + ostr << context[CONTEXT_REQUEST]["path"].asString(); + static const std::string DOC_HEADER("X-Documentation-URL"); + response->addHeader(DOC_HEADER, ostr.str()); + response->status(200, "OK"); +} + // virtual LLHTTPNode* LLHTTPNode::getChild(const std::string& name, LLSD& context) const @@ -386,6 +406,13 @@ void LLHTTPNode::Response::methodNotAllowed() status(405, "Method Not Allowed"); } +void LLHTTPNode::Response::addHeader( + const std::string& name, + const std::string& value) +{ + mHeaders[name] = value; +} + void LLHTTPNode::describe(Description& desc) const { desc.shortInfo("unknown service (missing describe() method)"); diff --git a/indra/llmessage/llhttpnode.h b/indra/llmessage/llhttpnode.h index e27056a51f..54715f2b38 100644 --- a/indra/llmessage/llhttpnode.h +++ b/indra/llmessage/llhttpnode.h @@ -82,33 +82,79 @@ public: */ //@{ public: - virtual LLSD get() const; - virtual LLSD put(const LLSD& input) const; - virtual LLSD post(const LLSD& input) const; - virtual LLSD del(const LLSD& context) const; + virtual LLSD get() const; + virtual LLSD put(const LLSD& input) const; + virtual LLSD post(const LLSD& input) const; + virtual LLSD del(const LLSD& context) const; - class Response : public LLRefCount - { - protected: - virtual ~Response(); + class Response : public LLRefCount + { + protected: + virtual ~Response(); - public: - virtual void result(const LLSD&) = 0; - virtual void status(S32 code, const std::string& message) = 0; + public: + /** + * @brief Return the LLSD content and a 200 OK. + */ + virtual void result(const LLSD&) = 0; + + /** + * @brief return status code and reason string on http header, + * but do not return a payload. + */ + virtual void status(S32 code, const std::string& message) = 0; + + /** + * @brief Return no body, just status code and 'UNKNOWN ERROR'. + */ + void status(S32 code); + + void notFound(const std::string& message); + void notFound(); + void methodNotAllowed(); + + /** + * @breif Add a name: value http header. + * + * No effort is made to ensure the response is a valid http + * header. + * The headers are stored as a map of header name : value. + * Though HTTP allows the same header name to be transmitted + * more than once, this implementation only stores a header + * name once. + * @param name The name of the header, eg, "Content-Encoding" + * @param value The value of the header, eg, "gzip" + */ + void addHeader(const std::string& name, const std::string& value); + + protected: + /** + * @brief Headers to be sent back with the HTTP response. + * + * Protected class membership since derived classes are + * expected to use it and there is no use case yet for other + * uses. If such a use case arises, I suggest making a + * headers() public method, and moving this member data into + * private. + */ + LLSD mHeaders; + }; - void status(S32 code); - void notFound(const std::string& message); - void notFound(); - void methodNotAllowed(); - }; - typedef LLPointer<Response> ResponsePtr; + typedef LLPointer<Response> ResponsePtr; - virtual void get(ResponsePtr, const LLSD& context) const; - virtual void put(ResponsePtr, const LLSD& context, const LLSD& input) const; - virtual void post(ResponsePtr, const LLSD& context, const LLSD& input) const; - virtual void del(ResponsePtr, const LLSD& context) const; + virtual void get(ResponsePtr, const LLSD& context) const; + virtual void put( + ResponsePtr, + const LLSD& context, + const LLSD& input) const; + virtual void post( + ResponsePtr, + const LLSD& context, + const LLSD& input) const; + virtual void del(ResponsePtr, const LLSD& context) const; + virtual void options(ResponsePtr, const LLSD& context) const; //@} diff --git a/indra/llmessage/lliohttpserver.cpp b/indra/llmessage/lliohttpserver.cpp index ec5cb93d69..90f8ef7638 100644 --- a/indra/llmessage/lliohttpserver.cpp +++ b/indra/llmessage/lliohttpserver.cpp @@ -57,10 +57,13 @@ 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 CONTEXT_VERB("verb"); +static const std::string CONTEXT_HEADERS("headers"); 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"); +static const std::string HTTP_VERB_OPTIONS("OPTIONS"); static LLIOHTTPServer::timing_callback_t sTimingCallback = NULL; static void* sTimingCallbackData = NULL; @@ -130,6 +133,7 @@ private: LLSD mGoodResult; S32 mStatusCode; std::string mStatusMessage; + LLSD mHeaders; }; LLIOPipe::EStatus LLHTTPPipe::process_impl( @@ -164,7 +168,7 @@ 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) { mNode.get(LLHTTPNode::ResponsePtr(mResponse), context); @@ -185,6 +189,10 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( { mNode.del(LLHTTPNode::ResponsePtr(mResponse), context); } + else if(verb == HTTP_VERB_OPTIONS) + { + mNode.options(LLHTTPNode::ResponsePtr(mResponse), context); + } else { mResponse->methodNotAllowed(); @@ -231,7 +239,9 @@ LLIOPipe::EStatus LLHTTPPipe::process_impl( case STATE_GOOD_RESULT: { - context[CONTEXT_RESPONSE]["contentType"] = "application/xml"; + LLSD headers = mHeaders; + headers["Content-Type"] = "application/xml"; + context[CONTEXT_RESPONSE][CONTEXT_HEADERS] = headers; LLBufferStream ostr(channels, buffer.get()); LLSDSerialize::toXML(mGoodResult, ostr); @@ -240,7 +250,9 @@ 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()); @@ -287,6 +299,7 @@ void LLHTTPPipe::Response::result(const LLSD& r) mPipe->mStatusMessage = "OK"; mPipe->mGoodResult = r; mPipe->mState = STATE_GOOD_RESULT; + mPipe->mHeaders = mHeaders; mPipe->unlockChain(); } @@ -302,6 +315,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(); } @@ -389,17 +403,24 @@ 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()); @@ -606,11 +627,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; @@ -721,7 +743,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,7 +787,7 @@ 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(); diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 20876d630f..85cb5dd007 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -28,7 +28,6 @@ include(LScript) include(Linking) include(Mozlib) include(NDOF) -include(Smartheap) include(TemplateCheck) include(UI) include(UnixInstall) diff --git a/indra/test/lliohttpserver_tut.cpp b/indra/test/lliohttpserver_tut.cpp index 5401d1a8ae..e5607cdad7 100644 --- a/indra/test/lliohttpserver_tut.cpp +++ b/indra/test/lliohttpserver_tut.cpp @@ -327,6 +327,23 @@ namespace tut ensure_starts_with("large echo status", result, "HTTP/1.0 200 OK\r\n"); } + template<> template<> + void HTTPServiceTestObject::test<8>() + { + // test the OPTIONS http method -- the default implementation + // should return the X-Documentation-URL + std::ostringstream http_request; + http_request << "OPTIONS / HTTP/1.0\r\nHost: localhost\r\n\r\n"; + bool timeout = false; + std::string result = makeRequest("/", http_request.str(), timeout); + ensure_starts_with("OPTIONS verb ok", result, "HTTP/1.0 200 OK\r\n"); + ensure_contains( + "Doc url header exists", + result, + "X-Documentation-URL: http://localhost"); + } + + /* TO DO: test generation of not found and method not allowed errors */ |