From 1076272e2da97d6df6c2f2423b92a1694709e65a Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Mon, 11 Apr 2005 10:04:34 +0000 Subject: [PATCH] - http_parse_headers() now accepts a callback for handling of request/response lines (used in the http_message api) --- http_functions.c | 15 ---- http_headers_api.c | 126 ++++++++++++++++++------------- http_message_api.c | 165 ++++++++++++++++++++++------------------- php_http_headers_api.h | 13 +++- php_http_message_api.h | 28 +++++-- 5 files changed, 193 insertions(+), 154 deletions(-) diff --git a/http_functions.c b/http_functions.c index a0eb58b..aeeea5b 100644 --- a/http_functions.c +++ b/http_functions.c @@ -999,21 +999,6 @@ PHP_FUNCTION(http_build_query) PHP_FUNCTION(http_test) { -#define HTTP_MESSAGE_STR \ - "GET / HTTP/1.1\r\n" \ - "Content-Type: foo/bar\r\n" \ - "Robots: Noindex,Nofollow\r\n" \ - "\r\n" \ - "Body Data!\n" -#define HTTP_MESSAGE_LEN lenof(HTTP_MESSAGE_STR) - http_message *msg = http_message_parse(HTTP_MESSAGE_STR, HTTP_MESSAGE_LEN); - char *str; - size_t len; - - http_message_tostring(msg, &str, &len); - - RETVAL_STRINGL(str, len, 0); - http_message_free(msg); } /* diff --git a/http_headers_api.c b/http_headers_api.c index 930c496..933b4c8 100644 --- a/http_headers_api.c +++ b/http_headers_api.c @@ -239,10 +239,11 @@ PHP_HTTP_API http_range_status _http_get_request_ranges(HashTable *ranges, size_ /* }}} */ /* {{{ STATUS http_parse_headers(char *, size_t, HashTable *, zend_bool) */ -PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len, - HashTable *headers, zend_bool prettify TSRMLS_DC) +PHP_HTTP_API STATUS _http_parse_headers_ex(char *header, size_t header_len, + HashTable *headers, zend_bool prettify, + http_parse_headers_callback_t func, void **callback_data TSRMLS_DC) { - const char *colon = NULL, *line = NULL, *begin = header; + char *colon = NULL, *line = NULL, *begin = header; zval array; Z_ARRVAL(array) = headers; @@ -251,16 +252,6 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len return FAILURE; } - /* status code */ - if (!strncmp(header, "HTTP/1.", 7)) { - char *end = strstr(header, HTTP_CRLF); - size_t len = end - (header + lenof("HTTP/1.x ")); - char *val = estrndup(header + lenof("HTTP/1.x "), len); - - add_assoc_stringl(&array, "Status", val, len, 0); - header = end + 2; - } - line = header; while (header_len >= (line - begin)) { @@ -271,55 +262,66 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len case 0: --value_len; /* we don't have CR so value length is one char less */ case '\n': - if (colon && ((!(*line - 1)) || ((*line != ' ') && (*line != '\t')))) { - - /* skip empty key */ - if (header != colon) { - zval **previous = NULL; - char *value = empty_string; - int keylen = colon - header; - char *key = estrndup(header, keylen); - - if (prettify) { - key = pretty_key(key, keylen, 1, 1); + if ((!(*line - 1)) || ((*line != ' ') && (*line != '\t'))) { + /* response/request line */ + if ( (!strncmp(header, "HTTP/1.", lenof("HTTP/1."))) || + (!strncmp(line - lenof("HTTP/1.x\r") + value_len, "HTTP/1.", lenof("HTTP/1.")))) { + if (func) { + func(callback_data, header, header - (line + 1), &headers TSRMLS_CC); + Z_ARRVAL(array) = headers; } + } else + + /* "header: value" pair */ + if (colon) { + + /* skip empty key */ + if (header != colon) { + zval **previous = NULL; + char *value = empty_string; + int keylen = colon - header; + char *key = estrndup(header, keylen); + + if (prettify) { + key = pretty_key(key, keylen, 1, 1); + } - value_len += line - colon - 1; - - /* skip leading ws */ - while (isspace(*(++colon))) --value_len; - /* skip trailing ws */ - while (isspace(colon[value_len - 1])) --value_len; + value_len += line - colon - 1; - if (value_len > 0) { - value = estrndup(colon, value_len); - } else { - value_len = 0; - } + /* skip leading ws */ + while (isspace(*(++colon))) --value_len; + /* skip trailing ws */ + while (isspace(colon[value_len - 1])) --value_len; - /* if we already have got such a header make an array of those */ - if (SUCCESS == zend_hash_find(headers, key, keylen + 1, (void **) &previous)) { - /* already an array? - just add */ - if (Z_TYPE_PP(previous) == IS_ARRAY) { - add_next_index_stringl(*previous, value, value_len, 0); + if (value_len > 0) { + value = estrndup(colon, value_len); } else { - /* create the array */ - zval *new_array; - MAKE_STD_ZVAL(new_array); - array_init(new_array); - - add_next_index_stringl(new_array, Z_STRVAL_PP(previous), Z_STRLEN_PP(previous), 1); - add_next_index_stringl(new_array, value, value_len, 0); - add_assoc_zval(&array, key, new_array); + value_len = 0; } - previous = NULL; - } else { - add_assoc_stringl(&array, key, value, value_len, 0); + /* if we already have got such a header make an array of those */ + if (SUCCESS == zend_hash_find(headers, key, keylen + 1, (void **) &previous)) { + /* already an array? - just add */ + if (Z_TYPE_PP(previous) == IS_ARRAY) { + add_next_index_stringl(*previous, value, value_len, 0); + } else { + /* create the array */ + zval *new_array; + MAKE_STD_ZVAL(new_array); + array_init(new_array); + + add_next_index_stringl(new_array, Z_STRVAL_PP(previous), Z_STRLEN_PP(previous), 1); + add_next_index_stringl(new_array, value, value_len, 0); + add_assoc_zval(&array, key, new_array); + } + + previous = NULL; + } else { + add_assoc_stringl(&array, key, value, value_len, 0); + } + efree(key); } - efree(key); } - colon = NULL; value_len = 0; header += line - header; @@ -337,6 +339,24 @@ PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len } /* }}} */ +PHP_HTTP_API void _http_parse_headers_default_callback(void **cb_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC) +{ + zval array; + Z_ARRVAL(array) = *headers; + + /* response */ + if (!strncmp(http_line, "HTTP/1.", lenof("HTTP/1."))) { + add_assoc_stringl(&array, "Response Status", http_line + lenof("HTTP/1.x "), line_length - lenof("HTTP/1.x \r\n"), 0); + } else + /* request */ + if (!strncmp(http_line + line_length - lenof("HTTP/1.x\r\n"), "HTTP/1.", lenof("HTTP/1."))) { + char *sep = strchr(http_line, ' '); + + add_assoc_stringl(&array, "Request Method", http_line, sep - http_line, 1); + add_assoc_stringl(&array, "Request Uri", sep + 1, strstr(sep, "HTTP/1.") - sep + 1 + 1, 1); + } +} + /* {{{ */ PHP_HTTP_API STATUS _http_parse_cookie(const char *cookie, HashTable *values TSRMLS_DC) { diff --git a/http_message_api.c b/http_message_api.c index e9eccbf..0dec409 100644 --- a/http_message_api.c +++ b/http_message_api.c @@ -27,36 +27,6 @@ #include "phpstr/phpstr.h" -PHP_HTTP_API void _http_message_dtor(http_message *message) -{ - if (message) { - zend_hash_destroy(&message->hdrs); - phpstr_dtor(PHPSTR(message)); - if (message->raw) { - efree(message->raw); - } - 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); - } - } - } -} - -PHP_HTTP_API void _http_message_free(http_message *message) -{ - if (message) { - if (message->nested) { - http_message_free(message->nested); - } - http_message_dtor(message); - efree(message); - } -} - PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_message_type type) { if (!message) { @@ -71,63 +41,70 @@ PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_mes return message; } -PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t length, zend_bool dup TSRMLS_DC) +PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t message_length, zend_bool dup TSRMLS_DC) { - char *message_start = message, *body = NULL; - size_t message_length = length, header_length = 0; + char *body = NULL; + size_t header_length = 0; http_message *msg; - if (length < HTTP_MSG_MIN_SIZE) { - return NULL; - } - if (!message) { + if (message_length < HTTP_MSG_MIN_SIZE) { return NULL; } - if (!(message_start = strstr(message, HTTP_CRLF))) { + if (!message) { return NULL; } - msg = http_message_init(); + msg = http_message_new(); + msg->len = message_length; + msg->raw = dup ? estrndup(message, message_length) : message; - msg->len = length; - msg->raw = dup ? estrndup(message, length) : message; - - // response - if (!strncmp(message, "HTTP/1.", lenof("HTTP/1."))) { - msg->type = HTTP_MSG_RESPONSE; - msg->info.response.http_version = atof(message + lenof("HTTP/")); - msg->info.response.status = atoi(message + lenof("HTTP/1.1 ")); - } else - // request - if (!strncmp(message_start - lenof("HTTP/1.1"), "HTTP/1.", lenof("HTTP/1."))) { - const char *method_sep_uri = strchr(message, ' '); - - msg->type = HTTP_MSG_REQUEST; - msg->info.request.http_version = atof(message_start - lenof("1.1")); - msg->info.request.method = estrndup(message, method_sep_uri - message); - msg->info.request.URI = estrndup(method_sep_uri + 1, message_start - method_sep_uri - 1 - lenof(" HTTP/1.1")); + if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) { + body += lenof(HTTP_CRLF HTTP_CRLF); + header_length = body - message; } else { - http_message_free(msg); - return NULL; + header_length = message_length; } - - message_start += lenof(HTTP_CRLF); - message_length -= message_start - message; - - if (body = strstr(message_start, HTTP_CRLF HTTP_CRLF)) { - body += lenof(HTTP_CRLF HTTP_CRLF); - header_length = body - message_start; + + http_parse_headers_cb(message, header_length, &msg->hdrs, 1, http_message_parse_headers_callback, (void **) &msg); + + if (body) { phpstr_from_string_ex(PHPSTR(msg), body, message_length - header_length); + } + + return msg; +} + +PHP_HTTP_API void _http_message_parse_headers_callback(void **message, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC) +{ + http_message *old = (http_message *) *message; + http_message *new; + + if (old->type || zend_hash_num_elements(&old->hdrs) || PHPSTR_LEN(old)) { + new = http_message_new(); + + new->nested = old; + *message = new; + *headers = &new->hdrs; } else { - header_length = message_length; + new = old; } + + // 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.status = 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, ' '); - if (SUCCESS != http_parse_headers_ex(message_start, header_length, &msg->hdrs, 1)) { - http_message_free(msg); - return NULL; + 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")); } - return msg; } PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length) @@ -138,6 +115,7 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_ zval **header; phpstr_init_ex(&str, msg->len, 1); + /* set sane alloc size */ str.size = 4096; switch (msg->type) @@ -158,6 +136,8 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_ FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) { if (key) { + zval **single_header; + switch (Z_TYPE_PP(header)) { case IS_STRING: @@ -165,13 +145,9 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_ break; case IS_ARRAY: - { - zval **single_header; - FOREACH_VAL(*header, single_header) { phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header)); } - } break; } @@ -181,7 +157,6 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_ phpstr_appends(&str, HTTP_CRLF); phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg)); - phpstr_fix(&str); data = phpstr_data(&str, string, length); if (!string) { @@ -190,3 +165,43 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_ phpstr_dtor(&str); } + +PHP_HTTP_API void _http_message_dtor(http_message *message) +{ + if (message) { + zend_hash_destroy(&message->hdrs); + phpstr_dtor(PHPSTR(message)); + if (message->raw) { + efree(message->raw); + } + 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); + } + } + } +} + +PHP_HTTP_API void _http_message_free(http_message *message) +{ + if (message) { + if (message->nested) { + http_message_free(message->nested); + } + http_message_dtor(message); + efree(message); + } +} + +/* + * 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_headers_api.h b/php_http_headers_api.h index 033160c..7907b62 100644 --- a/php_http_headers_api.h +++ b/php_http_headers_api.h @@ -26,9 +26,16 @@ typedef enum { RANGE_ERR } http_range_status; -#define http_parse_headers(h, l, a) _http_parse_headers_ex((h), (l), Z_ARRVAL_P(a), 1 TSRMLS_CC) -#define http_parse_headers_ex(h, l, ht, p) _http_parse_headers_ex((h), (l), (ht), (p) TSRMLS_CC) -PHP_HTTP_API STATUS _http_parse_headers_ex(const char *header, size_t header_len, HashTable *headers, zend_bool prettify TSRMLS_DC); +typedef void (*http_parse_headers_callback_t)(void **callback_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC); + +#define http_parse_headers_default_callback _http_parse_headers_default_callback +PHP_HTTP_API void _http_parse_headers_default_callback(void **cb_data, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC); + +#define http_parse_headers(h, l, a) _http_parse_headers_ex((h), (l), Z_ARRVAL_P(a), 1, _http_parse_headers_default_callback, NULL TSRMLS_CC) +#define http_parse_headers_ex(h, l, ht, p) _http_parse_headers_ex((h), (l), (ht), (p), _http_parse_headers_default_callback, NULL TSRMLS_CC) +#define http_parse_headers_cb(h, l, ht, p, f, d) _http_parse_headers_ex((h), (l), (ht), (p), (f), (d) TSRMLS_CC) +PHP_HTTP_API STATUS _http_parse_headers_ex(char *header, size_t header_len, HashTable *headers, zend_bool prettify, + http_parse_headers_callback_t func, void *callback_data TSRMLS_DC); #define http_parse_cookie(c, ht) _http_parse_cookie((c), (ht) TSRMLS_CC) PHP_HTTP_API STATUS _http_parse_cookie(const char *cookie, HashTable *values TSRMLS_DC); diff --git a/php_http_message_api.h b/php_http_message_api.h index 4de90bc..507693f 100644 --- a/php_http_message_api.h +++ b/php_http_message_api.h @@ -27,6 +27,7 @@ HttpMessage */ #include "phpstr/phpstr.h" +#include "php_http_headers_api.h" typedef enum { HTTP_MSG_NONE, @@ -64,21 +65,32 @@ struct _http_message { /* required minimum length of an HTTP message "HTTP/1.1 200\r\n" */ #define HTTP_MSG_MIN_SIZE 15 -#define HTTP_MSG_RAW(msg) ( (msg)->dup ? msg->raw.dup : msg->raw.ptr ) - -#define http_message_init() _http_message_init_ex(NULL, 0) +#define http_message_new() _http_message_init_ex(NULL, 0) +#define http_message_init(m) _http_message_init_ex((m), 0) #define http_message_init_ex(m, t) _http_message_init_ex((m), (t)) -#define http_message_free(m) _http_message_free((m)) -#define http_message_dtor(m) _http_message_dtor((m)) - -PHP_HTTP_API void _http_message_dtor(http_message *message); -PHP_HTTP_API void _http_message_free(http_message *message); +PHP_HTTP_API http_message *_http_message_init_ex(http_message *m, http_message_type t); #define http_message_parse(m, l) http_message_parse_ex((m), (l), 1) #define http_message_parse_ex(m, l, d) _http_message_parse_ex((m), (l), (d) TSRMLS_CC) PHP_HTTP_API http_message *_http_message_parse_ex(char *message, size_t length, zend_bool duplicate TSRMLS_DC); +#define http_message_parse_headers_callback _http_message_parse_headers_callback +PHP_HTTP_API void _http_message_parse_headers_callback(void *message, char *http_line, size_t line_length, HashTable **headers TSRMLS_DC); + #define http_message_tostring(m, s, l) _http_message_tostring((m), (s), (l)) PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length); +#define http_message_dtor(m) _http_message_dtor((m)) +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); +/* + * 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 + */ -- 2.30.2