Skip to content

Commit 951622a

Browse files
committed
improve http parser:
* support multiple line header field and value. * support http upgrade mechanism (you have to parse websocket protocol manually)
1 parent fe317d6 commit 951622a

File tree

3 files changed

+84
-11
lines changed

3 files changed

+84
-11
lines changed

php_uv.c

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2316,6 +2316,12 @@ static int on_message_complete(http_parser *p)
23162316
php_http_parser_context *result = p->data;
23172317
result->finished = 1;
23182318

2319+
if (result->tmp != NULL) {
2320+
efree(result->tmp);
2321+
result->tmp = NULL;
2322+
result->tmp_len = 0;
2323+
}
2324+
23192325
return 0;
23202326
}
23212327

@@ -2332,7 +2338,6 @@ static int on_url_cb(http_parser *p, const char *at, size_t len)
23322338
zval *data = result->data;
23332339

23342340
http_parser_parse_url(at, len, 0, &result->handle);
2335-
23362341
add_assoc_stringl(data, "QUERY_STRING", (char*)at, len, 1);
23372342

23382343
PHP_HTTP_PARSER_PARSE_URL(UF_SCHEMA, SCHEME);
@@ -2369,10 +2374,24 @@ char *php_uv_strtoupper(char *s, size_t len)
23692374
static int header_field_cb(http_parser *p, const char *at, size_t len)
23702375
{
23712376
php_http_parser_context *result = p->data;
2372-
/* TODO: */
2373-
result->tmp = estrndup(at, len);
2374-
php_uv_strtoupper(result->tmp, len);
23752377

2378+
if (result->was_header_value) {
2379+
if (result->tmp != NULL) {
2380+
efree(result->tmp);
2381+
result->tmp = NULL;
2382+
result->tmp_len = 0;
2383+
}
2384+
result->tmp = estrndup(at, len);
2385+
php_uv_strtoupper(result->tmp, len);
2386+
result->tmp_len = len;
2387+
} else {
2388+
result->tmp = erealloc(result->tmp, len + result->tmp_len + 1);
2389+
memcpy(result->tmp + result->tmp_len, at, len);
2390+
result->tmp[result->tmp_len + len] = '\0';
2391+
result->tmp_len = result->tmp_len + len;
2392+
}
2393+
2394+
result->was_header_value = 0;
23762395
return 0;
23772396
}
23782397

@@ -2381,10 +2400,21 @@ static int header_value_cb(http_parser *p, const char *at, size_t len)
23812400
php_http_parser_context *result = p->data;
23822401
zval *data = result->headers;
23832402

2384-
add_assoc_stringl(data, result->tmp, (char*)at, len, 1);
2385-
/* TODO: */
2386-
efree(result->tmp);
2387-
result->tmp = NULL;
2403+
if (result->was_header_value) {
2404+
zval **element;
2405+
2406+
if (zend_hash_find(Z_ARRVAL_P(data), result->tmp, result->tmp_len+1, (void **)&element) == SUCCESS) {
2407+
Z_STRVAL_PP(element) = erealloc(Z_STRVAL_PP(element), Z_STRLEN_PP(element) + len + 1);
2408+
memcpy(Z_STRVAL_PP(element) + Z_STRLEN_PP(element), at, len);
2409+
2410+
Z_STRVAL_PP(element)[Z_STRLEN_PP(element)+len] = '\0';
2411+
Z_STRLEN_PP(element) = Z_STRLEN_PP(element) + len;
2412+
}
2413+
} else {
2414+
add_assoc_stringl(data, result->tmp, (char*)at, len, 1);
2415+
}
2416+
2417+
result->was_header_value = 1;
23882418
return 0;
23892419
}
23902420

@@ -6317,6 +6347,9 @@ PHP_FUNCTION(uv_http_parser_init)
63176347
ctx->data = result;
63186348
ctx->headers = header;
63196349
ctx->finished = 0;
6350+
ctx->was_header_value = 1;
6351+
ctx->tmp = NULL;
6352+
ctx->tmp_len = 0;
63206353

63216354
if (target == HTTP_RESPONSE) {
63226355
ctx->is_response = 1;
@@ -6369,8 +6402,8 @@ PHP_FUNCTION(uv_http_parser_execute)
63696402
}
63706403

63716404
if (nparsed != body_len) {
6372-
php_error_docref(NULL TSRMLS_CC, E_NOTICE, "parse failed.");
6373-
RETURN_FALSE;
6405+
zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "parse failed");
6406+
return;
63746407
}
63756408

63766409
ZVAL_ZVAL(result, context->data, 1, 0);

php_uv.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ typedef struct {
197197
zval *data;
198198
zval *headers;
199199
char *tmp;
200+
size_t tmp_len;
200201
} php_http_parser_context;
201202

202203
#define PHP_UV_HTTPPARSER_RESOURCE_NAME "uv_httpparser"

tests/999-uv_http_parser.phpt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
--TEST--
2-
Check for uv_cwd
2+
Check for uv_http_parser
33
--FILE--
44
<?php
55
$parser = uv_http_parser_init();
@@ -33,7 +33,22 @@ Cache-Control: max-age=0
3333
echo $result['PATH'] . PHP_EOL;
3434
echo $result['QUERY'] . PHP_EOL;
3535
echo $result['FRAGMENT'] . PHP_EOL;
36+
echo $result['UPGRADE'] . PHP_EOL;
3637
}
38+
39+
$buffer = "GET /demo HTTP/1.1
40+
Upgrade: WebSocket
41+
Connection: Upgrade
42+
Host: example.com
43+
Origin: http://example.com
44+
WebSocket-Protocol: sample
45+
46+
";
47+
48+
$parser = uv_http_parser_init();
49+
$result = array();
50+
uv_http_parser_execute($parser, $buffer, $result);
51+
var_dump($result);
3752
--EXPECT--
3853
# Headers count
3954
9
@@ -52,3 +67,27 @@ max-age=0
5267
/img/http-parser.png
5368
key=value
5469
frag
70+
0
71+
array(5) {
72+
["QUERY_STRING"]=>
73+
string(5) "/demo"
74+
["PATH"]=>
75+
string(5) "/demo"
76+
["REQUEST_METHOD"]=>
77+
string(3) "GET"
78+
["UPGRADE"]=>
79+
int(1)
80+
["HEADERS"]=>
81+
array(5) {
82+
["UPGRADE"]=>
83+
string(9) "WebSocket"
84+
["CONNECTION"]=>
85+
string(7) "Upgrade"
86+
["HOST"]=>
87+
string(11) "example.com"
88+
["ORIGIN"]=>
89+
string(18) "http://example.com"
90+
["WEBSOCKET_PROTOCOL"]=>
91+
string(6) "sample"
92+
}
93+
}

0 commit comments

Comments
 (0)