From 5389d7b1cc701efea82a2c6cbb1cc1b234f705e3 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Fri, 24 Feb 2012 14:29:59 +0000 Subject: [PATCH] experimental stream message parser --- package.xml | 9 +++-- php_http_message.c | 41 ++++++++++++++-------- php_http_message_parser.c | 73 ++++++++++++++++++++++++++++++++++++++- php_http_message_parser.h | 1 + 4 files changed, 106 insertions(+), 18 deletions(-) diff --git a/package.xml b/package.xml index 222c516..2e2dec0 100644 --- a/package.xml +++ b/package.xml @@ -27,9 +27,9 @@ Extended HTTP support. Again. Keep in mind that it's got the major version 2, be mike@php.net yes - 2012-01-23 + 2012-02-17 - 2.0.0dev + 2.0.0dev5 2.0.0 @@ -38,6 +38,11 @@ Extended HTTP support. Again. Keep in mind that it's got the major version 2, be BSD, revised diff --git a/php_http_message.c b/php_http_message.c index 7c91ac5..ab2c862 100644 --- a/php_http_message.c +++ b/php_http_message.c @@ -17,7 +17,7 @@ PHP_HTTP_API zend_bool php_http_message_info_callback(php_http_message_t **messa php_http_message_t *old = *message; /* advance message */ - if (old->type || zend_hash_num_elements(&old->hdrs) || PHP_HTTP_BUFFER_LEN(old)) { + if (!old || old->type || zend_hash_num_elements(&old->hdrs) || PHP_HTTP_BUFFER_LEN(old)) { (*message) = php_http_message_init(NULL, 0 TSRMLS_CC); (*message)->parent = old; (*headers) = &((*message)->hdrs); @@ -1282,25 +1282,36 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) PHP_METHOD(HttpMessage, __construct) { - int length = 0; - char *message = NULL; + zval *zmessage = NULL; + php_http_message_t *msg = NULL; php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); with_error_handling(EH_THROW, php_http_exception_class_entry) { - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|s", &message, &length) && message && length) { - if (message && length) { - php_http_message_t *msg = php_http_message_parse(NULL, message, length TSRMLS_CC); + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!", &zmessage) && zmessage) { + if (Z_TYPE_P(zmessage) == IS_RESOURCE) { + php_stream *s; + php_http_message_parser_t p; + + php_stream_from_zval(s, &zmessage); + if (s && php_http_message_parser_init(&p TSRMLS_CC)) { + php_http_message_parser_parse_stream(&p, s, &msg); + php_http_message_parser_dtor(&p); + } + } else { + zmessage = php_http_ztyp(IS_STRING, zmessage); + msg = php_http_message_parse(NULL, Z_STRVAL_P(zmessage), Z_STRLEN_P(zmessage) TSRMLS_CC); + zval_ptr_dtor(&zmessage); + } - if (!msg) { - php_http_error(HE_THROW, PHP_HTTP_E_MESSAGE, "could not parse message: %.*s", 25, message); - } else { - php_http_message_dtor(obj->message); - obj->message = msg;{ - if (obj->message->parent) { - obj->parent = php_http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL TSRMLS_CC); - } + if (msg) { + php_http_message_dtor(obj->message); + obj->message = msg; + if (obj->message->parent) { + obj->parent = php_http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL TSRMLS_CC); } - } end_error_handling(); + } else { + php_http_error(HE_THROW, PHP_HTTP_E_MESSAGE, "could not parse message: %.*s", 25, Z_STRVAL_P(zmessage)); + } } if (!obj->message) { obj->message = php_http_message_init(NULL, 0 TSRMLS_CC); diff --git a/php_http_message_parser.c b/php_http_message_parser.c index cf4d4fa..4162e37 100644 --- a/php_http_message_parser.c +++ b/php_http_message_parser.c @@ -102,6 +102,77 @@ PHP_HTTP_API void php_http_message_parser_free(php_http_message_parser_t **parse } } +PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_stream *s, php_http_message_t **message) +{ + php_http_buffer_t buf; + TSRMLS_FETCH_FROM_CTX(parser->ts); + + php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC); + + while (!php_stream_eof(s)) { + size_t len = 0; + + switch (php_http_message_parser_state_is(parser)) { + case PHP_HTTP_MESSAGE_PARSER_STATE_START: + case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER: + case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE: + /* read line */ + php_stream_get_line(s, buf.data, buf.free, &len); + buf.used += len; + buf.free -= len; + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB: + /* read all */ + len = php_stream_read(s, buf.data, buf.free); + buf.used += len; + buf.free -= len; + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH: + /* read body_length */ + len = php_stream_read(s, buf.data, MIN(buf.free, parser->body_length)); + buf.used += len; + buf.free -= len; + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED: + /* duh, this is very naive */ + if (len) { + size_t read = php_stream_read(s, buf.data, MIN(len, buf.free)); + + buf.used += read; + buf.free -= read; + + len -= read; + } else { + php_stream_get_line(s, buf.data, buf.free, &len); + buf.used += len; + buf.free -= len; + + len = strtoul(buf.data - len, NULL, 16); + } + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY: + case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE: + /* should not occur */ + abort(); + break; + + case PHP_HTTP_MESSAGE_PARSER_STATE_DONE: + case PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE: + php_http_buffer_dtor(&buf); + return php_http_message_parser_state_is(parser); + } + + php_http_message_parser_parse(parser, &buf, 0, message); + } + + php_http_buffer_dtor(&buf); + return PHP_HTTP_MESSAGE_PARSER_STATE_DONE; +} + PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message) { @@ -113,7 +184,7 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h while (buffer->used || !php_http_message_parser_states[php_http_message_parser_state_is(parser)].need_data) { #if 0 const char *state[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "DONE"}; - fprintf(stderr, "#MP: %s (%d)\n", php_http_message_parser_state_is(parser) < 0 ? "FAILURE" : state[php_http_message_parser_state_is(parser)], (*message)->type); + fprintf(stderr, "#MP: %s (%d)\n", php_http_message_parser_state_is(parser) < 0 ? "FAILURE" : state[php_http_message_parser_state_is(parser)], message && *message ? (*message)->type : -1); _dpf(0, buffer->data, buffer->used); #endif diff --git a/php_http_message_parser.h b/php_http_message_parser.h index 31c3004..bb7bfdd 100644 --- a/php_http_message_parser.h +++ b/php_http_message_parser.h @@ -53,6 +53,7 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_state_pop(p PHP_HTTP_API void php_http_message_parser_dtor(php_http_message_parser_t *parser); PHP_HTTP_API void php_http_message_parser_free(php_http_message_parser_t **parser); PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_parser_t *parser, php_http_buffer_t *buffer, unsigned flags, php_http_message_t **message); +PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_stream *s, php_http_message_t **message); #endif -- 2.30.2