|
|
@ -369,12 +369,16 @@ enum header_states
|
|
|
|
, h_upgrade
|
|
|
|
, h_upgrade
|
|
|
|
|
|
|
|
|
|
|
|
, h_matching_transfer_encoding_chunked
|
|
|
|
, h_matching_transfer_encoding_chunked
|
|
|
|
|
|
|
|
, h_matching_connection_token_start
|
|
|
|
, h_matching_connection_keep_alive
|
|
|
|
, h_matching_connection_keep_alive
|
|
|
|
, h_matching_connection_close
|
|
|
|
, h_matching_connection_close
|
|
|
|
|
|
|
|
, h_matching_connection_upgrade
|
|
|
|
|
|
|
|
, h_matching_connection_token
|
|
|
|
|
|
|
|
|
|
|
|
, h_transfer_encoding_chunked
|
|
|
|
, h_transfer_encoding_chunked
|
|
|
|
, h_connection_keep_alive
|
|
|
|
, h_connection_keep_alive
|
|
|
|
, h_connection_close
|
|
|
|
, h_connection_close
|
|
|
|
|
|
|
|
, h_connection_upgrade
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
enum http_host_state
|
|
|
|
enum http_host_state
|
|
|
@ -406,6 +410,8 @@ enum http_host_state
|
|
|
|
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
|
|
|
|
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
|
|
|
|
(c) == '$' || (c) == ',')
|
|
|
|
(c) == '$' || (c) == ',')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define STRICT_TOKEN(c) (tokens[(unsigned char)c])
|
|
|
|
|
|
|
|
|
|
|
|
#if HTTP_PARSER_STRICT
|
|
|
|
#if HTTP_PARSER_STRICT
|
|
|
|
#define TOKEN(c) (tokens[(unsigned char)c])
|
|
|
|
#define TOKEN(c) (tokens[(unsigned char)c])
|
|
|
|
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
|
|
|
|
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
|
|
|
@ -1481,11 +1487,17 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
|
/* looking for 'Connection: close' */
|
|
|
|
/* looking for 'Connection: close' */
|
|
|
|
} else if (c == 'c') {
|
|
|
|
} else if (c == 'c') {
|
|
|
|
parser->header_state = h_matching_connection_close;
|
|
|
|
parser->header_state = h_matching_connection_close;
|
|
|
|
|
|
|
|
} else if (c == 'u') {
|
|
|
|
|
|
|
|
parser->header_state = h_matching_connection_upgrade;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
parser->header_state = h_general;
|
|
|
|
parser->header_state = h_matching_connection_token;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Multi-value `Connection` header */
|
|
|
|
|
|
|
|
case h_matching_connection_token_start:
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
parser->header_state = h_general;
|
|
|
|
parser->header_state = h_general;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
@ -1585,12 +1597,28 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case h_matching_connection_token_start:
|
|
|
|
|
|
|
|
/* looking for 'Connection: keep-alive' */
|
|
|
|
|
|
|
|
if (c == 'k') {
|
|
|
|
|
|
|
|
h_state = h_matching_connection_keep_alive;
|
|
|
|
|
|
|
|
/* looking for 'Connection: close' */
|
|
|
|
|
|
|
|
} else if (c == 'c') {
|
|
|
|
|
|
|
|
h_state = h_matching_connection_close;
|
|
|
|
|
|
|
|
} else if (c == 'u') {
|
|
|
|
|
|
|
|
h_state = h_matching_connection_upgrade;
|
|
|
|
|
|
|
|
} else if (STRICT_TOKEN(c)) {
|
|
|
|
|
|
|
|
h_state = h_matching_connection_token;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
h_state = h_general;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
/* looking for 'Connection: keep-alive' */
|
|
|
|
/* looking for 'Connection: keep-alive' */
|
|
|
|
case h_matching_connection_keep_alive:
|
|
|
|
case h_matching_connection_keep_alive:
|
|
|
|
parser->index++;
|
|
|
|
parser->index++;
|
|
|
|
if (parser->index > sizeof(KEEP_ALIVE)-1
|
|
|
|
if (parser->index > sizeof(KEEP_ALIVE)-1
|
|
|
|
|| c != KEEP_ALIVE[parser->index]) {
|
|
|
|
|| c != KEEP_ALIVE[parser->index]) {
|
|
|
|
h_state = h_general;
|
|
|
|
h_state = h_matching_connection_token;
|
|
|
|
} else if (parser->index == sizeof(KEEP_ALIVE)-2) {
|
|
|
|
} else if (parser->index == sizeof(KEEP_ALIVE)-2) {
|
|
|
|
h_state = h_connection_keep_alive;
|
|
|
|
h_state = h_connection_keep_alive;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1600,16 +1628,50 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
|
case h_matching_connection_close:
|
|
|
|
case h_matching_connection_close:
|
|
|
|
parser->index++;
|
|
|
|
parser->index++;
|
|
|
|
if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
|
|
|
|
if (parser->index > sizeof(CLOSE)-1 || c != CLOSE[parser->index]) {
|
|
|
|
h_state = h_general;
|
|
|
|
h_state = h_matching_connection_token;
|
|
|
|
} else if (parser->index == sizeof(CLOSE)-2) {
|
|
|
|
} else if (parser->index == sizeof(CLOSE)-2) {
|
|
|
|
h_state = h_connection_close;
|
|
|
|
h_state = h_connection_close;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* looking for 'Connection: upgrade' */
|
|
|
|
|
|
|
|
case h_matching_connection_upgrade:
|
|
|
|
|
|
|
|
parser->index++;
|
|
|
|
|
|
|
|
if (parser->index > sizeof(UPGRADE) - 1 ||
|
|
|
|
|
|
|
|
c != UPGRADE[parser->index]) {
|
|
|
|
|
|
|
|
h_state = h_matching_connection_token;
|
|
|
|
|
|
|
|
} else if (parser->index == sizeof(UPGRADE)-2) {
|
|
|
|
|
|
|
|
h_state = h_connection_upgrade;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case h_matching_connection_token:
|
|
|
|
|
|
|
|
if (ch == ',') {
|
|
|
|
|
|
|
|
h_state = h_matching_connection_token_start;
|
|
|
|
|
|
|
|
parser->index = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case h_transfer_encoding_chunked:
|
|
|
|
case h_transfer_encoding_chunked:
|
|
|
|
|
|
|
|
if (ch != ' ') h_state = h_general;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case h_connection_keep_alive:
|
|
|
|
case h_connection_keep_alive:
|
|
|
|
case h_connection_close:
|
|
|
|
case h_connection_close:
|
|
|
|
if (ch != ' ') h_state = h_general;
|
|
|
|
case h_connection_upgrade:
|
|
|
|
|
|
|
|
if (ch == ',') {
|
|
|
|
|
|
|
|
if (h_state == h_connection_keep_alive) {
|
|
|
|
|
|
|
|
parser->flags |= F_CONNECTION_KEEP_ALIVE;
|
|
|
|
|
|
|
|
} else if (h_state == h_connection_close) {
|
|
|
|
|
|
|
|
parser->flags |= F_CONNECTION_CLOSE;
|
|
|
|
|
|
|
|
} else if (h_state == h_connection_upgrade) {
|
|
|
|
|
|
|
|
parser->flags |= F_CONNECTION_UPGRADE;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
h_state = h_matching_connection_token_start;
|
|
|
|
|
|
|
|
parser->index = 0;
|
|
|
|
|
|
|
|
} else if (ch != ' ') {
|
|
|
|
|
|
|
|
h_state = h_matching_connection_token;
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
default:
|
|
|
@ -1653,6 +1715,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
|
case h_transfer_encoding_chunked:
|
|
|
|
case h_transfer_encoding_chunked:
|
|
|
|
parser->flags |= F_CHUNKED;
|
|
|
|
parser->flags |= F_CHUNKED;
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case h_connection_upgrade:
|
|
|
|
|
|
|
|
parser->flags |= F_CONNECTION_UPGRADE;
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -1674,6 +1739,23 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
|
UPDATE_STATE(s_header_value_discard_ws);
|
|
|
|
UPDATE_STATE(s_header_value_discard_ws);
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
|
|
|
|
switch (parser->header_state) {
|
|
|
|
|
|
|
|
case h_connection_keep_alive:
|
|
|
|
|
|
|
|
parser->flags |= F_CONNECTION_KEEP_ALIVE;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case h_connection_close:
|
|
|
|
|
|
|
|
parser->flags |= F_CONNECTION_CLOSE;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case h_connection_upgrade:
|
|
|
|
|
|
|
|
parser->flags |= F_CONNECTION_UPGRADE;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case h_transfer_encoding_chunked:
|
|
|
|
|
|
|
|
parser->flags |= F_CHUNKED;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* header value was empty */
|
|
|
|
/* header value was empty */
|
|
|
|
MARK(header_value);
|
|
|
|
MARK(header_value);
|
|
|
|
UPDATE_STATE(s_header_field_start);
|
|
|
|
UPDATE_STATE(s_header_field_start);
|
|
|
@ -1697,7 +1779,9 @@ size_t http_parser_execute (http_parser *parser,
|
|
|
|
|
|
|
|
|
|
|
|
/* Set this here so that on_headers_complete() callbacks can see it */
|
|
|
|
/* Set this here so that on_headers_complete() callbacks can see it */
|
|
|
|
parser->upgrade =
|
|
|
|
parser->upgrade =
|
|
|
|
(parser->flags & F_UPGRADE || parser->method == HTTP_CONNECT);
|
|
|
|
((parser->flags & (F_UPGRADE | F_CONNECTION_UPGRADE)) ==
|
|
|
|
|
|
|
|
(F_UPGRADE | F_CONNECTION_UPGRADE) ||
|
|
|
|
|
|
|
|
parser->method == HTTP_CONNECT);
|
|
|
|
|
|
|
|
|
|
|
|
/* Here we call the headers_complete callback. This is somewhat
|
|
|
|
/* Here we call the headers_complete callback. This is somewhat
|
|
|
|
* different than other callbacks because if the user returns 1, we
|
|
|
|
* different than other callbacks because if the user returns 1, we
|
|
|
|