From f945415210f0e18c2c6d941fda6b7d45cb0f06f1 Mon Sep 17 00:00:00 2001 From: Don Kjer Date: Wed, 13 Mar 2013 06:26:25 +0000 Subject: Large changes to the LLCurl::Responder API, as well as pulling in some changes to common libraries from the server codebase: * Additional error checking in http handlers. * Uniform log spam for http errors. * Switch to using constants for http heads and status codes. * Fixed bugs in incorrectly checking if parsing LLSD xml resulted in an error. * Reduced spam regarding LLSD parsing errors in the default completedRaw http handler. It should not longer be necessary to short-circuit completedRaw to avoid spam. * Ported over a few bug fixes from the server code. * Switch mode http status codes to use S32 instead of U32. * Ported LLSD::asStringRef from server code; avoids copying strings all over the place. * Ported server change to LLSD::asBinary; this always returns a reference now instead of copying the entire binary blob. * Ported server pretty notation format (and pretty binary format) to llsd serialization. * The new LLCurl::Responder API no longer has two error handlers to choose from. Overriding the following methods have been deprecated: ** error - use httpFailure ** errorWithContent - use httpFailure ** result - use httpSuccess ** completed - use httpCompleted ** completedHeader - no longer necessary; call getResponseHeaders() from a completion method to obtain these headers. * In order to 'catch' a completed http request, override one of these methods: ** httpSuccess - Called for any 2xx status code. ** httpFailure - Called for any non-2xx status code. ** httpComplete - Called for all status codes. Default implementation is to call either httpSuccess or httpFailure. * It is recommended to keep these methods protected/private in order to avoid triggering of these methods without using a 'push' method (see below). * Uniform error handling should followed whenever possible by calling a variant of this during httpFailure: ** llwarns << dumpResponse() << llendl; * Be sure to include LOG_CLASS(your_class_name) in your class in order for the log entry to give more context. * In order to 'push' a result into the responder, you should no longer call error, errorWithContent, result, or completed. * Nor should you directly call httpSuccess/Failure/Completed (unless passing a message up to a parent class). * Instead, you can set the internal content of a responder and trigger a corresponding method using the following methods: ** successResult - Sets results and calls httpSuccess ** failureResult - Sets results and calls httpFailure ** completedResult - Sets results and calls httpCompleted * To obtain information about a the response from a reponder method, use the following getters: ** getStatus - HTTP status code ** getReason - Reason string ** getContent - Content (Parsed body LLSD) ** getResponseHeaders - Response Headers (LLSD map) ** getHTTPMethod - HTTP method of the request ** getURL - URL of the request * It is still possible to override completeRaw if you want to manipulate data directly out of LLPumpIO. * See indra/llmessage/llcurl.h for more information. --- indra/llmessage/llhttpconstants.cpp | 219 ++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 indra/llmessage/llhttpconstants.cpp (limited to 'indra/llmessage/llhttpconstants.cpp') diff --git a/indra/llmessage/llhttpconstants.cpp b/indra/llmessage/llhttpconstants.cpp new file mode 100644 index 0000000000..2134024a14 --- /dev/null +++ b/indra/llmessage/llhttpconstants.cpp @@ -0,0 +1,219 @@ +/** + * @file llhttpconstants.cpp + * @brief Implementation of the HTTP request / response constant lookups + * + * $LicenseInfo:firstyear=2013&license=viewergpl$ + * + * Copyright (c) 2013, 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 "llhttpconstants.h" +#include "lltimer.h" + +// for curl_getdate() (apparently parsing RFC 1123 dates is hard) +#include + +const std::string HTTP_HEADER_ACCEPT("Accept"); +const std::string HTTP_HEADER_ACCEPT_CHARSET("Accept-Charset"); +const std::string HTTP_HEADER_ACCEPT_ENCODING("Accept-Encoding"); +const std::string HTTP_HEADER_ACCEPT_LANGUAGE("Accept-Language"); +const std::string HTTP_HEADER_ACCEPT_RANGES("Accept-Ranges"); +const std::string HTTP_HEADER_AGE("Age"); +const std::string HTTP_HEADER_ALLOW("Allow"); +const std::string HTTP_HEADER_AUTHORIZATION("Authorization"); +const std::string HTTP_HEADER_CACHE_CONTROL("Cache-Control"); +const std::string HTTP_HEADER_CONNECTION("Connection"); +const std::string HTTP_HEADER_CONTENT_DESCRIPTION("Content-Description"); +const std::string HTTP_HEADER_CONTENT_ENCODING("Content-Encoding"); +const std::string HTTP_HEADER_CONTENT_ID("Content-ID"); +const std::string HTTP_HEADER_CONTENT_LANGUAGE("Content-Language"); +const std::string HTTP_HEADER_CONTENT_LENGTH("Content-Length"); +const std::string HTTP_HEADER_CONTENT_LOCATION("Content-Location"); +const std::string HTTP_HEADER_CONTENT_MD5("Content-MD5"); +const std::string HTTP_HEADER_CONTENT_RANGE("Content-Range"); +const std::string HTTP_HEADER_CONTENT_TRANSFER_ENCODING("Content-Transfer-Encoding"); +const std::string HTTP_HEADER_CONTENT_TYPE("Content-Type"); +const std::string HTTP_HEADER_COOKIE("Cookie"); +const std::string HTTP_HEADER_DATE("Date"); +const std::string HTTP_HEADER_DESTINATION("Destination"); +const std::string HTTP_HEADER_ETAG("ETag"); +const std::string HTTP_HEADER_EXPECT("Expect"); +const std::string HTTP_HEADER_EXPIRES("Expires"); +const std::string HTTP_HEADER_FROM("From"); +const std::string HTTP_HEADER_HOST("Host"); +const std::string HTTP_HEADER_IF_MATCH("If-Match"); +const std::string HTTP_HEADER_IF_MODIFIED_SINCE("If-Modified-Since"); +const std::string HTTP_HEADER_IF_NONE_MATCH("If-None-Match"); +const std::string HTTP_HEADER_IF_RANGE("If-Range"); +const std::string HTTP_HEADER_IF_UNMODIFIED_SINCE("If-Unmodified-Since"); +const std::string HTTP_HEADER_KEEP_ALIVE("Keep-Alive"); +const std::string HTTP_HEADER_LAST_MODIFIED("Last-Modified"); +const std::string HTTP_HEADER_LOCATION("Location"); +const std::string HTTP_HEADER_MAX_FORWARDS("Max-Forwards"); +const std::string HTTP_HEADER_MIME_VERSION("MIME-Version"); +const std::string HTTP_HEADER_PRAGMA("Pragma"); +const std::string HTTP_HEADER_PROXY_AUTHENTICATE("Proxy-Authenticate"); +const std::string HTTP_HEADER_PROXY_AUTHORIZATION("Proxy-Authorization"); +const std::string HTTP_HEADER_RANGE("Range"); +const std::string HTTP_HEADER_REFERER("Referer"); +const std::string HTTP_HEADER_RETRY_AFTER("Retry-After"); +const std::string HTTP_HEADER_SERVER("Server"); +const std::string HTTP_HEADER_SET_COOKIE("Set-Cookie"); +const std::string HTTP_HEADER_TE("TE"); +const std::string HTTP_HEADER_TRAILER("Trailer"); +const std::string HTTP_HEADER_TRANSFER_ENCODING("Transfer-Encoding"); +const std::string HTTP_HEADER_UPGRADE("Upgrade"); +const std::string HTTP_HEADER_USER_AGENT("User-Agent"); +const std::string HTTP_HEADER_VARY("Vary"); +const std::string HTTP_HEADER_VIA("Via"); +const std::string HTTP_HEADER_WARNING("Warning"); +const std::string HTTP_HEADER_WWW_AUTHENTICATE("WWW-Authenticate"); + + +// Sadly, our proxied headers do not follow http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html +// We need to deal with lowercase headers +const std::string HTTP_HEADER_LOWER_ACCEPT_LANGUAGE("accept-language"); +const std::string HTTP_HEADER_LOWER_CACHE_CONTROL("cache-control"); +const std::string HTTP_HEADER_LOWER_CONTENT_LENGTH("content-length"); +const std::string HTTP_HEADER_LOWER_CONTENT_TYPE("content-type"); +const std::string HTTP_HEADER_LOWER_HOST("host"); +const std::string HTTP_HEADER_LOWER_USER_AGENT("user-agent"); +const std::string HTTP_HEADER_LOWER_X_FORWARDED_FOR("x-forwarded-for"); + +const std::string HTTP_CONTENT_LLSD_XML("application/llsd+xml"); +const std::string HTTP_CONTENT_OCTET_STREAM("application/octet-stream"); +const std::string HTTP_CONTENT_XML("application/xml"); +const std::string HTTP_CONTENT_JSON("application/json"); +const std::string HTTP_CONTENT_TEXT_HTML("text/html"); +const std::string HTTP_CONTENT_TEXT_HTML_UTF8("text/html; charset=utf-8"); +const std::string HTTP_CONTENT_TEXT_PLAIN_UTF8("text/plain; charset=utf-8"); +const std::string HTTP_CONTENT_TEXT_LLSD("text/llsd"); +const std::string HTTP_CONTENT_TEXT_XML("text/xml"); +const std::string HTTP_CONTENT_TEXT_LSL("text/lsl"); +const std::string HTTP_CONTENT_TEXT_PLAIN("text/plain"); +const std::string HTTP_CONTENT_IMAGE_X_J2C("image/x-j2c"); +const std::string HTTP_CONTENT_IMAGE_J2C("image/j2c"); +const std::string HTTP_CONTENT_IMAGE_JPEG("image/jpeg"); +const std::string HTTP_CONTENT_IMAGE_PNG("image/png"); +const std::string HTTP_CONTENT_IMAGE_BMP("image/bmp"); + +const std::string HTTP_VERB_INVALID("(invalid)"); +const std::string HTTP_VERB_HEAD("HEAD"); +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_MOVE("MOVE"); +const std::string HTTP_VERB_OPTIONS("OPTIONS"); + +const std::string& httpMethodAsVerb(EHTTPMethod method) +{ + static const std::string VERBS[] = + { + HTTP_VERB_INVALID, + HTTP_VERB_HEAD, + HTTP_VERB_GET, + HTTP_VERB_PUT, + HTTP_VERB_POST, + HTTP_VERB_DELETE, + HTTP_VERB_MOVE, + HTTP_VERB_OPTIONS + }; + if(((S32)method <=0) || ((S32)method >= HTTP_METHOD_COUNT)) + { + return VERBS[0]; + } + return VERBS[method]; +} + +bool isHttpInformationalStatus(S32 status) +{ + // Check for status 1xx. + return((100 <= status) && (status < 200)); +} + +bool isHttpGoodStatus(S32 status) +{ + // Check for status 2xx. + return((200 <= status) && (status < 300)); +} + +bool isHttpRedirectStatus(S32 status) +{ + // Check for status 3xx. + return((300 <= status) && (status < 400)); +} + +bool isHttpClientErrorStatus(S32 status) +{ + // Status 499 is sometimes used for re-interpreted status 2xx errors + // based on body content. Treat these as potentially retryable 'server' status errors, + // since we do not have enough context to know if this will always fail. + if (HTTP_INTERNAL_ERROR == status) return false; + + // Check for status 5xx. + return((400 <= status) && (status < 500)); +} + +bool isHttpServerErrorStatus(S32 status) +{ + // Status 499 is sometimes used for re-interpreted status 2xx errors. + // Allow retry of these, since we don't have enough information in this + // context to know if this will always fail. + if (HTTP_INTERNAL_ERROR == status) return true; + + // Check for status 5xx. + return((500 <= status) && (status < 600)); +} + +// Parses 'Retry-After' header contents and returns seconds until retry should occur. +bool getSecondsUntilRetryAfter(const std::string& retry_after, F32& seconds_to_wait) +{ + // *TODO: This needs testing! Not in use yet. + // Examples of Retry-After headers: + // Retry-After: Fri, 31 Dec 1999 23:59:59 GMT + // Retry-After: 120 + + // Check for number of seconds version, first: + char* end = 0; + // Parse as double + double seconds = std::strtod(retry_after.c_str(), &end); + if ( end != 0 && *end == 0 ) + { + // Successful parse + seconds_to_wait = (F32) seconds; + return true; + } + + // Parse rfc1123 date. + time_t date = curl_getdate(retry_after.c_str(), NULL ); + if (-1 == date) return false; + + seconds_to_wait = (F32)date - (F32)LLTimer::getTotalSeconds(); + return true; +} + -- cgit v1.2.3