412 lines
14 KiB
C++
Executable File
412 lines
14 KiB
C++
Executable File
/**
|
|
* @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 (somewhat) 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 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.
|
|
/// - HttpRequest::startThread() to kick off the worker thread and
|
|
/// begin servicing requests.
|
|
///
|
|
/// 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.
|
|
///
|
|
/// Initialization. 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.
|
|
///
|
|
/// HttpHandler. Having started life as a non-indra library,
|
|
/// this code broke away from the classic Responder model and
|
|
/// introduced a handler class to represent an interface for
|
|
/// request responses. This is a non-reference-counted entity
|
|
/// which can be used as a base class or a mixin. An instance
|
|
/// of a handler can be used for each request or can be shared
|
|
/// among any number of requests. Your choice but expect to
|
|
/// code something like the following:
|
|
///
|
|
/// class AppHandler : public LLCore::HttpHandler
|
|
/// {
|
|
/// public:
|
|
/// virtual void onCompleted(HttpHandle handle,
|
|
/// HttpResponse * response)
|
|
/// {
|
|
/// ...
|
|
/// }
|
|
/// ...
|
|
/// };
|
|
/// ...
|
|
/// handler = new handler(...);
|
|
///
|
|
///
|
|
/// Issuing requests. Using 'hr' above,
|
|
///
|
|
/// hr->requestGet(HttpRequest::DEFAULT_POLICY_ID,
|
|
/// 0, // Priority, not used yet
|
|
/// url,
|
|
/// NULL, // options
|
|
/// NULL, // additional headers
|
|
/// handler);
|
|
///
|
|
/// If that returns a value other than LLCORE_HTTP_HANDLE_INVALID,
|
|
/// the request was successfully issued and there will eventally
|
|
/// be a status delivered to the handler. If invalid is returnedd,
|
|
/// the actual status can be retrieved by calling hr->getStatus().
|
|
///
|
|
/// Completing requests and delivering notifications. Operations
|
|
/// are all performed by the worker thread and will be driven to
|
|
/// completion regardless of caller actions. Notification of
|
|
/// completion (success or failure) is done by calls to
|
|
/// HttpRequest::update() which will invoke handlers for completed
|
|
/// requests:
|
|
///
|
|
/// hr->update(0);
|
|
/// // Callbacks into handler->onCompleted()
|
|
///
|
|
///
|
|
/// Threads.
|
|
///
|
|
/// Threads are supported and used by this library. The various
|
|
/// classes, methods and members are documented with thread
|
|
/// constraints which programmers must follow and which are
|
|
/// defined as follows:
|
|
///
|
|
/// consumer Any thread that has instanced HttpRequest and is
|
|
/// issuing requests. A particular instance can only
|
|
/// be used by one consumer thread but a consumer may
|
|
/// have many instances available to it.
|
|
/// init Special consumer thread, usually the main thread,
|
|
/// involved in setting up the library at startup.
|
|
/// worker Thread used internally by the library to perform
|
|
/// HTTP operations. Consumers will not have to deal
|
|
/// with this thread directly but some APIs are reserved
|
|
/// to it.
|
|
/// any Consumer or worker thread.
|
|
///
|
|
/// For the most part, API users will not have to do much in the
|
|
/// way of ensuring thread safely. However, there is a tremendous
|
|
/// amount of sharing between threads of read-only data. So when
|
|
/// documentation declares that an option or header instance
|
|
/// becomes shared between consumer and worker, the consumer must
|
|
/// not modify the shared object.
|
|
///
|
|
/// Internally, there is almost no thread synchronization. During
|
|
/// normal operations (non-init, non-term), only the request queue
|
|
/// and the multiple reply queues are shared between threads and
|
|
/// only here are mutexes used.
|
|
///
|
|
|
|
#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,
|
|
|
|
// Couldn't allocate resource, typically libcurl handle
|
|
HE_BAD_ALLOC = 10
|
|
|
|
}; // 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; ///< mStatus is an error from a curl_easy_*() call
|
|
static const type_enum_t EXT_CURL_MULTI = 1; ///< mStatus is an error from a curl_multi_*() call
|
|
static const type_enum_t LLCORE = 2; ///< mStatus is an HE_* error code
|
|
///< 100-999 directly represent HTTP status codes
|
|
|
|
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;
|
|
|
|
/// Convert status to a compact string representation
|
|
/// of the form: "<type>_<value>". The <type> will be
|
|
/// one of: Core, Http, Easy, Multi, Unknown. And
|
|
/// <value> will be an unsigned integer. More easily
|
|
/// interpreted than the hex representation, it's still
|
|
/// compact and easily searched.
|
|
std::string toTerseString() 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_
|