From 34a2055472bf6640874dbb61d640f9901e1d02aa Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Wed, 22 Oct 2014 15:39:36 +0200 Subject: [PATCH] let the parser handle non-blocking streams --- package.xml | 1 + php_http_message.c | 6 ++-- php_http_message_parser.c | 50 +++++++++++++++------------- php_http_message_parser.h | 2 +- tests/messageparser002.phpt | 66 +++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 26 deletions(-) create mode 100644 tests/messageparser002.phpt diff --git a/package.xml b/package.xml index da76837..6d0b0a5 100644 --- a/package.xml +++ b/package.xml @@ -251,6 +251,7 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/ + diff --git a/php_http_message.c b/php_http_message.c index 3141065..c965317 100644 --- a/php_http_message.c +++ b/php_http_message.c @@ -1041,13 +1041,15 @@ static PHP_METHOD(HttpMessage, __construct) if (s && php_http_message_parser_init(&p TSRMLS_CC)) { unsigned flags = (greedy ? PHP_HTTP_MESSAGE_PARSER_GREEDY : 0); + php_http_buffer_t buf; - if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse_stream(&p, s, flags, &msg)) { + php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC); + if (PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE == php_http_message_parser_parse_stream(&p, &buf, s, flags, &msg)) { if (!EG(exception)) { php_http_throw(bad_message, "Could not parse message from stream", NULL); } } - + php_http_buffer_dtor(&buf); php_http_message_parser_dtor(&p); } diff --git a/php_http_message_parser.c b/php_http_message_parser.c index 0d11bf6..e73360e 100644 --- a/php_http_message_parser.c +++ b/php_http_message_parser.c @@ -115,16 +115,16 @@ void php_http_message_parser_free(php_http_message_parser_t **parser) } } -php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_stream *s, unsigned flags, php_http_message_t **message) +php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_message_parser_t *parser, php_http_buffer_t *buf, php_stream *s, unsigned flags, php_http_message_t **message) { - php_http_buffer_t buf; php_http_message_parser_state_t state = PHP_HTTP_MESSAGE_PARSER_STATE_START; TSRMLS_FETCH_FROM_CTX(parser->ts); - php_http_buffer_init_ex(&buf, 0x1000, PHP_HTTP_BUFFER_INIT_PREALLOC); - + if (!buf->data) { + php_http_buffer_resize_ex(buf, 0x1000, 1, 0); + } while (!php_stream_eof(s)) { - size_t len = 0; + size_t justread = 0; #if DBG_PARSER fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags); #endif @@ -133,34 +133,36 @@ php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_me 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.used, buf.free, &len); - php_http_buffer_account(&buf, len); + php_stream_get_line(s, buf->data + buf->used, buf->free, &justread); + php_http_buffer_account(buf, justread); break; case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB: /* read all */ - php_http_buffer_account(&buf, php_stream_read(s, buf.data + buf.used, buf.free)); + justread = php_stream_read(s, buf->data + buf->used, buf->free); + php_http_buffer_account(buf, justread); break; case PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH: /* read body_length */ - php_http_buffer_account(&buf, php_stream_read(s, buf.data + buf.used, MIN(buf.free, parser->body_length))); + justread = php_stream_read(s, buf->data + buf->used, MIN(buf->free, parser->body_length)); + php_http_buffer_account(buf, justread); 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 + buf.used, MIN(len, buf.free)); + if (parser->body_length) { + justread = php_stream_read(s, buf->data + buf->used, MIN(parser->body_length, buf->free)); - php_http_buffer_account(&buf, read); + php_http_buffer_account(buf, justread); - len -= read; + parser->body_length -= justread; } else { - php_http_buffer_resize(&buf, 24); - php_stream_get_line(s, buf.data, buf.free, &len); - php_http_buffer_account(&buf, len); + php_http_buffer_resize(buf, 24); + php_stream_get_line(s, buf->data, buf->free, &justread); + php_http_buffer_account(buf, justread); - len = strtoul(buf.data + buf.used - len, NULL, 16); + parser->body_length = strtoul(buf->data + buf->used - justread, NULL, 16); } break; @@ -172,14 +174,16 @@ php_http_message_parser_state_t php_http_message_parser_parse_stream(php_http_me 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); } - state = php_http_message_parser_parse(parser, &buf, flags, message); + if (justread) { + state = php_http_message_parser_parse(parser, buf, flags, message); + } else { + return state; + } } - php_http_buffer_dtor(&buf); return PHP_HTTP_MESSAGE_PARSER_STATE_DONE; } @@ -588,8 +592,8 @@ static PHP_METHOD(HttpMessageParser, parse) zval_dtor(zmsg); if (parser_obj->parser->message) { - ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0); - } + ZVAL_OBJVAL(zmsg, php_http_message_object_new_ex(php_http_message_class_entry, php_http_message_copy(parser_obj->parser->message, NULL), NULL TSRMLS_CC), 0); + } } ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3) @@ -612,7 +616,7 @@ static PHP_METHOD(HttpMessageParser, stream) zend_restore_error_handling(&zeh TSRMLS_CC); parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); - RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, s, flags, &parser_obj->parser->message)); + RETVAL_LONG(php_http_message_parser_parse_stream(parser_obj->parser, parser_obj->buffer, s, flags, &parser_obj->parser->message)); zval_dtor(zmsg); if (parser_obj->parser->message) { diff --git a/php_http_message_parser.h b/php_http_message_parser.h index 5b04351..c2bee15 100644 --- a/php_http_message_parser.h +++ b/php_http_message_parser.h @@ -54,7 +54,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, 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_http_buffer_t *buffer, php_stream *s, unsigned flags, php_http_message_t **message); typedef struct php_http_message_parser_object { zend_object zo; diff --git a/tests/messageparser002.phpt b/tests/messageparser002.phpt new file mode 100644 index 0000000..2030e93 --- /dev/null +++ b/tests/messageparser002.phpt @@ -0,0 +1,66 @@ +--TEST-- +message parser with nonblocking stream +--SKIPIF-- + +--FILE-- +stream($socket[0], 0, $msg); + fwrite($socket[1], $line); + $parser->stream($socket[0], 0, $msg); +} + +var_dump($msg, (string) $msg->getBody()); + +?> +DONE +--EXPECTF-- +Test +object(http\Message)#%d (9) { + ["type":protected]=> + int(1) + ["body":protected]=> + object(http\Message\Body)#2 (0) { + } + ["requestMethod":protected]=> + string(3) "GET" + ["requestUrl":protected]=> + string(1) "/" + ["responseStatus":protected]=> + string(0) "" + ["responseCode":protected]=> + int(0) + ["httpVersion":protected]=> + string(3) "1.1" + ["headers":protected]=> + array(3) { + ["Host"]=> + string(9) "localhost" + ["Content-Length"]=> + int(3) + ["X-Original-Content-Length"]=> + string(1) "3" + } + ["parentMessage":protected]=> + NULL +} +string(3) "OK +" +DONE \ No newline at end of file -- 2.30.2