src: simple Connection header multi-value parsing

Reviewed-By: Fedor Indutny <fedor@indutny.com>
PR-URL: https://github.com/joyent/http-parser/pull/100
make-http-max-header-size-gyp-configurable
Jon Kolb 13 years ago committed by Fedor Indutny
parent 959f4cb127
commit 091ebb8778

@ -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

@ -136,9 +136,10 @@ enum flags
{ F_CHUNKED = 1 << 0 { F_CHUNKED = 1 << 0
, F_CONNECTION_KEEP_ALIVE = 1 << 1 , F_CONNECTION_KEEP_ALIVE = 1 << 1
, F_CONNECTION_CLOSE = 1 << 2 , F_CONNECTION_CLOSE = 1 << 2
, F_TRAILING = 1 << 3 , F_CONNECTION_UPGRADE = 1 << 3
, F_UPGRADE = 1 << 4 , F_TRAILING = 1 << 4
, F_SKIPBODY = 1 << 5 , F_UPGRADE = 1 << 5
, F_SKIPBODY = 1 << 6
}; };

@ -950,6 +950,42 @@ const struct message requests[] =
,.body= "" ,.body= ""
} }
#define CONNECTION_MULTI 35
, {.name = "multiple connection header values with folding"
,.type= HTTP_REQUEST
,.raw= "GET /demo HTTP/1.1\r\n"
"Host: example.com\r\n"
"Connection: Something,\r\n"
" Upgrade, ,Keep-Alive\r\n"
"Sec-WebSocket-Key2: 12998 5 Y3 1 .P00\r\n"
"Sec-WebSocket-Protocol: sample\r\n"
"Upgrade: WebSocket\r\n"
"Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5\r\n"
"Origin: http://example.com\r\n"
"\r\n"
"Hot diggity dogg"
,.should_keep_alive= TRUE
,.message_complete_on_eof= FALSE
,.http_major= 1
,.http_minor= 1
,.method= HTTP_GET
,.query_string= ""
,.fragment= ""
,.request_path= "/demo"
,.request_url= "/demo"
,.num_headers= 7
,.upgrade="Hot diggity dogg"
,.headers= { { "Host", "example.com" }
, { "Connection", "Something, Upgrade, ,Keep-Alive" }
, { "Sec-WebSocket-Key2", "12998 5 Y3 1 .P00" }
, { "Sec-WebSocket-Protocol", "sample" }
, { "Upgrade", "WebSocket" }
, { "Sec-WebSocket-Key1", "4 @1 46546xW%0l 1 5" }
, { "Origin", "http://example.com" }
}
,.body= ""
}
, {.name= NULL } /* sentinel */ , {.name= NULL } /* sentinel */
}; };

Loading…
Cancel
Save