]> granicus.if.org Git - libevent/commitdiff
Added http method extending
authorThomas Bernard <miniupnp@free.fr>
Fri, 8 Jan 2016 21:36:20 +0000 (13:36 -0800)
committerAzat Khuzhin <azat@libevent.org>
Thu, 11 Apr 2019 19:48:01 +0000 (22:48 +0300)
User can define his own response method by calling
evhttp_set_ext_method_cmp() on the struct http, or
evhttp_connection_set_ext_method_cmp() on the connection.

We expose a new stucture `evhttp_ext_method` which is passed to the
callback if it's set. So any field can be modified, with some exceptions
(in evhttp_method_):

If the cmp function is set, it has the ability to modify method, and
flags. Other fields will be ignored. Flags returned are OR'd with the
current flags.

Based on changes to the #282 from: Mark Ellzey <socket@gmail.com>

http-internal.h
http.c
include/event2/http.h
test/regress_http.c
whatsnew-2.2.txt

index 72016a01a351b312dcfc887ee74aba808068feac..3bf16b5b7eda816701439d51a0c6ab7a898d5ac0 100644 (file)
@@ -105,6 +105,8 @@ struct evhttp_connection {
        struct event_base *base;
        struct evdns_base *dns_base;
        int ai_family;
+
+       evhttp_ext_method_cb ext_method_cmp;
 };
 
 /* A callback for an http server */
@@ -175,6 +177,8 @@ struct evhttp {
        void *newreqcbarg;
 
        struct event_base *base;
+
+       evhttp_ext_method_cb ext_method_cmp;
 };
 
 /* XXX most of these functions could be static. */
diff --git a/http.c b/http.c
index 00267828eb3ccba6c7efbb4721ffb1941e3f10d1..ecb12bbffdf37588ec0d0da98aec3312489a1873 100644 (file)
--- a/http.c
+++ b/http.c
@@ -192,13 +192,15 @@ static void evhttp_get_request(struct evhttp *, evutil_socket_t, struct sockaddr
 static void evhttp_write_buffer(struct evhttp_connection *,
     void (*)(struct evhttp_connection *, void *), void *);
 static void evhttp_make_header(struct evhttp_connection *, struct evhttp_request *);
+static int evhttp_method_may_have_body_(struct evhttp_connection *, enum evhttp_cmd_type);
 
 /* callbacks for bufferevent */
 static void evhttp_read_cb(struct bufferevent *, void *);
 static void evhttp_write_cb(struct bufferevent *, void *);
 static void evhttp_error_cb(struct bufferevent *bufev, short what, void *arg);
-static int evhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp,
-                 const char *hostname);
+static int evhttp_find_vhost(struct evhttp *http, struct evhttp **outhttp, const char *hostname);
+static const char *evhttp_method_(struct evhttp_connection *evcon,
+       enum evhttp_cmd_type type, ev_uint16_t *flags);
 
 #ifndef EVENT__HAVE_STRSEP
 /* strsep replacement for platforms that lack it.  Only works if
@@ -296,12 +298,15 @@ evhttp_htmlescape(const char *html)
 }
 
 /** Given an evhttp_cmd_type, returns a constant string containing the
- * equivalent HTTP command, or NULL if the evhttp_command_type is
+ * equivalent HTTP command, or NULL if the evhttp_cmd_type is
  * unrecognized. */
 static const char *
-evhttp_method(enum evhttp_cmd_type type)
+evhttp_method_(struct evhttp_connection *evcon,
+              enum evhttp_cmd_type type, ev_uint16_t *flags)
 {
-       const char *method;
+       struct evhttp_ext_method ext_method;
+       const char *method    = NULL;
+       ev_uint16_t tmp_flags = EVHTTP_METHOD_HAS_BODY;
 
        switch (type) {
        case EVHTTP_REQ_GET:
@@ -312,6 +317,7 @@ evhttp_method(enum evhttp_cmd_type type)
                break;
        case EVHTTP_REQ_HEAD:
                method = "HEAD";
+               tmp_flags &= ~EVHTTP_METHOD_HAS_BODY;
                break;
        case EVHTTP_REQ_PUT:
                method = "PUT";
@@ -324,6 +330,7 @@ evhttp_method(enum evhttp_cmd_type type)
                break;
        case EVHTTP_REQ_TRACE:
                method = "TRACE";
+               tmp_flags &= ~EVHTTP_METHOD_HAS_BODY;
                break;
        case EVHTTP_REQ_CONNECT:
                method = "CONNECT";
@@ -353,10 +360,41 @@ evhttp_method(enum evhttp_cmd_type type)
                method = "MOVE";
                break;
        default:
-               method = NULL;
+               /* setup the structure to allow for the cmp.
+                *
+                * if the cmp function is set, it has the ability to
+                * modify method and flags. Other fields will be
+                * ignored.
+                *
+                * NOTE: the flags returned are OR'd with the current
+                *       flags.
+                */
+               tmp_flags = 0;
+               ext_method.method = NULL;
+               ext_method.type   = type;
+               ext_method.flags  = tmp_flags;
+
+               if (evcon->ext_method_cmp != NULL &&
+                       evcon->ext_method_cmp(&ext_method) == 0) {
+
+                       if (ext_method.type != type) {
+                               event_debug(("%s: callback modified type from %u to %u, not allowed",
+                                           __func__, type, ext_method.type));
+                               return NULL;
+                       }
+
+                       method     = ext_method.method;
+                       tmp_flags |= ext_method.flags;
+               }
+
                break;
        }
 
+       event_debug(("%s: type=%04x => '%s' flags=%04x",
+                    __func__, (int)type, method, tmp_flags));
+
+       if (flags)
+               *flags = tmp_flags;
        return (method);
 }
 
@@ -452,11 +490,12 @@ evhttp_make_header_request(struct evhttp_connection *evcon,
     struct evhttp_request *req)
 {
        const char *method;
+       ev_uint16_t flags;
 
        evhttp_remove_header(req->output_headers, "Proxy-Connection");
 
        /* Generate request line */
-       if (!(method = evhttp_method(req->type))) {
+       if (!(method = evhttp_method_(evcon, req->type, &flags))) {
                method = "NULL";
        }
 
@@ -464,9 +503,12 @@ evhttp_make_header_request(struct evhttp_connection *evcon,
            "%s %s HTTP/%d.%d\r\n",
            method, req->uri, req->major, req->minor);
 
-       /* Add the content length on a post or put request if missing */
-       if ((req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) &&
-           evhttp_find_header(req->output_headers, "Content-Length") == NULL){
+       /* Add the content length on a request if missing
+        * Always add it for POST and PUT requests as clients expect it */
+       if ((flags & EVHTTP_METHOD_HAS_BODY) &&
+           (evbuffer_get_length(req->output_buffer) > 0 ||
+            req->type == EVHTTP_REQ_POST || req->type == EVHTTP_REQ_PUT) &&
+           evhttp_find_header(req->output_headers, "Content-Length") == NULL) {
                char size[22];
                evutil_snprintf(size, sizeof(size), EV_SIZE_FMT,
                    EV_SIZE_ARG(evbuffer_get_length(req->output_buffer)));
@@ -1940,11 +1982,30 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line, size_t len)
                break;
        } /* switch */
 
+       if (!type) {
+               /* check extended methods, we only care about the
+                * type set by the cmp function if the cmp function
+                * returns a 0 value.
+                */
+               struct evhttp_ext_method ext_method;
+
+               ext_method.method = method;
+               ext_method.type = 0;
+
+               if (req->evcon->ext_method_cmp &&
+                   req->evcon->ext_method_cmp(&ext_method) == 0) {
+                       /* TODO: make sure the other fields in ext_method are
+                        * not changed by the callback.
+                        */
+                       type = ext_method.type;
+               }
+       }
+
        if (!type) {
                event_debug(("%s: bad method %s on request %p from %s",
-                       __func__, method, req, req->remote_host));
-               /* No error yet; we'll give a better error later when we see that
-                * req->type is unsupported in evhttp_handle_request(). */
+                           __func__, method, req, req->remote_host));
+               /* No error yet; we'll give a better error later when
+                * we see that req->type is unsupported. */
        }
 
        req->type = type;
@@ -2278,31 +2339,11 @@ evhttp_get_body_length(struct evhttp_request *req)
 }
 
 static int
-evhttp_method_may_have_body(enum evhttp_cmd_type type)
+evhttp_method_may_have_body_(struct evhttp_connection *evcon, enum evhttp_cmd_type type)
 {
-       switch (type) {
-       case EVHTTP_REQ_POST:
-       case EVHTTP_REQ_PUT:
-       case EVHTTP_REQ_PATCH:
-       case EVHTTP_REQ_PROPFIND:
-       case EVHTTP_REQ_PROPPATCH:
-       case EVHTTP_REQ_MKCOL:
-       case EVHTTP_REQ_LOCK:
-       case EVHTTP_REQ_UNLOCK:
-       case EVHTTP_REQ_COPY:
-       case EVHTTP_REQ_MOVE:
-
-       case EVHTTP_REQ_GET:
-       case EVHTTP_REQ_DELETE:
-       case EVHTTP_REQ_OPTIONS:
-       case EVHTTP_REQ_CONNECT:
-               return 1;
-
-       case EVHTTP_REQ_TRACE:
-       case EVHTTP_REQ_HEAD:
-       default:
-               return 0;
-       }
+       ev_uint16_t flags;
+       evhttp_method_(evcon, type, &flags);
+       return (flags & EVHTTP_METHOD_HAS_BODY) ? 1 : 0;
 }
 
 static void
@@ -2312,7 +2353,7 @@ evhttp_get_body(struct evhttp_connection *evcon, struct evhttp_request *req)
 
        /* If this is a request without a body, then we are done */
        if (req->kind == EVHTTP_REQUEST &&
-           !evhttp_method_may_have_body(req->type)) {
+           !evhttp_method_may_have_body_(evcon, req->type)) {
                evhttp_connection_done(evcon);
                return;
        }
@@ -2575,6 +2616,13 @@ int evhttp_connection_set_flags(struct evhttp_connection *evcon,
        return 0;
 }
 
+void
+evhttp_connection_set_ext_method_cmp(struct evhttp_connection *evcon,
+       evhttp_ext_method_cb cmp)
+{
+       evcon->ext_method_cmp = cmp;
+}
+
 void
 evhttp_connection_set_base(struct evhttp_connection *evcon,
     struct event_base *base)
@@ -4040,6 +4088,12 @@ evhttp_set_allowed_methods(struct evhttp* http, ev_uint32_t methods)
        http->allowed_methods = methods;
 }
 
+void
+evhttp_set_ext_method_cmp(struct evhttp *http, evhttp_ext_method_cb cmp)
+{
+       http->ext_method_cmp = cmp;
+}
+
 int
 evhttp_set_cb(struct evhttp *http, const char *uri,
     void (*cb)(struct evhttp_request *, void *), void *cbarg)
@@ -4476,6 +4530,7 @@ evhttp_get_request(struct evhttp *http, evutil_socket_t fd,
         * we need to know which http server it belongs to.
         */
        evcon->http_server = http;
+       evcon->ext_method_cmp = http->ext_method_cmp;
        TAILQ_INSERT_TAIL(&http->connections, evcon, next);
 
        if (evhttp_associate_new_request_with_connection(evcon) == -1)
index c9ec117520bfe98013789fafb55191f05097bee3..c5cd7bd081b409bd990db23a975bf20fcf4cefad 100644 (file)
@@ -73,6 +73,7 @@ struct evkeyvalq;
 struct evhttp_bound_socket;
 struct evconnlistener;
 struct evdns_base;
+struct evhttp_ext_method;
 
 /**
  * Create a new HTTP server.
@@ -248,6 +249,26 @@ void evhttp_set_default_content_type(struct evhttp *http,
 EVENT2_EXPORT_SYMBOL
 void evhttp_set_allowed_methods(struct evhttp* http, ev_uint32_t methods);
 
+typedef int (*evhttp_ext_method_cb)(struct evhttp_ext_method *);
+/**
+  Sets the callback function which allows HTTP extended methods
+  to be supported by this server.
+
+  The callback should :
+   - if method field is NULL : set method field according to type field
+   - else : set type and flags fields according to method string
+   - return 0 for success (known method / type)
+   - return -1 for error (unknown method / type)
+
+  evhttp_set_allowed_methods still needs to be called.
+
+  @param http the http server on which to add support to the methods
+  @param cmp the extended method callback
+  @see evhttp_ext_method
+*/
+EVENT2_EXPORT_SYMBOL
+void evhttp_set_ext_method_cmp(struct evhttp *http, evhttp_ext_method_cb cmp);
+
 /**
    Set a callback for a specified URI
 
@@ -552,6 +573,23 @@ enum evhttp_cmd_type {
        EVHTTP_REQ_MOVE    = 1 << 15,
 };
 
+#define EVHTTP_REQ_MAX EVHTTP_REQ_MOVE
+
+/**
+ * @brief stucture that is passed to (and modified by) the
+ * extended method callback function
+ *
+ * @see evhttp_set_ext_method_cmp
+ * @see evhttp_connection_set_ext_method_cmp
+ */
+struct evhttp_ext_method {
+       const char *method;
+       ev_uint32_t type;       /* @see enum evhttp_cmd_type */
+       ev_uint16_t flags;      /* Available flag : EVHTTP_METHOD_HAS_BODY */
+};
+
+#define EVHTTP_METHOD_HAS_BODY 0x0001
+
 /** a request object can represent either a request or a reply */
 enum evhttp_request_kind { EVHTTP_REQUEST, EVHTTP_RESPONSE };
 
@@ -742,6 +780,15 @@ void evhttp_request_own(struct evhttp_request *req);
 EVENT2_EXPORT_SYMBOL
 int evhttp_request_is_owned(struct evhttp_request *req);
 
+/**
+ * Sets extended method cmp callback for this http connection.
+ *
+ * @see evhttp_set_ext_method_cmp
+ */
+EVENT2_EXPORT_SYMBOL
+void evhttp_connection_set_ext_method_cmp(struct evhttp_connection *evcon,
+       evhttp_ext_method_cb cmp);
+
 /**
  * Returns the connection object associated with the request or NULL
  *
index fdffee018ca5b9b1fa7b3a21d62e4d3d2e76ef9b..226a9b64bb181670958c5224d01068cbe47eb721 100644 (file)
@@ -71,6 +71,29 @@ static struct event_base *exit_base;
 
 static char const BASIC_REQUEST_BODY[] = "This is funny";
 
+/* defines an extended HTTP method "CUSTOM"
+ * without body */
+#define EVHTTP_REQ_CUSTOM      ((EVHTTP_REQ_MAX) << 1)
+
+static int ext_method_cb(struct evhttp_ext_method *p)
+{
+       if (p == NULL)
+               return -1;
+       if (p->method) {
+               if (strcmp(p->method, "CUSTOM") == 0) {
+                       p->type = EVHTTP_REQ_CUSTOM;
+                       p->flags = 0;   /*EVHTTP_METHOD_HAS_BODY*/
+                       return 0;
+               }
+       } else {
+               if (p->type == EVHTTP_REQ_CUSTOM) {
+                       p->method = "CUSTOM";
+                       return 0;
+               }
+       }
+       return -1;
+}
+
 static void http_basic_cb(struct evhttp_request *req, void *arg);
 static void http_timeout_cb(struct evhttp_request *req, void *arg);
 static void http_large_cb(struct evhttp_request *req, void *arg);
@@ -78,6 +101,7 @@ static void http_chunked_cb(struct evhttp_request *req, void *arg);
 static void http_post_cb(struct evhttp_request *req, void *arg);
 static void http_put_cb(struct evhttp_request *req, void *arg);
 static void http_genmethod_cb(struct evhttp_request *req, void *arg);
+static void http_custom_cb(struct evhttp_request *req, void *arg);
 static void http_delay_cb(struct evhttp_request *req, void *arg);
 static void http_large_delay_cb(struct evhttp_request *req, void *arg);
 static void http_badreq_cb(struct evhttp_request *req, void *arg);
@@ -148,6 +172,9 @@ http_setup_gencb(ev_uint16_t *pport, struct event_base *base, int mask,
 
        evhttp_set_gencb(myhttp, cb, cbarg);
 
+       /* add support for extended HTTP methods */
+       evhttp_set_ext_method_cmp(myhttp, ext_method_cb);
+
        /* Register a callback for certain types of requests */
        evhttp_set_cb(myhttp, "/test", http_basic_cb, myhttp);
        evhttp_set_cb(myhttp, "/test nonconformant", http_basic_cb, myhttp);
@@ -165,6 +192,7 @@ http_setup_gencb(ev_uint16_t *pport, struct event_base *base, int mask,
        evhttp_set_cb(myhttp, "/unlockit", http_genmethod_cb, base);
        evhttp_set_cb(myhttp, "/copyit", http_genmethod_cb, base);
        evhttp_set_cb(myhttp, "/moveit", http_genmethod_cb, base);
+       evhttp_set_cb(myhttp, "/custom", http_custom_cb, base);
        evhttp_set_cb(myhttp, "/delay", http_delay_cb, base);
        evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, base);
        evhttp_set_cb(myhttp, "/badrequest", http_badreq_cb, base);
@@ -872,6 +900,78 @@ http_genmethod_test(void *arg, enum evhttp_cmd_type method, const char *name, co
                evutil_closesocket(fd);
 }
 
+/*
+ * HTTP CUSTOM test,  just piggyback on the basic test
+ */
+static void
+http_custom_cb(struct evhttp_request *req, void *arg)
+{
+       struct evbuffer *evb = evbuffer_new();
+       int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL;
+
+       /* Expecting a CUSTOM request */
+       if (evhttp_request_get_command(req) != EVHTTP_REQ_CUSTOM) {
+               fprintf(stdout, "FAILED (custom type)\n");
+               exit(1);
+       }
+
+       TT_BLATHER(("%s: called\n", __func__));
+       evbuffer_add_printf(evb, BASIC_REQUEST_BODY);
+
+       /* allow sending of an empty reply */
+       evhttp_send_reply(req, HTTP_OK, "Everything is fine",
+           !empty ? evb : NULL);
+
+       evbuffer_free(evb);
+}
+
+static void
+http_custom_test(void *arg)
+{
+       struct basic_test_data *data = arg;
+       struct bufferevent *bev;
+       evutil_socket_t fd = -1;
+       const char *http_request;
+       ev_uint16_t port = 0;
+       struct evhttp *http;
+
+       test_ok = 0;
+
+       http = http_setup(&port, data->base, 0);
+       /* Allow custom */
+       evhttp_set_allowed_methods(http, EVHTTP_REQ_CUSTOM);
+
+       tt_assert(http);
+       fd = http_connect("127.0.0.1", port);
+       tt_int_op(fd, >=, 0);
+
+       /* Stupid thing to send a request */
+       bev = bufferevent_socket_new(data->base, fd, 0);
+       bufferevent_setcb(bev, http_readcb, http_writecb,
+           http_errorcb, data->base);
+
+       http_request =
+           "CUSTOM /custom HTTP/1.1\r\n"
+           "Host: somehost\r\n"
+           "Connection: close\r\n"
+           "\r\n";
+
+       bufferevent_write(bev, http_request, strlen(http_request));
+
+       event_base_dispatch(data->base);
+
+       bufferevent_free(bev);
+       evutil_closesocket(fd);
+       fd = -1;
+
+       evhttp_free(http);
+
+       tt_int_op(test_ok, ==, 2);
+ end:
+       if (fd >= 0)
+               evutil_closesocket(fd);
+}
+
 static void
 http_delete_test(void *arg)
 {
@@ -1024,10 +1124,10 @@ static void
 http_allowed_methods_test(void *arg)
 {
        struct basic_test_data *data = arg;
-       struct bufferevent *bev1, *bev2, *bev3;
-       evutil_socket_t fd1=-1, fd2=-1, fd3=-1;
+       struct bufferevent *bev1, *bev2, *bev3, *bev4;
+       evutil_socket_t fd1=-1, fd2=-1, fd3=-1, fd4=-1;
        const char *http_request;
-       char *result1=NULL, *result2=NULL, *result3=NULL;
+       char *result1=NULL, *result2=NULL, *result3=NULL, *result4=NULL;
        ev_uint16_t port = 0;
        struct evhttp *http = http_setup(&port, data->base, 0);
 
@@ -1037,8 +1137,8 @@ http_allowed_methods_test(void *arg)
        fd1 = http_connect("127.0.0.1", port);
        tt_assert(fd1 != EVUTIL_INVALID_SOCKET);
 
-       /* GET is out; PATCH is in. */
-       evhttp_set_allowed_methods(http, EVHTTP_REQ_PATCH);
+       /* GET is out; PATCH & CUSTOM are in. */
+       evhttp_set_allowed_methods(http, EVHTTP_REQ_PATCH | EVHTTP_REQ_CUSTOM);
 
        /* Stupid thing to send a request */
        bev1 = bufferevent_socket_new(data->base, fd1, 0);
@@ -1092,9 +1192,28 @@ http_allowed_methods_test(void *arg)
 
        event_base_dispatch(data->base);
 
+       fd4 = http_connect("127.0.0.1", port);
+       tt_int_op(fd4, >=, 0);
+
+       bev4 = bufferevent_socket_new(data->base, fd4, 0);
+       bufferevent_enable(bev4, EV_READ|EV_WRITE);
+       bufferevent_setcb(bev4, NULL, NULL,
+           http_allowed_methods_eventcb, &result4);
+
+       http_request =
+           "CUSTOM /test HTTP/1.1\r\n"
+           "Host: somehost\r\n"
+           "Connection: close\r\n"
+           "\r\n";
+
+       bufferevent_write(bev4, http_request, strlen(http_request));
+
+       event_base_dispatch(data->base);
+
        bufferevent_free(bev1);
        bufferevent_free(bev2);
        bufferevent_free(bev3);
+       bufferevent_free(bev4);
 
        evhttp_free(http);
 
@@ -1110,6 +1229,10 @@ http_allowed_methods_test(void *arg)
        tt_assert(result3);
        tt_assert(!strncmp(result3, "HTTP/1.1 501 ", strlen("HTTP/1.1 501 ")));
 
+       /* Custom method (and allowed) */
+       tt_assert(result4);
+       tt_assert(!strncmp(result4, "HTTP/1.1 200 ", strlen("HTTP/1.1 200 ")));
+
  end:
        if (result1)
                free(result1);
@@ -1117,12 +1240,16 @@ http_allowed_methods_test(void *arg)
                free(result2);
        if (result3)
                free(result3);
+       if (result4)
+               free(result4);
        if (fd1 >= 0)
                evutil_closesocket(fd1);
        if (fd2 >= 0)
                evutil_closesocket(fd2);
        if (fd3 >= 0)
                evutil_closesocket(fd3);
+       if (fd4 >= 0)
+               evutil_closesocket(fd4);
 }
 
 static void http_request_no_action_done(struct evhttp_request *, void *);
@@ -1153,6 +1280,8 @@ http_connection_test_(struct basic_test_data *data, int persistent,
        }
        tt_assert(http);
 
+       evhttp_set_allowed_methods(http, EVHTTP_REQ_GET | EVHTTP_REQ_CUSTOM);
+
        if (ssl) {
 #ifdef EVENT__HAVE_OPENSSL
                SSL *ssl = SSL_new(get_ssl_ctx());
@@ -1177,6 +1306,9 @@ http_connection_test_(struct basic_test_data *data, int persistent,
 
        tt_assert(evhttp_connection_get_server(evcon) == NULL);
 
+       /* add support for CUSTOM method */
+       evhttp_connection_set_ext_method_cmp(evcon, ext_method_cb);
+
        /*
         * At this point, we want to schedule a request to the HTTP
         * server using our make request method.
@@ -1233,6 +1365,21 @@ http_connection_test_(struct basic_test_data *data, int persistent,
 
        event_base_dispatch(data->base);
 
+       /* make a CUSTOM request */
+       test_ok = 0;
+
+       req = evhttp_request_new(http_request_empty_done, data->base);
+
+       /* our CUSTOM method doesn't have Body */
+       evhttp_add_header(evhttp_request_get_output_headers(req), "Empty", "itis");
+
+       /* We give ownership of the request to the connection */
+       if (evhttp_make_request(evcon, req, EVHTTP_REQ_CUSTOM, "/test") == -1) {
+               tt_abort_msg("Couldn't make request");
+       }
+
+       event_base_dispatch(data->base);
+
  end:
        if (evcon)
                evhttp_connection_free(evcon);
@@ -5080,6 +5227,7 @@ struct testcase_t http_testcases[] = {
        HTTP(unlock),
        HTTP(copy),
        HTTP(move),
+       HTTP(custom),
        HTTP(allowed_methods),
        HTTP(failure),
        HTTP(connection),
index c13d22fc36e2c334f78642230d84edfe102af69c..c89eee59939d34643ddd8fcc13fe73c3a74e2298 100644 (file)
@@ -68,3 +68,33 @@ heavy computation.
 The watcher API is defined in <event2/watch.h>. A concrete example of how
 watchers can help monitor server performance is available in
 "sample/watch-timing.c".
+
+* Support for custom HTTP methods
+
+Libevent HTTP code now supports defining custom HTTP methods. It is done
+through a callback:
+
+  #define EVHTTP_REQ_CUSTOM      ((EVHTTP_REQ_MAX) << 1)
+  static int ext_method_cb(struct evhttp_ext_method *p)
+  {
+    if (p == NULL)
+      return -1;
+    if (p->method) {
+      if (strcmp(p->method, "CUSTOM") == 0) {
+        p->type = EVHTTP_REQ_CUSTOM;
+        p->flags = 0;   /*EVHTTP_METHOD_HAS_BODY*/
+        return 0;
+      }
+    } else {
+      if (p->type == EVHTTP_REQ_CUSTOM) {
+        p->method = "CUSTOM";
+        return 0;
+      }
+    }
+  }
+
+And to register this callback with http server you can use:
+  evhttp_set_ext_method_cmp(http, ext_method_cb);
+
+Or registering callback with one client only:
+  evhttp_connection_set_ext_method_cmp(evcon, ext_method_cb);