Skip to content

Instantly share code, notes, and snippets.

@indutny
Created June 27, 2014 12:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save indutny/3403029c5815fd0799ee to your computer and use it in GitHub Desktop.
Save indutny/3403029c5815fd0799ee to your computer and use it in GitHub Desktop.
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