improve http parser:

* support multiple line header field and value.
* support http upgrade mechanism (you have to parse websocket protocol manually)
This commit is contained in:
Shuhei Tanuma 2014-02-25 17:17:34 +09:00
parent fe317d62c0
commit 951622a3aa
3 changed files with 84 additions and 11 deletions

View File

@ -2316,6 +2316,12 @@ static int on_message_complete(http_parser *p)
php_http_parser_context *result = p->data; php_http_parser_context *result = p->data;
result->finished = 1; result->finished = 1;
if (result->tmp != NULL) {
efree(result->tmp);
result->tmp = NULL;
result->tmp_len = 0;
}
return 0; return 0;
} }
@ -2332,7 +2338,6 @@ static int on_url_cb(http_parser *p, const char *at, size_t len)
zval *data = result->data; zval *data = result->data;
http_parser_parse_url(at, len, 0, &result->handle); http_parser_parse_url(at, len, 0, &result->handle);
add_assoc_stringl(data, "QUERY_STRING", (char*)at, len, 1); add_assoc_stringl(data, "QUERY_STRING", (char*)at, len, 1);
PHP_HTTP_PARSER_PARSE_URL(UF_SCHEMA, SCHEME); PHP_HTTP_PARSER_PARSE_URL(UF_SCHEMA, SCHEME);
@ -2369,10 +2374,24 @@ char *php_uv_strtoupper(char *s, size_t len)
static int header_field_cb(http_parser *p, const char *at, size_t len) static int header_field_cb(http_parser *p, const char *at, size_t len)
{ {
php_http_parser_context *result = p->data; php_http_parser_context *result = p->data;
/* TODO: */
result->tmp = estrndup(at, len);
php_uv_strtoupper(result->tmp, len);
if (result->was_header_value) {
if (result->tmp != NULL) {
efree(result->tmp);
result->tmp = NULL;
result->tmp_len = 0;
}
result->tmp = estrndup(at, len);
php_uv_strtoupper(result->tmp, len);
result->tmp_len = len;
} else {
result->tmp = erealloc(result->tmp, len + result->tmp_len + 1);
memcpy(result->tmp + result->tmp_len, at, len);
result->tmp[result->tmp_len + len] = '\0';
result->tmp_len = result->tmp_len + len;
}
result->was_header_value = 0;
return 0; return 0;
} }
@ -2381,10 +2400,21 @@ static int header_value_cb(http_parser *p, const char *at, size_t len)
php_http_parser_context *result = p->data; php_http_parser_context *result = p->data;
zval *data = result->headers; zval *data = result->headers;
add_assoc_stringl(data, result->tmp, (char*)at, len, 1); if (result->was_header_value) {
/* TODO: */ zval **element;
efree(result->tmp);
result->tmp = NULL; if (zend_hash_find(Z_ARRVAL_P(data), result->tmp, result->tmp_len+1, (void **)&element) == SUCCESS) {
Z_STRVAL_PP(element) = erealloc(Z_STRVAL_PP(element), Z_STRLEN_PP(element) + len + 1);
memcpy(Z_STRVAL_PP(element) + Z_STRLEN_PP(element), at, len);
Z_STRVAL_PP(element)[Z_STRLEN_PP(element)+len] = '\0';
Z_STRLEN_PP(element) = Z_STRLEN_PP(element) + len;
}
} else {
add_assoc_stringl(data, result->tmp, (char*)at, len, 1);
}
result->was_header_value = 1;
return 0; return 0;
} }
@ -6317,6 +6347,9 @@ PHP_FUNCTION(uv_http_parser_init)
ctx->data = result; ctx->data = result;
ctx->headers = header; ctx->headers = header;
ctx->finished = 0; ctx->finished = 0;
ctx->was_header_value = 1;
ctx->tmp = NULL;
ctx->tmp_len = 0;
if (target == HTTP_RESPONSE) { if (target == HTTP_RESPONSE) {
ctx->is_response = 1; ctx->is_response = 1;
@ -6369,8 +6402,8 @@ PHP_FUNCTION(uv_http_parser_execute)
} }
if (nparsed != body_len) { if (nparsed != body_len) {
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "parse failed."); zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "parse failed");
RETURN_FALSE; return;
} }
ZVAL_ZVAL(result, context->data, 1, 0); ZVAL_ZVAL(result, context->data, 1, 0);

View File

@ -197,6 +197,7 @@ typedef struct {
zval *data; zval *data;
zval *headers; zval *headers;
char *tmp; char *tmp;
size_t tmp_len;
} php_http_parser_context; } php_http_parser_context;
#define PHP_UV_HTTPPARSER_RESOURCE_NAME "uv_httpparser" #define PHP_UV_HTTPPARSER_RESOURCE_NAME "uv_httpparser"

View File

@ -1,5 +1,5 @@
--TEST-- --TEST--
Check for uv_cwd Check for uv_http_parser
--FILE-- --FILE--
<?php <?php
$parser = uv_http_parser_init(); $parser = uv_http_parser_init();
@ -33,7 +33,22 @@ Cache-Control: max-age=0
echo $result['PATH'] . PHP_EOL; echo $result['PATH'] . PHP_EOL;
echo $result['QUERY'] . PHP_EOL; echo $result['QUERY'] . PHP_EOL;
echo $result['FRAGMENT'] . PHP_EOL; echo $result['FRAGMENT'] . PHP_EOL;
echo $result['UPGRADE'] . PHP_EOL;
} }
$buffer = "GET /demo HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com
Origin: http://example.com
WebSocket-Protocol: sample
";
$parser = uv_http_parser_init();
$result = array();
uv_http_parser_execute($parser, $buffer, $result);
var_dump($result);
--EXPECT-- --EXPECT--
# Headers count # Headers count
9 9
@ -52,3 +67,27 @@ max-age=0
/img/http-parser.png /img/http-parser.png
key=value key=value
frag frag
0
array(5) {
["QUERY_STRING"]=>
string(5) "/demo"
["PATH"]=>
string(5) "/demo"
["REQUEST_METHOD"]=>
string(3) "GET"
["UPGRADE"]=>
int(1)
["HEADERS"]=>
array(5) {
["UPGRADE"]=>
string(9) "WebSocket"
["CONNECTION"]=>
string(7) "Upgrade"
["HOST"]=>
string(11) "example.com"
["ORIGIN"]=>
string(18) "http://example.com"
["WEBSOCKET_PROTOCOL"]=>
string(6) "sample"
}
}