Created
June 27, 2014 12:57
-
-
Save indutny/3403029c5815fd0799ee to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
commit c732e933f995b42317e63875bfaa6907085a5811 | |
Author: James M Snell <jasnell@gmail.com> | |
Date: Wed Aug 21 14:39:54 2013 -0700 | |
Support for unknown extension methods in the http parser.. implemented | |
in a way that does not slow the parser down. | |
Conflicts: | |
.gitignore | |
http_parser.h | |
test.c | |
diff --git a/http_parser.c b/http_parser.c | |
index 69dea71..3e58f27 100644 | |
--- a/http_parser.c | |
+++ b/http_parser.c | |
@@ -57,6 +57,10 @@ do { \ | |
} while(0) | |
+/* Just for consistency with old API */ | |
+static const int HPE_CB_unknown_method = HPE_INVALID_METHOD; | |
+ | |
+ | |
/* Run the notify callback FOR, returning ER if it fails */ | |
#define CALLBACK_NOTIFY_(FOR, ER) \ | |
do { \ | |
@@ -583,6 +587,7 @@ size_t http_parser_execute (http_parser *parser, | |
const char *p = data; | |
const char *header_field_mark = 0; | |
const char *header_value_mark = 0; | |
+ const char *unknown_method_mark = 0; | |
const char *url_mark = 0; | |
const char *body_mark = 0; | |
const char *status_mark = 0; | |
@@ -618,6 +623,8 @@ size_t http_parser_execute (http_parser *parser, | |
header_field_mark = data; | |
if (parser->state == s_header_value) | |
header_value_mark = data; | |
+ if (parser->state == s_start_req) | |
+ unknown_method_mark = data; | |
switch (parser->state) { | |
case s_req_path: | |
case s_req_schema: | |
@@ -912,7 +919,7 @@ size_t http_parser_execute (http_parser *parser, | |
parser->flags = 0; | |
parser->content_length = ULLONG_MAX; | |
- if (!IS_ALPHA(ch)) { | |
+ if (!TOKEN(ch)) { | |
SET_ERRNO(HPE_INVALID_METHOD); | |
goto error; | |
} | |
@@ -936,8 +943,7 @@ size_t http_parser_execute (http_parser *parser, | |
case 'T': parser->method = HTTP_TRACE; break; | |
case 'U': parser->method = HTTP_UNLOCK; /* or UNSUBSCRIBE */ break; | |
default: | |
- SET_ERRNO(HPE_INVALID_METHOD); | |
- goto error; | |
+ parser->method = HTTP_unknown; | |
} | |
parser->state = s_req_method; | |
@@ -955,8 +961,18 @@ size_t http_parser_execute (http_parser *parser, | |
} | |
matcher = method_strings[parser->method]; | |
- if (ch == ' ' && matcher[parser->index] == '\0') { | |
+ | |
+ if (!TOKEN(ch) && ch != ' ') { | |
+ SET_ERRNO(HPE_INVALID_METHOD); | |
+ goto error; | |
+ } | |
+ | |
+ if (ch == ' ' && | |
+ (parser->method == HTTP_unknown || | |
+ matcher[parser->index] == '\0')) { | |
parser->state = s_req_spaces_before_url; | |
+ } else if (parser->method == HTTP_unknown) { | |
+ ; /* nada */ | |
} else if (ch == matcher[parser->index]) { | |
; /* nada */ | |
} else if (parser->method == HTTP_CONNECT) { | |
@@ -965,8 +981,7 @@ size_t http_parser_execute (http_parser *parser, | |
} else if (parser->index == 2 && ch == 'P') { | |
parser->method = HTTP_COPY; | |
} else { | |
- SET_ERRNO(HPE_INVALID_METHOD); | |
- goto error; | |
+ parser->method = HTTP_unknown; | |
} | |
} else if (parser->method == HTTP_MKCOL) { | |
if (parser->index == 1 && ch == 'O') { | |
@@ -980,15 +995,13 @@ size_t http_parser_execute (http_parser *parser, | |
} else if (parser->index == 3 && ch == 'A') { | |
parser->method = HTTP_MKCALENDAR; | |
} else { | |
- SET_ERRNO(HPE_INVALID_METHOD); | |
- goto error; | |
+ parser->method = HTTP_unknown; | |
} | |
} else if (parser->method == HTTP_SUBSCRIBE) { | |
if (parser->index == 1 && ch == 'E') { | |
parser->method = HTTP_SEARCH; | |
} else { | |
- SET_ERRNO(HPE_INVALID_METHOD); | |
- goto error; | |
+ parser->method = HTTP_unknown; | |
} | |
} else if (parser->index == 1 && parser->method == HTTP_POST) { | |
if (ch == 'R') { | |
@@ -998,33 +1011,28 @@ size_t http_parser_execute (http_parser *parser, | |
} else if (ch == 'A') { | |
parser->method = HTTP_PATCH; | |
} else { | |
- SET_ERRNO(HPE_INVALID_METHOD); | |
- goto error; | |
+ parser->method = HTTP_unknown; | |
} | |
} else if (parser->index == 2) { | |
if (parser->method == HTTP_PUT) { | |
if (ch == 'R') { | |
parser->method = HTTP_PURGE; | |
} else { | |
- SET_ERRNO(HPE_INVALID_METHOD); | |
- goto error; | |
+ parser->method = HTTP_unknown; | |
} | |
} else if (parser->method == HTTP_UNLOCK) { | |
if (ch == 'S') { | |
parser->method = HTTP_UNSUBSCRIBE; | |
} else { | |
- SET_ERRNO(HPE_INVALID_METHOD); | |
- goto error; | |
+ parser->method = HTTP_unknown; | |
} | |
} else { | |
- SET_ERRNO(HPE_INVALID_METHOD); | |
- goto error; | |
+ parser->method = HTTP_unknown; | |
} | |
} else if (parser->index == 4 && parser->method == HTTP_PROPFIND && ch == 'P') { | |
parser->method = HTTP_PROPPATCH; | |
} else { | |
- SET_ERRNO(HPE_INVALID_METHOD); | |
- goto error; | |
+ parser->method = HTTP_unknown; | |
} | |
++parser->index; | |
@@ -1033,6 +1041,21 @@ size_t http_parser_execute (http_parser *parser, | |
case s_req_spaces_before_url: | |
{ | |
+ // Check if the method is unknown, if it is, send it to the | |
+ // unknown method callback handler (if set). If that callback | |
+ // returns != 0, specify an HPE_INVALID_METHOD error. Otherwise, | |
+ // just accept it and keep going. | |
+ if (parser->method == HTTP_unknown) { | |
+ if (settings->on_unknown_method) { | |
+ CALLBACK_DATA_(unknown_method, | |
+ p - unknown_method_mark - 1, | |
+ p - data); | |
+ } else { | |
+ SET_ERRNO(HPE_INVALID_METHOD); | |
+ goto error; | |
+ } | |
+ } | |
+ | |
if (ch == ' ') break; | |
MARK(url); | |
diff --git a/http_parser.h b/http_parser.h | |
index 3ba4c7e..90dbcf9 100644 | |
--- a/http_parser.h | |
+++ b/http_parser.h | |
@@ -119,6 +119,7 @@ typedef int (*http_cb) (http_parser*); | |
XX(25, PURGE, PURGE) \ | |
/* CalDAV */ \ | |
XX(26, MKCALENDAR, MKCALENDAR) \ | |
+ XX(99, unknown, unknown) \ | |
enum http_method | |
{ | |
@@ -239,6 +240,7 @@ struct http_parser_settings { | |
http_cb on_headers_complete; | |
http_data_cb on_body; | |
http_cb on_message_complete; | |
+ http_data_cb on_unknown_method; | |
}; | |
diff --git a/test.c b/test.c | |
index 9799dc6..b4e6c03 100644 | |
--- a/test.c | |
+++ b/test.c | |
@@ -50,6 +50,7 @@ struct message { | |
enum http_parser_type type; | |
enum http_method method; | |
int status_code; | |
+ char method_string[MAX_ELEMENT_SIZE]; | |
char response_status[MAX_ELEMENT_SIZE]; | |
char request_path[MAX_ELEMENT_SIZE]; | |
char request_url[MAX_ELEMENT_SIZE]; | |
@@ -1619,6 +1620,27 @@ request_url_cb (http_parser *p, const char *buf, size_t len) | |
} | |
int | |
+status_complete_cb (http_parser *p) { | |
+ assert(p == parser); | |
+ p->data++; | |
+ return 0; | |
+} | |
+ | |
+int | |
+unknown_method_cb (http_parser *p, const char *buf, size_t len) | |
+{ | |
+ assert(p == parser); | |
+ strlncat(messages[num_messages].method_string, | |
+ sizeof(messages[num_messages].method_string), | |
+ buf, | |
+ len); | |
+ // fprintf(stderr, messages[num_messages].method_string | |
+ // return 0 if the unknown method is handled by the callback, | |
+ // otherwise return -1 to cause an HPE_INVALID_METHOD | |
+ return 0; | |
+} | |
+ | |
+int | |
header_field_cb (http_parser *p, const char *buf, size_t len) | |
{ | |
assert(p == parser); | |
@@ -1910,6 +1932,7 @@ static http_parser_settings settings_pause = | |
,.on_body = pause_body_cb | |
,.on_headers_complete = pause_headers_complete_cb | |
,.on_message_complete = pause_message_complete_cb | |
+ ,.on_unknown_method = unknown_method_cb | |
}; | |
static http_parser_settings settings = | |
@@ -1921,6 +1944,7 @@ static http_parser_settings settings = | |
,.on_body = body_cb | |
,.on_headers_complete = headers_complete_cb | |
,.on_message_complete = message_complete_cb | |
+ ,.on_unknown_method = unknown_method_cb | |
}; | |
static http_parser_settings settings_count_body = | |
@@ -1932,6 +1956,7 @@ static http_parser_settings settings_count_body = | |
,.on_body = count_body_cb | |
,.on_headers_complete = headers_complete_cb | |
,.on_message_complete = message_complete_cb | |
+ ,.on_unknown_method = unknown_method_cb | |
}; | |
static http_parser_settings settings_null = | |
@@ -1943,6 +1968,7 @@ static http_parser_settings settings_null = | |
,.on_body = 0 | |
,.on_headers_complete = 0 | |
,.on_message_complete = 0 | |
+ ,.on_unknown_method = 0 | |
}; | |
void | |
@@ -3456,7 +3482,9 @@ main (void) | |
test_simple(buf, HPE_OK); | |
} | |
- static const char *bad_methods[] = { | |
+ static const char *ext_methods[] = { | |
+ "LINK", | |
+ "UNLINK", | |
"ASDF", | |
"C******", | |
"COLA", | |
@@ -3468,8 +3496,23 @@ main (void) | |
"PUN", | |
"PX", | |
"SA", | |
- "hello world", | |
- 0 }; | |
+ "+1", | |
+ 0 | |
+ }; | |
+ | |
+ for (this_method = ext_methods; *this_method; this_method++) { | |
+ char buf[200]; | |
+ sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method); | |
+ test_simple(buf, HPE_OK); | |
+ } | |
+ | |
+ static const char *bad_methods[] = { | |
+ "JO]", | |
+ "GE]", | |
+ "[GET]", | |
+ 0 | |
+ }; | |
+ | |
for (this_method = bad_methods; *this_method; this_method++) { | |
char buf[200]; | |
sprintf(buf, "%s / HTTP/1.1\r\n\r\n", *this_method); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment