/* +----------------------------------------------------------------------+ | Swoole | +----------------------------------------------------------------------+ | This source file is subject to version 2.0 of the Apache license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.apache.org/licenses/LICENSE-2.0.html | | If you did not receive a copy of the Apache2.0 license and are unable| | to obtain it through the world-wide-web, please send a note to | | license@swoole.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Author: Tianfeng Han | +----------------------------------------------------------------------+ */ #ifndef SWOOLE_HTTP_CLIENT_H_ #define SWOOLE_HTTP_CLIENT_H_ #include "ext/standard/basic_functions.h" #include "ext/standard/php_http.h" #include "ext/standard/base64.h" #include "websocket.h" #include "thirdparty/php_http_parser.h" #ifdef SW_HAVE_ZLIB #include #endif enum http_client_state { HTTP_CLIENT_STATE_WAIT, HTTP_CLIENT_STATE_READY, HTTP_CLIENT_STATE_BUSY, //WebSocket HTTP_CLIENT_STATE_UPGRADE, HTTP_CLIENT_STATE_WAIT_CLOSE, HTTP_CLIENT_STATE_CLOSED, }; #ifdef SW_COROUTINE typedef enum { HTTP_CLIENT_STATE_DEFER_INIT, HTTP_CLIENT_STATE_DEFER_SEND, HTTP_CLIENT_STATE_DEFER_WAIT, HTTP_CLIENT_STATE_DEFER_DONE, } http_client_defer_state; #endif typedef struct { zval *onConnect; zval *onError; zval *onClose; zval *onMessage; zval *onResponse; #if PHP_MAJOR_VERSION >= 7 zval _object; zval _request_body; zval _request_header; zval _request_upload_files; zval _download_file; zval _cookies; zval _onConnect; zval _onError; zval _onClose; zval _onMessage; #endif zval *cookies; zval *request_header; zval *request_body; zval *request_upload_files; zval *download_file; off_t download_offset; char *request_method; int callback_index; double request_timeout; uint8_t shutdown; #ifdef SW_COROUTINE zend_bool defer; //0 normal 1 wait for receive zend_bool defer_result;//0 zend_bool defer_chunk_status;// 0 1 now use rango http->complete zend_bool send_yield; http_client_defer_state defer_status; int cid; /** * for websocket */ swLinkedList *message_queue; #endif } http_client_property; typedef struct { swClient *cli; char *host; zend_size_t host_len; long port; double timeout; char* uri; zend_size_t uri_len; swTimer_node *timer; char *tmp_header_field_name; int tmp_header_field_name_len; #ifdef SW_HAVE_ZLIB z_stream gzip_stream; swString *gzip_buffer; #endif /** * download page */ int file_fd; php_http_parser parser; swString *body; uint8_t state; //0 wait 1 ready 2 busy uint8_t keep_alive; //0 no 1 keep uint8_t upgrade; uint8_t gzip; uint8_t chunked; //Transfer-Encoding: chunked uint8_t completed; uint8_t websocket_mask; uint8_t download; //save http response to file uint8_t header_completed; int8_t method; } http_client; int http_client_parser_on_header_field(php_http_parser *parser, const char *at, size_t length); int http_client_parser_on_header_value(php_http_parser *parser, const char *at, size_t length); int http_client_parser_on_body(php_http_parser *parser, const char *at, size_t length); int http_client_parser_on_headers_complete(php_http_parser *parser); int http_client_parser_on_message_complete(php_http_parser *parser); http_client* http_client_create(zval *object TSRMLS_DC); void http_client_free(zval *object TSRMLS_DC); static sw_inline void http_client_create_token(int length, char *buf) { char characters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\"ยง$%&/()=[]{}"; int i; assert(length < 1024); for (i = 0; i < length; i++) { buf[i] = characters[rand() % (sizeof(characters) - 1)]; } buf[length] = '\0'; } static sw_inline int http_client_check_data(zval *data TSRMLS_DC) { if (Z_TYPE_P(data) != IS_ARRAY && Z_TYPE_P(data) != IS_STRING) { swoole_php_error(E_WARNING, "parameter $data must be an array or string."); return SW_ERR; } else if (Z_TYPE_P(data) == IS_ARRAY && php_swoole_array_length(data) == 0) { swoole_php_error(E_WARNING, "parameter $data is empty."); } else if (Z_TYPE_P(data) == IS_STRING && Z_STRLEN_P(data) == 0) { swoole_php_error(E_WARNING, "parameter $data is empty."); } return SW_OK; } static sw_inline void http_client_swString_append_headers(swString* swStr, char* key, zend_size_t key_len, char* data, zend_size_t data_len) { swString_append_ptr(swStr, key, key_len); swString_append_ptr(swStr, ZEND_STRL(": ")); swString_append_ptr(swStr, data, data_len); swString_append_ptr(swStr, ZEND_STRL("\r\n")); } static sw_inline void http_client_append_content_length(swString* buf, int length) { char content_length_str[32]; int n = snprintf(content_length_str, sizeof(content_length_str), "Content-Length: %d\r\n\r\n", length); swString_append_ptr(buf, content_length_str, n); } #ifdef SW_HAVE_ZLIB extern swString *swoole_zlib_buffer; #endif #endif /* SWOOLE_HTTP_CLIENT_H_ */