From b813b2c567747c86ebe2c3de24ed6af26119ccf2 Mon Sep 17 00:00:00 2001 From: Liong Sauw Ming Date: Thu, 4 Feb 2010 18:29:16 +0000 Subject: Implemented ticket #1018: Simple HTTP client implementation * pjlib-util: * implement http_client * pjlib-util-test: * unit test for http_client * pjsip-apps/samples: * sample http client implementation * build: * added http_client support on VS6, VS2005, MMP, and Makefile git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3087 74dad513-b988-da41-8d7b-12977e46ad98 --- pjlib-util/include/pjlib-util.h | 3 + pjlib-util/include/pjlib-util/config.h | 11 + pjlib-util/include/pjlib-util/errno.h | 30 ++- pjlib-util/include/pjlib-util/http_client.h | 381 ++++++++++++++++++++++++++++ 4 files changed, 423 insertions(+), 2 deletions(-) create mode 100644 pjlib-util/include/pjlib-util/http_client.h (limited to 'pjlib-util/include') diff --git a/pjlib-util/include/pjlib-util.h b/pjlib-util/include/pjlib-util.h index 7e39e21d..edcdcc75 100644 --- a/pjlib-util/include/pjlib-util.h +++ b/pjlib-util/include/pjlib-util.h @@ -60,4 +60,7 @@ /* PCAP */ #include +/* HTTP */ +#include + #endif /* __PJLIB_UTIL_H__ */ diff --git a/pjlib-util/include/pjlib-util/config.h b/pjlib-util/include/pjlib-util/config.h index da2fb8c5..0723ab40 100644 --- a/pjlib-util/include/pjlib-util/config.h +++ b/pjlib-util/include/pjlib-util/config.h @@ -255,6 +255,17 @@ #endif +/* ************************************************************************** + * HTTP Client configuration + */ +/** + * Timeout value for HTTP request operation. The value is in ms. + * Default: 60000ms + */ +#ifndef PJ_HTTP_DEFAULT_TIMEOUT +# define PJ_HTTP_DEFAULT_TIMEOUT (60000) +#endif + /** * @} */ diff --git a/pjlib-util/include/pjlib-util/errno.h b/pjlib-util/include/pjlib-util/errno.h index 26786a4c..5fa3d8fe 100644 --- a/pjlib-util/include/pjlib-util/errno.h +++ b/pjlib-util/include/pjlib-util/errno.h @@ -357,8 +357,34 @@ //#define PJ_STATUS_FROM_STUN_CODE(code) (PJLIB_UTIL_ERRNO_START+code) - - +/************************************************************ + * HTTP Client ERROR + ***********************************************************/ +/** + * @hideinitializer + * Invalid URL format + */ +#define PJLIB_UTIL_EHTTPINURL (PJLIB_UTIL_ERRNO_START+151)/* 320151 */ +/** + * @hideinitializer + * Invalid port number + */ +#define PJLIB_UTIL_EHTTPINPORT (PJLIB_UTIL_ERRNO_START+152)/* 320152 */ +/** + * @hideinitializer + * Incomplete headers received + */ +#define PJLIB_UTIL_EHTTPINCHDR (PJLIB_UTIL_ERRNO_START+153)/* 320153 */ +/** + * @hideinitializer + * Insufficient buffer + */ +#define PJLIB_UTIL_EHTTPINSBUF (PJLIB_UTIL_ERRNO_START+154)/* 320154 */ +/** + * @hideinitializer + * Connection lost + */ +#define PJLIB_UTIL_EHTTPLOST (PJLIB_UTIL_ERRNO_START+155)/* 320155 */ /** * @} diff --git a/pjlib-util/include/pjlib-util/http_client.h b/pjlib-util/include/pjlib-util/http_client.h new file mode 100644 index 00000000..9c4e6da9 --- /dev/null +++ b/pjlib-util/include/pjlib-util/http_client.h @@ -0,0 +1,381 @@ +/* $Id$ */ +/* + * Copyright (C) 2008-2010 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 +#include + +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 + +/** + * 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 +{ + unsigned count; /**< Number of header fields */ + struct pj_http_header_elmt + { + pj_str_t name; + pj_str_t value; + } header[PJ_HTTP_HEADER_SIZE]; /**< Header elements/fields */ +} pj_http_headers; + +/** + * 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; +} pj_http_req_param; + +/** + * This structure describes HTTP response. + */ +typedef struct pj_http_resp +{ + pj_str_t version; /**< HTTP version of the server */ + pj_str_t status_code; /**< Status code of the request */ + pj_str_t reason; /**< Reason phrase */ + pj_http_headers headers; /**< Response headers */ + /** + * The value of content-length header field. -1 if not + * specified. + */ + pj_int32_t content_length; + 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 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. + * + * @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 value 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 value 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__ */ -- cgit v1.2.3