X-Git-Url: https://git.m6w6.name/?p=m6w6%2Fext-http;a=blobdiff_plain;f=http_message_api.c;h=dd2cfff2c55035cd949266aa6c37f5f984636940;hp=e9eccbff55fde8f22cdc3cbd5285d878ccb43a64;hb=456dd6f5e057a0fc4ff3dbaf006d71cf5c247f19;hpb=b7f44747c3fc6585c377e276e6f272904bf0fca1 diff --git a/http_message_api.c b/http_message_api.c index e9eccbf..dd2cfff 100644 --- a/http_message_api.c +++ b/http_message_api.c @@ -23,38 +23,28 @@ #include "php_http.h" #include "php_http_std_defs.h" #include "php_http_message_api.h" +#include "php_http_api.h" #include "php_http_headers_api.h" #include "phpstr/phpstr.h" -PHP_HTTP_API void _http_message_dtor(http_message *message) +#define http_message_parse_nested(msg, begin, length) _http_message_parse_nested((msg), (begin), (length) TSRMLS_CC) +static inline http_message *_http_message_parse_nested(http_message *msg, const char *begin, size_t length TSRMLS_DC) { - 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); - } + http_message *new; + + while (isspace(*begin)) { + ++begin; + if (!length--) { + return NULL; } } -} -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); + if (new = http_message_parse(begin, length)) { + new->nested = msg; + return new; } + return NULL; } PHP_HTTP_API http_message *_http_message_init_ex(http_message *message, http_message_type type) @@ -71,63 +61,98 @@ 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(http_message *msg, const char *message, size_t message_length TSRMLS_DC) { - char *message_start = message, *body = NULL; - size_t message_length = length, header_length = 0; - http_message *msg; + char *body = NULL; + size_t header_length = 0; + zend_bool free_msg = msg ? 0 : 1; - if (length < HTTP_MSG_MIN_SIZE) { + if (message_length < HTTP_MSG_MIN_SIZE) { return NULL; } + if (!message) { return NULL; } - if (!(message_start = strstr(message, HTTP_CRLF))) { + msg = http_message_init(msg); + + if (body = strstr(message, HTTP_CRLF HTTP_CRLF)) { + body += lenof(HTTP_CRLF HTTP_CRLF); + header_length = body - message; + } else { + header_length = message_length; + } + + if (SUCCESS != http_parse_headers_cb((char *)message, header_length, &msg->hdrs, 1, http_message_parse_headers_callback, (void **) &msg)) { + if (free_msg) { + http_message_free(msg); + } return NULL; } - msg = http_message_init(); + if (body) { + zval **c; + http_message *nested; - msg->len = length; - msg->raw = dup ? estrndup(message, length) : message; + if (SUCCESS == zend_hash_find(&msg->hdrs, "Content-Length", sizeof("Content-Length"), (void **) &c)) { + long len = atol(Z_STRVAL_PP(c)); + phpstr_from_string_ex(PHPSTR(msg), body, len); + if (nested = http_message_parse_nested(msg, body + len, message + message_length - body - len)) { + return nested; + } + } else if ( + SUCCESS == zend_hash_find(&msg->hdrs, "Transfer-Encoding", sizeof("Transfer-Encoding"), (void **) &c) && + !strcasecmp("chunked", Z_STRVAL_PP(c))) { - // 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, ' '); + char *decoded, *end; + size_t decoded_len; - 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")); - } else { - http_message_free(msg); - return NULL; + if (end = http_chunked_decode(body, message_length - header_length, &decoded, &decoded_len)) { + phpstr_from_string_ex(PHPSTR(msg), decoded, decoded_len); + efree(decoded); + if (nested = http_message_parse_nested(msg, end, message + message_length - end)) { + return nested; + } + } + } else { + phpstr_from_string_ex(PHPSTR(msg), body, message_length - header_length); + } } - message_start += lenof(HTTP_CRLF); - message_length -= message_start - message; + return msg; +} - if (body = strstr(message_start, HTTP_CRLF HTTP_CRLF)) { - body += lenof(HTTP_CRLF HTTP_CRLF); - header_length = body - message_start; - phpstr_from_string_ex(PHPSTR(msg), body, message_length - header_length); +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; } - if (SUCCESS != http_parse_headers_ex(message_start, header_length, &msg->hdrs, 1)) { - http_message_free(msg); - return NULL; + // 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")); } - return msg; } PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_t *length) @@ -137,51 +162,52 @@ PHP_HTTP_API void _http_message_tostring(http_message *msg, char **string, size_ ulong idx; zval **header; - phpstr_init_ex(&str, msg->len, 1); - str.size = 4096; - - switch (msg->type) - { - 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); - break; - - case HTTP_MSG_RESPONSE: - phpstr_appendf(&str, "HTTP/%1.1f %d" HTTP_CRLF, - msg->info.response.http_version, - msg->info.response.status); - break; - } + phpstr_init_ex(&str, 4096, 0); + + do { + + switch (msg->type) + { + 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); + break; + + case HTTP_MSG_RESPONSE: + phpstr_appendf(&str, "HTTP/%1.1f %d" HTTP_CRLF, + msg->info.response.http_version, + msg->info.response.code); + break; + } + + FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) { + if (key) { + zval **single_header; - FOREACH_HASH_KEYVAL(&msg->hdrs, key, idx, header) { - if (key) { - switch (Z_TYPE_PP(header)) - { - case IS_STRING: - phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(header)); - break; - - case IS_ARRAY: + switch (Z_TYPE_PP(header)) { - zval **single_header; - - FOREACH_VAL(*header, single_header) { - phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header)); - } + case IS_STRING: + phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(header)); + break; + + case IS_ARRAY: + FOREACH_VAL(*header, single_header) { + phpstr_appendf(&str, "%s: %s" HTTP_CRLF, key, Z_STRVAL_PP(single_header)); + } + break; } - break; + + key = NULL; } - - key = NULL; } - } - phpstr_appends(&str, HTTP_CRLF); - phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg)); - phpstr_fix(&str); + phpstr_appends(&str, HTTP_CRLF); + phpstr_append(&str, PHPSTR_VAL(msg), PHPSTR_LEN(msg)); + phpstr_appends(&str, HTTP_CRLF); + + } while (msg = msg->nested); data = phpstr_data(&str, string, length); if (!string) { @@ -190,3 +216,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->type == HTTP_MSG_REQUEST) { + 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; + } + } + } +} + +PHP_HTTP_API void _http_message_free(http_message *message) +{ + if (message) { + if (message->nested) { + http_message_free(message->nested); + message->nested = NULL; + } + 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 + */ +