/* 
 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */
#ifndef __PJLIB_UTIL_HTTP_CLIENT_H__
#define __PJLIB_UTIL_HTTP_CLIENT_H__

/**
 * @file http_client.h
 * @brief Simple HTTP Client
 */
#include <pj/activesock.h>
#include <pjlib-util/types.h>

PJ_BEGIN_DECL

/**
 * @defgroup PJ_HTTP_CLIENT Simple HTTP Client
 * @ingroup PJ_PROTOCOLS
 * @{
 * This contains a simple HTTP client implementation.
 * Some known limitations: 
 * - Does not support chunked Transfer-Encoding.
 */

/**
 * This opaque structure describes the http request.
 */
typedef struct pj_http_req pj_http_req;

/**
 * Defines the maximum number of elements in a pj_http_headers
 * structure.
 */
#define PJ_HTTP_HEADER_SIZE 32

/**
 * HTTP header representation.
 */
typedef struct pj_http_header_elmt
{
    pj_str_t name;      /**< Header name */
    pj_str_t value;     /**< Header value */
} pj_http_header_elmt;

/**
 * This structure describes http request/response headers.
 * Application should call #pj_http_headers_add_elmt() to
 * add a header field.
 */
typedef struct pj_http_headers
{
    /**< Number of header fields */
    unsigned     count;

    /** Header elements/fields */
    pj_http_header_elmt header[PJ_HTTP_HEADER_SIZE];
} pj_http_headers;

/**
 * Structure to save HTTP authentication credential.
 */
typedef struct pj_http_auth_cred
{
    /**
     * Specify specific authentication schemes to be responded. Valid values
     * are "basic" and "digest". If this field is not set, any authentication
     * schemes will be responded.
     *
     * Default is empty.
     */
    pj_str_t    scheme;

    /**
     * Specify specific authentication realm to be responded. If this field
     * is set, only 401/407 response with matching realm will be responded.
     * If this field is not set, any realms will be responded.
     *
     * Default is empty.
     */
    pj_str_t    realm;

    /**
     * Specify authentication username.
     *
     * Default is empty.
     */
    pj_str_t    username;

    /**
     * The type of password in \a data field. Currently only 0 is
     * supported, meaning the \a data contains plain-text password.
     *
     * Default is 0.
     */
    unsigned    data_type;

    /**
     * Specify authentication password. The encoding of the password depends
     * on the value of \a data_type field above.
     *
     * Default is empty.
     */
    pj_str_t    data;

} pj_http_auth_cred;


/**
 * Parameters that can be given during http request creation. Application
 * must initialize this structure with #pj_http_req_param_default().
 */
typedef struct pj_http_req_param 
{
    /** 
     * The address family of the URL.
     *  Default is pj_AF_INET().
     */
    int             addr_family;

    /** 
     * The HTTP request method.
     * Default is GET.
     */
    pj_str_t        method;

    /** 
     * The HTTP protocol version ("1.0" or "1.1").
     * Default is "1.0".
     */
    pj_str_t        version;

    /** 
     * HTTP request operation timeout.
     * Default is PJ_HTTP_DEFAULT_TIMEOUT.
     */
    pj_time_val     timeout;

    /** 
     * User-defined data.
     * Default is NULL.
     */
    void            *user_data;

    /** 
     * HTTP request headers.
     * Default is empty.
     */
    pj_http_headers headers;

    /**
      * This structure describes the http request body. If application
      * specifies the data to send, the data must remain valid until 
      * the HTTP request is sent. Alternatively, application can choose
      * to specify total_size as the total data size to send instead
      * while leaving the data NULL (and its size 0). In this case,
      * HTTP request will then call on_send_data() callback once it is 
      * ready to send the request body. This will be useful if 
      * application does not wish to load the data into the buffer at 
      * once.
      * 
      * Default is empty.
      */
    struct pj_http_reqdata
    {
        void       *data;          /**< Request body data */
        pj_size_t  size;           /**< Request body size */
        pj_size_t  total_size;     /**< If total_size > 0, data */
                                   /**< will be provided later  */
    } reqdata; /**< The request body */

    /**
     * Authentication credential needed to respond to 401/407 response.
     */
    pj_http_auth_cred   auth_cred;

    /**
     * Optional source port range to use when binding the socket.
     * This can be used if the source port needs to be within a certain range
     * for instance due to strict firewall settings. The port used will be
     * randomized within the range.
     *
     * Note that if authentication is configured, the authentication response
     * will be a new transaction
     *
     * Default is 0 (The OS will select the source port automatically)
     */
    pj_uint16_t         source_port_range_start;

    /**
     * Optional source port range to use when binding.
     * The size of the port restriction range
     *
     * Default is 0 (The OS will select the source port automatically))
     */
    pj_uint16_t         source_port_range_size;

    /**
     * Max number of retries if binding to a port fails.
     * Note that this does not adress the scenario where a request times out
     * or errors. This needs to be taken care of by the on_complete callback.
     *
     * Default is 3
     */
    pj_uint16_t         max_retries;

} pj_http_req_param;

/**
 * HTTP authentication challenge, parsed from WWW-Authenticate header.
 */
typedef struct pj_http_auth_chal
{
    pj_str_t    scheme;         /**< Auth scheme.               */
    pj_str_t    realm;          /**< Realm for the challenge.   */
    pj_str_t    domain;         /**< Domain.                    */
    pj_str_t    nonce;          /**< Nonce challenge.           */
    pj_str_t    opaque;         /**< Opaque value.              */
    int         stale;          /**< Stale parameter.           */
    pj_str_t    algorithm;      /**< Algorithm parameter.       */
    pj_str_t    qop;            /**< Quality of protection.     */
} pj_http_auth_chal;

/**
 * This structure describes HTTP response.
 */
typedef struct pj_http_resp
{
    pj_str_t        version;        /**< HTTP version of the server */
    pj_uint16_t     status_code;    /**< Status code of the request */
    pj_str_t        reason;         /**< Reason phrase */
    pj_http_headers headers;        /**< Response headers */
    pj_http_auth_chal auth_chal;    /**< Parsed WWW-Authenticate header, if
                                         any. */
    pj_int32_t      content_length; /**< The value of content-length header
                                         field. -1 if not specified. */
    void            *data;          /**< Data received */
    pj_size_t       size;           /**< Data size */
} pj_http_resp;

/**
 * This structure describes HTTP URL.
 */
typedef struct pj_http_url
{
    pj_str_t    username;           /**< Username part */
    pj_str_t    passwd;             /**< Password part */
    pj_str_t    protocol;           /**< Protocol used */
    pj_str_t    host;               /**< Host name */
    pj_uint16_t port;               /**< Port number */
    pj_str_t    path;               /**< Path */
} pj_http_url;

/**
 * This structure describes the callbacks to be called by the HTTP request.
 */
typedef struct pj_http_req_callback
{
    /**
     * This callback is called when a complete HTTP response header 
     * is received.
     *
     * @param http_req  The http request.
     * @param resp      The response of the request.
     */
    void (*on_response)(pj_http_req *http_req, const pj_http_resp *resp);

    /**
     * This callback is called when the HTTP request is ready to send
     * its request body. Application may wish to use this callback if
     * it wishes to load the data at a later time or if it does not 
     * wish to load the whole data into memory. In order for this
     * callback to be called, application MUST set http_req_param.total_size
     * to a value greater than 0.
     *
     * @param http_req  The http request.
     * @param data      Pointer to the data that will be sent. Application
     *                  must set the pointer to the current data chunk/segment
     *                  to be sent. Data must remain valid until the next 
     *                  on_send_data() callback or for the last segment,
     *                  until it is sent.
     * @param size      Pointer to the data size that will be sent.
     */
    void (*on_send_data)(pj_http_req *http_req,
                         void **data, pj_size_t *size);

    /**
     * This callback is called when a segment of response body data
     * arrives. If this callback is specified (i.e. not NULL), the
     * on_complete() callback will be called with zero-length data
     * (within the response parameter), hence the application must 
     * store and manage its own data buffer, otherwise the 
     * on_complete() callback will be called with the response 
     * parameter containing the complete data. 
     * 
     * @param http_req  The http request.
     * @param data      The buffer containing the data.
     * @param size      The length of data in the buffer.
     */
    void (*on_data_read)(pj_http_req *http_req,
                         void *data, pj_size_t size);

    /**
     * This callback is called when the HTTP request is completed.
     * If the callback on_data_read() is specified, the variable
     * response->data will be set to NULL, otherwise it will
     * contain the complete data. Response data is allocated from
     * pj_http_req's internal memory pool so the data remain valid
     * as long as pj_http_req is not destroyed and application does
     * not start a new request.
     *
     * If no longer required, application may choose to destroy 
     * pj_http_req immediately by calling #pj_http_req_destroy() inside 
     * the callback.
     *
     * @param http_req  The http request.
     * @param status    The status of the request operation. PJ_SUCCESS
     *                  if the operation completed successfully
     *                  (connection-wise). To check the server's 
     *                  status-code response to the HTTP request, 
     *                  application should check resp->status_code instead.
     * @param resp      The response of the corresponding request. If 
     *                  the status argument is non-PJ_SUCCESS, this 
     *                  argument will be set to NULL.
     */
    void (*on_complete)(pj_http_req *http_req,
                        pj_status_t status,
                        const pj_http_resp *resp);

} pj_http_req_callback;


/**
 * Initialize the http request parameters with the default values.
 *
 * @param param         The parameter to be initialized.
 */
PJ_DECL(void) pj_http_req_param_default(pj_http_req_param *param);

/**
 * Add a header element/field. Application MUST make sure that 
 * name and val pointer remains valid until the HTTP request is sent.
 *
 * @param headers       The headers.
 * @param name          The header field name.
 * @param val           The header field value.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_http_headers_add_elmt(pj_http_headers *headers, 
                                              pj_str_t *name, 
                                              pj_str_t *val);

/** 
 * The same as #pj_http_headers_add_elmt() with char * as
 * its parameters. Application MUST make sure that name and val pointer 
 * remains valid until the HTTP request is sent.
 *
 * @param headers       The headers.
 * @param name          The header field name.
 * @param val           The header field value.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_http_headers_add_elmt2(pj_http_headers *headers, 
                                               char *name, char *val);

/**
 * Parse a http URL into its components.
 *
 * @param url           The URL to be parsed.
 * @param hurl          Pointer to receive the parsed result.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_http_req_parse_url(const pj_str_t *url, 
                                           pj_http_url *hurl);

/**
 * Create the HTTP request.
 *
 * @param pool          Pool to use. HTTP request will use the pool's factory
 *                      to allocate its own memory pool.
 * @param url           HTTP URL request.
 * @param timer         The timer to use.
 * @param ioqueue       The ioqueue to use.
 * @param param         Optional parameters. When this parameter is not 
 *                      specifed (NULL), the default values will be used.
 * @param hcb           Pointer to structure containing application
 *                      callbacks.
 * @param http_req      Pointer to receive the http request instance.
 *
 * @return              PJ_SUCCESS if the operation has been successful,
 *                      or the appropriate error code on failure.
 */
PJ_DECL(pj_status_t) pj_http_req_create(pj_pool_t *pool,
                                        const pj_str_t *url,
                                        pj_timer_heap_t *timer,
                                        pj_ioqueue_t *ioqueue,
                                        const pj_http_req_param *param,
                                        const pj_http_req_callback *hcb,
                                        pj_http_req **http_req);

/**
 * Set the timeout of the HTTP request operation. Note that if the 
 * HTTP request is currently running, the timeout will only affect 
 * subsequent request operations.
 *
 * @param http_req  The http request.
 * @param timeout   Timeout value for HTTP request operation.
 */
PJ_DECL(void) pj_http_req_set_timeout(pj_http_req *http_req,
                                      const pj_time_val* timeout);

/**
 * Starts an asynchronous HTTP request to the URL specified.
 *
 * @param http_req  The http request.
 *
 * @return
 *  - PJ_SUCCESS    if success
 *  - non-zero      which indicates the appropriate error code.
 */
PJ_DECL(pj_status_t) pj_http_req_start(pj_http_req *http_req);

/**
 * Cancel the asynchronous HTTP request. 
 *
 * @param http_req  The http request.
 * @param notify    If non-zero, the on_complete() callback will be 
 *                  called with status PJ_ECANCELLED to notify that 
 *                  the query has been cancelled.
 *
 * @return
 *  - PJ_SUCCESS    if success
 *  - non-zero      which indicates the appropriate error code.
 */
PJ_DECL(pj_status_t) pj_http_req_cancel(pj_http_req *http_req,
                                        pj_bool_t notify);

/**
 * Destroy the http request.
 *
 * @param http_req      The http request to be destroyed.
 *
 * @return              PJ_SUCCESS if success.
 */
PJ_DECL(pj_status_t) pj_http_req_destroy(pj_http_req *http_req);

/**
 * Find out whether the http request is running.
 *
 * @param http_req      The http request.
 *
 * @return              PJ_TRUE if a request is pending, or
 *                      PJ_FALSE if idle
 */
PJ_DECL(pj_bool_t) pj_http_req_is_running(const pj_http_req *http_req);

/**
 * Retrieve the user data previously associated with this http
 * request.
 *
 * @param http_req  The http request.
 *
 * @return          The user data.
 */
PJ_DECL(void *) pj_http_req_get_user_data(pj_http_req *http_req);

/**
 * @}
 */

PJ_END_DECL


#endif  /* __PJLIB_UTIL_HTTP_CLIENT_H__ */
