Now parsing some req headers

event_stream
Ryan Dahl 15 years ago
parent 433202d825
commit c5a92f792f

@ -19,6 +19,11 @@ do { \
if (0 != FOR##_callback(parser, p)) return (p - data); \
} while (0)
#define CALLBACK2(FOR) \
do { \
if (0 != FOR##_callback(parser)) return (p - data); \
} while (0)
#if 0
do { \
@ -39,64 +44,43 @@ do { \
} while(0)
#endif
static inline int uri_callback (http_parser *parser, const char *p)
{
assert(parser->uri_mark);
const char *mark = parser->uri_mark;
parser->uri_size += p - mark;
if (parser->uri_size > MAX_FIELD_SIZE) return -1;
if (parser->on_uri == NULL) return 0;
return parser->on_uri(parser, mark, p - mark);
}
static inline int path_callback (http_parser *parser, const char *p)
{
assert(parser->path_mark);
const char *mark = parser->path_mark;
parser->path_size += p - mark;
if (parser->path_size > MAX_FIELD_SIZE) return -1;
if (parser->on_path == NULL) return 0;
return parser->on_path(parser, mark, p - mark);
#define DEFINE_CALLBACK(FOR) \
static inline int FOR##_callback (http_parser *parser, const char *p) \
{ \
if (!parser->FOR##_mark) return 0; \
assert(parser->FOR##_mark); \
const char *mark = parser->FOR##_mark; \
parser->FOR##_size += p - mark; \
if (parser->FOR##_size > MAX_FIELD_SIZE) return -1; \
int r = 0; \
if (parser->on_##FOR) r = parser->on_##FOR(parser, mark, p - mark); \
parser->FOR##_mark = NULL; \
return r; \
}
static inline int query_string_callback (http_parser *parser, const char *p)
{
assert(parser->query_string_mark);
const char *mark = parser->query_string_mark;
parser->query_string_size += p - mark;
if (parser->query_string_size > MAX_FIELD_SIZE) return -1;
if (parser->on_query_string == NULL) return 0;
return parser->on_query_string(parser, mark, p - mark);
}
DEFINE_CALLBACK(uri)
DEFINE_CALLBACK(path)
DEFINE_CALLBACK(query_string)
DEFINE_CALLBACK(fragment)
DEFINE_CALLBACK(header_field)
DEFINE_CALLBACK(header_value)
static inline int fragment_callback (http_parser *parser, const char *p)
static inline int headers_complete_callback (http_parser *parser)
{
assert(parser->fragment_mark);
const char *mark = parser->fragment_mark;
parser->fragment_size += p - mark;
if (parser->fragment_size > MAX_FIELD_SIZE) return -1;
if (parser->on_fragment == NULL) return 0;
return parser->on_fragment(parser, mark, p - mark);
if (parser->on_headers_complete == NULL) return 0;
return parser->on_headers_complete(parser);
}
static inline int header_field_callback (http_parser *parser, const char *p)
static inline int message_begin_callback (http_parser *parser)
{
assert(parser->header_field_mark);
const char *mark = parser->header_field_mark;
parser->header_field_size += p - mark;
if (parser->header_field_size > MAX_FIELD_SIZE) return -1;
if (parser->on_header_field == NULL) return 0;
return parser->on_header_field(parser, mark, p - mark);
if (parser->on_message_begin == NULL) return 0;
return parser->on_message_begin(parser);
}
static inline int header_value_callback (http_parser *parser, const char *p)
static inline int message_complete_callback (http_parser *parser)
{
assert(parser->header_value_mark);
const char *mark = parser->header_value_mark;
parser->header_value_size += p - mark;
if (parser->header_value_size > MAX_FIELD_SIZE) return -1;
if (parser->on_header_value == NULL) return 0;
return parser->on_header_value(parser, mark, p - mark);
if (parser->on_message_complete == NULL) return 0;
return parser->on_message_complete(parser);
}
#define CONNECTION "connection"
@ -119,7 +103,7 @@ static const uint32_t usual[] = {
0xffffdbfe, /* 1111 1111 1111 1111 1101 1011 1111 1110 */
/* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */
0x7fff37d6, /* 0111 1111 1111 1111 1111 1111 1111 0110 */
0x7ffffff6, /* 0111 1111 1111 1111 1111 1111 1111 0110 */
/* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */
0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */
@ -163,7 +147,9 @@ enum state
, s_port
, s_path
, s_query_string_start
, s_query_string
, s_fragment_start
, s_fragment
, s_http_start
@ -188,6 +174,10 @@ enum state
, s_headers_almost_done
, s_headers_done
, s_body_chunked
, s_body_identity
, s_body_identity_eof
};
enum header_states
@ -202,10 +192,20 @@ enum header_states
, h_content_length
, h_transfer_encoding
, h_encoding_C
, h_encoding_CH
, h_encoding_CHU
, h_encoding_CHUN
, h_encoding_CHUNK
, h_encoding_CHUNKE
, h_encoding_CHUNKED
, h_connection_K
, h_connection_C
};
enum flags
{ F_CHUNKED = 0x0001
};
#define ERROR (p - data);
#define CR '\r'
#define LF '\n'
@ -220,6 +220,11 @@ size_t http_parser_execute (http_parser *parser, const char *data, size_t len)
enum header_states header_state = parser->header_state;
size_t header_index = parser->header_index;
if (len == 0 && state == s_body_identity_eof) {
CALLBACK2(message_complete);
return 0;
}
if (parser->header_field_mark) parser->header_field_mark = data;
if (parser->header_value_mark) parser->header_value_mark = data;
if (parser->fragment_mark) parser->fragment_mark = data;
@ -232,6 +237,11 @@ size_t http_parser_execute (http_parser *parser, const char *data, size_t len)
switch (state) {
case s_start:
{
parser->flags = 0;
parser->content_length = -1;
CALLBACK2(message_begin);
switch (ch) {
/* GET */
case 'G':
@ -480,13 +490,45 @@ size_t http_parser_execute (http_parser *parser, const char *data, size_t len)
break;
case '?':
CALLBACK(path);
MARK(query_string);
state = s_query_string;
state = s_query_string_start;
break;
case '#':
CALLBACK(path);
MARK(fragment);
state = s_fragment;
state = s_fragment_start;
break;
default:
return ERROR;
}
break;
}
case s_query_string_start:
{
if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
MARK(query_string);
state = s_query_string;
break;
}
switch (ch) {
case '?':
break; // XXX ignore extra '?' ... is this right?
case ' ':
CALLBACK(uri);
state = s_http_start;
break;
case CR:
CALLBACK(uri);
parser->http_minor = 9;
state = s_req_line_almost_done;
break;
case LF:
CALLBACK(uri);
parser->http_minor = 9;
state = s_header_field_start;
break;
case '#':
state = s_fragment_start;
break;
default:
return ERROR;
@ -518,9 +560,43 @@ size_t http_parser_execute (http_parser *parser, const char *data, size_t len)
break;
case '#':
CALLBACK(query_string);
state = s_fragment_start;
break;
default:
return ERROR;
}
break;
}
case s_fragment_start:
{
if (usual[ch >> 5] & (1 << (ch & 0x1f))) {
MARK(fragment);
state = s_fragment;
break;
}
switch (ch) {
case ' ':
CALLBACK(uri);
state = s_http_start;
break;
case CR:
CALLBACK(uri);
parser->http_minor = 9;
state = s_req_line_almost_done;
break;
case LF:
CALLBACK(uri);
parser->http_minor = 9;
state = s_header_field_start;
break;
case '?':
MARK(fragment);
state = s_fragment;
break;
case '#':
break;
default:
return ERROR;
}
@ -760,6 +836,7 @@ size_t http_parser_execute (http_parser *parser, const char *data, size_t len)
assert(0 && "Unknown header_state");
break;
}
break;
}
if (ch == ':') {
@ -787,48 +864,129 @@ size_t http_parser_execute (http_parser *parser, const char *data, size_t len)
{
if (ch == ' ') break;
MARK(header_value);
if (ch == CR) {
header_state = h_general;
state = s_header_almost_done;
break;
}
if (ch == LF) {
state = s_header_field_start;
break;
}
MARK(header_value);
c = lowcase[(int)ch];
if (!c) {
state = s_header_value;
header_state = h_general;
state = s_headers_done;
} else {
switch (header_state) {
case h_transfer_encoding:
/* looking for 'Transfer-Encoding: chunked' */
if ('c' == c) {
header_state = h_encoding_C;
} else {
header_state = h_general;
}
break;
case h_content_length:
if (ch < '0' || ch > '9') return ERROR;
parser->content_length = ch - '0';
break;
case h_connection:
/* looking for 'Connection: keep-alive' */
if (c == 'k') {
header_state = h_connection_K;
/* looking for 'Connection: close' */
} else if (c == 'c') {
header_state = h_connection_C;
} else {
header_state = h_general;
}
break;
default:
state = s_header_value;
header_state = h_general;
break;
}
break;
}
break;
}
case s_header_value:
{
c = lowcase[(int)ch];
if (!c) return ERROR;
if (!c) {
if (ch == CR) {
CALLBACK(header_value);
state = s_header_almost_done;
break;
}
if (ch == LF) {
CALLBACK(header_value);
state = s_header_field_start;
break;
}
break;
}
switch (header_state) {
case h_connection:
case h_transfer_encoding:
/* looking for 'Transfer-Encoding: chunked' */
if ('c' == c) {
header_state = h_encoding_C;
} else {
header_state = h_general;
}
assert(0 && "Shouldn't get here.");
break;
case h_general:
break;
case h_content_length:
if (ch < '0' || ch > '9') return ERROR;
parser->content_length = ch - '0';
parser->content_length *= 10;
parser->content_length += ch - '0';
break;
case h_connection:
/* looking for 'Connection: keep-alive' */
if (c == 'k') {
header_state = h_connection_K;
/* looking for 'Connection: close' */
} else if (c == 'c') {
header_state = h_connection_C;
} else {
header_state = h_general;
/* Transfer-Encoding: chunked */
case h_encoding_C:
header_state = (c == 'h' ? h_encoding_CH : h_general);
break;
case h_encoding_CH:
header_state = (c == 'u' ? h_encoding_CHU : h_general);
break;
case h_encoding_CHU:
header_state = (c == 'n' ? h_encoding_CHUN : h_general);
break;
case h_encoding_CHUN:
header_state = (c == 'k' ? h_encoding_CHUNK : h_general);
break;
case h_encoding_CHUNK:
header_state = (c == 'e' ? h_encoding_CHUNKE : h_general);
break;
case h_encoding_CHUNKE:
if (c == 'd') {
parser->flags |= F_CHUNKED;
header_state = h_encoding_CHUNKED;
}
break;
case h_encoding_CHUNKED:
if (ch != ' ') return ERROR;
break;
/* looking for 'Connection: keep-alive' */
/* looking for 'Connection: close' */
case h_connection_K:
case h_connection_C:
header_state = h_general;
break;
default:
state = s_header_value;
@ -838,16 +996,39 @@ size_t http_parser_execute (http_parser *parser, const char *data, size_t len)
break;
}
case s_header_value:
{
break;
}
case s_header_almost_done:
if (ch != LF) return ERROR;
state = s_header_field_start;
break;
case s_headers_almost_done:
if (ch != LF) return ERROR;
CALLBACK2(headers_complete);
if (parser->flags & F_CHUNKED) {
state = s_body_chunked;
} else {
if (parser->content_length == 0) {
CALLBACK2(message_complete);
state = s_start;
} else if (parser->content_length < 0) {
state = s_body_identity_eof;
} else {
state = s_body_identity;
}
}
break;
/* read until EOF */
case s_body_identity_eof:
break;
case s_body_identity:
break;
case s_body_chunked:
break;
default:
assert(0 && "unhandled state");
return ERROR;

@ -64,7 +64,6 @@ struct http_parser {
int http_major;
int http_minor;
short keep_alive;
ssize_t content_length;
/** PUBLIC **/
@ -94,9 +93,8 @@ void http_parser_init (http_parser *parser, enum http_parser_type);
size_t http_parser_execute (http_parser *parser, const char *data, size_t len);
/*
#if 0
int http_parser_has_error (http_parser *parser);
*/
static inline int
http_parser_should_keep_alive (http_parser *parser)
@ -104,6 +102,7 @@ http_parser_should_keep_alive (http_parser *parser)
if (parser->keep_alive == -1) return (parser->http_major == 1 && parser->http_minor == 1);
return parser->keep_alive;
}
#endif
#ifdef __cplusplus

@ -38,7 +38,7 @@ struct message {
const char *name; // for debugging purposes
const char *raw;
enum http_parser_type type;
int method;
enum http_method method;
int status_code;
char request_path[MAX_ELEMENT_SIZE];
char request_uri[MAX_ELEMENT_SIZE];
@ -145,8 +145,8 @@ const struct message requests[] =
,.query_string= "page=1"
,.fragment= "posts-17408"
,.request_path= "/forums/1/topics/2375"
/* XXX request uri does not include fragment? */
,.request_uri= "/forums/1/topics/2375?page=1"
/* XXX request uri does include fragment? */
,.request_uri= "/forums/1/topics/2375?page=1#posts-17408"
,.num_headers= 0
,.body= ""
}
@ -739,7 +739,7 @@ test_error (const char *buf)
parsed = http_parser_execute(&parser, NULL, 0);
if (parsed != 0) return 1;
printf("No error found in the following: %s\n", buf);
printf("\n*** Error expected but none found ***\n\n%s", buf);
exit(1);
return 0;
@ -895,6 +895,10 @@ main (void)
"\r\n";
test_error(dumbfuck2);
#if 0
// NOTE(Wed Nov 18 11:57:27 CET 2009) this seems okay. we just read body
// until EOF.
//
// no content-length
// error if there is a body without content length
const char *bad_get_no_headers_no_body = "GET /bad_get_no_headers_no_body/world HTTP/1.1\r\n"
@ -902,7 +906,7 @@ main (void)
"\r\n"
"HELLO";
test_error(bad_get_no_headers_no_body);
#endif
/* TODO sending junk and large headers gets rejected */

Loading…
Cancel
Save