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;
}
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') {
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') {
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" */
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 */
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:
*/
/** 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
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 */
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);
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);
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);
}
}
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);
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);
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)
{
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),