diff options
Diffstat (limited to 'src/lib-http/http-server-resource.c')
-rw-r--r-- | src/lib-http/http-server-resource.c | 276 |
1 files changed, 276 insertions, 0 deletions
diff --git a/src/lib-http/http-server-resource.c b/src/lib-http/http-server-resource.c new file mode 100644 index 0000000..dc602e4 --- /dev/null +++ b/src/lib-http/http-server-resource.c @@ -0,0 +1,276 @@ +/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "bsearch-insert-pos.h" +#include "str-sanitize.h" + +#include "http-url.h" +#include "http-server-private.h" + +static struct event_category event_category_http_server_resource = { + .name = "http-server-resource" +}; + +/* + * Location + */ + +static int +http_server_location_cmp(struct http_server_location *const *loc1, + struct http_server_location *const *loc2) +{ + return strcmp((*loc1)->path, (*loc2)->path); +} + +static struct http_server_location * +http_server_location_add(struct http_server *server, pool_t pool, + const char *path) +{ + struct http_server_location qloc, *loc; + unsigned int insert_idx; + + i_zero(&qloc); + qloc.path = path; + loc = &qloc; + + if (array_bsearch_insert_pos(&server->locations, &loc, + http_server_location_cmp, &insert_idx)) + return array_idx_elem(&server->locations, insert_idx); + + loc = p_new(pool, struct http_server_location, 1); + loc->path = p_strdup(pool, path); + array_insert(&server->locations, insert_idx, &loc, 1); + return loc; +} + +static int +http_server_location_find(struct http_server *server, const char *path, + struct http_server_location **loc_r, + const char **sub_path_r) +{ + struct http_server_location qloc, *loc; + size_t loc_len; + unsigned int insert_idx; + + *sub_path_r = NULL; + *loc_r = NULL; + + i_zero(&qloc); + qloc.path = path; + loc = &qloc; + + if (array_bsearch_insert_pos(&server->locations, &loc, + http_server_location_cmp, &insert_idx)) { + /* Exact match */ + *loc_r = array_idx_elem(&server->locations, insert_idx); + *sub_path_r = ""; + return 1; + } + if (insert_idx == 0) { + /* Not found at all */ + return -1; + } + loc = array_idx_elem(&server->locations, insert_idx-1); + + loc_len = strlen(loc->path); + if (!str_begins(path, loc->path)) { + /* Location isn't a prefix of path */ + return -1; + } else if (path[loc_len] != '/') { + /* Match doesn't end at '/' */ + return -1; + } + + *sub_path_r = &path[loc_len + 1]; + *loc_r = loc; + return 0; +} + +static void +http_server_location_remove(struct http_server *server, + struct http_server_location *loc) +{ + struct http_server_location *const *locp; + + array_foreach(&server->locations, locp) { + if (*locp == loc) { + array_delete( + &server->locations, + array_foreach_idx(&server->locations, locp), 1); + return; + } + } +} + +/* + * Resource + */ + +static void http_server_resource_update_event(struct http_server_resource *res) +{ + struct http_server_location *const *locs; + unsigned int locs_count; + + locs = array_get(&res->locations, &locs_count); + if (locs_count == 0) { + event_set_append_log_prefix(res->event, "resource: "); + return; + } + + event_add_str(res->event, "path", locs[0]->path); + event_set_append_log_prefix( + res->event, t_strdup_printf("resource %s: ", + str_sanitize(locs[0]->path, 128))); +} + +#undef http_server_resource_create +struct http_server_resource * +http_server_resource_create(struct http_server *server, pool_t pool, + http_server_resource_callback_t *callback, + void *context) +{ + struct http_server_resource *res; + + pool_ref(pool); + + res = p_new(pool, struct http_server_resource, 1); + res->pool = pool; + res->server = server; + + res->callback = callback; + res->context = context; + + p_array_init(&res->locations, pool, 4); + + res->event = event_create(server->event); + event_add_category(res->event, &event_category_http_server_resource); + http_server_resource_update_event(res); + + array_append(&server->resources, &res, 1); + + return res; +} + +void http_server_resource_free(struct http_server_resource **_res) +{ + struct http_server_resource *res = *_res; + struct http_server_location *loc; + + if (res == NULL) + return; + + *_res = NULL; + + e_debug(res->event, "Free"); + + if (res->destroy_callback != NULL) { + res->destroy_callback(res->destroy_context); + res->destroy_callback = NULL; + } + + array_foreach_elem(&res->locations, loc) + http_server_location_remove(res->server, loc); + + event_unref(&res->event); + pool_unref(&res->pool); +} + +pool_t http_server_resource_get_pool(struct http_server_resource *res) +{ + return res->pool; +} + +const char *http_server_resource_get_path(struct http_server_resource *res) +{ + struct http_server_location *const *locs; + unsigned int locs_count; + + locs = array_get(&res->locations, &locs_count); + i_assert(locs_count > 0); + + return locs[0]->path; +} + +struct event *http_server_resource_get_event(struct http_server_resource *res) +{ + return res->event; +} + +void http_server_resource_add_location(struct http_server_resource *res, + const char *path) +{ + struct http_server_location *loc; + + i_assert(*path == '\0' || *path == '/'); + + loc = http_server_location_add(res->server, res->pool, path); + i_assert(loc->resource == NULL); + + loc->resource = res; + array_append(&res->locations, &loc, 1); + + if (array_count(&res->locations) == 1) + http_server_resource_update_event(res); +} + +int http_server_resource_find(struct http_server *server, const char *path, + struct http_server_resource **res_r, + const char **sub_path_r) +{ + struct http_server_location *loc; + int ret; + + if (path == NULL) + return -1; + + *res_r = NULL; + *sub_path_r = NULL; + + ret = http_server_location_find(server, path, &loc, sub_path_r); + if (ret < 0) + return -1; + + i_assert(loc->resource != NULL); + *res_r = loc->resource; + return ret; +} + +bool http_server_resource_callback(struct http_server_request *req) +{ + struct http_server *server = req->server; + struct http_server_resource *res; + const char *sub_path; + + switch (req->req.target.format) { + case HTTP_REQUEST_TARGET_FORMAT_ORIGIN: + /* According to RFC 7240, Section 5.3.1 only the origin form is + applicable to local resources on an origin server. + */ + break; + case HTTP_REQUEST_TARGET_FORMAT_ABSOLUTE: + case HTTP_REQUEST_TARGET_FORMAT_AUTHORITY: + case HTTP_REQUEST_TARGET_FORMAT_ASTERISK: + /* Not applicable for a local resource. */ + return FALSE; + } + + if (http_server_resource_find(server, req->req.target.url->path, + &res, &sub_path) < 0) + return FALSE; + + e_debug(res->event, "Got request: %s", http_server_request_label(req)); + + i_assert(res->callback != NULL); + res->callback(res->context, req, sub_path); + return TRUE; +} + +#undef http_server_resource_set_destroy_callback +void http_server_resource_set_destroy_callback(struct http_server_resource *res, + void (*callback)(void *), + void *context) +{ + res->destroy_callback = callback; + res->destroy_context = context; +} |