From 628bdcb6edd65cb683b43af16cd0ce619b56d58a Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Fri, 9 Nov 2012 13:30:39 +0000 Subject: [PATCH] allow interrupted message parsing from stream --- gen_curlinfo.php | 4 ++-- php_http_client.c | 1 - php_http_curl_client.c | 20 +++++++++++++++++++- php_http_message.c | 31 ++++++++++++++++++++---------- php_http_message.h | 2 +- php_http_message_parser.c | 40 ++++++++++++++++++++++++++++++++------- php_http_message_parser.h | 3 ++- 7 files changed, 78 insertions(+), 23 deletions(-) diff --git a/gen_curlinfo.php b/gen_curlinfo.php index 726fd48..fea14bb 100644 --- a/gen_curlinfo.php +++ b/gen_curlinfo.php @@ -91,8 +91,8 @@ foreach ($infos as $info) { if (isset($ifdefs[$short])) printf("#endif\n"); } -file_put_contents("php_http_curl.c", +file_put_contents("php_http_curl_client.c", preg_replace('/(\/\* BEGIN::CURLINFO \*\/\n).*(\n\s*\/\* END::CURLINFO \*\/)/s', '$1'. ob_get_contents() .'$2', - file_get_contents("php_http_curl.c"))); + file_get_contents("php_http_curl_client.c"))); ?> diff --git a/php_http_client.c b/php_http_client.c index 79a0ef8..0de62c9 100644 --- a/php_http_client.c +++ b/php_http_client.c @@ -916,7 +916,6 @@ PHP_METHOD(HttpClient, request) with_error_handling(EH_THROW, php_http_exception_get_class_entry()) { if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|a!z!a!/", &meth_str, &meth_len, &url_str, &url_len, &zheader, &zbody, &zoptions)) { - php_http_client_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); php_http_message_object_t *msg_obj; zend_object_value ov; zval *req, *res = NULL; diff --git a/php_http_curl_client.c b/php_http_curl_client.c index 32dbb7e..bf04651 100644 --- a/php_http_curl_client.c +++ b/php_http_curl_client.c @@ -574,6 +574,19 @@ static STATUS set_options(php_http_client_t *h, HashTable *options) if ((zoption = get_option(&curl->options.cache, options, ZEND_STRS("connecttimeout"), IS_DOUBLE))) { curl_easy_setopt(ch, CURLOPT_CONNECTTIMEOUT_MS, (long)(Z_DVAL_P(zoption)*1000)); } + +#if PHP_HTTP_CURL_VERSION(7,25,0) + /* tcp */ + if ((zoption = get_option(&curl->options.cache, options, ZEND_STRS("tcp_keepalive"), IS_BOOL))) { + curl_easy_setopt(ch, CURLOPT_TCP_KEEPALIVE, (long)Z_BVAL_P(zoption)); + } + if ((zoption = get_option(&curl->options.cache, options, ZEND_STRS("tcp_keepidle"), IS_LONG))) { + curl_easy_setopt(ch, CURLOPT_TCP_KEEPIDLE, Z_LVAL_P(zoption)); + } + if ((zoption = get_option(&curl->options.cache, options, ZEND_STRS("tcp_keepintvl"), IS_LONG))) { + curl_easy_setopt(ch, CURLOPT_TCP_KEEPINTVL, Z_LVAL_P(zoption)); + } +#endif /* ssl */ if ((zoption = get_option(&curl->options.cache, options, ZEND_STRS("ssl"), IS_ARRAY))) { @@ -971,7 +984,12 @@ static STATUS php_http_curl_client_reset(php_http_client_t *h) curl_easy_setopt(ch, CURLOPT_TIMECONDITION, 0L); curl_easy_setopt(ch, CURLOPT_TIMEVALUE, 0L); curl_easy_setopt(ch, CURLOPT_TIMEOUT, 0L); - curl_easy_setopt(ch, CURLOPT_CONNECTTIMEOUT, 3); + curl_easy_setopt(ch, CURLOPT_CONNECTTIMEOUT, 3L); +#if PHP_HTTP_CURL_VERSION(7,25,0) + curl_easy_setopt(ch, CURLOPT_TCP_KEEPALIVE, 0L); + curl_easy_setopt(ch, CURLOPT_TCP_KEEPIDLE, 60L); + curl_easy_setopt(ch, CURLOPT_TCP_KEEPINTVL, 60L); +#endif curl_easy_setopt(ch, CURLOPT_SSLCERT, NULL); curl_easy_setopt(ch, CURLOPT_SSLCERTTYPE, NULL); curl_easy_setopt(ch, CURLOPT_SSLCERTPASSWD, NULL); diff --git a/php_http_message.c b/php_http_message.c index e4b4660..912de70 100644 --- a/php_http_message.c +++ b/php_http_message.c @@ -118,10 +118,11 @@ PHP_HTTP_API php_http_message_t *php_http_message_init_env(php_http_message_t *m return message; } -PHP_HTTP_API php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len TSRMLS_DC) +PHP_HTTP_API php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy TSRMLS_DC) { php_http_message_parser_t p; php_http_buffer_t buf; + unsigned flags = PHP_HTTP_MESSAGE_PARSER_CLEANUP; int free_msg; php_http_buffer_from_string_ex(&buf, str, len); @@ -131,7 +132,10 @@ PHP_HTTP_API php_http_message_t *php_http_message_parse(php_http_message_t *msg, msg = php_http_message_init(NULL, 0 TSRMLS_CC); } - if (FAILURE == php_http_message_parser_parse(&p, &buf, PHP_HTTP_MESSAGE_PARSER_CLEANUP, &msg)) { + if (greedy) { + flags |= PHP_HTTP_MESSAGE_PARSER_GREEDY; + } + if (FAILURE == php_http_message_parser_parse(&p, &buf, flags, &msg)) { if (free_msg) { php_http_message_free(&msg); } @@ -566,6 +570,7 @@ PHP_HTTP_API void php_http_message_free(php_http_message_t **message) PHP_HTTP_BEGIN_ARGS(__construct, 0) PHP_HTTP_ARG_VAL(message, 0) + PHP_HTTP_ARG_VAL(greedy, 0) PHP_HTTP_END_ARGS; PHP_HTTP_EMPTY_ARGS(getBody); @@ -934,7 +939,7 @@ void php_http_message_object_reverse(zval *this_ptr, zval *return_value TSRMLS_D if (i > 1) { zend_object_value *ovalues = NULL; php_http_message_object_t **objects = NULL; - int last = i - 1; + int last; objects = ecalloc(i, sizeof(**objects)); ovalues = ecalloc(i, sizeof(*ovalues)); @@ -1281,24 +1286,33 @@ static HashTable *php_http_message_object_get_props(zval *object TSRMLS_DC) PHP_METHOD(HttpMessage, __construct) { + zend_bool greedy = 1; 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_get_class_entry()) { - if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!", &zmessage) && zmessage) { + if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|z!b", &zmessage, &greedy) && 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); + unsigned flags = (greedy ? PHP_HTTP_MESSAGE_PARSER_GREEDY : 0); + + php_http_message_parser_parse_stream(&p, s, flags, &msg); php_http_message_parser_dtor(&p); } + + if (!msg) { + php_http_error(HE_THROW, PHP_HTTP_E_MESSAGE, "could not parse message from stream"); + } } else { zmessage = php_http_ztyp(IS_STRING, zmessage); - msg = php_http_message_parse(NULL, Z_STRVAL_P(zmessage), Z_STRLEN_P(zmessage) TSRMLS_CC); + if (!(msg = php_http_message_parse(NULL, Z_STRVAL_P(zmessage), Z_STRLEN_P(zmessage), greedy TSRMLS_CC))) { + php_http_error(HE_THROW, PHP_HTTP_E_MESSAGE, "could not parse message: %.*s", MIN(25, Z_STRLEN_P(zmessage)), Z_STRVAL_P(zmessage)); + } zval_ptr_dtor(&zmessage); } @@ -1308,8 +1322,6 @@ PHP_METHOD(HttpMessage, __construct) if (obj->message->parent) { obj->parent = php_http_message_object_new_ex(Z_OBJCE_P(getThis()), obj->message->parent, NULL TSRMLS_CC); } - } else { - php_http_error(HE_THROW, PHP_HTTP_E_MESSAGE, "could not parse message: %.*s", 25, Z_STRVAL_P(zmessage)); } } if (!obj->message) { @@ -1342,7 +1354,6 @@ PHP_METHOD(HttpMessage, setBody) if (SUCCESS == zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "O", &zbody, php_http_message_body_get_class_entry())) { php_http_message_object_t *obj = zend_object_store_get_object(getThis() TSRMLS_CC); - php_http_message_body_object_t *body_obj = zend_object_store_get_object(zbody TSRMLS_CC); if (!obj->message) { obj->message = php_http_message_init(NULL, 0 TSRMLS_CC); @@ -1876,7 +1887,7 @@ PHP_METHOD(HttpMessage, unserialize) php_http_message_dtor(obj->message); efree(obj->message); } - if ((msg = php_http_message_parse(NULL, serialized, (size_t) length TSRMLS_CC))) { + if ((msg = php_http_message_parse(NULL, serialized, (size_t) length, 1 TSRMLS_CC))) { obj->message = msg; } else { obj->message = php_http_message_init(NULL, 0 TSRMLS_CC); diff --git a/php_http_message.h b/php_http_message.h index 766c0b4..f4bc22c 100644 --- a/php_http_message.h +++ b/php_http_message.h @@ -69,7 +69,7 @@ PHP_HTTP_API php_http_message_t *php_http_message_zip(php_http_message_t *one, p for (c = 0; __tmp_msg; __tmp_msg = __tmp_msg->parent, ++(c)); \ } -PHP_HTTP_API php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len TSRMLS_DC); +PHP_HTTP_API php_http_message_t *php_http_message_parse(php_http_message_t *msg, const char *str, size_t len, zend_bool greedy TSRMLS_DC); /* PHP */ diff --git a/php_http_message_parser.c b/php_http_message_parser.c index fae3df3..634a764 100644 --- a/php_http_message_parser.c +++ b/php_http_message_parser.c @@ -12,6 +12,10 @@ #include "php_http_api.h" +#ifndef DBG_PARSER +# define DBG_PARSER 0 +#endif + typedef struct php_http_message_parser_state_spec { php_http_message_parser_state_t state; unsigned need_data:1; @@ -29,6 +33,17 @@ static const php_http_message_parser_state_spec_t php_http_message_parser_states {PHP_HTTP_MESSAGE_PARSER_STATE_DONE, 0} }; +#if DBG_PARSER +const char *php_http_message_parser_state_name(php_http_message_parser_state_t state) { + const char *states[] = {"START", "HEADER", "HEADER_DONE", "BODY", "BODY_DUMB", "BODY_LENGTH", "BODY_CHUNK", "BODY_DONE", "DONE"}; + + if (state < 0 || state > (sizeof(states)/sizeof(char*))-1) { + return "FAILURE"; + } + return states[state]; +} +#endif + PHP_HTTP_API php_http_message_parser_t *php_http_message_parser_init(php_http_message_parser_t *parser TSRMLS_DC) { if (!parser) { @@ -102,17 +117,20 @@ 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_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_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); while (!php_stream_eof(s)) { size_t len = 0; - - switch (php_http_message_parser_state_is(parser)) { +#if DBG_PARSER + fprintf(stderr, "#SP: %s (f:%u)\n", php_http_message_parser_state_name(state), flags); +#endif + switch (state) { case PHP_HTTP_MESSAGE_PARSER_STATE_START: case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER: case PHP_HTTP_MESSAGE_PARSER_STATE_HEADER_DONE: @@ -160,7 +178,7 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse_strea return php_http_message_parser_state_is(parser); } - php_http_message_parser_parse(parser, &buf, 0, message); + state = php_http_message_parser_parse(parser, &buf, flags, message); } php_http_buffer_dtor(&buf); @@ -176,9 +194,13 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h TSRMLS_FETCH_FROM_CTX(parser->ts); 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 && *message ? (*message)->type : -1); +#if DBG_PARSER + fprintf(stderr, "#MP: %s (f: %u, t:%d, l:%zu)\n", + php_http_message_parser_state_name(php_http_message_parser_state_is(parser)), + flags, + message && *message ? (*message)->type : -1, + buffer->used + ); _dpf(0, buffer->data, buffer->used); #endif @@ -477,6 +499,10 @@ PHP_HTTP_API php_http_message_parser_state_t php_http_message_parser_parse(php_h } php_http_buffer_cut(buffer, 0, ptr - buffer->data); + + if (!(flags & PHP_HTTP_MESSAGE_PARSER_GREEDY)) { + return PHP_HTTP_MESSAGE_PARSER_STATE_DONE; + } break; } } diff --git a/php_http_message_parser.h b/php_http_message_parser.h index bb7bfdd..09e038b 100644 --- a/php_http_message_parser.h +++ b/php_http_message_parser.h @@ -33,6 +33,7 @@ typedef enum php_http_message_parser_state { #define PHP_HTTP_MESSAGE_PARSER_CLEANUP 0x1 #define PHP_HTTP_MESSAGE_PARSER_DUMB_BODIES 0x2 #define PHP_HTTP_MESSAGE_PARSER_EMPTY_REDIRECTS 0x4 +#define PHP_HTTP_MESSAGE_PARSER_GREEDY 0x8 typedef struct php_http_message_parser { php_http_header_parser_t header; @@ -53,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, 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); #endif -- 2.30.2