From d7b938bdca9c683800c9aaa4530c082f1e28b5ee Mon Sep 17 00:00:00 2001 From: Patrik Stutz Date: Thu, 17 Jan 2013 08:38:34 -0600 Subject: [PATCH] Parse and emit status message of response --- http_parser.c | 35 ++++++++++++++++--- http_parser.h | 4 +-- test.c | 95 ++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 104 insertions(+), 30 deletions(-) diff --git a/http_parser.c b/http_parser.c index 9695525..b9dabb8 100644 --- a/http_parser.c +++ b/http_parser.c @@ -248,6 +248,7 @@ enum state , s_res_http_minor , s_res_first_status_code , s_res_status_code + , s_res_status_start , s_res_status , s_res_line_almost_done @@ -581,6 +582,7 @@ size_t http_parser_execute (http_parser *parser, const char *header_value_mark = 0; const char *url_mark = 0; const char *body_mark = 0; + const char *status_mark = 0; /* We're in an error state. Don't bother doing anything. */ if (HTTP_PARSER_ERRNO(parser) != HPE_OK) { @@ -627,6 +629,9 @@ size_t http_parser_execute (http_parser *parser, case s_req_fragment: url_mark = data; break; + case s_res_status: + status_mark = data; + break; } for (p=data; p != data + len; p++) { @@ -833,7 +838,7 @@ size_t http_parser_execute (http_parser *parser, if (!IS_NUM(ch)) { switch (ch) { case ' ': - parser->state = s_res_status; + parser->state = s_res_status_start; break; case CR: parser->state = s_res_line_almost_done; @@ -859,24 +864,42 @@ size_t http_parser_execute (http_parser *parser, break; } + case s_res_status_start: + { + if (ch == CR) { + parser->state = s_res_line_almost_done; + break; + } + + if (ch == LF) { + parser->state = s_header_field_start; + break; + } + + MARK(status); + parser->state = s_res_status; + parser->index = 0; + break; + } + case s_res_status: - /* the human readable status. e.g. "NOT FOUND" - * we are not humans so just ignore this */ if (ch == CR) { parser->state = s_res_line_almost_done; + CALLBACK_DATA(status); break; } if (ch == LF) { parser->state = s_header_field_start; + CALLBACK_DATA(status); break; } + break; case s_res_line_almost_done: STRICT_CHECK(ch != LF); parser->state = s_header_field_start; - CALLBACK_NOTIFY(status_complete); break; case s_start_req: @@ -1854,12 +1877,14 @@ size_t http_parser_execute (http_parser *parser, assert(((header_field_mark ? 1 : 0) + (header_value_mark ? 1 : 0) + (url_mark ? 1 : 0) + - (body_mark ? 1 : 0)) <= 1); + (body_mark ? 1 : 0) + + (status_mark ? 1 : 0)) <= 1); CALLBACK_DATA_NOADVANCE(header_field); CALLBACK_DATA_NOADVANCE(header_value); CALLBACK_DATA_NOADVANCE(url); CALLBACK_DATA_NOADVANCE(body); + CALLBACK_DATA_NOADVANCE(status); return len; diff --git a/http_parser.h b/http_parser.h index 4810cdc..a1ab446 100644 --- a/http_parser.h +++ b/http_parser.h @@ -143,13 +143,13 @@ enum flags \ /* Callback-related errors */ \ XX(CB_message_begin, "the on_message_begin callback failed") \ - XX(CB_status_complete, "the on_status_complete callback failed") \ XX(CB_url, "the on_url callback failed") \ XX(CB_header_field, "the on_header_field callback failed") \ XX(CB_header_value, "the on_header_value callback failed") \ XX(CB_headers_complete, "the on_headers_complete callback failed") \ XX(CB_body, "the on_body callback failed") \ XX(CB_message_complete, "the on_message_complete callback failed") \ + XX(CB_status, "the on_status callback failed") \ \ /* Parsing-related errors */ \ XX(INVALID_EOF_STATE, "stream ended at an unexpected time") \ @@ -224,7 +224,7 @@ struct http_parser { struct http_parser_settings { http_cb on_message_begin; http_data_cb on_url; - http_cb on_status_complete; + http_data_cb on_status; http_data_cb on_header_field; http_data_cb on_header_value; http_cb on_headers_complete; diff --git a/test.c b/test.c index 656bc9f..18e081e 100644 --- a/test.c +++ b/test.c @@ -44,6 +44,7 @@ struct message { enum http_parser_type type; enum http_method method; int status_code; + char response_status[MAX_ELEMENT_SIZE]; char request_path[MAX_ELEMENT_SIZE]; char request_url[MAX_ELEMENT_SIZE]; char fragment[MAX_ELEMENT_SIZE]; @@ -927,6 +928,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 301 + ,.response_status= "Moved Permanently" ,.num_headers= 8 ,.headers= { { "Location", "http://www.google.com/" } @@ -975,6 +977,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 5 ,.headers= { { "Date", "Tue, 04 Aug 2009 07:59:32 GMT" } @@ -1003,6 +1006,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 404 + ,.response_status= "Not Found" ,.num_headers= 0 ,.headers= {} ,.body_size= 0 @@ -1018,6 +1022,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 301 + ,.response_status= "" ,.num_headers= 0 ,.headers= {} ,.body= "" @@ -1043,6 +1048,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 2 ,.headers= { {"Content-Type", "text/plain" } @@ -1068,6 +1074,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 2 ,.headers= { {"Content-Type", "text/html; charset=utf-8" } @@ -1091,6 +1098,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 4 ,.headers= { {"Content-Type", "text/html; charset=UTF-8" } @@ -1116,6 +1124,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 4 ,.headers= { {"Server", "DCLK-AdSvr" } @@ -1148,6 +1157,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 0 ,.status_code= 301 + ,.response_status= "Moved Permanently" ,.num_headers= 9 ,.headers= { { "Date", "Thu, 03 Jun 2010 09:56:32 GMT" } @@ -1186,6 +1196,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 11 ,.headers= { { "Date", "Tue, 28 Sep 2010 01:14:13 GMT" } @@ -1217,6 +1228,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 500 + ,.response_status= "Oriƫntatieprobleem" ,.num_headers= 3 ,.headers= { { "Date", "Fri, 5 Nov 2010 23:07:12 GMT+2" } @@ -1237,6 +1249,7 @@ const struct message responses[] = ,.http_major= 0 ,.http_minor= 9 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 0 ,.headers= {} @@ -1259,6 +1272,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 1 ,.headers= { { "Content-Type", "text/plain" } @@ -1277,6 +1291,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 0 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 1 ,.headers= { { "Connection", "keep-alive" } @@ -1296,6 +1311,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 0 ,.status_code= 204 + ,.response_status= "No content" ,.num_headers= 1 ,.headers= { { "Connection", "keep-alive" } @@ -1314,6 +1330,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 0 ,.headers={} ,.body_size= 0 @@ -1330,6 +1347,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 204 + ,.response_status= "No content" ,.num_headers= 0 ,.headers={} ,.body_size= 0 @@ -1347,6 +1365,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 204 + ,.response_status= "No content" ,.num_headers= 1 ,.headers= { { "Connection", "close" } @@ -1368,6 +1387,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 1 ,.headers= { { "Transfer-Encoding", "chunked" } @@ -1396,6 +1416,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 7 ,.headers= { { "Server", "Microsoft-IIS/6.0" } @@ -1433,6 +1454,7 @@ const struct message responses[] = ,.http_major= 1 ,.http_minor= 1 ,.status_code= 301 + ,.response_status= "MovedPermanently" ,.num_headers= 9 ,.headers= { { "Date", "Wed, 15 May 2013 17:06:33 GMT" } , { "Server", "Server" } @@ -1447,6 +1469,22 @@ const struct message responses[] = ,.body= "\n" } +#define EMPTY_REASON_PHRASE_AFTER_SPACE 20 +, {.name= "empty reason phrase after space" + ,.type= HTTP_RESPONSE + ,.raw= "HTTP/1.1 200 \r\n" + "\r\n" + ,.should_keep_alive= FALSE + ,.message_complete_on_eof= TRUE + ,.http_major= 1 + ,.http_minor= 1 + ,.status_code= 200 + ,.response_status= "" + ,.num_headers= 0 + ,.headers= {} + ,.body= "" + } + , {.name= NULL } /* sentinel */ }; @@ -1528,13 +1566,6 @@ request_url_cb (http_parser *p, const char *buf, size_t len) return 0; } -int -status_complete_cb (http_parser *p) { - assert(p == parser); - p->data++; - return 0; -} - int header_field_cb (http_parser *p, const char *buf, size_t len) { @@ -1660,6 +1691,17 @@ message_complete_cb (http_parser *p) return 0; } +int +response_status_cb (http_parser *p, const char *buf, size_t len) +{ + assert(p == parser); + strlncat(messages[num_messages].response_status, + sizeof(messages[num_messages].response_status), + buf, + len); + return 0; +} + /* These dontcall_* callbacks exist so that we can verify that when we're * paused, no additional callbacks are invoked */ int @@ -1720,11 +1762,20 @@ dontcall_message_complete_cb (http_parser *p) abort(); } +int +dontcall_response_status_cb (http_parser *p, const char *buf, size_t len) +{ + if (p || buf || len) { } // gcc + fprintf(stderr, "\n\n*** on_status() called on paused parser ***\n\n"); + abort(); +} + static http_parser_settings settings_dontcall = {.on_message_begin = dontcall_message_begin_cb ,.on_header_field = dontcall_header_field_cb ,.on_header_value = dontcall_header_value_cb ,.on_url = dontcall_request_url_cb + ,.on_status = dontcall_response_status_cb ,.on_body = dontcall_body_cb ,.on_headers_complete = dontcall_headers_complete_cb ,.on_message_complete = dontcall_message_complete_cb @@ -1790,11 +1841,20 @@ pause_message_complete_cb (http_parser *p) return message_complete_cb(p); } +int +pause_response_status_cb (http_parser *p, const char *buf, size_t len) +{ + http_parser_pause(p, 1); + *current_pause_parser = settings_dontcall; + return response_status_cb(p, buf, len); +} + static http_parser_settings settings_pause = {.on_message_begin = pause_message_begin_cb ,.on_header_field = pause_header_field_cb ,.on_header_value = pause_header_value_cb ,.on_url = pause_request_url_cb + ,.on_status = pause_response_status_cb ,.on_body = pause_body_cb ,.on_headers_complete = pause_headers_complete_cb ,.on_message_complete = pause_message_complete_cb @@ -1805,6 +1865,7 @@ static http_parser_settings settings = ,.on_header_field = header_field_cb ,.on_header_value = header_value_cb ,.on_url = request_url_cb + ,.on_status = response_status_cb ,.on_body = body_cb ,.on_headers_complete = headers_complete_cb ,.on_message_complete = message_complete_cb @@ -1815,6 +1876,7 @@ static http_parser_settings settings_count_body = ,.on_header_field = header_field_cb ,.on_header_value = header_value_cb ,.on_url = request_url_cb + ,.on_status = response_status_cb ,.on_body = count_body_cb ,.on_headers_complete = headers_complete_cb ,.on_message_complete = message_complete_cb @@ -1825,6 +1887,7 @@ static http_parser_settings settings_null = ,.on_header_field = 0 ,.on_header_value = 0 ,.on_url = 0 + ,.on_status = 0 ,.on_body = 0 ,.on_headers_complete = 0 ,.on_message_complete = 0 @@ -1948,6 +2011,7 @@ message_eq (int index, const struct message *expected) MESSAGE_CHECK_NUM_EQ(expected, m, method); } else { MESSAGE_CHECK_NUM_EQ(expected, m, status_code); + MESSAGE_CHECK_STR_EQ(expected, m, response_status); } MESSAGE_CHECK_NUM_EQ(expected, m, should_keep_alive); @@ -3133,20 +3197,6 @@ create_large_chunked_message (int body_size_in_kb, const char* headers) return buf; } -void -test_status_complete (void) -{ - parser_init(HTTP_RESPONSE); - parser->data = 0; - http_parser_settings settings = settings_null; - settings.on_status_complete = status_complete_cb; - - char *response = "don't mind me, just a simple response"; - http_parser_execute(parser, &settings, response, strlen(response)); - assert(parser->data == (void*)0); // the status_complete callback was never called - assert(parser->http_errno == HPE_INVALID_CONSTANT); // the errno for an invalid status line -} - /* Verify that we can pause parsing at any of the bytes in the * message and still get the result that we're expecting. */ void @@ -3280,6 +3330,7 @@ main (void) ,.http_major= 1 ,.http_minor= 0 ,.status_code= 200 + ,.response_status= "OK" ,.num_headers= 2 ,.headers= { { "Transfer-Encoding", "chunked" } @@ -3468,8 +3519,6 @@ main (void) , &requests[CONNECT_REQUEST] ); - test_status_complete(); - puts("requests okay"); return 0;