parser: fix HTTP version parsing

Only one digit is allowed for the major version and only one is
allowed for the minor version according to RFC 7230.

PR-URL: https://github.com/nodejs/http-parser/pull/366
Reviewed-By: Fedor Indutny <fedor@indutny.com>
make-http-max-header-size-gyp-configurable
Brian White 8 years ago committed by Fedor Indutny
parent 335850f6b8
commit 9f489a474d

@ -286,10 +286,10 @@ enum state
, s_res_HT , s_res_HT
, s_res_HTT , s_res_HTT
, s_res_HTTP , s_res_HTTP
, s_res_first_http_major
, s_res_http_major , s_res_http_major
, s_res_first_http_minor , s_res_http_dot
, s_res_http_minor , s_res_http_minor
, s_res_http_end
, s_res_first_status_code , s_res_first_status_code
, s_res_status_code , s_res_status_code
, s_res_status_start , s_res_status_start
@ -316,10 +316,10 @@ enum state
, s_req_http_HT , s_req_http_HT
, s_req_http_HTT , s_req_http_HTT
, s_req_http_HTTP , s_req_http_HTTP
, s_req_first_http_major
, s_req_http_major , s_req_http_major
, s_req_first_http_minor , s_req_http_dot
, s_req_http_minor , s_req_http_minor
, s_req_http_end
, s_req_line_almost_done , s_req_line_almost_done
, s_header_field_start , s_header_field_start
@ -795,75 +795,48 @@ reexecute:
case s_res_HTTP: case s_res_HTTP:
STRICT_CHECK(ch != '/'); STRICT_CHECK(ch != '/');
UPDATE_STATE(s_res_first_http_major); UPDATE_STATE(s_res_http_major);
break; break;
case s_res_first_http_major: case s_res_http_major:
if (UNLIKELY(ch < '0' || ch > '9')) { if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION); SET_ERRNO(HPE_INVALID_VERSION);
goto error; goto error;
} }
parser->http_major = ch - '0'; parser->http_major = ch - '0';
UPDATE_STATE(s_res_http_major); UPDATE_STATE(s_res_http_dot);
break; break;
/* major HTTP version or dot */ case s_res_http_dot:
case s_res_http_major:
{ {
if (ch == '.') { if (UNLIKELY(ch != '.')) {
UPDATE_STATE(s_res_first_http_minor);
break;
}
if (!IS_NUM(ch)) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major *= 10;
parser->http_major += ch - '0';
if (UNLIKELY(parser->http_major > 999)) {
SET_ERRNO(HPE_INVALID_VERSION); SET_ERRNO(HPE_INVALID_VERSION);
goto error; goto error;
} }
UPDATE_STATE(s_res_http_minor);
break; break;
} }
/* first digit of minor HTTP version */ case s_res_http_minor:
case s_res_first_http_minor:
if (UNLIKELY(!IS_NUM(ch))) { if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION); SET_ERRNO(HPE_INVALID_VERSION);
goto error; goto error;
} }
parser->http_minor = ch - '0'; parser->http_minor = ch - '0';
UPDATE_STATE(s_res_http_minor); UPDATE_STATE(s_res_http_end);
break; break;
/* minor HTTP version or end of request line */ case s_res_http_end:
case s_res_http_minor:
{ {
if (ch == ' ') { if (UNLIKELY(ch != ' ')) {
UPDATE_STATE(s_res_first_status_code);
break;
}
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor *= 10;
parser->http_minor += ch - '0';
if (UNLIKELY(parser->http_minor > 999)) {
SET_ERRNO(HPE_INVALID_VERSION); SET_ERRNO(HPE_INVALID_VERSION);
goto error; goto error;
} }
UPDATE_STATE(s_res_first_status_code);
break; break;
} }
@ -1153,57 +1126,41 @@ reexecute:
case s_req_http_HTTP: case s_req_http_HTTP:
STRICT_CHECK(ch != '/'); STRICT_CHECK(ch != '/');
UPDATE_STATE(s_req_first_http_major);
break;
/* first digit of major HTTP version */
case s_req_first_http_major:
if (UNLIKELY(ch < '1' || ch > '9')) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_major = ch - '0';
UPDATE_STATE(s_req_http_major); UPDATE_STATE(s_req_http_major);
break; break;
/* major HTTP version or dot */
case s_req_http_major: case s_req_http_major:
{
if (ch == '.') {
UPDATE_STATE(s_req_first_http_minor);
break;
}
if (UNLIKELY(!IS_NUM(ch))) { if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION); SET_ERRNO(HPE_INVALID_VERSION);
goto error; goto error;
} }
parser->http_major *= 10; parser->http_major = ch - '0';
parser->http_major += ch - '0'; UPDATE_STATE(s_req_http_dot);
break;
if (UNLIKELY(parser->http_major > 999)) { case s_req_http_dot:
{
if (UNLIKELY(ch != '.')) {
SET_ERRNO(HPE_INVALID_VERSION); SET_ERRNO(HPE_INVALID_VERSION);
goto error; goto error;
} }
UPDATE_STATE(s_req_http_minor);
break; break;
} }
/* first digit of minor HTTP version */ case s_req_http_minor:
case s_req_first_http_minor:
if (UNLIKELY(!IS_NUM(ch))) { if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION); SET_ERRNO(HPE_INVALID_VERSION);
goto error; goto error;
} }
parser->http_minor = ch - '0'; parser->http_minor = ch - '0';
UPDATE_STATE(s_req_http_minor); UPDATE_STATE(s_req_http_end);
break; break;
/* minor HTTP version or end of request line */ case s_req_http_end:
case s_req_http_minor:
{ {
if (ch == CR) { if (ch == CR) {
UPDATE_STATE(s_req_line_almost_done); UPDATE_STATE(s_req_line_almost_done);
@ -1215,21 +1172,8 @@ reexecute:
break; break;
} }
/* XXX allow spaces after digit? */
if (UNLIKELY(!IS_NUM(ch))) {
SET_ERRNO(HPE_INVALID_VERSION);
goto error;
}
parser->http_minor *= 10;
parser->http_minor += ch - '0';
if (UNLIKELY(parser->http_minor > 999)) {
SET_ERRNO(HPE_INVALID_VERSION); SET_ERRNO(HPE_INVALID_VERSION);
goto error; goto error;
}
break; break;
} }

@ -3313,9 +3313,11 @@ test_message_count_body (const struct message *message)
} }
void void
test_simple (const char *buf, enum http_errno err_expected) test_simple_type (const char *buf,
enum http_errno err_expected,
enum http_parser_type type)
{ {
parser_init(HTTP_REQUEST); parser_init(type);
enum http_errno err; enum http_errno err;
@ -3339,6 +3341,12 @@ test_simple (const char *buf, enum http_errno err_expected)
} }
} }
void
test_simple (const char *buf, enum http_errno err_expected)
{
test_simple_type(buf, err_expected, HTTP_REQUEST);
}
void void
test_invalid_header_content (int req, const char* str) test_invalid_header_content (int req, const char* str)
{ {
@ -3949,6 +3957,12 @@ main (void)
//// RESPONSES //// RESPONSES
test_simple_type("HTP/1.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
test_simple_type("HTTP/01.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
test_simple_type("HTTP/11.1 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
test_simple_type("HTTP/1.01 200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
test_simple_type("HTTP/1.1\t200 OK\r\n\r\n", HPE_INVALID_VERSION, HTTP_RESPONSE);
for (i = 0; i < response_count; i++) { for (i = 0; i < response_count; i++) {
test_message(&responses[i]); test_message(&responses[i]);
} }
@ -4026,6 +4040,9 @@ main (void)
/// REQUESTS /// REQUESTS
test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION); test_simple("GET / HTP/1.1\r\n\r\n", HPE_INVALID_VERSION);
test_simple("GET / HTTP/01.1\r\n\r\n", HPE_INVALID_VERSION);
test_simple("GET / HTTP/11.1\r\n\r\n", HPE_INVALID_VERSION);
test_simple("GET / HTTP/1.01\r\n\r\n", HPE_INVALID_VERSION);
// Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js // Extended characters - see nodejs/test/parallel/test-http-headers-obstext.js
test_simple("GET / HTTP/1.1\r\n" test_simple("GET / HTTP/1.1\r\n"

Loading…
Cancel
Save