expose http\Message\Parser class
authorMichael Wallner <mike@php.net>
Tue, 19 Aug 2014 19:34:59 +0000 (21:34 +0200)
committerMichael Wallner <mike@php.net>
Tue, 19 Aug 2014 19:36:54 +0000 (21:36 +0200)
package.xml
php_http.c
php_http.h
php_http_message.c
php_http_message_parser.c
php_http_message_parser.h
tests/data/message_r_multipart_put.txt
tests/info_001.phpt
tests/message002.phpt
tests/message006.phpt
tests/messageparser001.phpt [new file with mode: 0644]

index 33ebef1..8c74393 100644 (file)
@@ -39,8 +39,8 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/
  </lead>
  <date>2014-08-19</date>
  <version>
-  <release>2.1.0RC3</release>
-  <api>2.1.0</api>
+  <release>2.2.0dev</release>
+  <api>2.2.0</api>
  </version>
  <stability>
   <release>beta</release>
@@ -48,11 +48,8 @@ v2: http://dev.iworks.at/ext-http/lcov/ext/http/
  </stability>
  <license>BSD, revised</license>
  <notes><![CDATA[
-Changes from RC2:
-* Fixed PHP-5.3 compatibility
-* Fixed possible bus error on shutdown when using events
-+ Added curlcode transfer info
-- Removed port and scheme guessing of http\Url for portability
+- var_dump(http\Message) no longer automatically creates an empty body
++ Added http\Message\Parser class
 ]]></notes>
  <contents>
   <dir name="/">
@@ -251,6 +248,7 @@ Changes from RC2:
      <file role="test" name="messagebody008.phpt"/>
      <file role="test" name="messagebody009.phpt"/>
      <file role="test" name="messagebody010.phpt"/>
+     <file role="test" name="messageparser001.phpt"/>
      <file role="test" name="negotiate001.phpt"/>
      <file role="test" name="params001.phpt"/>
      <file role="test" name="params002.phpt"/>
index 2101f0b..5ee038d 100644 (file)
@@ -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)
index 2aba98b..98332cb 100644 (file)
@@ -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
index 88def26..70dcae8 100644 (file)
@@ -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);
index 20ef3ac..0d11bf6 100644 (file)
@@ -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
index ebc2142..5b04351 100644 (file)
@@ -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
 
 /*
index 52776d4..b3284cf 100644 (file)
@@ -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
 
index 11b83f6..370d70e 100644 (file)
@@ -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]=>
index 403fb26..0809676 100644 (file)
@@ -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]=>
index 52e534d..4d1a693 100644 (file)
@@ -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 (file)
index 0000000..f4ec2d9
--- /dev/null
@@ -0,0 +1,53 @@
+--TEST--
+message parser
+--SKIPIF--
+<?php
+include "skipif.inc";
+?>
+--FILE--
+<?php
+
+echo "Test\n";
+
+use http\Message\Parser;
+
+foreach (glob(__DIR__."/data/message_*.txt") as $file) {
+       $parser = new Parser;
+       $fd = fopen($file, "r") or die("Could not open $file");
+       while (!feof($fd)) {
+               switch ($parser->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