HTTP parser: fixed parsing of target after literal space character.

In theory, all space characters in request target must be encoded; however,
some clients may violate the specification.  For the sake of interoperability,
Unit supports unencoded space characters.

Previously, if there was a space character before the extension or arguments
parts, those parts weren't recognized.  Also, quoted symbols and complex
target weren't detected after a space character.
This commit is contained in:
Valentin Bartenev
2019-09-17 18:40:21 +03:00
parent 3b77e402a9
commit 6352c21a58
3 changed files with 63 additions and 3 deletions

View File

@@ -164,6 +164,7 @@ nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
{ {
u_char *p, ch, *after_slash, *exten, *args; u_char *p, ch, *after_slash, *exten, *args;
nxt_int_t rc; nxt_int_t rc;
nxt_bool_t rest;
nxt_http_ver_t ver; nxt_http_ver_t ver;
nxt_http_target_traps_e trap; nxt_http_target_traps_e trap;
@@ -256,6 +257,9 @@ nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
after_slash = p + 1; after_slash = p + 1;
exten = NULL; exten = NULL;
args = NULL; args = NULL;
rest = 0;
continue_target:
for ( ;; ) { for ( ;; ) {
p++; p++;
@@ -312,6 +316,8 @@ nxt_http_parse_request_line(nxt_http_request_parse_t *rp, u_char **pos,
rest_of_target: rest_of_target:
rest = 1;
for ( ;; ) { for ( ;; ) {
p++; p++;
@@ -378,9 +384,14 @@ space_after_target:
} }
rp->space_in_target = 1; rp->space_in_target = 1;
if (rest) {
goto rest_of_target; goto rest_of_target;
} }
goto continue_target;
}
/* " HTTP/1.1\r\n" or " HTTP/1.1\n" */ /* " HTTP/1.1\r\n" or " HTTP/1.1\n" */
if (nxt_slow_path(p[9] != '\r' && p[9] != '\n')) { if (nxt_slow_path(p[9] != '\r' && p[9] != '\n')) {
@@ -392,9 +403,14 @@ space_after_target:
} }
rp->space_in_target = 1; rp->space_in_target = 1;
if (rest) {
goto rest_of_target; goto rest_of_target;
} }
goto continue_target;
}
nxt_memcpy(ver.str, &p[1], 8); nxt_memcpy(ver.str, &p[1], 8);
if (nxt_fast_path(ver.ui64 == http11.ui64 if (nxt_fast_path(ver.ui64 == http11.ui64

View File

@@ -205,6 +205,19 @@ static nxt_http_parse_test_case_t nxt_http_test_cases[] = {
0, 0, 1 0, 0, 1
}} }}
}, },
{
nxt_string("GET /na %20me.ext?args HTTP/1.0\r\n\r\n"),
NXT_DONE,
&nxt_http_parse_test_request_line,
{ .request_line = {
nxt_string("GET"),
nxt_string("/na %20me.ext?args"),
nxt_string("ext"),
nxt_string("args"),
"HTTP/1.0",
0, 1, 1
}}
},
{ {
nxt_string("GET / HTTP/1.0 HTTP/1.1\r\n\r\n"), nxt_string("GET / HTTP/1.0 HTTP/1.1\r\n\r\n"),
NXT_DONE, NXT_DONE,
@@ -212,7 +225,7 @@ static nxt_http_parse_test_case_t nxt_http_test_cases[] = {
{ .request_line = { { .request_line = {
nxt_string("GET"), nxt_string("GET"),
nxt_string("/ HTTP/1.0"), nxt_string("/ HTTP/1.0"),
nxt_null_string, nxt_string("0"),
nxt_null_string, nxt_null_string,
"HTTP/1.1", "HTTP/1.1",
0, 0, 1 0, 0, 1

View File

@@ -71,6 +71,37 @@ class TestPythonApplication(TestApplicationPython):
'Query-String header', 'Query-String header',
) )
def test_python_application_query_string_space(self):
self.load('query_string')
resp = self.get(url='/ ?var1=val1&var2=val2')
self.assertEqual(
resp['headers']['Query-String'],
'var1=val1&var2=val2',
'Query-String space',
)
resp = self.get(url='/ %20?var1=val1&var2=val2')
self.assertEqual(
resp['headers']['Query-String'],
'var1=val1&var2=val2',
'Query-String space 2',
)
resp = self.get(url='/ %20 ?var1=val1&var2=val2')
self.assertEqual(
resp['headers']['Query-String'],
'var1=val1&var2=val2',
'Query-String space 3',
)
resp = self.get(url='/blah %20 blah? var1= val1 & var2=val2')
self.assertEqual(
resp['headers']['Query-String'],
' var1= val1 & var2=val2',
'Query-String space 4',
)
def test_python_application_query_string_empty(self): def test_python_application_query_string_empty(self):
self.load('query_string') self.load('query_string')