From: Michael Wallner Date: Tue, 23 Aug 2005 18:14:11 +0000 (+0000) Subject: - ditch http_split_response() X-Git-Tag: RELEASE_0_12_0~24 X-Git-Url: https://git.m6w6.name/?a=commitdiff_plain;h=0d925a2820b9d75b8e32c451fd400863e51e22fe;p=m6w6%2Fext-http - ditch http_split_response() - unify parsing of HTTP pre-header line; add info api - add several log facilities through http_exit() - several other fixbits # unfinished... --- diff --git a/config.m4 b/config.m4 index 85b6de3..de29b53 100644 --- a/config.m4 +++ b/config.m4 @@ -75,7 +75,8 @@ dnl ---- http_util_object.c http_message_object.c http_request_object.c http_request_pool_api.c \ http_response_object.c http_exception_object.c http_requestpool_object.c \ http_api.c http_auth_api.c http_cache_api.c http_request_api.c http_date_api.c \ - http_headers_api.c http_message_api.c http_send_api.c http_url_api.c" + http_headers_api.c http_message_api.c http_send_api.c http_url_api.c \ + http_info_api.c" PHP_NEW_EXTENSION([http], $PHP_HTTP_SOURCES, [$ext_shared]) PHP_ADD_BUILD_DIR($ext_builddir/phpstr, 1) PHP_SUBST([HTTP_SHARED_LIBADD]) diff --git a/config.w32 b/config.w32 index c705296..941ca4d 100644 --- a/config.w32 +++ b/config.w32 @@ -10,7 +10,8 @@ if (PHP_HTTP != "no") { "http_request_object.c http_response_object.c "+ "http_api.c http_auth_api.c http_cache_api.c http_request_pool_api.c "+ "http_request_api.c http_date_api.c http_headers_api.c "+ - "http_message_api.c http_send_api.c http_url_api.c ", + "http_message_api.c http_send_api.c http_url_api.c "+ + "http_info_api.c", null, "/I\"" + configure_module_dirname + "/phpstr\""); ADD_SOURCES(configure_module_dirname + "/phpstr", "phpstr.c", "http"); diff --git a/http.c b/http.c index 2ecaa58..444341e 100644 --- a/http.c +++ b/http.c @@ -84,7 +84,6 @@ function_entry http_functions[] = { PHP_FE(http_send_stream, NULL) PHP_FE(http_chunked_decode, NULL) PHP_FE(http_parse_message, NULL) - PHP_FE(http_split_response, NULL) PHP_FE(http_parse_headers, NULL) PHP_FE(http_get_request_headers, NULL) PHP_FE(http_get_request_body, NULL) @@ -184,6 +183,9 @@ PHP_INI_MH(http_update_allowed_methods) PHP_INI_BEGIN() HTTP_PHP_INI_ENTRY("http.allowed_methods", "", PHP_INI_ALL, http_update_allowed_methods, request.methods.allowed) HTTP_PHP_INI_ENTRY("http.cache_log", "", PHP_INI_ALL, OnUpdateString, log.cache) + HTTP_PHP_INI_ENTRY("http.redirect_log", "", PHP_INI_ALL, OnUpdateString, log.redirect) + HTTP_PHP_INI_ENTRY("http.allowed_methods_log", "", PHP_INI_ALL, OnUpdateString, log.allowed_methods) + HTTP_PHP_INI_ENTRY("http.composite_log", "", PHP_INI_ALL, OnUpdateString, log.composite) #ifdef ZEND_ENGINE_2 HTTP_PHP_INI_ENTRY("http.only_exceptions", "0", PHP_INI_ALL, OnUpdateBool, only_exceptions) #endif @@ -319,8 +321,8 @@ PHP_MINFO_FUNCTION(http) php_info_print_table_row(2, "Custom Request Methods:", PHPSTR_LEN(custom_request_methods) ? PHPSTR_VAL(custom_request_methods) : "none registered"); - phpstr_free(known_request_methods); - phpstr_free(custom_request_methods); + phpstr_free(&known_request_methods); + phpstr_free(&custom_request_methods); } php_info_print_table_end(); #endif diff --git a/http.dsp b/http.dsp index 7ccd176..b41373e 100644 --- a/http.dsp +++ b/http.dsp @@ -138,6 +138,10 @@ SOURCE=.\http_url_api.c # End Source File # Begin Source File +SOURCE=.\http_info_api.c +# End Source File +# Begin Source File + SOURCE=.\phpstr\phpstr.c # End Source File # End Group @@ -186,6 +190,10 @@ SOURCE=.\php_http_url_api.h # End Source File # Begin Source File +SOURCE=.\php_http_info_api.h +# End Source File +# Begin Source File + SOURCE=.\php_http_std_defs.h # End Source File # Begin Source File diff --git a/http_api.c b/http_api.c index de95a88..654bf88 100644 --- a/http_api.c +++ b/http_api.c @@ -38,7 +38,7 @@ ZEND_EXTERN_MODULE_GLOBALS(http); -/* char *pretty_key(char *, size_t, zend_bool, zebd_bool) */ +/* char *pretty_key(char *, size_t, zend_bool, zend_bool) */ char *_http_pretty_key(char *key, size_t key_len, zend_bool uctitle, zend_bool xhyphen) { if (key && key_len) { @@ -179,19 +179,53 @@ void _http_error_ex(long type, long code, const char *format, ...) } /* }}} */ -/* {{{ STATUS http_exit(int, char*) */ -STATUS _http_exit_ex(int status, char *header, zend_bool free_header TSRMLS_DC) +/* {{{ STATUS http_exit(int, char*, char*, zend_bool) */ +STATUS _http_exit_ex(int status, char *header, char *body, zend_bool send_header TSRMLS_DC) { - if (SUCCESS != http_send_status_header(status, header)) { + char datetime[128]; + + if (SUCCESS != http_send_status_header(status, send_header ? header : NULL)) { http_error_ex(HE_WARNING, HTTP_E_HEADER, "Failed to exit with status/header: %d - %s", status, header ? header : ""); - if (free_header && header) { - efree(header); - } + STR_FREE(header); + STR_FREE(body); return FAILURE; } - if (free_header && header) { - efree(header); + if (body) { + PHPWRITE(body, strlen(body)); + } + { + time_t now; + struct tm nowtm; + + time(&now); + strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", php_localtime_r(&now, &nowtm)); + } + +#define HTTP_LOG_WRITE(for, type, header) \ + HTTP_LOG_WRITE_EX(for, type, header); \ + HTTP_LOG_WRITE_EX(composite, type, header); + +#define HTTP_LOG_WRITE_EX(for, type, header) \ + if (HTTP_G(log).##for && strlen(HTTP_G(log).##for)) { \ + php_stream *log = php_stream_open_wrapper(HTTP_G(log).##for, "ab", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); \ + \ + if (log) { \ + php_stream_printf(log TSRMLS_CC, "%s [%12s] %32s <%s>%s", datetime, type, header, SG(request_info).request_uri, PHP_EOL); \ + php_stream_close(log); \ + } \ + \ } + switch (status) + { + case 301: HTTP_LOG_WRITE(redirect, "301-REDIRECT", header); break; + case 302: HTTP_LOG_WRITE(redirect, "302-REDIRECT", header); break; + case 304: HTTP_LOG_WRITE(cache, "304-CACHE", header); break; + case 401: HTTP_LOG_WRITE(auth, "401-AUTH", header); break; + case 403: HTTP_LOG_WRITE(auth, "403-AUTH", header); break; + case 405: HTTP_LOG_WRITE(allowed_methods, "405-ALLOWED", header); break; + } + STR_FREE(header); + STR_FREE(body); zend_bailout(); /* fake */ return SUCCESS; @@ -218,7 +252,7 @@ PHP_HTTP_API zval *_http_get_server_var_ex(const char *key, size_t key_size, zen zval **hsv; zval **var; - if (SUCCESS != zend_hash_find(&EG(symbol_table), "HTTP_SERVER_VARS", sizeof("HTTP_SERVER_VARS"), (void **) &hsv)) { + if (SUCCESS != zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &hsv)) { return NULL; } if (SUCCESS != zend_hash_find(Z_ARRVAL_PP(hsv), (char *) key, key_size, (void **) &var)) { @@ -306,29 +340,8 @@ PHP_HTTP_API const char *_http_chunked_decode(const char *encoded, size_t encode } /* }}} */ -/* {{{ STATUS http_split_response(char *, size_t, HashTable *, char **, size_t *) */ -PHP_HTTP_API STATUS _http_split_response(char *response, size_t response_len, - HashTable *headers, char **body, size_t *body_len TSRMLS_DC) -{ - char *header = response, *real_body = NULL; - - while (0 < (response_len - (response - header + 4))) { - if ( (*response++ == '\r') && - (*response++ == '\n') && - (*response++ == '\r') && - (*response++ == '\n')) { - real_body = response; - break; - } - } - - if (real_body && (*body_len = (response_len - (real_body - header)))) { - *body = ecalloc(1, *body_len + 1); - memcpy(*body, real_body, *body_len); - } - - return http_parse_headers_ex(header, headers, 1); -} +/* {{{ STATUS http_locate_body(char *, size_t) */ +PHP_HTTP_API /* }}} */ /* diff --git a/http_cache_api.c b/http_cache_api.c index 109a568..52d76ea 100644 --- a/http_cache_api.c +++ b/http_cache_api.c @@ -34,30 +34,6 @@ ZEND_EXTERN_MODULE_GLOBALS(http); -/* {{{ STATUS http_cache_exit(char *, zend_bool) */ -STATUS _http_cache_exit_ex(char *cache_token, zend_bool etag, zend_bool free_token TSRMLS_DC) -{ - if (HTTP_G(log).cache && strlen(HTTP_G(log).cache)) { - php_stream *log = php_stream_open_wrapper(HTTP_G(log).cache, "ab", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL); - - if (log) { - time_t now; - struct tm nowtm; - char datetime[128]; - - time(&now); - strftime(datetime, sizeof(datetime), "%Y-%m-%d %H:%M:%S", php_localtime_r(&now, &nowtm)); - php_stream_printf(log TSRMLS_CC, "%s [%s] %32s %s\n", datetime, etag ? "ETAG":"LMOD", cache_token, SG(request_info).request_uri); - php_stream_close(log); - } - } - if (free_token && cache_token) { - efree(cache_token); - } - return http_exit_ex(304, NULL, 0); -} -/* }}} */ - /* {{{ char *http_etag(void *, size_t, http_send_mode) */ PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC) { @@ -170,16 +146,20 @@ PHP_HTTP_API zend_bool _http_match_etag_ex(const char *entry, const char *etag, PHP_HTTP_API STATUS _http_cache_last_modified(time_t last_modified, time_t send_modified, const char *cache_control, size_t cc_len TSRMLS_DC) { + char *sent_header = NULL; + if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) { return FAILURE; } - if (SUCCESS != http_send_last_modified(send_modified)) { + if (SUCCESS != http_send_last_modified_ex(send_modified, &sent_header)) { return FAILURE; } if (http_match_last_modified("HTTP_IF_MODIFIED_SINCE", last_modified)) { - return http_cache_exit(http_date(last_modified), 0); + http_exit_ex(304, sent_header, NULL, 0); + } else { + STR_FREE(sent_header); } return SUCCESS; @@ -190,18 +170,22 @@ PHP_HTTP_API STATUS _http_cache_last_modified(time_t last_modified, PHP_HTTP_API STATUS _http_cache_etag(const char *etag, size_t etag_len, const char *cache_control, size_t cc_len TSRMLS_DC) { + char *sent_header = NULL; + if (cc_len && (SUCCESS != http_send_cache_control(cache_control, cc_len))) { return FAILURE; } if (etag_len) { - if (SUCCESS != http_send_etag(etag, etag_len)) { + if (SUCCESS != http_send_etag_ex(etag, etag_len, &sent_header)) { return FAILURE; } - if (!http_match_etag("HTTP_IF_NONE_MATCH", etag)) { - return SUCCESS; + if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) { + http_exit_ex(304, sent_header, NULL, 0); + } else { + STR_FREE(sent_header); } - return http_cache_exit_ex((char *)etag, 1, 0); + return SUCCESS; } /* if no etag is given and we didn't already start ob_etaghandler -- start it */ @@ -236,12 +220,16 @@ PHP_HTTP_API void _http_ob_etaghandler(char *output, uint output_len, /* just do that if desired */ if (HTTP_G(etag).started) { + char *sent_header = NULL; + make_digest(etag, digest); http_send_cache_control(HTTP_DEFAULT_CACHECONTROL, lenof(HTTP_DEFAULT_CACHECONTROL)); - http_send_etag(etag, 32); + http_send_etag_ex(etag, 32, &sent_header); if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) { - http_cache_exit_ex(etag, 1, 0); + http_exit_ex(304, sent_header, NULL, 0); + } else { + STR_FREE(sent_header); } } } diff --git a/http_functions.c b/http_functions.c index ae27b5e..ced4e81 100644 --- a/http_functions.c +++ b/http_functions.c @@ -461,9 +461,7 @@ PHP_FUNCTION(http_redirect) size_t query_len = 0; zend_bool session = 0, permanent = 0, free_params = 0; zval *params = NULL; - char *query = NULL, *url = NULL, *URI, - LOC[HTTP_URI_MAXLEN + sizeof("Location: ")], - RED[HTTP_URI_MAXLEN * 2 + sizeof("Redirecting to %s?%s.\n")]; + char *query = NULL, *url = NULL, *URI, *LOC, *RED = NULL; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|sa!/bb", &url, &url_len, ¶ms, &session, &permanent) != SUCCESS) { RETURN_FALSE; @@ -527,11 +525,15 @@ PHP_FUNCTION(http_redirect) URI = http_absolute_uri(url); if (query_len) { - snprintf(LOC, HTTP_URI_MAXLEN + sizeof("Location: "), "Location: %s?%s", URI, query); - sprintf(RED, "Redirecting to %s?%s.\n", URI, query, URI, query); + spprintf(&LOC, 0, "Location: %s?%s", URI, query); + if (SG(request_info).request_method && strcmp(SG(request_info).request_method, "HEAD")) { + spprintf(&RED, 0, "Redirecting to %s?%s.\n", URI, query, URI, query); + } } else { - snprintf(LOC, HTTP_URI_MAXLEN + sizeof("Location: "), "Location: %s", URI); - sprintf(RED, "Redirecting to %s.\n", URI, URI); + spprintf(&LOC, 0, "Location: %s", URI); + if (SG(request_info).request_method && strcmp(SG(request_info).request_method, "HEAD")) { + spprintf(&RED, 0, "Redirecting to %s.\n", URI, URI); + } } efree(URI); @@ -543,13 +545,7 @@ PHP_FUNCTION(http_redirect) FREE_ZVAL(params); } - if ((SUCCESS == http_send_header_string(LOC)) && (SUCCESS == http_send_status((permanent ? 301 : 302)))) { - if (SG(request_info).request_method && strcmp(SG(request_info).request_method, "HEAD")) { - PHPWRITE(RED, strlen(RED)); - } - RETURN_TRUE; - } - RETURN_FALSE; + RETURN_SUCCESS(http_exit_ex(permanent ? 301 : 302, LOC, RED, 1)); } /* }}} */ @@ -633,90 +629,6 @@ PHP_FUNCTION(http_chunked_decode) } /* }}} */ -/* {{{ proto array http_split_response(string http_response) - * - * This function splits an HTTP response into an array with headers and the - * content body. The returned array may look simliar to the following example: - * - *
- *  array(
- *         'Response Status' => '200 Ok',
- *         'Content-Type' => 'text/plain',
- *         'Content-Language' => 'en-US'
- *     ),
- *     1 => "Hello World!"
- * );
- * ?>
- * 
- */ -PHP_FUNCTION(http_split_response) -{ - char *response, *body; - int response_len; - size_t body_len; - zval *zheaders; - - if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &response, &response_len) != SUCCESS) { - RETURN_FALSE; - } - - MAKE_STD_ZVAL(zheaders); - array_init(zheaders); - - if (SUCCESS != http_split_response(response, response_len, Z_ARRVAL_P(zheaders), &body, &body_len)) { - RETURN_FALSE; - } - - array_init(return_value); - add_index_zval(return_value, 0, zheaders); - add_index_stringl(return_value, 1, body, body_len, 0); -} -/* }}} */ - -static void http_message_toobject_recursive(http_message *msg, zval *obj TSRMLS_DC) -{ - zval *headers; - - add_property_long(obj, "type", msg->type); - switch (msg->type) - { - case HTTP_MSG_RESPONSE: - add_property_double(obj, "httpVersion", msg->info.response.http_version); - add_property_long(obj, "responseCode", msg->info.response.code); - break; - - case HTTP_MSG_REQUEST: - add_property_double(obj, "httpVersion", msg->info.request.http_version); - add_property_string(obj, "requestMethod", msg->info.request.method, 1); - add_property_string(obj, "requestUri", msg->info.request.URI, 1); - break; - } - - MAKE_STD_ZVAL(headers); - array_init(headers); - zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); - add_property_zval(obj, "headers", headers); - zval_ptr_dtor(&headers); - - add_property_stringl(obj, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg), 1); - - if (msg->parent) { - zval *parent; - - MAKE_STD_ZVAL(parent); - object_init(parent); - add_property_zval(obj, "parentMessage", parent); - http_message_toobject_recursive(msg->parent, parent TSRMLS_CC); - zval_ptr_dtor(&parent); - } else { - add_property_null(obj, "parentMessage"); - } - http_message_dtor(msg); - efree(msg); -} - /* {{{ proto object http_parse_message(string message) * * Parses (a) http_message(s) into a simple recursive object structure: @@ -766,7 +678,7 @@ PHP_FUNCTION(http_parse_message) if (msg = http_message_parse(message, message_len)) { object_init(return_value); - http_message_toobject_recursive(msg, return_value TSRMLS_CC); + http_message_tostruct_recursive(msg, return_value); } else { RETURN_NULL(); } @@ -908,7 +820,6 @@ PHP_FUNCTION(http_match_request_header) * 'content_type' => 'text/html; charset=iso-8859-1', * 'redirect_time' => 0, * 'redirect_count' => 0, - * 'private' => '', * 'http_connectcode' => 0, * 'httpauth_avail' => 0, * 'proxyauth_avail' => 0, @@ -934,7 +845,7 @@ PHP_FUNCTION(http_get) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_get(URL, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETURN_PHPSTR_VAL(response); + RETURN_PHPSTR_VAL(&response); } else { RETURN_FALSE; } @@ -965,14 +876,14 @@ PHP_FUNCTION(http_head) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_head(URL, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETURN_PHPSTR_VAL(response); + RETURN_PHPSTR_VAL(&response); } else { RETURN_FALSE; } } /* }}} */ -/* {{{ proto string http_post_data(string url, string data[, array options[, &info]]) +/* {{{ proto string http_post_data(string url, string data[, array options[, array &info]]) * * Performs an HTTP POST request, posting data. * Returns the HTTP response as string. @@ -1001,7 +912,7 @@ PHP_FUNCTION(http_post_data) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_post(URL, &body, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETVAL_PHPSTR_VAL(response); + RETVAL_PHPSTR_VAL(&response); } else { RETVAL_FALSE; } @@ -1037,7 +948,7 @@ PHP_FUNCTION(http_post_fields) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_post(URL, &body, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETVAL_PHPSTR_VAL(response); + RETVAL_PHPSTR_VAL(&response); } else { RETVAL_FALSE; } @@ -1084,7 +995,7 @@ PHP_FUNCTION(http_put_file) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_put(URL, &body, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETVAL_PHPSTR_VAL(response); + RETVAL_PHPSTR_VAL(&response); } else { RETVAL_FALSE; } @@ -1128,7 +1039,7 @@ PHP_FUNCTION(http_put_stream) phpstr_init_ex(&response, HTTP_CURLBUF_SIZE, 0); if (SUCCESS == http_put(URL, &body, options ? Z_ARRVAL_P(options) : NULL, info ? Z_ARRVAL_P(info) : NULL, &response)) { - RETURN_PHPSTR_VAL(response); + RETURN_PHPSTR_VAL(&response); } else { RETURN_NULL(); } @@ -1381,12 +1292,12 @@ PHP_FUNCTION(http_build_query) formstr = phpstr_new(); if (SUCCESS != http_urlencode_hash_implementation_ex(HASH_OF(formdata), formstr, arg_sep, prefix, prefix_len, NULL, 0, NULL, 0, (Z_TYPE_P(formdata) == IS_OBJECT ? formdata : NULL))) { - phpstr_free(formstr); + phpstr_free(&formstr); RETURN_FALSE; } if (!formstr->used) { - phpstr_free(formstr); + phpstr_free(&formstr); RETURN_NULL(); } @@ -1398,6 +1309,13 @@ PHP_FUNCTION(http_build_query) PHP_FUNCTION(http_test) { + ulong idx; + char *key; + zval **data; + FOREACH_HASH_KEYVAL(&EG(symbol_table), key, idx, data) { + convert_to_string_ex(data); + fprintf(stderr, "\t %s => %s\n", key, Z_STRVAL_PP(data)); + } } /* diff --git a/http_headers_api.c b/http_headers_api.c index 37e16cd..5048266 100644 --- a/http_headers_api.c +++ b/http_headers_api.c @@ -27,6 +27,7 @@ #include "php_http_std_defs.h" #include "php_http_api.h" #include "php_http_headers_api.h" +#include "php_http_info_api.h" #include @@ -244,26 +245,25 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_ /* }}} */ /* {{{ STATUS http_parse_headers(char *, HashTable *, zend_bool) */ -PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *headers, zend_bool prettify, http_parse_headers_callback_t func, void **callback_data TSRMLS_DC) +PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *headers, zend_bool prettify, + http_info_callback callback_func, void **callback_data TSRMLS_DC) { - const char *colon = NULL, *line = NULL, *begin = header, *crlfcrlf = NULL; + const char *colon = NULL, *line = NULL, *begin = header; + const char *body = http_locate_body(header); size_t header_len; zval array; Z_ARRVAL(array) = headers; - - if (crlfcrlf = strstr(header, HTTP_CRLF HTTP_CRLF)) { - header_len = crlfcrlf - header + lenof(HTTP_CRLF); - } else { - header_len = strlen(header) + 1; - } - - - if (header_len < 2 || !strchr(header, ':')) { + header_len = body ? body - header : strlen(header) + 1; + +/* + if (header_len < 2 || ((!lflf) && (!crlfcrlf) && (!strchr(header, ':')))) { + fprintf(stderr, "header_len: %lu, lflf: %p, crlfcrlf: %p, ':': %p\n(%s)\n", + header_len, lflf, crlfcrlf, strchr(header, ':'), header); http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Cannot parse too short or malformed HTTP headers"); return FAILURE; } - +*/ line = header; while (header_len >= (size_t) (line - begin)) { @@ -271,11 +271,26 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *header switch (*line++) { + case ':': + if (!colon) { + colon = line - 1; + } + break; + case 0: --value_len; /* we don't have CR so value length is one char less */ case '\n': if ((!(*line - 1)) || ((*line != ' ') && (*line != '\t'))) { + http_info i; + /* response/request line */ + if (SUCCESS == http_info_parse(header, &i)) { + callback_func(callback_data, &headers, &i TSRMLS_CC); + http_info_dtor(&i); + Z_ARRVAL(array) = headers; + } else + + /* if ( (!strncmp(header, "HTTP/1.", lenof("HTTP/1."))) || (!strncmp(line - lenof("HTTP/1.x" HTTP_CRLF) + value_len, "HTTP/1.", lenof("HTTP/1.")))) { if (func) { @@ -283,6 +298,7 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *header Z_ARRVAL(array) = headers; } } else + */ /* "header: value" pair */ if (colon) { @@ -330,47 +346,12 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *header header += line - header; } break; - - case ':': - if (!colon) { - colon = line - 1; - } - break; } } return SUCCESS; } /* }}} */ -PHP_HTTP_API void _http_parse_headers_default_callback(const char *http_line, HashTable **headers, void **cb_data TSRMLS_DC) -{ - zval array; - char *crlf = NULL; - size_t line_length; - Z_ARRVAL(array) = *headers; - - if (crlf = strstr(http_line, HTTP_CRLF)) { - line_length = crlf - http_line; - } else { - line_length = strlen(http_line); - } - - /* response */ - if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) { - char *status = estrndup(http_line + lenof("HTTP/1.x "), line_length - lenof("HTTP/1.x ")); - add_assoc_stringl(&array, "Response Status", status, line_length - lenof("HTTP/1.x "), 0); - } else - /* request */ - if (!strncmp(http_line + line_length - lenof("HTTP/1.x"), "HTTP/1.", lenof("HTTP/1."))) { - char *sep = strchr(http_line, ' '); - char *url = estrndup(sep + 1, strstr(sep, "HTTP/1.") - sep + 1 + 1); - char *met = estrndup(http_line, sep - http_line); - - add_assoc_stringl(&array, "Request Method", met, sep - http_line, 0); - add_assoc_stringl(&array, "Request Uri", url, strstr(sep, "HTTP/1.") - sep + 1 + 1, 0); - } -} - /* {{{ void http_get_request_headers_ex(HashTable *, zend_bool) */ PHP_HTTP_API void _http_get_request_headers_ex(HashTable *headers, zend_bool prettify TSRMLS_DC) { @@ -380,7 +361,7 @@ PHP_HTTP_API void _http_get_request_headers_ex(HashTable *headers, zend_bool pre Z_ARRVAL(array) = headers; - if (SUCCESS == zend_hash_find(&EG(symbol_table), "HTTP_SERVER_VARS", sizeof("HTTP_SERVER_VARS"), (void **) &hsv)) { + if (SUCCESS == zend_hash_find(&EG(symbol_table), "_SERVER", sizeof("_SERVER"), (void **) &hsv)) { FOREACH_KEY(*hsv, key, idx) { if (key && !strncmp(key, "HTTP_", 5)) { zval **header; diff --git a/http_info_api.c b/http_info_api.c new file mode 100644 index 0000000..18fea6f --- /dev/null +++ b/http_info_api.c @@ -0,0 +1,149 @@ +/* + +----------------------------------------------------------------------+ + | PECL :: http | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, that | + | is bundled with this package in the file LICENSE, and is available | + | through the world-wide-web at http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Michael Wallner | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include "php.h" + +#include "php_http.h" +#include "php_http_api.h" +#include "php_http_std_defs.h" +#include "php_http_info_api.h" + +#include + +ZEND_EXTERN_MODULE_GLOBALS(http); + +PHP_HTTP_API void _http_info_default_callback(void **nothing, HashTable **headers, http_info *info TSRMLS_DC) +{ + zval array; + Z_ARRVAL(array) = *headers; + + switch (info->type) + { + case IS_HTTP_REQUEST: + add_assoc_string(&array, "Request Method", HTTP_INFO(info).request.method, 1); + add_assoc_string(&array, "Request Uri", HTTP_INFO(info).request.URI, 1); + break; + + case IS_HTTP_RESPONSE: + add_assoc_long(&array, "Response Code", (long) HTTP_INFO(info).response.code); + add_assoc_string(&array, "Response Status", HTTP_INFO(info).response.status, 1); + break; + } +} + +PHP_HTTP_API void _http_info_dtor(http_info *info) +{ + http_info_t *i = (http_info_t *) info; + + switch (info->type) + { + case IS_HTTP_REQUEST: + STR_SET(i->request.method, NULL); + STR_SET(i->request.URI, NULL); + break; + + case IS_HTTP_RESPONSE: + STR_SET(i->response.status, NULL); + break; + + default: + break; + } +} + +PHP_HTTP_API STATUS _http_info_parse_ex(const char *pre_header, http_info *info, zend_bool silent TSRMLS_DC) +{ + const char *end, *http; + + /* sane parameter */ + if ((!pre_header) || (!*pre_header)) { + if (!silent) { + http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Empty pre-header HTTP info"); + } + return FAILURE; + } + + /* where's the end of the line */ + if (!((end = strchr(pre_header, '\r')) || (end = strchr(pre_header, '\n')))) { + end = pre_header + strlen(pre_header); + } + + /* there must be HTTP/1.x in the line + * and nothing than SPACE or NUL after HTTP/1.x + */ + if ( (!(http = strstr(pre_header, "HTTP/1."))) || + (!(http < end)) || + (!isdigit(http[lenof("HTTP/1.")])) || + (http[lenof("HTTP/1.1")] && (!isspace(http[lenof("HTTP/1.1")])))) { + if (!silent) { + http_error(HE_WARNING, HTTP_E_MALFORMED_HEADERS, "Invalid or missing HTTP/1.x protocol identification"); + } + return FAILURE; + } + +#if 0 + { + char *line = estrndup(pre_header, end - pre_header); + fprintf(stderr, "http_parse_info('%s')\n", line); + efree(line); + } +#endif + + info->http.version = atof(http + lenof("HTTP/")); + + /* is response */ + if (pre_header == http) { + char *status = NULL; + const char *code = http + sizeof("HTTP/1.1"); + + info->type = IS_HTTP_RESPONSE; + HTTP_INFO(info).response.code = (code && (end > code)) ? strtol(code, &status, 10) : 0; + HTTP_INFO(info).response.status = (status && (end > ++status)) ? estrndup(status, end - status) : ecalloc(1,1); + + return SUCCESS; + } + + /* is request */ + else { + const char *url = strchr(pre_header, ' '); + + info->type = IS_HTTP_REQUEST; + if (url && http > url) { + HTTP_INFO(info).request.method = estrndup(pre_header, url - pre_header); + HTTP_INFO(info).request.URI = estrndup(url + 1, http - url - 2); + } else { + HTTP_INFO(info).request.method = ecalloc(1,1); + HTTP_INFO(info).request.URI = ecalloc(1,1); + } + + return SUCCESS; + } +} + + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/http_message_api.c b/http_message_api.c index 8d062b1..42bae1a 100644 --- a/http_message_api.c +++ b/http_message_api.c @@ -33,61 +33,51 @@ ZEND_EXTERN_MODULE_GLOBALS(http); -#define http_message_headers_cb _http_message_headers_cb -static void _http_message_headers_cb(const char *http_line, HashTable **headers, void **message TSRMLS_DC) +#define http_message_info_callback _http_message_info_callback +static void _http_message_info_callback(http_message **message, HashTable **headers, http_info *info TSRMLS_DC) { - size_t line_length; - char *crlf = NULL; - http_message *new, *old = (http_message *) *message; - - if (crlf = strstr(http_line, HTTP_CRLF)) { - line_length = crlf - http_line; - } else { - line_length = strlen(http_line); - } - + http_message *old = *message; + + /* advance message */ if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) { - new = http_message_new(); - - new->parent = old; - *message = new; - *headers = &new->hdrs; - } else { - new = old; + (*message) = http_message_new(); + (*message)->parent = old; + (*headers) = &((*message)->hdrs); } - - while (isspace(http_line[line_length-1])) --line_length; - - // response - if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) { - new->type = HTTP_MSG_RESPONSE; - new->info.response.http_version = atof(http_line + lenof("HTTP/")); - new->info.response.code = atoi(http_line + lenof("HTTP/1.1 ")); - } else - // request - if (!strncmp(http_line + line_length - lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) { - const char *method_sep_uri = strchr(http_line, ' '); - new->type = HTTP_MSG_REQUEST; - new->info.request.http_version = atof(http_line + line_length - lenof("1.1")); - new->info.request.method = estrndup(http_line, method_sep_uri - http_line); - new->info.request.URI = estrndup(method_sep_uri + 1, http_line + line_length - method_sep_uri - 1 - lenof(" HTTP/1.1")); + + (*message)->http.version = info->http.version; + + switch (info->type) + { + case IS_HTTP_REQUEST: + (*message)->type = HTTP_MSG_REQUEST; + HTTP_INFO(*message).request.URI = estrdup(HTTP_INFO(info).request.URI); + HTTP_INFO(*message).request.method = estrdup(HTTP_INFO(info).request.method); + break; + + case IS_HTTP_RESPONSE: + (*message)->type = HTTP_MSG_RESPONSE; + HTTP_INFO(*message).response.code = HTTP_INFO(info).response.code; + HTTP_INFO(*message).response.status = estrdup(HTTP_INFO(info).response.status); + break; } } #define http_message_init_type _http_message_init_type static inline void _http_message_init_type(http_message *message, http_message_type type) { + message->http.version = .0; + switch (message->type = type) { case HTTP_MSG_RESPONSE: - message->info.response.http_version = .0; - message->info.response.code = 0; + message->http.info.response.code = 0; + message->http.info.response.status = NULL; break; case HTTP_MSG_REQUEST: - message->info.request.http_version = .0; - message->info.request.method = NULL; - message->info.request.URI = NULL; + message->http.info.request.method = NULL; + message->http.info.request.URI = NULL; break; case HTTP_MSG_NONE: @@ -117,13 +107,19 @@ PHP_HTTP_API void _http_message_set_type(http_message *message, http_message_typ if (type != message->type) { /* free request info */ - if (message->type == HTTP_MSG_REQUEST) { - if (message->info.request.method) { - efree(message->info.request.method); - } - if (message->info.request.URI) { - efree(message->info.request.URI); - } + switch (message->type) + { + case HTTP_MSG_REQUEST: + STR_FREE(message->http.info.request.method); + STR_FREE(message->http.info.request.URI); + break; + + case HTTP_MSG_RESPONSE: + STR_FREE(message->http.info.response.status); + break; + + default: + break; } /* init */ @@ -133,33 +129,28 @@ PHP_HTTP_API void _http_message_set_type(http_message *message, http_message_typ PHP_HTTP_API http_message *_http_message_parse_ex(http_message *msg, const char *message, size_t message_length TSRMLS_DC) { - char *body = NULL; + char *body = NULL, *cr, *lf; zend_bool free_msg = msg ? 0 : 1; - if (message_length < HTTP_MSG_MIN_SIZE) { - return NULL; - } - - if (!message) { + if ((!message) || (message_length < HTTP_MSG_MIN_SIZE)) { return NULL; } msg = http_message_init(msg); - if (SUCCESS != http_parse_headers_cb(message, &msg->hdrs, 1, http_message_headers_cb, (void **) &msg)) { + if (SUCCESS != http_parse_headers_cb(message, &msg->hdrs, 1, (http_info_callback) http_message_info_callback, &msg)) { if (free_msg) { - http_message_free(msg); + http_message_free(&msg); } return NULL; } - /* header parsing stops at CRLF CRLF */ + /* header parsing stops at (CR)LF (CR)LF */ if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) { zval *c; const char *continue_at = NULL; body += lenof(HTTP_CRLF HTTP_CRLF); - /* message has content-length header */ if (c = http_message_header(msg, "Content-Length")) { long len = atol(Z_STRVAL_P(c)); @@ -242,15 +233,17 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_ { case HTTP_MSG_REQUEST: phpstr_appendf(&str, "%s %s HTTP/%1.1f" HTTP_CRLF, - msg->info.request.method, - msg->info.request.URI, - msg->info.request.http_version); + msg->http.info.request.method, + msg->http.info.request.URI, + msg->http.version); break; case HTTP_MSG_RESPONSE: - phpstr_appendf(&str, "HTTP/%1.1f %d" HTTP_CRLF, - msg->info.response.http_version, - msg->info.response.code); + phpstr_appendf(&str, "HTTP/%1.1f %d%s%s" HTTP_CRLF, + msg->http.version, + msg->http.info.response.code, + *msg->http.info.response.status ? " ":"", + msg->http.info.response.status); break; case HTTP_MSG_NONE: @@ -315,6 +308,56 @@ PHP_HTTP_API void _http_message_serialize(http_message *message, char **string, phpstr_dtor(&str); } +PHP_HTTP_API void _http_message_tostruct_recursive(http_message *msg, zval *obj TSRMLS_DC) +{ + zval strct; + zval *headers; + + Z_TYPE(strct) = IS_ARRAY; + Z_ARRVAL(strct) = HASH_OF(obj); + + add_assoc_long(&strct, "type", msg->type); + add_assoc_double(&strct, "httpVersion", msg->http.version); + switch (msg->type) + { + case HTTP_MSG_RESPONSE: + add_assoc_long(&strct, "responseCode", msg->http.info.response.code); + add_assoc_string(&strct, "responseStatus", msg->http.info.response.status, 1); + break; + + case HTTP_MSG_REQUEST: + add_assoc_string(&strct, "requestMethod", msg->http.info.request.method, 1); + add_assoc_string(&strct, "requestUri", msg->http.info.request.URI, 1); + break; + } + + MAKE_STD_ZVAL(headers); + array_init(headers); + zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + add_assoc_zval(&strct, "headers", headers); + zval_ptr_dtor(&headers); + + add_assoc_stringl(&strct, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg), 1); + + if (msg->parent) { + zval *parent; + + MAKE_STD_ZVAL(parent); + if (Z_TYPE_P(obj) == IS_ARRAY) { + array_init(parent); + } else { + object_init(parent); + } + add_assoc_zval(&strct, "parentMessage", parent); + http_message_tostruct_recursive(msg->parent, parent); + zval_ptr_dtor(&parent); + } else { + add_assoc_null(&strct, "parentMessage"); + } + http_message_dtor(msg); + efree(msg); +} + PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC) { STATUS rs = FAILURE; @@ -334,16 +377,16 @@ PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC) zval **data; FOREACH_VAL(*val, data) { - http_send_header_ex(key, strlen(key), Z_STRVAL_PP(data), Z_STRLEN_PP(data), first); + http_send_header_ex(key, strlen(key), Z_STRVAL_PP(data), Z_STRLEN_PP(data), first, NULL); first = 0; } } else { - http_send_header_ex(key, strlen(key), Z_STRVAL_PP(val), Z_STRLEN_PP(val), 1); + http_send_header_ex(key, strlen(key), Z_STRVAL_PP(val), Z_STRLEN_PP(val), 1, NULL); } key = NULL; } } - rs = SUCCESS == http_send_status(message->info.response.code) && + rs = SUCCESS == http_send_status(message->http.info.response.code) && SUCCESS == http_send_data(PHPSTR_VAL(message), PHPSTR_LEN(message)) ? SUCCESS : FAILURE; } @@ -374,26 +417,26 @@ PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC) host = estrndup(Z_STRVAL_PP(zhost), host_len = Z_STRLEN_PP(zhost)); } uri = http_absolute_uri_ex( - message->info.request.URI, strlen(message->info.request.URI), + message->http.info.request.URI, strlen(message->http.info.request.URI), NULL, 0, host, host_len, port); efree(host); } else { - uri = http_absolute_uri(message->info.request.URI); + uri = http_absolute_uri(message->http.info.request.URI); } - if (!strcasecmp("POST", message->info.request.method)) { + if (!strcasecmp("POST", message->http.info.request.method)) { http_request_body body = {HTTP_REQUEST_BODY_CSTRING, PHPSTR_VAL(message), PHPSTR_LEN(message)}; rs = http_post(uri, &body, Z_ARRVAL(options), NULL, NULL); } else - if (!strcasecmp("GET", message->info.request.method)) { + if (!strcasecmp("GET", message->http.info.request.method)) { rs = http_get(uri, Z_ARRVAL(options), NULL, NULL); } else - if (!strcasecmp("HEAD", message->info.request.method)) { + if (!strcasecmp("HEAD", message->http.info.request.method)) { rs = http_head(uri, Z_ARRVAL(options), NULL, NULL); } else { http_error_ex(HE_WARNING, HTTP_E_REQUEST_METHOD, "Cannot send HttpMessage. Request method %s not supported", - message->info.request.method); + message->http.info.request.method); } efree(uri); @@ -432,28 +475,33 @@ PHP_HTTP_API void _http_message_dtor(http_message *message) if (message) { zend_hash_destroy(&message->hdrs); phpstr_dtor(PHPSTR(message)); - if (HTTP_MSG_TYPE(REQUEST, message)) { - if (message->info.request.method) { - efree(message->info.request.method); - message->info.request.method = NULL; - } - if (message->info.request.URI) { - efree(message->info.request.URI); - message->info.request.URI = NULL; - } + + switch (message->type) + { + case HTTP_MSG_REQUEST: + STR_SET(message->http.info.request.method, NULL); + STR_SET(message->http.info.request.URI, NULL); + break; + + case HTTP_MSG_RESPONSE: + STR_SET(message->http.info.response.status, NULL); + break; + + default: + break; } } } -PHP_HTTP_API void _http_message_free(http_message *message) +PHP_HTTP_API void _http_message_free(http_message **message) { - if (message) { - if (message->parent) { - http_message_free(message->parent); - message->parent = NULL; + if (*message) { + if ((*message)->parent) { + http_message_free(&(*message)->parent); } - http_message_dtor(message); - efree(message); + http_message_dtor(*message); + efree(*message); + *message = NULL; } } diff --git a/http_message_object.c b/http_message_object.c index 18d3793..108848b 100644 --- a/http_message_object.c +++ b/http_message_object.c @@ -243,21 +243,7 @@ static zval *_http_message_object_read_prop(zval *object, zval *member, int type break; case HTTP_MSG_PROPHASH_HTTP_VERSION: - switch (msg->type) - { - case HTTP_MSG_REQUEST: - RETVAL_DOUBLE(msg->info.request.http_version); - break; - - case HTTP_MSG_RESPONSE: - RETVAL_DOUBLE(msg->info.response.http_version); - break; - - case HTTP_MSG_NONE: - default: - RETVAL_NULL(); - break; - } + RETVAL_DOUBLE(msg->http.version); break; case HTTP_MSG_PROPHASH_BODY: @@ -283,25 +269,22 @@ static zval *_http_message_object_read_prop(zval *object, zval *member, int type case HTTP_MSG_PROPHASH_PARENT_MESSAGE: if (msg->parent) { RETVAL_OBJVAL(obj->parent); - Z_TYPE_P(return_value) = IS_OBJECT; - return_value->value.obj = obj->parent; - zend_objects_store_add_ref(return_value TSRMLS_CC); } else { RETVAL_NULL(); } break; case HTTP_MSG_PROPHASH_REQUEST_METHOD: - if (HTTP_MSG_TYPE(REQUEST, msg) && msg->info.request.method) { - RETVAL_STRING(msg->info.request.method, 1); + if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.method) { + RETVAL_STRING(msg->http.info.request.method, 1); } else { RETVAL_NULL(); } break; case HTTP_MSG_PROPHASH_REQUEST_URI: - if (HTTP_MSG_TYPE(REQUEST, msg) && msg->info.request.URI) { - RETVAL_STRING(msg->info.request.URI, 1); + if (HTTP_MSG_TYPE(REQUEST, msg) && msg->http.info.request.URI) { + RETVAL_STRING(msg->http.info.request.URI, 1); } else { RETVAL_NULL(); } @@ -309,12 +292,20 @@ static zval *_http_message_object_read_prop(zval *object, zval *member, int type case HTTP_MSG_PROPHASH_RESPONSE_CODE: if (HTTP_MSG_TYPE(RESPONSE, msg)) { - RETVAL_LONG(msg->info.response.code); + RETVAL_LONG(msg->http.info.response.code); } else { RETVAL_NULL(); } break; - + + case HTTP_MSG_PROPHASH_RESPONSE_STATUS: + if (HTTP_MSG_TYPE(RESPONSE, msg) && msg->http.info.response.status) { + RETVAL_STRING(msg->http.info.response.status, 1); + } else { + RETVAL_NULL(); + } + break; + default: RETVAL_NULL(); break; @@ -339,36 +330,12 @@ static void _http_message_object_write_prop(zval *object, zval *member, zval *va { case HTTP_MSG_PROPHASH_TYPE: convert_to_long_ex(&value); - if ((http_message_type) Z_LVAL_P(value) != msg->type) { - if (HTTP_MSG_TYPE(REQUEST, msg)) { - if (msg->info.request.method) { - efree(msg->info.request.method); - } - if (msg->info.request.URI) { - efree(msg->info.request.URI); - } - } - msg->type = Z_LVAL_P(value); - if (HTTP_MSG_TYPE(REQUEST, msg)) { - msg->info.request.method = NULL; - msg->info.request.URI = NULL; - } - } - + http_message_set_type(msg, Z_LVAL_P(value)); break; case HTTP_MSG_PROPHASH_HTTP_VERSION: convert_to_double_ex(&value); - switch (msg->type) - { - case HTTP_MSG_REQUEST: - msg->info.request.http_version = Z_DVAL_P(value); - break; - - case HTTP_MSG_RESPONSE: - msg->info.response.http_version = Z_DVAL_P(value); - break; - } + msg->http.version = Z_DVAL_P(value); break; case HTTP_MSG_PROPHASH_BODY: @@ -394,31 +361,32 @@ static void _http_message_object_write_prop(zval *object, zval *member, zval *va break; case HTTP_MSG_PROPHASH_REQUEST_METHOD: - convert_to_string_ex(&value); if (HTTP_MSG_TYPE(REQUEST, msg)) { - if (msg->info.request.method) { - efree(msg->info.request.method); - } - msg->info.request.method = estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)); + convert_to_string_ex(&value); + STR_SET(msg->http.info.request.method, estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value))); } break; case HTTP_MSG_PROPHASH_REQUEST_URI: - convert_to_string_ex(&value); if (HTTP_MSG_TYPE(REQUEST, msg)) { - if (msg->info.request.URI) { - efree(msg->info.request.URI); - } - msg->info.request.URI = estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value)); + convert_to_string_ex(&value); + STR_SET(msg->http.info.request.URI, estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value))); } break; case HTTP_MSG_PROPHASH_RESPONSE_CODE: - convert_to_long_ex(&value); if (HTTP_MSG_TYPE(RESPONSE, msg)) { - msg->info.response.code = Z_LVAL_P(value); + convert_to_long_ex(&value); + msg->http.info.response.code = Z_LVAL_P(value); } break; + + case HTTP_MSG_PROPHASH_RESPONSE_STATUS: + if (HTTP_MSG_TYPE(RESPONSE, msg)) { + convert_to_string_ex(&value); + STR_SET(msg->http.info.response.status, estrndup(Z_STRVAL_P(value), Z_STRLEN_P(value))); + } + } } @@ -451,39 +419,39 @@ static HashTable *_http_message_object_get_props(zval *object TSRMLS_DC) zend_hash_clean(OBJ_PROP(obj)); ASSOC_PROP(obj, long, "type", msg->type); - ASSOC_STRINGL(obj, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg)); - - MAKE_STD_ZVAL(headers); - array_init(headers); - - zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); - ASSOC_PROP(obj, zval, "headers", headers); + ASSOC_PROP(obj, double, "httpVersion", msg->http.version); switch (msg->type) { case HTTP_MSG_REQUEST: - ASSOC_PROP(obj, double, "httpVersion", msg->info.request.http_version); ASSOC_PROP(obj, long, "responseCode", 0); - ASSOC_STRING(obj, "requestMethod", msg->info.request.method); - ASSOC_STRING(obj, "requestUri", msg->info.request.URI); + ASSOC_STRINGL(obj, "responseStatus", "", 0); + ASSOC_STRING(obj, "requestMethod", msg->http.info.request.method); + ASSOC_STRING(obj, "requestUri", msg->http.info.request.URI); break; case HTTP_MSG_RESPONSE: - ASSOC_PROP(obj, double, "httpVersion", msg->info.response.http_version); - ASSOC_PROP(obj, long, "responseCode", msg->info.response.code); - ASSOC_STRING(obj, "requestMethod", ""); - ASSOC_STRING(obj, "requestUri", ""); + ASSOC_PROP(obj, long, "responseCode", msg->http.info.response.code); + ASSOC_STRING(obj, "responseStatus", msg->http.info.response.status); + ASSOC_STRINGL(obj, "requestMethod", "", 0); + ASSOC_STRINGL(obj, "requestUri", "", 0); break; case HTTP_MSG_NONE: default: - ASSOC_PROP(obj, double, "httpVersion", 0.0); ASSOC_PROP(obj, long, "responseCode", 0); - ASSOC_STRING(obj, "requestMethod", ""); - ASSOC_STRING(obj, "requestUri", ""); + ASSOC_STRINGL(obj, "responseStatus", "", 0); + ASSOC_STRINGL(obj, "requestMethod", "", 0); + ASSOC_STRINGL(obj, "requestUri", "", 0); break; } + MAKE_STD_ZVAL(headers); + array_init(headers); + zend_hash_copy(Z_ARRVAL_P(headers), &msg->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); + ASSOC_PROP(obj, zval, "headers", headers); + ASSOC_STRINGL(obj, "body", PHPSTR_VAL(msg), PHPSTR_LEN(msg)); + return OBJ_PROP(obj); } @@ -659,7 +627,7 @@ PHP_METHOD(HttpMessage, getResponseCode) RETURN_NULL(); } - RETURN_LONG(obj->message->info.response.code); + RETURN_LONG(obj->message->http.info.response.code); } } /* }}} */ @@ -688,7 +656,7 @@ PHP_METHOD(HttpMessage, setResponseCode) RETURN_FALSE; } - obj->message->info.response.code = code; + obj->message->http.info.response.code = code; RETURN_TRUE; } /* }}} */ @@ -710,7 +678,7 @@ PHP_METHOD(HttpMessage, getRequestMethod) RETURN_NULL(); } - RETURN_STRING(obj->message->info.request.method, 1); + RETURN_STRING(obj->message->http.info.request.method, 1); } } /* }}} */ @@ -743,7 +711,7 @@ PHP_METHOD(HttpMessage, setRequestMethod) RETURN_FALSE; } - STR_SET(obj->message->info.request.method, estrndup(method, method_len)); + STR_SET(obj->message->http.info.request.method, estrndup(method, method_len)); RETURN_TRUE; } /* }}} */ @@ -764,7 +732,7 @@ PHP_METHOD(HttpMessage, getRequestUri) RETURN_NULL(); } - RETURN_STRING(obj->message->info.request.URI, 1); + RETURN_STRING(obj->message->http.info.request.URI, 1); } } /* }}} */ @@ -793,7 +761,7 @@ PHP_METHOD(HttpMessage, setRequestUri) RETURN_FALSE; } - STR_SET(obj->message->info.request.URI, estrndup(URI, URIlen)); + STR_SET(obj->message->http.info.request.URI, estrndup(URI, URIlen)); RETURN_TRUE; } /* }}} */ @@ -811,21 +779,7 @@ PHP_METHOD(HttpMessage, getHttpVersion) double version; getObject(http_message_object, obj); - switch (obj->message->type) - { - case HTTP_MSG_RESPONSE: - version = obj->message->info.response.http_version; - break; - - case HTTP_MSG_REQUEST: - version = obj->message->info.request.http_version; - break; - - case HTTP_MSG_NONE: - default: - RETURN_NULL(); - } - sprintf(ver, "%1.1lf", version); + sprintf(ver, "%1.1lf", obj->message->http.version); RETURN_STRINGL(ver, 3, 1); } } @@ -846,11 +800,6 @@ PHP_METHOD(HttpMessage, setHttpVersion) return; } - if (HTTP_MSG_TYPE(NONE, obj->message)) { - http_error(HE_WARNING, HTTP_E_MESSAGE_TYPE, "Message is neither of type HTTP_MSG_RESPONSE nor HTTP_MSG_REQUEST"); - RETURN_FALSE; - } - convert_to_double(zv); sprintf(v, "%1.1lf", Z_DVAL_P(zv)); if (strcmp(v, "1.0") && strcmp(v, "1.1")) { @@ -858,11 +807,7 @@ PHP_METHOD(HttpMessage, setHttpVersion) RETURN_FALSE; } - if (HTTP_MSG_TYPE(RESPONSE, obj->message)) { - obj->message->info.response.http_version = Z_DVAL_P(zv); - } else { - obj->message->info.request.http_version = Z_DVAL_P(zv); - } + obj->message->http.version = Z_DVAL_P(zv); RETURN_TRUE; } /* }}} */ diff --git a/http_request_api.c b/http_request_api.c index 5bb5b65..097049f 100644 --- a/http_request_api.c +++ b/http_request_api.c @@ -464,7 +464,7 @@ PHP_HTTP_API STATUS _http_request_init(CURL *ch, http_request_method meth, char phpstr_fix(qstr); HTTP_CURL_OPT(COOKIE, http_request_data_copy(COPY_STRING, qstr->data)); } - phpstr_free(qstr); + phpstr_free(&qstr); } else { HTTP_CURL_OPT(COOKIE, NULL); } diff --git a/http_request_object.c b/http_request_object.c index d6447ca..f2e6c46 100644 --- a/http_request_object.c +++ b/http_request_object.c @@ -525,11 +525,11 @@ STATUS _http_request_object_responsehandler(http_request_object *obj, zval *this } while ((response = response->parent) && (request = request->parent)); - http_message_free(free_msg); + http_message_free(&free_msg); phpstr_fix(&obj->history); } - UPD_PROP(obj, long, responseCode, msg->info.response.code); + UPD_PROP(obj, long, responseCode, msg->http.info.response.code); MAKE_STD_ZVAL(headers) array_init(headers); diff --git a/http_response_object.c b/http_response_object.c index d21225a..0fb1248 100644 --- a/http_response_object.c +++ b/http_response_object.c @@ -811,11 +811,11 @@ PHP_METHOD(HttpResponse, send) zval **data; FOREACH_VAL(*value, data) { - http_send_header_ex(name, strlen(name), Z_STRVAL_PP(data), Z_STRLEN_PP(data), first); + http_send_header_ex(name, strlen(name), Z_STRVAL_PP(data), Z_STRLEN_PP(data), first, NULL); first = 0; } } else { - http_send_header_ex(name, strlen(name), Z_STRVAL_PP(value), Z_STRLEN_PP(value), 1); + http_send_header_ex(name, strlen(name), Z_STRVAL_PP(value), Z_STRLEN_PP(value), 1, NULL); } name = NULL; } @@ -862,7 +862,7 @@ PHP_METHOD(HttpResponse, send) { zval *cd = GET_STATIC_PROP(contentDisposition); if (Z_STRLEN_P(cd)) { - http_send_header_ex("Content-Disposition", lenof("Content-Disposition"), Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1); + http_send_header_ex("Content-Disposition", lenof("Content-Disposition"), Z_STRVAL_P(cd), Z_STRLEN_P(cd), 1, NULL); } } diff --git a/http_send_api.c b/http_send_api.c index 9f11052..ba6122b 100644 --- a/http_send_api.c +++ b/http_send_api.c @@ -149,10 +149,10 @@ static STATUS _http_send_chunk(const void *data, size_t begin, size_t end, http_ /* }}} */ /* {{{ STATUS http_send_header(char *, char *, zend_bool) */ -PHP_HTTP_API STATUS _http_send_header_ex(const char *name, size_t name_len, const char *value, size_t value_len, zend_bool replace TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_header_ex(const char *name, size_t name_len, const char *value, size_t value_len, zend_bool replace, char **sent_header TSRMLS_DC) { STATUS ret; - size_t header_len = sizeof(": ") + name_len + value_len; + size_t header_len = sizeof(": ") + name_len + value_len + 1; char *header = emalloc(header_len + 1); header[header_len] = '\0'; @@ -176,7 +176,7 @@ PHP_HTTP_API STATUS _http_send_status_header_ex(int status, const char *header, /* }}} */ /* {{{ STATUS http_send_last_modified(int) */ -PHP_HTTP_API STATUS _http_send_last_modified(time_t t TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_last_modified_ex(time_t t, char **sent_header TSRMLS_DC) { STATUS ret; char *date = http_date(t); @@ -185,7 +185,7 @@ PHP_HTTP_API STATUS _http_send_last_modified(time_t t TSRMLS_DC) return FAILURE; } - ret = http_send_header("Last-Modified", date, 1); + ret = http_send_header_ex("Last-Modified", lenof("Last-Modifed"), date, strlen(date), 1, sent_header); efree(date); /* remember */ @@ -196,7 +196,7 @@ PHP_HTTP_API STATUS _http_send_last_modified(time_t t TSRMLS_DC) /* }}} */ /* {{{ STATUS http_send_etag(char *, size_t) */ -PHP_HTTP_API STATUS _http_send_etag(const char *etag, size_t etag_len TSRMLS_DC) +PHP_HTTP_API STATUS _http_send_etag_ex(const char *etag, size_t etag_len, char **sent_header TSRMLS_DC) { STATUS status; char *etag_header; @@ -213,7 +213,13 @@ PHP_HTTP_API STATUS _http_send_etag(const char *etag, size_t etag_len TSRMLS_DC) etag_header = ecalloc(1, sizeof("ETag: \"\"") + etag_len); sprintf(etag_header, "ETag: \"%s\"", etag); status = http_send_header_string(etag_header); - efree(etag_header); + + if (sent_header) { + *sent_header = etag_header; + } else { + efree(etag_header); + } + return status; } /* }}} */ @@ -230,7 +236,7 @@ PHP_HTTP_API STATUS _http_send_content_type(const char *content_type, size_t ct_ STR_FREE(HTTP_G(send).content_type); HTTP_G(send).content_type = estrndup(content_type, ct_len); - return http_send_header_ex("Content-Type", lenof("Content-Type"), content_type, ct_len, 1); + return http_send_header_ex("Content-Type", lenof("Content-Type"), content_type, ct_len, 1, NULL); } /* }}} */ @@ -381,9 +387,13 @@ PHP_HTTP_API STATUS _http_send(const void *data_ptr, size_t data_size, http_send if (!(etag = http_etag(data_ptr, data_size, data_mode))) { http_error(HE_NOTICE, HTTP_E_RUNTIME, "Failed to generate ETag for data source"); } else { - http_send_etag(etag, 32); + char *sent_header = NULL; + + http_send_etag_ex(etag, 32, &sent_header); if (http_match_etag("HTTP_IF_NONE_MATCH", etag)) { - return http_cache_exit_ex(etag, 1, 1); + return http_exit_ex(304, sent_header, NULL, 0); + } else { + STR_FREE(sent_header); } efree(etag); } @@ -391,7 +401,9 @@ PHP_HTTP_API STATUS _http_send(const void *data_ptr, size_t data_size, http_send /* send 304 Not Modified if last modified matches */ if (http_match_last_modified("HTTP_IF_MODIFIED_SINCE", HTTP_G(send).last_modified)) { - return http_cache_exit_ex(http_date(HTTP_G(send).last_modified), 0, 1); + char *sent_header = NULL; + http_send_last_modified_ex(HTTP_G(send).last_modified, &sent_header); + return http_exit_ex(304, sent_header, NULL, 0); } /* emit a content-length header */ diff --git a/http_url_api.c b/http_url_api.c index e66781f..10a9095 100644 --- a/http_url_api.c +++ b/http_url_api.c @@ -197,12 +197,12 @@ PHP_HTTP_API STATUS _http_urlencode_hash_ex(HashTable *hash, zend_bool override_ } if (SUCCESS != http_urlencode_hash_implementation(hash, qstr, arg_sep)) { - phpstr_free(qstr); + phpstr_free(&qstr); return FAILURE; } phpstr_data(qstr, encoded_data, encoded_len); - phpstr_free(qstr); + phpstr_free(&qstr); return SUCCESS; } diff --git a/http_util_object.c b/http_util_object.c index d4c544d..5f4d438 100644 --- a/http_util_object.c +++ b/http_util_object.c @@ -73,8 +73,8 @@ HTTP_BEGIN_ARGS(chunkedDecode, 1) HTTP_ARG_VAL(encoded_string, 0) HTTP_END_ARGS; -HTTP_BEGIN_ARGS(splitResponse, 1) - HTTP_ARG_VAL(response_string, 0) +HTTP_BEGIN_ARGS(parseMessage, 1) + HTTP_ARG_VAL(message_string, 0) HTTP_END_ARGS; HTTP_BEGIN_ARGS(parseHeaders, 1) @@ -102,7 +102,7 @@ zend_function_entry http_util_object_fe[] = { HTTP_UTIL_ALIAS(matchEtag, http_match_etag) HTTP_UTIL_ALIAS(matchRequestHeader, http_match_request_header) HTTP_UTIL_ALIAS(chunkedDecode, http_chunked_decode) - HTTP_UTIL_ALIAS(splitResponse, http_split_response) + HTTP_UTIL_ALIAS(parseMessage, http_parse_message) HTTP_UTIL_ALIAS(parseHeaders, http_parse_headers) HTTP_UTIL_ALIAS(authBasic, http_auth_basic) HTTP_UTIL_ALIAS(authBasicCallback, http_auth_basic_cb) diff --git a/missing.c b/missing.c index 81d5183..dcf4709 100644 --- a/missing.c +++ b/missing.c @@ -151,126 +151,6 @@ int zend_update_static_property(zend_class_entry *scope, char *name, size_t name return retval; } -int trash(zend_class_entry *scope, char *name, size_t name_len, zval *value TSRMLS_DC) -{ - int retval; - zval **property = NULL; - zend_class_entry *old_scope = EG(scope); - - EG(scope) = scope; - - if (!(property = zend_std_get_static_property(scope, name, name_len, 0 TSRMLS_CC))) { - retval = FAILURE; - } else if (*property == value) { - retval = SUCCESS; - } else if (scope->type & ZEND_INTERNAL_CLASS) { - int refcount; - zend_uchar is_ref; - - refcount = (*property)->refcount; - is_ref = (*property)->is_ref; - - /* clean */ - switch (Z_TYPE_PP(property)) - { - case IS_BOOL: case IS_LONG: case IS_NULL: - break; - - case IS_RESOURCE: - zend_list_delete(Z_LVAL_PP(property)); - break; - - case IS_STRING: case IS_CONSTANT: - free(Z_STRVAL_PP(property)); - break; - - case IS_OBJECT: - if (Z_OBJ_HT_PP(property)->del_ref) { - Z_OBJ_HT_PP(property)->del_ref(*property TSRMLS_CC); - } - break; - - case IS_ARRAY: case IS_CONSTANT_ARRAY: - if (Z_ARRVAL_PP(property) && Z_ARRVAL_PP(property) != &EG(symbol_table)) { - zend_hash_destroy(Z_ARRVAL_PP(property)); - free(Z_ARRVAL_PP(property)); - } - break; - } - - /* copy */ - **property = *value; - - /* ctor */ - switch (Z_TYPE_PP(property)) - { - case IS_BOOL: case IS_LONG: case IS_NULL: - break; - - case IS_RESOURCE: - zend_list_addref(Z_LVAL_PP(property)); - break; - - case IS_STRING: case IS_CONSTANT: - Z_STRVAL_PP(property) = (char *) zend_strndup(Z_STRVAL_PP(property), Z_STRLEN_PP(property)); - break; - - case IS_OBJECT: - if (Z_OBJ_HT_PP(property)->add_ref) { - Z_OBJ_HT_PP(property)->add_ref(*property TSRMLS_CC); - } - break; - - case IS_ARRAY: case IS_CONSTANT_ARRAY: - { - if (Z_ARRVAL_PP(property) != &EG(symbol_table)) { - zval *tmp; - HashTable *old = Z_ARRVAL_PP(property); - - Z_ARRVAL_PP(property) = (HashTable *) malloc(sizeof(HashTable)); - zend_hash_init(Z_ARRVAL_PP(property), 0, NULL, ZVAL_PTR_DTOR, 0); - zend_hash_copy(Z_ARRVAL_PP(property), old, (copy_ctor_func_t) zval_add_ref, (void *) &tmp, sizeof(zval *)); - } - } - break; - } - - (*property)->refcount = refcount; - (*property)->is_ref = is_ref; - - retval = SUCCESS; - - } else { - if (PZVAL_IS_REF(*property)) { - zval_dtor(*property); - (*property)->type = value->type; - (*property)->value = value->value; - - if (value->refcount) { - zval_copy_ctor(*property); - } - - retval = SUCCESS; - } else { - value->refcount++; - if (PZVAL_IS_REF(value)) { - SEPARATE_ZVAL(&value); - } - - retval = zend_hash_update(scope->static_members, name, name_len, &value, sizeof(zval *), NULL); - } - } - - if (!value->refcount) { - zval_dtor(value); - FREE_ZVAL(value); - } - - EG(scope) = old_scope; - - return retval; -} - int zend_update_static_property_bool(zend_class_entry *scope, char *name, size_t name_len, zend_bool value TSRMLS_DC) { zval *tmp = tmp_zval(); diff --git a/php_http.h b/php_http.h index 1b0f213..4dffa67 100644 --- a/php_http.h +++ b/php_http.h @@ -46,7 +46,11 @@ ZEND_BEGIN_MODULE_GLOBALS(http) } etag; struct _http_globals_log { + char *auth; char *cache; + char *redirect; + char *allowed_methods; + char *composite; } log; struct _http_globals_send { @@ -64,7 +68,7 @@ ZEND_BEGIN_MODULE_GLOBALS(http) } methods; #ifdef HTTP_HAVE_CURL - struct _http_globlas_request_copies { + struct _http_globals_request_copies { zend_llist strings; zend_llist slists; zend_llist contexts; @@ -107,7 +111,6 @@ PHP_FUNCTION(http_send_data); PHP_FUNCTION(http_send_file); PHP_FUNCTION(http_send_stream); PHP_FUNCTION(http_chunked_decode); -PHP_FUNCTION(http_split_response); PHP_FUNCTION(http_parse_message); PHP_FUNCTION(http_parse_headers); PHP_FUNCTION(http_get_request_headers); diff --git a/php_http_api.h b/php_http_api.h index 472c4b9..8906e2c 100644 --- a/php_http_api.h +++ b/php_http_api.h @@ -35,9 +35,9 @@ extern STATUS _http_parse_key_list(const char *list, HashTable *items, char sepa #define http_error_ex _http_error_ex extern void _http_error_ex(long type, long code, const char *format, ...) PHP_ATTRIBUTE_FORMAT(printf, 3, 4); -#define http_exit(s, h) http_exit_ex((s), (h), 1) -#define http_exit_ex(s, h, f) _http_exit_ex((s), (h), (f) TSRMLS_CC) -extern STATUS _http_exit_ex(int status, char *header, zend_bool free_header TSRMLS_DC); +#define http_exit(s, h) http_exit_ex((s), (h), NULL, 1) +#define http_exit_ex(s, h, b, e) _http_exit_ex((s), (h), (b), (e) TSRMLS_CC) +extern STATUS _http_exit_ex(int status, char *header, char *body, zend_bool send_header TSRMLS_DC); #define http_check_method(m) http_check_method_ex((m), HTTP_KNOWN_METHODS) #define http_check_method_ex(m, a) _http_check_method_ex((m), (a)) @@ -60,8 +60,20 @@ PHP_HTTP_API STATUS _http_get_request_body_ex(char **body, size_t *length, zend_ #define http_chunked_decode(e, el, d, dl) _http_chunked_decode((e), (el), (d), (dl) TSRMLS_CC) PHP_HTTP_API const char *_http_chunked_decode(const char *encoded, size_t encoded_len, char **decoded, size_t *decoded_len TSRMLS_DC); -#define http_split_response(r, rl, h, b, bl) _http_split_response((r), (rl), (h), (b), (bl) TSRMLS_CC) -PHP_HTTP_API STATUS _http_split_response(char *response, size_t repsonse_len, HashTable *headers, char **body, size_t *body_len TSRMLS_DC); +#define http_locate_body _http_locate_body +static inline const char *_http_locate_body(const char *message) +{ + const char *cr = strstr(message, "\r\n\r\n"); + const char *lf = strstr(message, "\n\n"); + + if (lf && cr) { + return MIN(lf + 2, cr + 4); + } else if (lf || cr) { + return MAX(lf + 2, cr + 4); + } else { + return NULL; + } +} #endif diff --git a/php_http_cache_api.h b/php_http_cache_api.h index e5b618f..e420963 100644 --- a/php_http_cache_api.h +++ b/php_http_cache_api.h @@ -22,10 +22,6 @@ #include "php_http_api.h" #include "php_http_send_api.h" -#define http_cache_exit(t, e) http_cache_exit_ex((t), (e), 1) -#define http_cache_exit_ex(t, e, f) _http_cache_exit_ex((t), (e), (f) TSRMLS_CC) -extern STATUS _http_cache_exit_ex(char *cache_token, zend_bool etag, zend_bool free_token TSRMLS_DC); - #define http_etag(p, l, m) _http_etag((p), (l), (m) TSRMLS_CC) PHP_HTTP_API char *_http_etag(const void *data_ptr, size_t data_len, http_send_mode data_mode TSRMLS_DC); diff --git a/php_http_headers_api.h b/php_http_headers_api.h index d3221f5..b02e4c8 100644 --- a/php_http_headers_api.h +++ b/php_http_headers_api.h @@ -19,6 +19,7 @@ #define PHP_HTTP_HEADERS_API_H #include "php_http_std_defs.h" +#include "php_http_info_api.h" typedef enum { RANGE_OK, @@ -26,15 +27,10 @@ typedef enum { RANGE_ERR } http_range_status; -typedef void (*http_parse_headers_callback_t)(const char *http_line, HashTable **headers, void **callback_data TSRMLS_DC); - -#define http_parse_headers_default_callback _http_parse_headers_default_callback -PHP_HTTP_API void _http_parse_headers_default_callback(const char *http_line, HashTable **headers, void **cb_data TSRMLS_DC); - -#define http_parse_headers(h, a) _http_parse_headers_ex((h), Z_ARRVAL_P(a), 1, _http_parse_headers_default_callback, NULL TSRMLS_CC) -#define http_parse_headers_ex(h, ht, p) _http_parse_headers_ex((h), (ht), (p), _http_parse_headers_default_callback, NULL TSRMLS_CC) +#define http_parse_headers(h, a) _http_parse_headers_ex((h), Z_ARRVAL_P(a), 1, http_info_default_callback, NULL TSRMLS_CC) +#define http_parse_headers_ex(h, ht, p) _http_parse_headers_ex((h), (ht), (p), http_info_default_callback, NULL TSRMLS_CC) #define http_parse_headers_cb(h, ht, p, f, d) _http_parse_headers_ex((h), (ht), (p), (f), (d) TSRMLS_CC) -PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *headers, zend_bool prettify, http_parse_headers_callback_t func, void **callback_data TSRMLS_DC); +PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, HashTable *headers, zend_bool prettify, http_info_callback callback_func, void **callback_data TSRMLS_DC); #define http_get_request_headers(h) _http_get_request_headers_ex(Z_ARRVAL_P(h), 1 TSRMLS_CC) #define http_get_request_headers_ex(h, p) _http_get_request_headers_ex((h), (p) TSRMLS_CC) diff --git a/php_http_info_api.h b/php_http_info_api.h new file mode 100644 index 0000000..cd229ed --- /dev/null +++ b/php_http_info_api.h @@ -0,0 +1,71 @@ +/* + +----------------------------------------------------------------------+ + | PECL :: http | + +----------------------------------------------------------------------+ + | This source file is subject to version 3.0 of the PHP license, that | + | is bundled with this package in the file LICENSE, and is available | + | through the world-wide-web at http://www.php.net/license/3_0.txt. | + | If you did not receive a copy of the PHP license and are unable to | + | obtain it through the world-wide-web, please send a note to | + | license@php.net so we can mail you a copy immediately. | + +----------------------------------------------------------------------+ + | Copyright (c) 2004-2005 Michael Wallner | + +----------------------------------------------------------------------+ +*/ + +/* $Id$ */ + +#ifndef PHP_HTTP_INFO_API_H +#define PHP_HTTP_INFO_API_H + +#define IS_HTTP_REQUEST 1 +#define IS_HTTP_RESPONSE 2 + +#define HTTP_INFO(ptr) (ptr)->http.info + +typedef struct { + char *method; + char *URI; +} http_request_info; + +typedef struct { + int code; + char *status; +} http_response_info; + +typedef union { + http_request_info request; + http_response_info response; +} http_info_t; + +struct http_info { + http_info_t info; + double version; +}; + +typedef struct { + struct http_info http; + int type; +} http_info; + +typedef void (*http_info_callback)(void **callback_data, HashTable **headers, http_info *info TSRMLS_DC); + +#define http_info_default_callback _http_info_default_callback +PHP_HTTP_API void _http_info_default_callback(void **nothing, HashTable **headers, http_info *info TSRMLS_DC); +#define http_info_dtor _http_info_dtor +PHP_HTTP_API void _http_info_dtor(http_info *info); +#define http_info_parse(p, i) _http_info_parse_ex((p), (i), 1 TSRMLS_CC) +#define http_info_parse_ex(p, i, s) _http_info_parse_ex((p), (i), (s) TSRMLS_CC) +PHP_HTTP_API STATUS _http__infoparse_ex(const char *pre_header, http_info *info , zend_bool silent TSRMLS_DC); + +#endif + +/* + * Local variables: + * tab-width: 4 + * c-basic-offset: 4 + * End: + * vim600: noet sw=4 ts=4 fdm=marker + * vim<600: noet sw=4 ts=4 + */ + diff --git a/php_http_message_api.h b/php_http_message_api.h index 16f4fc2..9ba6081 100644 --- a/php_http_message_api.h +++ b/php_http_message_api.h @@ -18,12 +18,13 @@ #ifndef PHP_HTTP_MESSAGE_API_H #define PHP_HTTP_MESSAGE_API_H +#include "php_http_info_api.h" #include "phpstr/phpstr.h" typedef enum { - HTTP_MSG_NONE, - HTTP_MSG_REQUEST, - HTTP_MSG_RESPONSE + HTTP_MSG_NONE = 0, + HTTP_MSG_REQUEST = IS_HTTP_REQUEST, + HTTP_MSG_RESPONSE = IS_HTTP_RESPONSE, } http_message_type; typedef struct _http_message http_message; @@ -32,26 +33,12 @@ struct _http_message { phpstr body; HashTable hdrs; http_message_type type; - - union { - struct { - double http_version; - char *method; - char *URI; - } request; - - struct { - double http_version; - int code; - } response; - - } info; - + struct http_info http; http_message *parent; }; -/* required minimum length of an HTTP message "HTTP/1.1 200\r\n" */ -#define HTTP_MSG_MIN_SIZE 15 +/* required minimum length of an HTTP message "HTTP/1.1" */ +#define HTTP_MSG_MIN_SIZE 8 /* shorthand for type checks */ #define HTTP_MSG_TYPE(TYPE, msg) ((msg) && ((msg)->type == HTTP_MSG_ ##TYPE)) @@ -85,6 +72,9 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_ #define http_message_serialize(m, s, l) _http_message_serialize((m), (s), (l)) PHP_HTTP_API void _http_message_serialize(http_message *message, char **string, size_t *length); +#define http_message_tostruct_recursive(m, s) _http_message_tostruct_recursive((m), (s) TSRMLS_CC) +PHP_HTTP_API void _http_message_tostruct_recursive(http_message *msg, zval *strct TSRMLS_DC); + #define http_message_send(m) _http_message_send((m) TSRMLS_CC) PHP_HTTP_API STATUS _http_message_send(http_message *message TSRMLS_DC); @@ -95,7 +85,7 @@ PHP_HTTP_API http_message *_http_message_dup(http_message *msg TSRMLS_DC); PHP_HTTP_API void _http_message_dtor(http_message *message); #define http_message_free(m) _http_message_free((m)) -PHP_HTTP_API void _http_message_free(http_message *message); +PHP_HTTP_API void _http_message_free(http_message **message); #endif diff --git a/php_http_send_api.h b/php_http_send_api.h index 5b9e016..84a0858 100644 --- a/php_http_send_api.h +++ b/php_http_send_api.h @@ -29,21 +29,23 @@ typedef enum { #define http_send_status(s) sapi_header_op(SAPI_HEADER_SET_STATUS, (void *) (s) TSRMLS_CC) #define http_send_header(n, v, r) _http_send_header_ex((n), strlen(n), (v), strlen(v), (r) TSRMLS_CC) -#define http_send_header_ex(n, nl, v, vl, r) _http_send_header_ex((n), (nl), (v), (vl), (r) TSRMLS_CC) -PHP_HTTP_API STATUS _http_send_header_ex(const char *name, size_t name_len, const char *value, size_t value_len, zend_bool replace TSRMLS_DC); +#define http_send_header_ex(n, nl, v, vl, r, s) _http_send_header_ex((n), (nl), (v), (vl), (r), (s) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_header_ex(const char *name, size_t name_len, const char *value, size_t value_len, zend_bool replace, char **sent_header TSRMLS_DC); #define http_send_header_string(h) _http_send_status_header_ex(0, (h), 1 TSRMLS_CC) #define http_send_header_string_ex(h, r) _http_send_status_header_ex(0, (h), (r) TSRMLS_CC) #define http_send_status_header(s, h) _http_send_status_header_ex((s), (h), 1 TSRMLS_CC) #define http_send_status_header_ex(s, h, r) _http_send_status_header_ex((s), (h), (r) TSRMLS_CC) PHP_HTTP_API STATUS _http_send_status_header_ex(int status, const char *header, zend_bool replace TSRMLS_DC); -#define http_send_last_modified(t) _http_send_last_modified((t) TSRMLS_CC) -PHP_HTTP_API STATUS _http_send_last_modified(time_t t TSRMLS_DC); +#define http_send_last_modified(t) _http_send_last_modified_ex((t), NULL TSRMLS_CC) +#define http_send_last_modified_ex(t, s) _http_send_last_modified_ex((t), (s) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_last_modified_ex(time_t t, char **sent_header TSRMLS_DC); -#define http_send_etag(e, l) _http_send_etag((e), (l) TSRMLS_CC) -PHP_HTTP_API STATUS _http_send_etag(const char *etag, size_t etag_len TSRMLS_DC); +#define http_send_etag(e, l) _http_send_etag_ex((e), (l), NULL TSRMLS_CC) +#define http_send_etag_ex(e, l, s) _http_send_etag_ex((e), (l), (s) TSRMLS_CC) +PHP_HTTP_API STATUS _http_send_etag_ex(const char *etag, size_t etag_len, char **sent_header TSRMLS_DC); -#define http_send_cache_control(cc, cl) http_send_header_ex("Cache-Control", lenof("Cache-Control"), (cc), (cl), 1) +#define http_send_cache_control(cc, cl) http_send_header_ex("Cache-Control", lenof("Cache-Control"), (cc), (cl), 1, NULL) #define http_send_content_type(c, l) _http_send_content_type((c), (l) TSRMLS_CC) PHP_HTTP_API STATUS _http_send_content_type(const char *content_type, size_t ct_len TSRMLS_DC); diff --git a/php_http_std_defs.h b/php_http_std_defs.h index e115b56..8bef4f8 100644 --- a/php_http_std_defs.h +++ b/php_http_std_defs.h @@ -36,10 +36,21 @@ typedef int STATUS; /* lenof() */ #define lenof(S) (sizeof(S) - 1) +#ifndef MIN +# define MIN(a,b) (ab?a:b) +#endif + /* STR_SET() */ -#define STR_SET(target, source) \ - if(target) efree(target); \ - target = source +#ifndef STR_SET +# define STR_SET(STR, SET) \ + { \ + STR_FREE(STR); \ + STR = SET; \ + } +#endif /* return bool (v == SUCCESS) */ #define RETVAL_SUCCESS(v) RETVAL_BOOL(SUCCESS == (v)) diff --git a/php_http_util_object.h b/php_http_util_object.h index 0742ef8..7357287 100644 --- a/php_http_util_object.h +++ b/php_http_util_object.h @@ -32,8 +32,8 @@ PHP_METHOD(HttpUtil, negotiateCharset); PHP_METHOD(HttpUtil, matchModified); PHP_METHOD(HttpUtil, matchEtag); PHP_METHOD(HttpUtil, chunkedDecode); -PHP_METHOD(HttpUtil, splitResponse); PHP_METHOD(HttpUtil, parseHeaders); +PHP_METHOD(HttpUril, parseMessage); PHP_METHOD(HttpUtil, authBasic); PHP_METHOD(HttpUtil, authBasicCallback); diff --git a/phpstr/phpstr.c b/phpstr/phpstr.c index 8d8df3e..acee06f 100644 --- a/phpstr/phpstr.c +++ b/phpstr/phpstr.c @@ -235,19 +235,17 @@ PHPSTR_API int phpstr_cmp(phpstr *left, phpstr *right) PHPSTR_API void phpstr_dtor(phpstr *buf) { - if (buf->data) { - efree(buf->data); - buf->data = NULL; - } + STR_SET(buf->data, NULL); buf->used = 0; buf->free = 0; } -PHPSTR_API void phpstr_free(phpstr *buf) +PHPSTR_API void phpstr_free(phpstr **buf) { - if (buf) { - phpstr_dtor(buf); - efree(buf); + if (*buf) { + phpstr_dtor(*buf); + efree(*buf); + *buf = NULL; } } diff --git a/phpstr/phpstr.h b/phpstr/phpstr.h index 40c59ed..a36f88a 100644 --- a/phpstr/phpstr.h +++ b/phpstr/phpstr.h @@ -6,6 +6,14 @@ #include "php.h" +#ifndef STR_SET +# define STR_SET(STR, SET) \ + { \ + STR_FREE(STR); \ + STR = SET; \ + } +#endif + #if defined(PHP_WIN32) # if defined(PHPSTR_EXPORTS) # define PHPSTR_API __declspec(dllexport) @@ -24,21 +32,26 @@ #define FREE_PHPSTR_PTR(STR) efree(STR) #define FREE_PHPSTR_VAL(STR) phpstr_dtor(STR) -#define FREE_PHPSTR_ALL(STR) phpstr_free(STR) +#define FREE_PHPSTR_ALL(STR) phpstr_free(&(STR)) #define FREE_PHPSTR(free, STR) \ switch (free) \ { \ + case PHPSTR_FREE_NOT: break; \ case PHPSTR_FREE_PTR: efree(STR); break; \ case PHPSTR_FREE_VAL: phpstr_dtor(STR); break; \ - case PHPSTR_FREE_ALL: phpstr_free(STR); break; \ - case PHPSTR_FREE_NOT: break; \ + case PHPSTR_FREE_ALL: \ + { \ + phpstr *PTR = (STR); \ + phpstr_free(&PTR); \ + } \ + break; \ default: break; \ } #define RETURN_PHPSTR_PTR(STR) RETURN_PHPSTR((STR), PHPSTR_FREE_PTR, 0) -#define RETURN_PHPSTR_VAL(STR) RETURN_PHPSTR(&(STR), PHPSTR_FREE_NOT, 0) +#define RETURN_PHPSTR_VAL(STR) RETURN_PHPSTR((STR), PHPSTR_FREE_NOT, 0) #define RETVAL_PHPSTR_PTR(STR) RETVAL_PHPSTR((STR), PHPSTR_FREE_PTR, 0) -#define RETVAL_PHPSTR_VAL(STR) RETVAL_PHPSTR(&(STR), PHPSTR_FREE_NOT, 0) +#define RETVAL_PHPSTR_VAL(STR) RETVAL_PHPSTR((STR), PHPSTR_FREE_NOT, 0) /* RETURN_PHPSTR(buf, PHPSTR_FREE_PTR, 0) */ #define RETURN_PHPSTR(STR, free, dup) \ RETVAL_PHPSTR((STR), (free), (dup)); \ @@ -136,7 +149,7 @@ PHPSTR_API void phpstr_fix(phpstr *buf); PHPSTR_API void phpstr_dtor(phpstr *buf); /* free a phpstr object completely */ -PHPSTR_API void phpstr_free(phpstr *buf); +PHPSTR_API void phpstr_free(phpstr **buf); #endif diff --git a/tests/split_response_001.phpt b/tests/split_response_001.phpt deleted file mode 100644 index f3d73a5..0000000 --- a/tests/split_response_001.phpt +++ /dev/null @@ -1,27 +0,0 @@ ---TEST-- -http_split_response() ---SKIPIF-- - ---FILE-- - ---EXPECTF-- -%sTEST -array ( - 0 => - array ( - 'Response Status' => '200 Ok', - 'Content-Type' => 'text/plain', - 'Content-Language' => 'de-AT', - 'Date' => 'Sat, 22 Jan 2005 18:10:02 GMT', - ), - 1 => 'Hallo Du!', -) -Done - diff --git a/tests/split_response_002.phpt b/tests/split_response_002.phpt deleted file mode 100644 index 6db00dd..0000000 --- a/tests/split_response_002.phpt +++ /dev/null @@ -1,24 +0,0 @@ ---TEST-- -http_split_response() list bug (mem-leaks) ---SKIPIF-- - ---FILE-- -r['headers'], $this->r['body']) = http_split_response($data); - } -} - -$t = new t; -$t->fail($data); -echo "Done\n"; -?> ---EXPECTF-- -%sTry -Done