aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/http
diff options
context:
space:
mode:
authorRobert Bielik <robert.bielik@dirac.com>2020-01-28 07:43:09 +0100
committerGarrett D'Amore <garrett@damore.org>2020-01-27 22:43:09 -0800
commit2545add6240145b419357b9260ae5e8c0d95ba6c (patch)
tree155ae2ccfc4c720df7206c7fc81f68531624bc35 /src/supplemental/http
parent9414a69a9575564d04022aef9bd898028e52bf09 (diff)
downloadnng-2545add6240145b419357b9260ae5e8c0d95ba6c.tar.gz
nng-2545add6240145b419357b9260ae5e8c0d95ba6c.tar.bz2
nng-2545add6240145b419357b9260ae5e8c0d95ba6c.zip
Add possibility to explicitly set a tree handler as exclusive (#1158)
- Default tree handler behavior is now non-exclusive - Add 'longest uri first' ordering for http handlers
Diffstat (limited to 'src/supplemental/http')
-rw-r--r--src/supplemental/http/http_api.h7
-rw-r--r--src/supplemental/http/http_public.c11
-rw-r--r--src/supplemental/http/http_server.c81
3 files changed, 80 insertions, 19 deletions
diff --git a/src/supplemental/http/http_api.h b/src/supplemental/http/http_api.h
index 45738318..6be681a9 100644
--- a/src/supplemental/http/http_api.h
+++ b/src/supplemental/http/http_api.h
@@ -301,6 +301,13 @@ extern void nni_http_handler_collect_body(nni_http_handler *, bool, size_t);
// will probably need to inspect the URL of the request.
extern int nni_http_handler_set_tree(nni_http_handler *);
+// nni_http_handler_set_tree_exclusive marks the handler as servicing the
+// entire tree (e.g. a directory) exclusively, rather than just a leaf node.
+// When servicing a tree exclusively, other handlers sharing parts of the uri
+// will induce an address conflict when adding them to a server. The handler
+// will probably need to inspect the URL of the request.
+extern int nni_http_handler_set_tree_exclusive(nni_http_handler *);
+
// nni_http_handler_set_host limits the handler to only being called for
// the given Host: field. This can be used to set up multiple virtual
// hosts. Note that host names must match exactly. If NULL or an empty
diff --git a/src/supplemental/http/http_public.c b/src/supplemental/http/http_public.c
index d2e876ab..60ca2693 100644
--- a/src/supplemental/http/http_public.c
+++ b/src/supplemental/http/http_public.c
@@ -636,6 +636,17 @@ nng_http_handler_set_tree(nng_http_handler *h)
}
int
+nng_http_handler_set_tree_exclusive(nng_http_handler *h)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_set_tree_exclusive(h));
+#else
+ NNI_ARG_UNUSED(h);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
nng_http_handler_set_data(nng_http_handler *h, void *dat, void (*dtor)(void *))
{
#ifdef NNG_SUPP_HTTP
diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c
index da12c20d..5eadf6d5 100644
--- a/src/supplemental/http/http_server.c
+++ b/src/supplemental/http/http_server.c
@@ -3,6 +3,7 @@
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 QXSoftware <lh563566994@126.com>
// Copyright 2019 Devolutions <info@devolutions.net>
+// Copyright 2020 Dirac Research <robert.bielik@dirac.com>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
@@ -37,6 +38,7 @@ struct nng_http_handler {
char * method;
char * host;
bool tree;
+ bool tree_exclusive;
nni_atomic_u64 ref;
nni_atomic_bool busy;
size_t maxbody;
@@ -110,11 +112,12 @@ nni_http_handler_init(
return (NNG_ENOMEM);
}
NNI_LIST_NODE_INIT(&h->node);
- h->cb = cb;
- h->data = NULL;
- h->dtor = NULL;
- h->host = NULL;
- h->tree = false;
+ h->cb = cb;
+ h->data = NULL;
+ h->dtor = NULL;
+ h->host = NULL;
+ h->tree = false;
+ h->tree_exclusive = false;
h->maxbody = 1024 * 1024; // By default we accept up to 1MB of body
h->getbody = true;
*hp = h;
@@ -177,7 +180,19 @@ nni_http_handler_set_tree(nni_http_handler *h)
if (nni_atomic_get_bool(&h->busy) != 0) {
return (NNG_EBUSY);
}
- h->tree = true;
+ h->tree = true;
+ h->tree_exclusive = false;
+ return (0);
+}
+
+int
+nni_http_handler_set_tree_exclusive(nni_http_handler *h)
+{
+ if (nni_atomic_get_bool(&h->busy) != 0) {
+ return (NNG_EBUSY);
+ }
+ h->tree = true;
+ h->tree_exclusive = true;
return (0);
}
@@ -1115,8 +1130,8 @@ nni_http_server_add_handler(nni_http_server *s, nni_http_handler *h)
}
nni_mtx_lock(&s->mtx);
- // General rule for finding a conflict is that if either string
- // is a strict substring of the other, then we have a
+ // General rule for finding a conflict is that if either uri
+ // string is an exact duplicate of the other, then we have a
// collision. (But only if the methods match, and the host
// matches.) Note that a wild card host matches both.
NNI_LIST_FOREACH (&s->handlers, h2) {
@@ -1146,26 +1161,54 @@ nni_http_server_add_handler(nni_http_server *s, nni_http_handler *h)
while ((len2 > 0) && (h2->uri[len2 - 1] == '/')) {
len2--; // ignore trailing '/'
}
- if (strncmp(h->uri, h2->uri, len > len2 ? len2 : len) != 0) {
- continue; // prefixes don't match.
- }
- if (len2 > len) {
- if ((h2->uri[len] == '/') && (h->tree)) {
- nni_mtx_unlock(&s->mtx);
- return (NNG_EADDRINUSE);
+ if ((h2->tree && h2->tree_exclusive) ||
+ (h->tree && h->tree_exclusive)) {
+ // Old behavior
+ if (strncmp(h->uri, h2->uri,
+ len > len2 ? len2 : len) != 0) {
+ continue; // prefixes don't match.
}
- } else if (len > len2) {
- if ((h->uri[len2] == '/') && (h2->tree)) {
+
+ if (len2 > len) {
+ if ((h2->uri[len] == '/') && (h->tree)) {
+ nni_mtx_unlock(&s->mtx);
+ return (NNG_EADDRINUSE);
+ }
+ } else if (len > len2) {
+ if ((h->uri[len2] == '/') && (h2->tree)) {
+ nni_mtx_unlock(&s->mtx);
+ return (NNG_EADDRINUSE);
+ }
+ } else {
nni_mtx_unlock(&s->mtx);
return (NNG_EADDRINUSE);
}
} else {
+ if (len != len2) {
+ continue; // length mismatch
+ }
+
+ if (strcmp(h->uri, h2->uri) != 0) {
+ continue; // not a duplicate
+ }
+
nni_mtx_unlock(&s->mtx);
return (NNG_EADDRINUSE);
}
}
- nni_list_append(&s->handlers, h);
+
+ // Maintain list of handlers in longest uri first order
+ NNI_LIST_FOREACH (&s->handlers, h2) {
+ size_t len2 = strlen(h2->uri);
+ if (len > len2) {
+ nni_list_insert_before(&s->handlers, h, h2);
+ break;
+ }
+ }
+ if (h2 == NULL) {
+ nni_list_append(&s->handlers, h);
+ }
// Note that we have borrowed the reference count on the handler.
// Thus we own it, and if the server is destroyed while we have it,
@@ -1533,7 +1576,7 @@ nni_http_handler_init_directory(
// We don't permit a body for getting a file.
nni_http_handler_collect_body(h, true, 0);
- if (((rv = nni_http_handler_set_tree(h)) != 0) ||
+ if (((rv = nni_http_handler_set_tree_exclusive(h)) != 0) ||
((rv = nni_http_handler_set_data(h, hf, http_file_free)) != 0)) {
http_file_free(hf);
nni_http_handler_fini(h);