From dedd682f1f67f8af921c87653c58863f355ead43 Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 19 Aug 2014 21:34:59 +0200 Subject: [PATCH] expose http\Message\Parser class --- package.xml | 12 +- php_http.c | 1 + php_http.h | 2 +- php_http_message.c | 27 +++-- php_http_message_parser.c | 148 +++++++++++++++++++++++++ php_http_message_parser.h | 15 +++ tests/data/message_r_multipart_put.txt | 2 +- tests/info_001.phpt | 3 +- tests/message002.phpt | 3 +- tests/message006.phpt | 3 +- tests/messageparser001.phpt | 53 +++++++++ 11 files changed, 240 insertions(+), 29 deletions(-) create mode 100644 tests/messageparser001.phpt diff --git a/package.xml b/package.xml index 33ebef1..8c74393 100644 --- a/package.xml +++ b/package.xml @@ -39,8 +39,8 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/ 2014-08-19 - 2.1.0RC3 - 2.1.0 + 2.2.0dev + 2.2.0 beta @@ -48,11 +48,8 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/ BSD, revised @@ -251,6 +248,7 @@ Changes from RC2: + diff --git a/php_http.c b/php_http.c index 2101f0b..5ee038d 100644 --- a/php_http.c +++ b/php_http.c @@ -142,6 +142,7 @@ PHP_MINIT_FUNCTION(http) || SUCCESS != PHP_MINIT_CALL(http_filter) || SUCCESS != PHP_MINIT_CALL(http_header) || SUCCESS != PHP_MINIT_CALL(http_message) + || SUCCESS != PHP_MINIT_CALL(http_message_parser) || SUCCESS != PHP_MINIT_CALL(http_message_body) || SUCCESS != PHP_MINIT_CALL(http_querystring) || SUCCESS != PHP_MINIT_CALL(http_client) diff --git a/php_http.h b/php_http.h index 2aba98b..98332cb 100644 --- a/php_http.h +++ b/php_http.h @@ -13,7 +13,7 @@ #ifndef PHP_EXT_HTTP_H #define PHP_EXT_HTTP_H -#define PHP_PECL_HTTP_VERSION "2.1.0RC3" +#define PHP_PECL_HTTP_VERSION "2.2.0dev" extern zend_module_entry http_module_entry; #define phpext_http_ptr &http_module_entry diff --git a/php_http_message.c b/php_http_message.c index 88def26..70dcae8 100644 --- a/php_http_message.c +++ b/php_http_message.c @@ -937,13 +937,11 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) { zval *headers; php_http_message_object_t *obj = zend_object_store_get_object(object TSRMLS_CC); - php_http_message_t *msg = obj->message; HashTable *props = zend_get_std_object_handlers()->get_properties(object TSRMLS_CC); zval array, *parent, *body; char *version; PHP_HTTP_MESSAGE_OBJECT_INIT(obj); - INIT_PZVAL_ARRAY(&array, props); #define ASSOC_PROP(ptype, n, val) \ @@ -964,20 +962,20 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) } \ } while(0) - ASSOC_PROP(long, "type", msg->type); - ASSOC_STRINGL_EX("httpVersion", version, spprintf(&version, 0, "%u.%u", msg->http.version.major, msg->http.version.minor), 0); + ASSOC_PROP(long, "type", obj->message->type); + ASSOC_STRINGL_EX("httpVersion", version, spprintf(&version, 0, "%u.%u", obj->message->http.version.major, obj->message->http.version.minor), 0); - switch (msg->type) { + switch (obj->message->type) { case PHP_HTTP_REQUEST: ASSOC_PROP(long, "responseCode", 0); ASSOC_STRINGL("responseStatus", "", 0); - ASSOC_STRING("requestMethod", STR_PTR(msg->http.info.request.method)); - ASSOC_STRING("requestUrl", STR_PTR(msg->http.info.request.url)); + ASSOC_STRING("requestMethod", STR_PTR(obj->message->http.info.request.method)); + ASSOC_STRING("requestUrl", STR_PTR(obj->message->http.info.request.url)); break; case PHP_HTTP_RESPONSE: - ASSOC_PROP(long, "responseCode", msg->http.info.response.code); - ASSOC_STRING("responseStatus", STR_PTR(msg->http.info.response.status)); + ASSOC_PROP(long, "responseCode", obj->message->http.info.response.code); + ASSOC_STRING("responseStatus", STR_PTR(obj->message->http.info.response.status)); ASSOC_STRINGL("requestMethod", "", 0); ASSOC_STRINGL("requestUrl", "", 0); break; @@ -993,18 +991,19 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) 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 *)); + zend_hash_copy(Z_ARRVAL_P(headers), &obj->message->hdrs, (copy_ctor_func_t) zval_add_ref, NULL, sizeof(zval *)); ASSOC_PROP(zval, "headers", headers); MAKE_STD_ZVAL(body); - if (!obj->body) { - php_http_new(NULL, php_http_message_body_class_entry, (php_http_new_t) php_http_message_body_object_new_ex, NULL, (void *) php_http_message_body_init(&obj->message->body, NULL TSRMLS_CC), (void *) &obj->body TSRMLS_CC); + if (obj->body) { + ZVAL_OBJVAL(body, obj->body->zv, 1); + } else { + ZVAL_NULL(body); } - ZVAL_OBJVAL(body, obj->body->zv, 1); ASSOC_PROP(zval, "body", body); MAKE_STD_ZVAL(parent); - if (msg->parent) { + if (obj->message->parent) { ZVAL_OBJVAL(parent, obj->parent->zv, 1); } else { ZVAL_NULL(parent); diff --git a/php_http_message_parser.c b/php_http_message_parser.c index 20ef3ac..0d11bf6 100644 --- a/php_http_message_parser.c +++ b/php_http_message_parser.c @@ -97,6 +97,7 @@ void php_http_message_parser_dtor(php_http_message_parser_t *parser) { php_http_header_parser_dtor(&parser->header); zend_ptr_stack_destroy(&parser->stack); + php_http_message_free(&parser->message); if (parser->dechunk) { php_http_encoding_stream_free(&parser->dechunk); } @@ -508,6 +509,153 @@ php_http_message_parser_state_t php_http_message_parser_parse(php_http_message_p return php_http_message_parser_state_is(parser); } +zend_class_entry *php_http_message_parser_class_entry; +static zend_object_handlers php_http_message_parser_object_handlers; + +zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC) +{ + return php_http_message_parser_object_new_ex(ce, NULL, NULL TSRMLS_CC); +} + +zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC) +{ + php_http_message_parser_object_t *o; + + o = ecalloc(1, sizeof(php_http_message_parser_object_t)); + zend_object_std_init((zend_object *) o, ce TSRMLS_CC); + object_properties_init((zend_object *) o, ce); + + if (ptr) { + *ptr = o; + } + + if (parser) { + o->parser = parser; + } else { + o->parser = php_http_message_parser_init(NULL TSRMLS_CC); + } + o->buffer = php_http_buffer_new(); + + o->zv.handle = zend_objects_store_put((zend_object *) o, NULL, php_http_message_parser_object_free, NULL TSRMLS_CC); + o->zv.handlers = &php_http_message_parser_object_handlers; + + return o->zv; +} + +void php_http_message_parser_object_free(void *object TSRMLS_DC) +{ + php_http_message_parser_object_t *o = (php_http_message_parser_object_t *) object; + + if (o->parser) { + php_http_message_parser_free(&o->parser); + } + if (o->buffer) { + php_http_buffer_free(&o->buffer); + } + zend_object_std_dtor((zend_object *) o TSRMLS_CC); + efree(o); +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_getState, 0, 0, 0) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpMessageParser, getState) +{ + php_http_message_parser_object_t *parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); + + zend_parse_parameters_none(); + /* always return the real state */ + RETVAL_LONG(php_http_message_parser_state_is(parser_obj->parser)); +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_parse, 0, 0, 3) + ZEND_ARG_INFO(0, data) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(1, message) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpMessageParser, parse) +{ + php_http_message_parser_object_t *parser_obj; + zval *zmsg; + char *data_str; + int data_len; + long flags; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slz", &data_str, &data_len, &flags, &zmsg), invalid_arg, return); + + parser_obj = zend_object_store_get_object(getThis() TSRMLS_CC); + php_http_buffer_append(parser_obj->buffer, data_str, data_len); + RETVAL_LONG(php_http_message_parser_parse(parser_obj->parser, parser_obj->buffer, flags, &parser_obj->parser->message)); + + 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); + } +} + +ZEND_BEGIN_ARG_INFO_EX(ai_HttpMessageParser_stream, 0, 0, 3) + ZEND_ARG_INFO(0, stream) + ZEND_ARG_INFO(0, flags) + ZEND_ARG_INFO(1, message) +ZEND_END_ARG_INFO(); +static PHP_METHOD(HttpMessageParser, stream) +{ + php_http_message_parser_object_t *parser_obj; + zend_error_handling zeh; + zval *zmsg, *zstream; + php_stream *s; + long flags; + + php_http_expect(SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &zstream, &flags, &zmsg), invalid_arg, return); + + zend_replace_error_handling(EH_THROW, php_http_exception_unexpected_val_class_entry, &zeh TSRMLS_CC); + php_stream_from_zval(s, &zstream); + 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)); + + 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); + } +} + +static zend_function_entry php_http_message_parser_methods[] = { + PHP_ME(HttpMessageParser, getState, ai_HttpMessageParser_getState, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageParser, parse, ai_HttpMessageParser_parse, ZEND_ACC_PUBLIC) + PHP_ME(HttpMessageParser, stream, ai_HttpMessageParser_stream, ZEND_ACC_PUBLIC) + {NULL, NULL, NULL} +}; + +PHP_MINIT_FUNCTION(http_message_parser) +{ + zend_class_entry ce; + + INIT_NS_CLASS_ENTRY(ce, "http\\Message", "Parser", php_http_message_parser_methods); + php_http_message_parser_class_entry = zend_register_internal_class(&ce TSRMLS_CC); + memcpy(&php_http_message_parser_object_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers)); + php_http_message_parser_class_entry->create_object = php_http_message_parser_object_new; + php_http_message_parser_object_handlers.clone_obj = NULL; + + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("CLEANUP"), PHP_HTTP_MESSAGE_PARSER_CLEANUP TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("DUMB_BODIES"), PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("EMPTY_REDIRECTS"), PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("GREEDY"), PHP_HTTP_MESSAGE_PARSER_GREEDY TSRMLS_CC); + + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_FAILURE"), PHP_HTTP_MESSAGE_PARSER_STATE_FAILURE TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_START"), PHP_HTTP_MESSAGE_PARSER_STATE_START TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_HEADER_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DUMB"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DUMB TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_LENGTH"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_LENGTH TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_CHUNKED"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_CHUNKED TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_BODY_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_BODY_DONE TSRMLS_CC); + zend_declare_class_constant_long(php_http_message_parser_class_entry, ZEND_STRL("STATE_DONE"), PHP_HTTP_MESSAGE_PARSER_STATE_DONE TSRMLS_CC); + + return SUCCESS; +} + /* * Local variables: * tab-width: 4 diff --git a/php_http_message_parser.h b/php_http_message_parser.h index ebc2142..5b04351 100644 --- a/php_http_message_parser.h +++ b/php_http_message_parser.h @@ -56,6 +56,21 @@ 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(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); +typedef struct php_http_message_parser_object { + zend_object zo; + zend_object_value zv; + php_http_buffer_t *buffer; + php_http_message_parser_t *parser; +} php_http_message_parser_object_t; + +PHP_HTTP_API zend_class_entry *php_http_message_parser_class_entry; + +PHP_MINIT_FUNCTION(http_message_parser); + +zend_object_value php_http_message_parser_object_new(zend_class_entry *ce TSRMLS_DC); +zend_object_value php_http_message_parser_object_new_ex(zend_class_entry *ce, php_http_message_parser_t *parser, php_http_message_parser_object_t **ptr TSRMLS_DC); +void php_http_message_parser_object_free(void *object TSRMLS_DC); + #endif /* diff --git a/tests/data/message_r_multipart_put.txt b/tests/data/message_r_multipart_put.txt index 52776d4..b3284cf 100644 --- a/tests/data/message_r_multipart_put.txt +++ b/tests/data/message_r_multipart_put.txt @@ -2,7 +2,7 @@ PUT /docs/ HTTP/1.1 User-Agent: curl/7.24.0 (x86_64-unknown-linux-gnu) libcurl/7.24.0 OpenSSL/1.0.0g zlib/1.2.6 libssh2/1.3.0 Host: drop Accept: */* -Content-Length: 2284 +Content-Length: 2273 Expect: 100-continue Content-Type: multipart/form-data; boundary=----------------------------6e182425881c diff --git a/tests/info_001.phpt b/tests/info_001.phpt index 11b83f6..370d70e 100644 --- a/tests/info_001.phpt +++ b/tests/info_001.phpt @@ -40,8 +40,7 @@ object(http\Message)#%d (9) { ["type":protected]=> int(1) ["body":protected]=> - object(http\Message\Body)#%d (0) { - } + NULL ["requestMethod":protected]=> string(3) "GET" ["requestUrl":protected]=> diff --git a/tests/message002.phpt b/tests/message002.phpt index 403fb26..0809676 100644 --- a/tests/message002.phpt +++ b/tests/message002.phpt @@ -37,8 +37,7 @@ object(%s)#%d (12) { ["type":protected]=> int(1) ["body":protected]=> - object(http\Message\Body)#%d (0) { - } + NULL ["requestMethod":protected]=> string(4) "POST" ["requestUrl":protected]=> diff --git a/tests/message006.phpt b/tests/message006.phpt index 52e534d..4d1a693 100644 --- a/tests/message006.phpt +++ b/tests/message006.phpt @@ -29,8 +29,7 @@ object(c)#%d (9) { ["type":protected]=> int(0) ["body":protected]=> - object(http\Message\Body)#%d (0) { - } + NULL ["requestMethod":protected]=> string(0) "" ["requestUrl":protected]=> diff --git a/tests/messageparser001.phpt b/tests/messageparser001.phpt new file mode 100644 index 0000000..f4ec2d9 --- /dev/null +++ b/tests/messageparser001.phpt @@ -0,0 +1,53 @@ +--TEST-- +message parser +--SKIPIF-- + +--FILE-- +parse(fgets($fd), 0, $message)) { + case Parser::STATE_DONE: + $string = (string) $message; + break 2; + case Parser::STATE_FAILURE: + throw new Exception(($e = error_get_last()) ? $e["message"] : "Could not parse $file"); + } + } + + $parser = new Parser; + rewind($fd); + unset($message); + + switch ($parser->stream($fd, 0, $message)) { + case Parser::STATE_DONE: + case Parser::STATE_START: + break; + default: + printf("Expected parser state 0 or 8, got %d", $parser->getState()); + } + if ($string !== (string) $message) { + $a = explode("\n", $string); + $b = explode("\n", (string) $message); + while ((null !== ($aa = array_shift($a))) || (null !== ($bb = array_shift($b)))) { + if ($aa !== $bb) { + isset($aa) and printf("-- %s\n", $aa); + isset($bb) and printf("++ %s\n", $bb); + } + } + } +} +?> +DONE +--EXPECT-- +Test +DONE -- 2.30.2