|
@ -1,25 +1,25 @@ |
|
|
/* Copyright 2009 Ryan Dahl <ry@tinyclouds.org>
|
|
|
/* Copyright 2009 Ryan Dahl <ry@tinyclouds.org>
|
|
|
* |
|
|
* |
|
|
* Some parts of this source file were taken from NGINX |
|
|
* Some parts of this source file were taken from NGINX |
|
|
* (src/http/ngx_http_parser.c) copyright (C) 2002-2009 Igor Sysoev. |
|
|
* (src/http/ngx_http_parser.c) copyright (C) 2002-2009 Igor Sysoev. |
|
|
* |
|
|
* |
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
|
* of this software and associated documentation files (the "Software"), to |
|
|
* of this software and associated documentation files (the "Software"), to |
|
|
* deal in the Software without restriction, including without limitation the |
|
|
* deal in the Software without restriction, including without limitation the |
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
|
|
* sell copies of the Software, and to permit persons to whom the Software is |
|
|
* sell copies of the Software, and to permit persons to whom the Software is |
|
|
* furnished to do so, subject to the following conditions: |
|
|
* furnished to do so, subject to the following conditions: |
|
|
* |
|
|
* |
|
|
* The above copyright notice and this permission notice shall be included in |
|
|
* The above copyright notice and this permission notice shall be included in |
|
|
* all copies or substantial portions of the Software. |
|
|
* all copies or substantial portions of the Software. |
|
|
* |
|
|
* |
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|
|
* IN THE SOFTWARE. |
|
|
* IN THE SOFTWARE. |
|
|
*/ |
|
|
*/ |
|
|
#include <http_parser.h> |
|
|
#include <http_parser.h> |
|
|
#include <stdint.h> |
|
|
#include <stdint.h> |
|
@ -148,7 +148,7 @@ static const uint32_t usual[] = { |
|
|
|
|
|
|
|
|
#define USUAL(c) (usual[c >> 5] & (1 << (c & 0x1f))) |
|
|
#define USUAL(c) (usual[c >> 5] & (1 << (c & 0x1f))) |
|
|
|
|
|
|
|
|
enum state |
|
|
enum state |
|
|
{ s_dead = 1 /* important that this is > 0 */ |
|
|
{ s_dead = 1 /* important that this is > 0 */ |
|
|
|
|
|
|
|
|
, s_start_res |
|
|
, s_start_res |
|
@ -198,7 +198,6 @@ enum state |
|
|
, s_header_almost_done |
|
|
, s_header_almost_done |
|
|
|
|
|
|
|
|
, s_headers_almost_done |
|
|
, s_headers_almost_done |
|
|
, s_headers_done |
|
|
|
|
|
|
|
|
|
|
|
, s_chunk_size_start |
|
|
, s_chunk_size_start |
|
|
, s_chunk_size |
|
|
, s_chunk_size |
|
@ -212,7 +211,7 @@ enum state |
|
|
, s_body_identity_eof |
|
|
, s_body_identity_eof |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
enum header_states |
|
|
enum header_states |
|
|
{ h_general = 0 |
|
|
{ h_general = 0 |
|
|
, h_C |
|
|
, h_C |
|
|
, h_CO |
|
|
, h_CO |
|
@ -236,10 +235,10 @@ enum header_states |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
enum flags |
|
|
enum flags |
|
|
{ F_CHUNKED = 0x0001 |
|
|
{ F_CHUNKED = 1 << 0 |
|
|
, F_CONNECTION_KEEP_ALIVE = 0x0002 |
|
|
, F_CONNECTION_KEEP_ALIVE = 1 << 1 |
|
|
, F_CONNECTION_CLOSE = 0x0004 |
|
|
, F_CONNECTION_CLOSE = 1 << 2 |
|
|
, F_TRAILING = 0x0010 |
|
|
, F_TRAILING = 1 << 3 |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
#define CR '\r' |
|
|
#define CR '\r' |
|
@ -249,15 +248,15 @@ enum flags |
|
|
#if HTTP_PARSER_STRICT |
|
|
#if HTTP_PARSER_STRICT |
|
|
# define STRICT_CHECK(cond) if (cond) goto error |
|
|
# define STRICT_CHECK(cond) if (cond) goto error |
|
|
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) |
|
|
# define NEW_MESSAGE() (http_should_keep_alive(parser) ? start_state : s_dead) |
|
|
#else |
|
|
#else |
|
|
# define STRICT_CHECK(cond) |
|
|
# define STRICT_CHECK(cond) |
|
|
# define NEW_MESSAGE() start_state |
|
|
# define NEW_MESSAGE() start_state |
|
|
#endif |
|
|
#endif |
|
|
|
|
|
|
|
|
static inline |
|
|
static inline |
|
|
size_t parse (http_parser *parser, const char *data, size_t len, int start_state) |
|
|
size_t parse (http_parser *parser, const char *data, size_t len, int start_state) |
|
|
{ |
|
|
{ |
|
|
char c, ch; |
|
|
char c, ch; |
|
|
const char *p, *pe; |
|
|
const char *p, *pe; |
|
|
ssize_t to_read; |
|
|
ssize_t to_read; |
|
|
|
|
|
|
|
@ -298,7 +297,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
|
|
|
|
|
|
switch (ch) { |
|
|
switch (ch) { |
|
|
case 'H': |
|
|
case 'H': |
|
|
state = s_res_H; |
|
|
state = s_res_H; |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case CR: |
|
|
case CR: |
|
@ -321,7 +320,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
state = s_res_HTT; |
|
|
state = s_res_HTT; |
|
|
break; |
|
|
break; |
|
|
|
|
|
|
|
|
case s_res_HTT: |
|
|
case s_res_HTT: |
|
|
STRICT_CHECK(ch != 'P'); |
|
|
STRICT_CHECK(ch != 'P'); |
|
|
state = s_res_HTTP; |
|
|
state = s_res_HTTP; |
|
|
break; |
|
|
break; |
|
@ -353,7 +352,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
if (parser->http_major > 999) goto error; |
|
|
if (parser->http_major > 999) goto error; |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/* first digit of minor HTTP version */ |
|
|
/* first digit of minor HTTP version */ |
|
|
case s_res_first_http_minor: |
|
|
case s_res_first_http_minor: |
|
|
if (ch < '0' || ch > '9') goto error; |
|
|
if (ch < '0' || ch > '9') goto error; |
|
@ -464,7 +463,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
if (strncmp(parser->buffer, "GET", 3) == 0) { |
|
|
if (strncmp(parser->buffer, "GET", 3) == 0) { |
|
|
parser->method = HTTP_GET; |
|
|
parser->method = HTTP_GET; |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (strncmp(parser->buffer, "PUT", 3) == 0) { |
|
|
if (strncmp(parser->buffer, "PUT", 3) == 0) { |
|
|
parser->method = HTTP_PUT; |
|
|
parser->method = HTTP_PUT; |
|
@ -706,7 +705,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
|
|
|
|
|
|
switch (ch) { |
|
|
switch (ch) { |
|
|
case '?': |
|
|
case '?': |
|
|
break; // XXX ignore extra '?' ... is this right?
|
|
|
break; // XXX ignore extra '?' ... is this right?
|
|
|
case ' ': |
|
|
case ' ': |
|
|
CALLBACK(url); |
|
|
CALLBACK(url); |
|
|
state = s_req_http_start; |
|
|
state = s_req_http_start; |
|
@ -931,8 +930,10 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if (ch == LF) { |
|
|
if (ch == LF) { |
|
|
state = s_headers_done; |
|
|
/* they might be just sending \n instead of \r\n so this would be
|
|
|
break; |
|
|
* the second \n to denote the end of headers*/ |
|
|
|
|
|
state = s_headers_almost_done; |
|
|
|
|
|
goto headers_almost_done; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
c = LOWER(ch); |
|
|
c = LOWER(ch); |
|
@ -1089,7 +1090,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
|
|
|
|
|
|
header_state = h_general; |
|
|
header_state = h_general; |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
switch (header_state) { |
|
|
switch (header_state) { |
|
|
case h_transfer_encoding: |
|
|
case h_transfer_encoding: |
|
@ -1138,8 +1139,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
|
|
|
|
|
|
if (ch == LF) { |
|
|
if (ch == LF) { |
|
|
CALLBACK(header_value); |
|
|
CALLBACK(header_value); |
|
|
state = s_header_field_start; |
|
|
goto header_almost_done; |
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
@ -1162,7 +1162,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
/* Transfer-Encoding: chunked */ |
|
|
/* Transfer-Encoding: chunked */ |
|
|
case h_matching_transfer_encoding_chunked: |
|
|
case h_matching_transfer_encoding_chunked: |
|
|
index++; |
|
|
index++; |
|
|
if (index > sizeof(CHUNKED)-1 |
|
|
if (index > sizeof(CHUNKED)-1 |
|
|
|| c != CHUNKED[index]) { |
|
|
|| c != CHUNKED[index]) { |
|
|
header_state = h_general; |
|
|
header_state = h_general; |
|
|
} else if (index == sizeof(CHUNKED)-2) { |
|
|
} else if (index == sizeof(CHUNKED)-2) { |
|
@ -1206,6 +1206,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
case s_header_almost_done: |
|
|
case s_header_almost_done: |
|
|
|
|
|
header_almost_done: |
|
|
{ |
|
|
{ |
|
|
STRICT_CHECK(ch != LF); |
|
|
STRICT_CHECK(ch != LF); |
|
|
|
|
|
|
|
@ -1228,6 +1229,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
case s_headers_almost_done: |
|
|
case s_headers_almost_done: |
|
|
|
|
|
headers_almost_done: |
|
|
{ |
|
|
{ |
|
|
STRICT_CHECK(ch != LF); |
|
|
STRICT_CHECK(ch != LF); |
|
|
|
|
|
|
|
@ -1241,7 +1243,7 @@ size_t parse (http_parser *parser, const char *data, size_t len, int start_state |
|
|
parser->body_read = 0; |
|
|
parser->body_read = 0; |
|
|
|
|
|
|
|
|
CALLBACK2(headers_complete); |
|
|
CALLBACK2(headers_complete); |
|
|
|
|
|
|
|
|
if (parser->flags & F_CHUNKED) { |
|
|
if (parser->flags & F_CHUNKED) { |
|
|
/* chunked encoding - ignore Content-Length header */ |
|
|
/* chunked encoding - ignore Content-Length header */ |
|
|
state = s_chunk_size_start; |
|
|
state = s_chunk_size_start; |
|
|