/**
 * @file httprequest.h
 * @brief Public-facing declarations for HttpRequest class
 *
 * $LicenseInfo:firstyear=2012&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2012, 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_REQUEST_H_
#define	_LLCORE_HTTP_REQUEST_H_


#include "httpcommon.h"
#include "httphandler.h"


namespace LLCore
{

class HttpRequestQueue;
class HttpReplyQueue;
class HttpService;
class HttpOptions;
class HttpHeaders;
class HttpOperation;
class BufferArray;

/// HttpRequest supplies the entry into the HTTP transport
/// services in the LLCore libraries.  Services provided include:
///
/// - Some, but not all, global initialization of libcurl.
/// - Starting asynchronous, threaded HTTP requests.
/// - Definition of policy classes affect request handling.
/// - Utilities to control request options and headers
///
/// Requests
///
/// The class supports the current HTTP request operations:
///
/// - requestGetByteRange:  GET with Range header for a single range of bytes
///
/// Policy Classes
///
/// <TBD>
///
/// Usage
///
/// <TBD>
///
/// Threading:  An instance may only be used by one application/
/// consumer thread.  But a thread may have as many instances of
/// this as it likes.
///
/// Allocation:  Not refcounted, may be stack allocated though that
/// hasn't been tested.  Queued requests can still run and any
/// queued replies will keep refcounts to the reply queue leading
/// to memory leaks.
///
/// @pre Before using this class (static or instances), some global
/// initialization is required.  See @see httpcommon.h for more information.
///
/// @nosubgrouping
///

class HttpRequest
{
public:
	HttpRequest();
	virtual ~HttpRequest();

private:
	HttpRequest(const HttpRequest &);			// Disallowed
	void operator=(const HttpRequest &);		// Disallowed		

public:
	typedef unsigned int policy_t;
	typedef unsigned int priority_t;
	
public:
	/// @name PolicyMethods
	/// @{

	/// Represents a default, catch-all policy class that guarantees
	/// eventual service for any HTTP request.
	static const int DEFAULT_POLICY_ID = 0;

	enum EGlobalPolicy
	{
		/// Maximum number of connections the library will use to
		/// perform operations.  This is somewhat soft as the underlying
		/// transport will cache some connections (up to 5).

		/// A long value setting the maximum number of connections
		/// allowed over all policy classes.  Note that this will be
		/// a somewhat soft value.  There may be an additional five
		/// connections per policy class depending upon runtime
		/// behavior.
		GP_CONNECTION_LIMIT,

		/// String containing a system-appropriate directory name
		/// where SSL certs are stored.
		GP_CA_PATH,

		/// String giving a full path to a file containing SSL certs.
		GP_CA_FILE,

		/// String of host/port to use as simple HTTP proxy.  This is
		/// going to change in the future into something more elaborate
		/// that may support richer schemes.
		GP_HTTP_PROXY,

		/// Long value that if non-zero enables the use of the
		/// traditional LLProxy code for http/socks5 support.  If
		/// enabled, has priority over GP_HTTP_PROXY.
		GP_LLPROXY,

		/// Long value setting the logging trace level for the
		/// library.  Possible values are:
		/// 0 - No tracing (default)
		/// 1 - Basic tracing of request start, stop and major events.
		/// 2 - Connection, header and payload size information from
		///     HTTP transactions.
		/// 3 - Partial logging of payload itself.
		///
		/// These values are also used in the trace modes for
		/// individual requests in HttpOptions.  Also be aware that
		/// tracing tends to impact performance of the viewer.
		GP_TRACE
	};

	/// Set a parameter on a global policy option.  Calls
	/// made after the start of the servicing thread are
	/// not honored and return an error status.
	///
	/// @param opt		Enum of option to be set.
	/// @param value	Desired value of option.
	/// @return			Standard status code.
	static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, long value);
	static HttpStatus setPolicyGlobalOption(EGlobalPolicy opt, const std::string & value);

	/// Create a new policy class into which requests can be made.
	///
	/// @return			If positive, the policy_id used to reference
	///					the class in other methods.  If 0, an error
	///					occurred and @see getStatus() may provide more
	///					detail on the reason.
	static policy_t createPolicyClass();

	enum EClassPolicy
	{
		/// Limits the number of connections used for the class.
		CP_CONNECTION_LIMIT,

		/// Limits the number of connections used for a single
		/// literal address/port pair within the class.
		CP_PER_HOST_CONNECTION_LIMIT,

		/// Suitable requests are allowed to pipeline on their
		/// connections when they ask for it.
		CP_ENABLE_PIPELINING
	};
	
	/// Set a parameter on a class-based policy option.  Calls
	/// made after the start of the servicing thread are
	/// not honored and return an error status.
	///
	/// @param policy_id		ID of class as returned by @see createPolicyClass().
	/// @param opt				Enum of option to be set.
	/// @param value			Desired value of option.
	/// @return					Standard status code.
	static HttpStatus setPolicyClassOption(policy_t policy_id, EClassPolicy opt, long value);

	/// @}

	/// @name RequestMethods
	///
	/// @{

	/// Some calls expect to succeed as the normal part of operation and so
	/// return a useful value rather than a status.  When they do fail, the
	/// status is saved and can be fetched with this method.
	///
	/// @return			Status of the failing method invocation.  If the
	///					preceding call succeeded or other HttpStatus
	///					returning calls immediately preceded this method,
	///					the returned value may not be reliable.
	///
	HttpStatus getStatus() const;

	/// Queue a full HTTP GET request to be issued for entire entity.
	/// The request is queued and serviced by the working thread and
	/// notification of completion delivered to the optional HttpHandler
	/// argument during @see update() calls.
	///
	/// With a valid handle returned, it can be used to reference the
	/// request in other requests (like cancellation) and will be an
	/// argument when any HttpHandler object is invoked.
	///
	/// Headers supplied by default:
	/// - Connection: keep-alive
	/// - Accept: */*
	/// - Accept-Encoding: deflate, gzip
	/// - Keep-alive: 300
	/// - Host: <stuff>
	///
	/// Some headers excluded by default:
	/// - Pragma:
	/// - Cache-control:
	/// - Range:
	/// - Transfer-Encoding:
	/// - Referer:
	///
	/// @param	policy_id		Default or user-defined policy class under
	///							which this request is to be serviced.
	/// @param	priority		Standard priority scheme inherited from
	///							Indra code base (U32-type scheme).
	/// @param	url				URL with any encoded query parameters to
	///							be accessed.
	/// @param	options			Optional instance of an HttpOptions object
	///							to provide additional controls over the request
	///							function for this request only.  Any such
	///							object then becomes shared-read across threads
	///							and no code should modify the HttpOptions
	///							instance.
	/// @param	headers			Optional instance of an HttpHeaders object
	///							to provide additional and/or overridden
	///							headers for the request.  As with options,
	///							the instance becomes shared-read across threads
	///							and no code should modify the HttpHeaders
	///							instance.
	/// @param	handler			Optional pointer to an HttpHandler instance
	///							whose onCompleted() method will be invoked
	///							during calls to update().  This is a non-
	///							reference-counted object which would be a
	///							problem for shutdown and other edge cases but
	///							the pointer is only dereferenced during
	///							calls to update().
	///
	/// @return					The handle of the request if successfully
	///							queued or LLCORE_HTTP_HANDLE_INVALID if the
	///							request could not be queued.  In the latter
	///							case, @see getStatus() will return more info.
	///
	HttpHandle requestGet(policy_t policy_id,
						  priority_t priority,
						  const std::string & url,
						  HttpOptions * options,
						  HttpHeaders * headers,
						  HttpHandler * handler);


	/// Queue a full HTTP GET request to be issued with a 'Range' header.
	/// The request is queued and serviced by the working thread and
	/// notification of completion delivered to the optional HttpHandler
	/// argument during @see update() calls.
	///
	/// With a valid handle returned, it can be used to reference the
	/// request in other requests (like cancellation) and will be an
	/// argument when any HttpHandler object is invoked.
	///
	/// Headers supplied by default:
	/// - Connection: keep-alive
	/// - Accept: */*
	/// - Accept-Encoding: deflate, gzip
	/// - Keep-alive: 300
	/// - Host: <stuff>
	/// - Range: <stuff>  (will be omitted if offset == 0 and len == 0)
	///
	/// Some headers excluded by default:
	/// - Pragma:
	/// - Cache-control:
	/// - Transfer-Encoding:
	/// - Referer:
	///
	/// @param	policy_id		@see requestGet()
	/// @param	priority		"
	/// @param	url				"
	/// @param	offset			Offset of first byte into resource to be returned.
	/// @param	len				Count of bytes to be returned
	/// @param	options			@see requestGet()
	/// @param	headers			"
	/// @param	handler			"
	/// @return					"
	///
	HttpHandle requestGetByteRange(policy_t policy_id,
								   priority_t priority,
								   const std::string & url,
								   size_t offset,
								   size_t len,
								   HttpOptions * options,
								   HttpHeaders * headers,
								   HttpHandler * handler);


	/// Queue a full HTTP POST.  Query arguments and body may
	/// be provided.  Caller is responsible for escaping and
	/// encoding and communicating the content types.
	///
	/// Headers supplied by default:
	/// - Connection: keep-alive
	/// - Accept: */*
	/// - Accept-Encoding: deflate, gzip
	/// - Keep-Alive: 300
	/// - Host: <stuff>
	/// - Content-Length: <digits>
	/// - Content-Type: application/x-www-form-urlencoded
	///
	/// Some headers excluded by default:
	/// - Pragma:
	/// - Cache-Control:
	/// - Transfer-Encoding: ... chunked ...
	/// - Referer:
	/// - Content-Encoding:
	/// - Expect:
	///
	/// @param	policy_id		@see requestGet()
	/// @param	priority		"
	/// @param	url				"
	/// @param	body			Byte stream to be sent as the body.  No
	///							further encoding or escaping will be done
	///							to the content.
	/// @param	options			@see requestGet()K(optional)
	/// @param	headers			"
	/// @param	handler			"
	/// @return					"
	///
	HttpHandle requestPost(policy_t policy_id,
						   priority_t priority,
						   const std::string & url,
						   BufferArray * body,
						   HttpOptions * options,
						   HttpHeaders * headers,
						   HttpHandler * handler);


	/// Queue a full HTTP PUT.  Query arguments and body may
	/// be provided.  Caller is responsible for escaping and
	/// encoding and communicating the content types.
	///
	/// Headers supplied by default:
	/// - Connection: keep-alive
	/// - Accept: */*
	/// - Accept-Encoding: deflate, gzip
	/// - Keep-Alive: 300
	/// - Host: <stuff>
	/// - Content-Length: <digits>
	///
	/// Some headers excluded by default:
	/// - Pragma:
	/// - Cache-Control:
	/// - Transfer-Encoding: ... chunked ...
	/// - Referer:
	/// - Content-Encoding:
	/// - Expect:
	/// - Content-Type:
	///
	/// @param	policy_id		@see requestGet()
	/// @param	priority		"
	/// @param	url				"
	/// @param	body			Byte stream to be sent as the body.  No
	///							further encoding or escaping will be done
	///							to the content.
	/// @param	options			@see requestGet()K(optional)
	/// @param	headers			"
	/// @param	handler			"
	/// @return					"
	///
	HttpHandle requestPut(policy_t policy_id,
						  priority_t priority,
						  const std::string & url,
						  BufferArray * body,
						  HttpOptions * options,
						  HttpHeaders * headers,
						  HttpHandler * handler);


	/// Queue a NoOp request.
	/// The request is queued and serviced by the working thread which
	/// immediately processes it and returns the request to the reply
	/// queue.
	///
	/// @param	handler			@see requestGet()
	/// @return					"
	///
	HttpHandle requestNoOp(HttpHandler * handler);

	/// While all the heavy work is done by the worker thread, notifications
	/// must be performed in the context of the application thread.  These
	/// are done synchronously during calls to this method which gives the
	/// library control so notification can be performed.  Application handlers
	/// are expected to return 'quickly' and do any significant processing
	/// outside of the notification callback to onCompleted().
	///
	/// @param	usecs			Maximum number of wallclock microseconds to
	///							spend in the call.  As hinted at above, this
	///							is partly a function of application code so it's
	///							a soft limit.  A '0' value will run without
	///							time limit until everything queued has been
	///							delivered.
	///
	/// @return					Standard status code.
	HttpStatus update(long usecs);

	/// @}
	
	/// @name RequestMgmtMethods
	///
	/// @{
	
	HttpHandle requestCancel(HttpHandle request, HttpHandler *);

	/// Request that a previously-issued request be reprioritized.
	/// The status of whether the change itself succeeded arrives
	/// via notification.  
	///
	/// @param	request			Handle of previously-issued request to
	///							be changed.
	/// @param	priority		New priority value.
	/// @param	handler			@see requestGet()
	/// @return					"
	///
	HttpHandle requestSetPriority(HttpHandle request, priority_t priority, HttpHandler * handler);

	/// @}

	/// @name UtilityMethods
	///
	/// @{

	/// Initialization method that needs to be called before queueing any
	/// requests.  Doesn't start the worker thread and may be called befoer
	/// or after policy setup.
	static HttpStatus createService();

	/// Mostly clean shutdown of services prior to exit.  Caller is expected
	/// to have stopped a running worker thread before calling this.
	static HttpStatus destroyService();

	/// Called once after @see createService() to start the worker thread.
	/// Stopping the thread is achieved by requesting it via @see requestStopThread().
	/// May be called before or after requests are issued.
	static HttpStatus startThread();

	/// Queues a request to the worker thread to have it stop processing
	/// and exit (without exiting the program).  When the operation is
	/// picked up by the worker thread, it immediately processes it and
	/// begins detaching from refcounted resources like request and
	/// reply queues and then returns to the host OS.  It *does* queue a
	/// reply to give the calling application thread a notification that
	/// the operation has been performed.
	///
	/// @param	handler			(optional)
	/// @return					The handle of the request if successfully
	///							queued or LLCORE_HTTP_HANDLE_INVALID if the
	///							request could not be queued.  In the latter
	///							case, @see getStatus() will return more info.
	///							As the request cannot be cancelled, the handle
	///							is generally not useful.
	///
	HttpHandle requestStopThread(HttpHandler * handler);

	/// Queue a Spin request.
	/// DEBUG/TESTING ONLY.  This puts the worker into a CPU spin for
	/// test purposes.
	///
	/// @param	mode			0 for hard spin, 1 for soft spin
	/// @return					Standard handle return cases.
	///
	HttpHandle requestSpin(int mode);

	/// @}
	
	/// @name DynamicPolicyMethods
	///
	/// @{

	/// Request that a running transport pick up a new proxy setting.
	/// An empty string will indicate no proxy is to be used.
	HttpHandle requestSetHttpProxy(const std::string & proxy, HttpHandler * handler);

    /// @}

protected:
	void generateNotification(HttpOperation * op);

private:
	/// @name InstanceData
	///
	/// @{
	HttpStatus			mLastReqStatus;
	HttpReplyQueue *	mReplyQueue;
	HttpRequestQueue *	mRequestQueue;
	
	/// @}
	
	// ====================================
	/// @name GlobalState
	///
	/// @{
	///
	/// Must be established before any threading is allowed to
	/// start.
	///
	static policy_t		sNextPolicyID;
	
	/// @}
	// End Global State
	// ====================================
	
};  // end class HttpRequest


}  // end namespace LLCore



#endif	// _LLCORE_HTTP_REQUEST_H_