diff --git a/http-internal.h b/http-internal.h index 932f1c03..5b3f6397 100644 --- a/http-internal.h +++ b/http-internal.h @@ -145,6 +145,9 @@ struct evhttp { size_t default_max_headers_size; ev_uint64_t default_max_body_size; + /* bitmask of all allowed methods */ + short allowed_methods; + /* Fallback callback if all the other callbacks for this connection don't match. */ void (*gencb)(struct evhttp_request *req, void *); diff --git a/http.c b/http.c index d3d82fe7..70e0d13d 100644 --- a/http.c +++ b/http.c @@ -288,6 +288,18 @@ evhttp_method(enum evhttp_cmd_type type) case EVHTTP_REQ_DELETE: method = "DELETE"; break; + case EVHTTP_REQ_OPTIONS: + method = "OPTIONS"; + break; + case EVHTTP_REQ_TRACE: + method = "TRACE"; + break; + case EVHTTP_REQ_CONNECT: + method = "CONNECT"; + break; + case EVHTTP_REQ_PATCH: + method = "PATCH"; + break; default: method = NULL; break; @@ -1379,6 +1391,12 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line) req->type = EVHTTP_REQ_PUT; } else if (strcmp(method, "DELETE") == 0) { req->type = EVHTTP_REQ_DELETE; + } else if (strcmp(method, "OPTIONS") == 0) { + req->type = EVHTTP_REQ_OPTIONS; + } else if (strcmp(method, "TRACE") == 0) { + req->type = EVHTTP_REQ_TRACE; + } else if (strcmp(method, "PATCH") == 0) { + req->type = EVHTTP_REQ_PATCH; } else { event_debug(("%s: bad method %s on request %p from %s", __func__, method, req, req->remote_host)); @@ -2668,11 +2686,17 @@ evhttp_handle_request(struct evhttp_request *req, void *arg) /* we have a new request on which the user needs to take action */ req->userdone = 0; - if (req->uri == NULL) { + if (req->type == 0 || req->uri == NULL) { evhttp_send_error(req, HTTP_BADREQUEST, NULL); return; } + if ((http->allowed_methods & req->type) == 0) { + event_debug(("Rejecting disallowed method %d (allowed: %d)\n", req->type, http->allowed_methods)); + evhttp_send_error(req, HTTP_BADMETHOD, NULL); + return; + } + /* handle potential virtual hosts */ hostname = evhttp_find_header(req->input_headers, "Host"); if (hostname != NULL) { @@ -2858,6 +2882,11 @@ evhttp_new_object(void) http->timeout = -1; evhttp_set_max_headers_size(http, EV_SIZE_MAX); evhttp_set_max_body_size(http, EV_SIZE_MAX); + evhttp_set_allowed_methods(http, EVHTTP_REQ_GET | + EVHTTP_REQ_POST | + EVHTTP_REQ_HEAD | + EVHTTP_REQ_PUT | + EVHTTP_REQ_DELETE); TAILQ_INIT(&http->sockets); TAILQ_INIT(&http->callbacks); @@ -2990,6 +3019,12 @@ evhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size) http->default_max_body_size = max_body_size; } +void +evhttp_set_allowed_methods(struct evhttp* http, short methods) +{ + http->allowed_methods = methods; +} + int evhttp_set_cb(struct evhttp *http, const char *uri, void (*cb)(struct evhttp_request *, void *), void *cbarg) diff --git a/include/event2/http.h b/include/event2/http.h index e0fdefe0..45d3b245 100644 --- a/include/event2/http.h +++ b/include/event2/http.h @@ -57,6 +57,7 @@ struct event_base; #define HTTP_NOTMODIFIED 304 /**< page was not modified from last */ #define HTTP_BADREQUEST 400 /**< invalid http request was made */ #define HTTP_NOTFOUND 404 /**< could not find content for uri */ +#define HTTP_BADMETHOD 405 /**< method not allowed */ #define HTTP_SERVUNAVAIL 503 /**< the server is not available */ struct evhttp; @@ -183,6 +184,17 @@ void evhttp_set_max_headers_size(struct evhttp* http, ev_ssize_t max_headers_siz /** XXX Document. */ void evhttp_set_max_body_size(struct evhttp* http, ev_ssize_t max_body_size); +/** + Sets the what HTTP methods are supported in requests accepted by this server. + If not supported they will generate a "405 Method not allowed" response. + + By default this includes the following methods: GET, POST, HEAD, PUT, DELETE + + @param http the http server on which to set the methods + @param methods bit mask constructed from evhttp_cmd_type values +*/ +void evhttp_set_allowed_methods(struct evhttp* http, short methods); + /** Set a callback for a specified URI @@ -329,11 +341,15 @@ void evhttp_send_reply_end(struct evhttp_request *req); /** the different request types supported by evhttp */ enum evhttp_cmd_type { - EVHTTP_REQ_GET, - EVHTTP_REQ_POST, - EVHTTP_REQ_HEAD, - EVHTTP_REQ_PUT, - EVHTTP_REQ_DELETE + EVHTTP_REQ_GET = 1 << 0, + EVHTTP_REQ_POST = 1 << 1, + EVHTTP_REQ_HEAD = 1 << 2, + EVHTTP_REQ_PUT = 1 << 3, + EVHTTP_REQ_DELETE = 1 << 4, + EVHTTP_REQ_OPTIONS = 1 << 5, + EVHTTP_REQ_TRACE = 1 << 6, + EVHTTP_REQ_CONNECT = 1 << 7, + EVHTTP_REQ_PATCH = 1 << 8 }; /** a request object can represent either a request or a reply */