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:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user