Define enumerators for all HTTP methods, including PATCH from RFC5789

This patch defines enumerators for all HTTP methods that exist
(including PATCH introduced in RFC 5789).

It also makes them bit-masky (that's not a word, is it?), breaking
binary- but not source-code compatibility.

evhttp now stores a bitmask specifying for which methods requests to
dispatch and which ones to reject with "405 Method Not Allowed".

By default that's the ones we currently have (GET, POST, HEAD, PUT,
DELETE), thereby keeping functional compatibility (besides the minor
change that one of the other methods will now cause 405 instead of
400. But I believe that could even be considered a bug-fix).

evhttp is extended by evhttp_set_allowed_methods() with which the
user can change that bitmask.

no regressions here and my test-app still works. Haven't yet
actually tested any of the new methods.

What's obviously missing here is the special logic for the methods:

OPTIONS: We should be fine here - I believe our current dispatch
logic should work fine. Some convenience functions would be fine
though.

TRACE: I'm pretty certain we should never dispatch this to the
callbacks and simply implement the necessary functionality built-in.

CONNECT: Pretty straight-forward to implement (and considering the
framework in which we implement it very efficient too). Should
probably go built-in.

PATCH: Except for checking the RFC against our pre-dispatch logic
(there just might be some "MUST not have Some-Header" lurking
somewhere) there is nothing to be done here, this is completely up
to the user. Nothing to do.
This commit is contained in:
Felix Nawothnig 2010-11-04 11:25:35 -04:00 committed by Nick Mathewson
parent 78762383b7
commit 75a73414a4
3 changed files with 60 additions and 6 deletions

View File

@ -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 *);

37
http.c
View File

@ -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)

View File

@ -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 */