]> granicus.if.org Git - libevent/commitdiff
http: add WebDAV methods support
authorAlexander Drozdov <al.drozdov@gmail.com>
Wed, 13 Mar 2019 07:51:55 +0000 (10:51 +0300)
committerAlexander Drozdov <al.drozdov@gmail.com>
Thu, 14 Mar 2019 08:12:46 +0000 (11:12 +0300)
WebDAV introduced new HTTP methods (RFC4918):
PROPFIND, PROPPATCH, MKCOL, LOCK, UNLOCK, COPY, MOVE.

Add support of the methods.

http.c
include/event2/http.h
test/regress_http.c

diff --git a/http.c b/http.c
index 89e6afa2d11c0e150db32b280097dfc9b2c40bf7..799cfb368f104cac2d168cefb65f5a8491a3cf1c 100644 (file)
--- a/http.c
+++ b/http.c
@@ -331,6 +331,27 @@ evhttp_method(enum evhttp_cmd_type type)
        case EVHTTP_REQ_PATCH:
                method = "PATCH";
                break;
+       case EVHTTP_REQ_PROPFIND:
+               method = "PROPFIND";
+               break;
+       case EVHTTP_REQ_PROPPATCH:
+               method = "PROPPATCH";
+               break;
+       case EVHTTP_REQ_MKCOL:
+               method = "MKCOL";
+               break;
+       case EVHTTP_REQ_LOCK:
+               method = "LOCK";
+               break;
+       case EVHTTP_REQ_UNLOCK:
+               method = "UNLOCK";
+               break;
+       case EVHTTP_REQ_COPY:
+               method = "COPY";
+               break;
+       case EVHTTP_REQ_MOVE:
+               method = "MOVE";
+               break;
        default:
                method = NULL;
                break;
@@ -1794,7 +1815,7 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line, size_t len)
                }
                break;
            case 4:
-               /* The method length is 4 bytes, leaving only the methods "POST" and "HEAD" */
+               /* The method length is 4 bytes, leaving only the methods POST, HEAD, LOCK, COPY and MOVE */
                switch (*method) {
                    case 'P':
                        if (method[3] == 'T' && method[2] == 'S' && method[1] == 'O') {
@@ -1806,12 +1827,27 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line, size_t len)
                            type = EVHTTP_REQ_HEAD;
                        }
                        break;
+                   case 'L':
+                       if (method[3] == 'K' && method[2] == 'C' && method[1] == 'O') {
+                           type = EVHTTP_REQ_LOCK;
+                       }
+                       break;
+                   case 'C':
+                       if (method[3] == 'Y' && method[2] == 'P' && method[1] == 'O') {
+                           type = EVHTTP_REQ_COPY;
+                       }
+                       break;
+                   case 'M':
+                       if (method[3] == 'E' && method[2] == 'V' && method[1] == 'O') {
+                           type = EVHTTP_REQ_MOVE;
+                       }
+                       break;
                    default:
                        break;
                }
                break;
            case 5:
-               /* Method length is 5 bytes, which can only encompass PATCH and TRACE */
+               /* Method length is 5 bytes, which can only encompass PATCH, TRACE and MKCOL */
                switch (*method) {
                    case 'P':
                        if (method[4] == 'H' && method[3] == 'C' && method[2] == 'T' && method[1] == 'A') {
@@ -1823,23 +1859,34 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line, size_t len)
                            type = EVHTTP_REQ_TRACE;
                        }
                     
+                       break;
+                   case 'M':
+                       if (method[4] == 'L' && method[3] == 'O' && method[2] == 'C' && method[1] == 'K') {
+                           type = EVHTTP_REQ_MKCOL;
+                       }
                        break;
                    default:
                        break;
                }
                break;
            case 6:
-               /* Method length is 6, only valid method 6 bytes in length is DELEte */
-            
-               /* If the first byte isn't 'D' then it's invalid */
-               if (*method != 'D') {
-                   break;
-               }
-
-               if (method[5] == 'E' && method[4] == 'T' && method[3] == 'E' && method[2] == 'L' && method[1] == 'E') {
-                   type = EVHTTP_REQ_DELETE;
+               /* Method length is 6, only valid methods 6 bytes in length is DELETE and UNLOCK */
+               switch (*method) {
+                   case 'D':
+                       if (method[5] == 'E' && method[4] == 'T' && method[3] == 'E' &&
+                               method[2] == 'L' && method[1] == 'E') {
+                           type = EVHTTP_REQ_DELETE;
+                       }
+                       break;
+                   case 'U':
+                       if (method[5] == 'K' && method[4] == 'C' && method[3] == 'O' &&
+                               method[2] == 'L' && method[1] == 'N') {
+                           type = EVHTTP_REQ_UNLOCK;
+                       }
+                       break;
+                   default:
+                       break;
                }
-
                break;
            case 7:
                /* Method length is 7, only valid methods are "OPTIONS" and "CONNECT" */
@@ -1861,6 +1908,36 @@ evhttp_parse_request_line(struct evhttp_request *req, char *line, size_t len)
                    default:
                        break;
                }
+               break;
+           case 8:
+               /* Method length is 8, only valid method 8 bytes in length is PROPFIND */
+
+               /* If the first byte isn't 'P' then it's invalid */
+               if (*method != 'P') {
+                   break;
+               }
+
+               if (method[7] == 'D' && method[6] == 'N' && method[5] == 'I' &&
+                       method[4] == 'F' && method[3] == 'P' && method[2] == 'O' &&
+                       method[1] == 'R') {
+                   type = EVHTTP_REQ_PROPFIND;
+               }
+
+               break;
+           case 9:
+               /* Method length is 9, only valid method 9 bytes in length is PROPPATCH */
+
+               /* If the first byte isn't 'P' then it's invalid */
+               if (*method != 'P') {
+                   break;
+               }
+
+               if (method[8] == 'H' && method[7] == 'C' && method[6] == 'T' &&
+                       method[5] == 'A' && method[4] == 'P' && method[3] == 'P' &&
+                       method[2] == 'O' && method[1] == 'R') {
+                   type = EVHTTP_REQ_PROPPATCH;
+               }
+
                break;
        } /* switch */
 
@@ -2208,6 +2285,13 @@ evhttp_method_may_have_body(enum evhttp_cmd_type 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:
index 393c536f4e724f3dfea6323abfebb11ca692fc04..53c38444fdec518dabb15ad9a1c1be8335e323c2 100644 (file)
@@ -524,7 +524,10 @@ void evhttp_send_reply_end(struct evhttp_request *req);
  */
 
 /** The different request types supported by evhttp.  These are as specified
- * in RFC2616, except for PATCH which is specified by RFC5789.
+ * in RFC2616, except for:
+ * - PATCH which is specified by RFC5789
+ * - PROPFIND, PROPPATCH, MKCOL, LOCK, UNLOCK, COPY, MOVE
+ *   which are specified by RFC4918
  *
  * By default, only some of these methods are accepted and passed to user
  * callbacks; use evhttp_set_allowed_methods() to change which methods
@@ -539,7 +542,14 @@ enum evhttp_cmd_type {
        EVHTTP_REQ_OPTIONS = 1 << 5,
        EVHTTP_REQ_TRACE   = 1 << 6,
        EVHTTP_REQ_CONNECT = 1 << 7,
-       EVHTTP_REQ_PATCH   = 1 << 8
+       EVHTTP_REQ_PATCH   = 1 << 8,
+       EVHTTP_REQ_PROPFIND= 1 << 9,
+       EVHTTP_REQ_PROPPATCH=1 << 10,
+       EVHTTP_REQ_MKCOL   = 1 << 11,
+       EVHTTP_REQ_LOCK    = 1 << 12,
+       EVHTTP_REQ_UNLOCK  = 1 << 13,
+       EVHTTP_REQ_COPY    = 1 << 14,
+       EVHTTP_REQ_MOVE    = 1 << 15,
 };
 
 /** a request object can represent either a request or a reply */
index 5e4e1a6803aa4bfb4f2a7b88d3085b0125ba24d8..e29cd1b5a9546b627b8f7ccbe9e42c0222bfddbe 100644 (file)
@@ -77,7 +77,7 @@ static void http_large_cb(struct evhttp_request *req, void *arg);
 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_delete_cb(struct evhttp_request *req, void *arg);
+static void http_genmethod_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);
@@ -157,7 +157,14 @@ http_setup_gencb(ev_uint16_t *pport, struct event_base *base, int mask,
        evhttp_set_cb(myhttp, "/streamed", http_chunked_cb, base);
        evhttp_set_cb(myhttp, "/postit", http_post_cb, base);
        evhttp_set_cb(myhttp, "/putit", http_put_cb, base);
-       evhttp_set_cb(myhttp, "/deleteit", http_delete_cb, base);
+       evhttp_set_cb(myhttp, "/deleteit", http_genmethod_cb, base);
+       evhttp_set_cb(myhttp, "/propfind", http_genmethod_cb, base);
+       evhttp_set_cb(myhttp, "/proppatch", http_genmethod_cb, base);
+       evhttp_set_cb(myhttp, "/mkcol", http_genmethod_cb, base);
+       evhttp_set_cb(myhttp, "/lockit", http_genmethod_cb, base);
+       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, "/delay", http_delay_cb, base);
        evhttp_set_cb(myhttp, "/largedelay", http_large_delay_cb, base);
        evhttp_set_cb(myhttp, "/badrequest", http_badreq_cb, base);
@@ -775,18 +782,36 @@ http_large_delay_cb(struct evhttp_request *req, void *arg)
        evhttp_connection_fail_(delayed_client, EVREQ_HTTP_EOF);
 }
 
-/*
- * HTTP DELETE test,  just piggyback on the basic test
- */
-
 static void
-http_delete_cb(struct evhttp_request *req, void *arg)
+http_genmethod_cb(struct evhttp_request *req, void *arg)
 {
+       const char *uri = evhttp_request_get_uri(req);
        struct evbuffer *evb = evbuffer_new();
        int empty = evhttp_find_header(evhttp_request_get_input_headers(req), "Empty") != NULL;
-
-       /* Expecting a DELETE request */
-       if (evhttp_request_get_command(req) != EVHTTP_REQ_DELETE) {
+       enum evhttp_cmd_type method;
+
+       if (!strcmp(uri, "/deleteit"))
+           method = EVHTTP_REQ_DELETE;
+       else if (!strcmp(uri, "/propfind"))
+           method = EVHTTP_REQ_PROPFIND;
+       else if (!strcmp(uri, "/proppatch"))
+           method = EVHTTP_REQ_PROPPATCH;
+       else if (!strcmp(uri, "/mkcol"))
+           method = EVHTTP_REQ_MKCOL;
+       else if (!strcmp(uri, "/lockit"))
+           method = EVHTTP_REQ_LOCK;
+       else if (!strcmp(uri, "/unlockit"))
+           method = EVHTTP_REQ_UNLOCK;
+       else if (!strcmp(uri, "/copyit"))
+           method = EVHTTP_REQ_COPY;
+       else if (!strcmp(uri, "/moveit"))
+           method = EVHTTP_REQ_MOVE;
+       else {
+               fprintf(stdout, "FAILED (unexpected path)\n");
+               exit(1);
+       }
+       /* Expecting correct request method */
+       if (evhttp_request_get_command(req) != method) {
                fprintf(stdout, "FAILED (delete type)\n");
                exit(1);
        }
@@ -802,12 +827,12 @@ http_delete_cb(struct evhttp_request *req, void *arg)
 }
 
 static void
-http_delete_test(void *arg)
+http_genmethod_test(void *arg, enum evhttp_cmd_type method, const char *name, const char *path)
 {
        struct basic_test_data *data = arg;
        struct bufferevent *bev;
+       struct evbuffer *evb;
        evutil_socket_t fd = EVUTIL_INVALID_SOCKET;
-       const char *http_request;
        ev_uint16_t port = 0;
        struct evhttp *http = http_setup(&port, data->base, 0);
 
@@ -818,18 +843,20 @@ http_delete_test(void *arg)
        fd = http_connect("127.0.0.1", port);
        tt_assert(fd != EVUTIL_INVALID_SOCKET);
 
+       evhttp_set_allowed_methods(http, method);
+
        /* 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 =
-           "DELETE /deleteit HTTP/1.1\r\n"
+       evb = bufferevent_get_output(bev);
+       evbuffer_add_printf(
+           evb,
+           "%s %s HTTP/1.1\r\n"
            "Host: somehost\r\n"
            "Connection: close\r\n"
-           "\r\n";
-
-       bufferevent_write(bev, http_request, strlen(http_request));
+           "\r\n"
+           "body", name, path);
 
        event_base_dispatch(data->base);
 
@@ -845,6 +872,54 @@ http_delete_test(void *arg)
                evutil_closesocket(fd);
 }
 
+static void
+http_delete_test(void *arg)
+{
+       http_genmethod_test(arg, EVHTTP_REQ_DELETE, "DELETE", "/deleteit");
+}
+
+static void
+http_propfind_test(void *arg)
+{
+       http_genmethod_test(arg, EVHTTP_REQ_PROPFIND, "PROPFIND", "/propfind");
+}
+
+static void
+http_proppatch_test(void *arg)
+{
+       http_genmethod_test(arg, EVHTTP_REQ_PROPPATCH, "PROPPATCH", "/proppatch");
+}
+
+static void
+http_mkcol_test(void *arg)
+{
+       http_genmethod_test(arg, EVHTTP_REQ_MKCOL, "MKCOL", "/mkcol");
+}
+
+static void
+http_lock_test(void *arg)
+{
+       http_genmethod_test(arg, EVHTTP_REQ_LOCK, "LOCK", "/lockit");
+}
+
+static void
+http_unlock_test(void *arg)
+{
+       http_genmethod_test(arg, EVHTTP_REQ_UNLOCK, "UNLOCK", "/unlockit");
+}
+
+static void
+http_copy_test(void *arg)
+{
+       http_genmethod_test(arg, EVHTTP_REQ_COPY, "COPY", "/copyit");
+}
+
+static void
+http_move_test(void *arg)
+{
+       http_genmethod_test(arg, EVHTTP_REQ_MOVE, "MOVE", "/moveit");
+}
+
 static void
 http_sent_cb(struct evhttp_request *req, void *arg)
 {
@@ -4998,6 +5073,13 @@ struct testcase_t http_testcases[] = {
        HTTP(post),
        HTTP(put),
        HTTP(delete),
+       HTTP(propfind),
+       HTTP(proppatch),
+       HTTP(mkcol),
+       HTTP(lock),
+       HTTP(unlock),
+       HTTP(copy),
+       HTTP(move),
        HTTP(allowed_methods),
        HTTP(failure),
        HTTP(connection),