/** * @file httpcommon.h * @brief Public-facing declarations and definitions of common types * * $LicenseInfo:firstyear=2012&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2012-2013, 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. * * 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. * * 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 * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ #ifndef _LLCORE_HTTP_COMMON_H_ #define _LLCORE_HTTP_COMMON_H_ /// @package LLCore::HTTP /// /// This library implements a high-level, Indra-code-free client interface to /// HTTP services based on actual patterns found in the viewer and simulator. /// Interfaces are similar to those supplied by the legacy classes /// LLCurlRequest and LLHTTPClient. To that is added a policy scheme that /// allows an application to specify connection behaviors: limits on /// connections, HTTP keepalive, HTTP pipelining, retry-on-error limits, etc. /// /// Features of the library include: /// - Single, private working thread where all transport and processing occurs. /// - Support for multiple consumers running in multiple threads. /// - Scatter/gather (a.k.a. buffer array) model for bulk data movement. /// - Reference counting used for many object instance lifetimes. /// - Minimal data sharing across threads for correctness and low latency. /// /// The public interface is declared in a few key header files: /// - "llcorehttp/bufferarray.h" /// - "llcorehttp/httpcommon.h" /// - "llcorehttp/httphandler.h" /// - "llcorehttp/httpheaders.h" /// - "llcorehttp/httpoptions.h" /// - "llcorehttp/httprequest.h" /// - "llcorehttp/httpresponse.h" /// /// The library is still under early development and particular users /// may need access to internal implementation details that are found /// in the _*.h header files. But this is a crutch to be avoided if at /// all possible and probably indicates some interface work is neeeded. /// /// Using the library is fairly easy. Global setup needs a few /// steps: /// /// - libcurl initialization including thread-safely callbacks for SSL: /// . curl_global_init(...) /// . CRYPTO_set_locking_callback(...) /// . CRYPTO_set_id_callback(...) /// - HttpRequest::createService() called to instantiate singletons /// and support objects. /// /// An HTTP consumer in an application, and an application may have many /// consumers, does a few things: /// /// - Instantiate and retain an object based on HttpRequest. This /// object becomes the portal into runtime services for the consumer. /// - Derive or mixin the HttpHandler class if you want notification /// when requests succeed or fail. This object's onCompleted() /// method is invoked and an instance can be shared across /// requests. /// /// Issuing a request is straightforward: /// - Construct a suitable URL. /// - Configure HTTP options for the request. (optional) /// - Build a list of additional headers. (optional) /// - Invoke one of the requestXXXX() methods (requestGetByteRange, /// requestPost, etc.) on the HttpRequest instance supplying the /// above along with a policy class, a priority and an optional /// pointer to an HttpHandler instance. Work is then queued to /// the worker thread and occurs asynchronously. /// - Periodically invoke the update() method on the HttpRequest /// instance which performs completion notification to HttpHandler /// objects. /// - Do completion processing in your onCompletion() method. /// /// Code fragments: /// Rather than a poorly-maintained example in comments, look in the /// example subdirectory which is a minimal yet functional tool to do /// GET request performance testing. With four calls: /// /// init_curl(); /// LLCore::HttpRequest::createService(); /// LLCore::HttpRequest::startThread(); /// LLCore::HttpRequest * hr = new LLCore::HttpRequest(); /// /// the program is basically ready to issue requests. /// #include "linden_common.h" // Modifies curl/curl.h interfaces #include <string> namespace LLCore { /// All queued requests are represented by an HttpHandle value. /// The invalid value is returned when a request failed to queue. /// The actual status for these failures is then fetched with /// HttpRequest::getStatus(). /// /// The handle is valid only for the life of a request. On /// return from any HttpHandler notification, the handle immediately /// becomes invalid and may be recycled for other queued requests. typedef void * HttpHandle; #define LLCORE_HTTP_HANDLE_INVALID (NULL) /// For internal scheduling and metrics, we use a microsecond /// timebase compatible with the environment. typedef U64 HttpTime; /// Error codes defined by the library itself as distinct from /// libcurl (or any other transport provider). enum HttpError { // Successful value compatible with the libcurl codes. HE_SUCCESS = 0, // Intended for HTTP reply codes 100-999, indicates that // the reply should be considered an error by the application. HE_REPLY_ERROR = 1, // Service is shutting down and requested operation will // not be queued or performed. HE_SHUTTING_DOWN = 2, // Operation was canceled by request. HE_OP_CANCELED = 3, // Invalid content range header received. HE_INV_CONTENT_RANGE_HDR = 4, // Request handle not found HE_HANDLE_NOT_FOUND = 5, // Invalid datatype for option/setting HE_INVALID_ARG = 6, // Option hasn't been explicitly set HE_OPT_NOT_SET = 7, // Option not dynamic, must be set during init phase HE_OPT_NOT_DYNAMIC = 8, // Invalid HTTP status code returned by server HE_INVALID_HTTP_STATUS = 9 }; // end enum HttpError /// HttpStatus encapsulates errors from libcurl (easy, multi), HTTP /// reply status codes and internal errors as well. The encapsulation /// isn't expected to completely isolate the caller from libcurl but /// basic operational tests (success or failure) are provided. /// /// Non-HTTP status are encoded as (type, status) with type being /// one of: EXT_CURL_EASY, EXT_CURL_MULTI or LLCORE and status /// being the success/error code from that domain. HTTP status /// is encoded as (status, error_flag). Status should be in the /// range [100, 999] and error_flag is either HE_SUCCESS or /// HE_REPLY_ERROR to indicate whether this should be treated as /// a successful status or an error. The application is responsible /// for making that determination and a range like [200, 299] isn't /// automatically assumed to be definitive. /// /// Examples: /// /// 1. Construct a default, successful status code: /// HttpStatus(); /// /// 2. Construct a successful, HTTP 200 status code: /// HttpStatus(200); /// /// 3. Construct a failed, HTTP 404 not-found status code: /// HttpStatus(404); /// /// 4. Construct a failed libcurl couldn't connect status code: /// HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_COULDNT_CONNECT); /// /// 5. Construct an HTTP 301 status code to be treated as success: /// HttpStatus(301, HE_SUCCESS); /// struct HttpStatus { typedef unsigned short type_enum_t; HttpStatus() : mType(LLCORE), mStatus(HE_SUCCESS) {} HttpStatus(type_enum_t type, short status) : mType(type), mStatus(status) {} HttpStatus(int http_status) : mType(http_status), mStatus(http_status >= 200 && http_status <= 299 ? HE_SUCCESS : HE_REPLY_ERROR) { llassert(http_status >= 100 && http_status <= 999); } HttpStatus(const HttpStatus & rhs) : mType(rhs.mType), mStatus(rhs.mStatus) {} HttpStatus & operator=(const HttpStatus & rhs) { // Don't care if lhs & rhs are the same object mType = rhs.mType; mStatus = rhs.mStatus; return *this; } static const type_enum_t EXT_CURL_EASY = 0; static const type_enum_t EXT_CURL_MULTI = 1; static const type_enum_t LLCORE = 2; type_enum_t mType; short mStatus; /// Test for successful status in the code regardless /// of error source (internal, libcurl). /// /// @return 'true' when status is successful. /// operator bool() const { return 0 == mStatus; } /// Inverse of previous operator. /// /// @return 'true' on any error condition bool operator !() const { return 0 != mStatus; } /// Equality and inequality tests to bypass bool conversion /// which will do the wrong thing in conditional expressions. bool operator==(const HttpStatus & rhs) const { return mType == rhs.mType && mStatus == rhs.mStatus; } bool operator!=(const HttpStatus & rhs) const { return ! operator==(rhs); } /// Convert to single numeric representation. Mainly /// for logging or other informal purposes. Also /// creates an ambiguous second path to integer conversion /// which tends to find programming errors such as formatting /// the status to a stream (operator<<). operator unsigned long() const; unsigned long toULong() const { return operator unsigned long(); } /// And to convert to a hex string. std::string toHex() const; /// Convert status to a string representation. For /// success, returns an empty string. For failure /// statuses, a string as appropriate for the source of /// the error code (libcurl easy, libcurl multi, or /// LLCore itself). std::string toString() const; /// Returns true if the status value represents an /// HTTP response status (100 - 999). bool isHttpStatus() const { return mType >= type_enum_t(100) && mType <= type_enum_t(999); } /// Returns true if the status is one that will be retried /// internally. Provided for external consumption for cases /// where that logic needs to be replicated. Only applies /// to failed statuses, successful statuses will return false. bool isRetryable() const; }; // end struct HttpStatus } // end namespace LLCore #endif // _LLCORE_HTTP_COMMON_H_