aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/supplemental/http/CMakeLists.txt1
-rw-r--r--src/supplemental/http/http_server_test.c915
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/httpserver.c1044
4 files changed, 916 insertions, 1045 deletions
diff --git a/src/supplemental/http/CMakeLists.txt b/src/supplemental/http/CMakeLists.txt
index db6bcdac..0e4cc808 100644
--- a/src/supplemental/http/CMakeLists.txt
+++ b/src/supplemental/http/CMakeLists.txt
@@ -26,3 +26,4 @@ nng_sources_if(NNG_SUPP_HTTP
http_public.c
http_schemes.c
http_server.c)
+nng_test_if(NNG_SUPP_HTTP http_server_test)
diff --git a/src/supplemental/http/http_server_test.c b/src/supplemental/http/http_server_test.c
new file mode 100644
index 00000000..2d1e4b28
--- /dev/null
+++ b/src/supplemental/http/http_server_test.c
@@ -0,0 +1,915 @@
+//
+// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// 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
+// file was obtained (LICENSE.txt). A copy of the license may also be
+// found online at https://opensource.org/licenses/MIT.
+//
+
+// Basic HTTP server tests.
+#include <nng/nng.h>
+#include <nng/supplemental/http/http.h>
+#include <nng/supplemental/tls/tls.h>
+
+#include <nuts.h>
+
+const char *doc1 = "<html><body>Someone <b>is</b> home!</body></html>";
+const char *doc2 = "This is a text file.";
+const char *doc3 = "<html><body>This is doc number 3.</body></html>";
+const char *doc4 = "<html><body>Whoops, Errored!</body></html>";
+
+struct server_test {
+ nng_url *url;
+ nng_aio *aio;
+ nng_http_server *s;
+ nng_http_handler *h;
+ nng_http_client *cli;
+ nng_http_conn *conn;
+ nng_http_req *req;
+ nng_http_res *res;
+ char urlstr[2048];
+};
+
+static int
+httpdo(nng_url *url, nng_http_req *req, nng_http_res *res, void **datap,
+ size_t *sizep)
+{
+ int rv;
+ nng_aio *aio = NULL;
+ nng_http_client *cli = NULL;
+ nng_http_conn *h = NULL;
+ size_t clen = 0;
+ void *data = NULL;
+ const char *ptr;
+
+ if (((rv = nng_aio_alloc(&aio, NULL, NULL)) != 0) ||
+ ((rv = nng_http_client_alloc(&cli, url)) != 0)) {
+ goto fail;
+ }
+ nng_http_client_connect(cli, aio);
+ nng_aio_wait(aio);
+ if ((rv = nng_aio_result(aio)) != 0) {
+ goto fail;
+ }
+
+ h = nng_aio_get_output(aio, 0);
+
+ nng_http_conn_write_req(h, req, aio);
+ nng_aio_wait(aio);
+ if ((rv = nng_aio_result(aio)) != 0) {
+ goto fail;
+ }
+ nng_http_conn_read_res(h, res, aio);
+ nng_aio_wait(aio);
+ if ((rv = nng_aio_result(aio)) != 0) {
+ goto fail;
+ }
+
+ clen = 0;
+ if ((ptr = nng_http_res_get_header(res, "Content-Length")) != NULL) {
+ clen = atoi(ptr);
+ }
+
+ if (clen > 0) {
+ nng_iov iov;
+ data = nng_alloc(clen);
+ iov.iov_buf = data;
+ iov.iov_len = clen;
+ nng_aio_set_iov(aio, 1, &iov);
+ nng_http_conn_read_all(h, aio);
+ nng_aio_wait(aio);
+ if ((rv = nng_aio_result(aio)) != 0) {
+ goto fail;
+ }
+ }
+
+ *datap = data;
+ *sizep = clen;
+
+fail:
+ if (aio != NULL) {
+ nng_aio_free(aio);
+ }
+ if (h != NULL) {
+ nng_http_conn_close(h);
+ }
+ if (cli != NULL) {
+ nng_http_client_free(cli);
+ }
+
+ return (rv);
+}
+
+static int
+httpget(struct server_test *st, void **datap, size_t *sizep, uint16_t *statp,
+ char **ctypep)
+{
+ int rv;
+ size_t clen = 0;
+ void *data = NULL;
+ char *ctype = NULL;
+ const char *ptr;
+
+ if ((rv = httpdo(st->url, st->req, st->res, &data, &clen)) != 0) {
+ goto fail;
+ }
+
+ *statp = nng_http_res_get_status(st->res);
+
+ if (clen > 0) {
+ if ((ptr = nng_http_res_get_header(st->res, "Content-Type")) !=
+ NULL) {
+ ctype = nng_strdup(ptr);
+ }
+ }
+
+ *datap = data;
+ *sizep = clen;
+ *ctypep = ctype;
+
+fail:
+ if (rv != 0) {
+ if (data != NULL) {
+ nng_free(data, clen);
+ }
+ free(ctype);
+ }
+
+ return (rv);
+}
+
+static void
+httpecho(nng_aio *aio)
+{
+ nng_http_req *req = nng_aio_get_input(aio, 0);
+ nng_http_res *res;
+ int rv;
+ void *body;
+ size_t len;
+
+ nng_http_req_get_data(req, &body, &len);
+
+ if (((rv = nng_http_res_alloc(&res)) != 0) ||
+ ((rv = nng_http_res_copy_data(res, body, len)) != 0) ||
+ ((rv = nng_http_res_set_header(
+ res, "Content-type", "text/plain")) != 0) ||
+ ((rv = nng_http_res_set_status(res, NNG_HTTP_STATUS_OK)) != 0)) {
+ nng_http_res_free(res);
+ nng_aio_finish(aio, rv);
+ return;
+ }
+ nng_aio_set_output(aio, 0, res);
+ nng_aio_finish(aio, 0);
+}
+
+static void
+server_setup(struct server_test *st, nng_http_handler *h)
+{
+ nng_sockaddr sa;
+ memset(st, 0, sizeof(*st));
+ NUTS_PASS(nng_url_parse(&st->url, "http://127.0.0.1:0"));
+ NUTS_PASS(nng_aio_alloc(&st->aio, NULL, NULL));
+ NUTS_PASS(nng_http_server_hold(&st->s, st->url));
+ if (h != NULL) {
+ st->h = h;
+ NUTS_PASS(nng_http_server_add_handler(st->s, h));
+ }
+ NUTS_PASS(nng_http_server_start(st->s));
+ NUTS_PASS(nng_http_server_get_addr(st->s, &sa));
+ nng_url_resolve_port(st->url, nng_sockaddr_port(&sa));
+ nng_url_sprintf(st->urlstr, sizeof(st->urlstr), st->url);
+
+ NUTS_PASS(nng_http_client_alloc(&st->cli, st->url));
+ nng_http_client_connect(st->cli, st->aio);
+ nng_aio_wait(st->aio);
+
+ NUTS_PASS(nng_aio_result(st->aio));
+ st->conn = nng_aio_get_output(st->aio, 0);
+ NUTS_TRUE(st->conn != NULL);
+ NUTS_PASS(nng_http_req_alloc(&st->req, st->url));
+ NUTS_PASS(nng_http_res_alloc(&st->res));
+}
+
+static void
+server_reset(struct server_test *st)
+{
+ nng_http_req_free(st->req);
+ nng_http_res_free(st->res);
+ nng_http_req_alloc(&st->req, st->url);
+ nng_http_res_alloc(&st->res);
+}
+
+static void
+server_free(struct server_test *st)
+{
+ if (st->aio != NULL) {
+ nng_aio_free(st->aio);
+ }
+ if (st->cli != NULL) {
+ nng_http_client_free(st->cli);
+ }
+ if (st->conn != NULL) {
+ nng_http_conn_close(st->conn);
+ }
+ if (st->s != NULL) {
+ nng_http_server_release(st->s);
+ }
+ if (st->url != NULL) {
+ nng_url_free(st->url);
+ }
+ if (st->req != NULL) {
+ nng_http_req_free(st->req);
+ }
+ if (st->res != NULL) {
+ nng_http_res_free(st->res);
+ }
+}
+
+static void
+test_server_basic(void)
+{
+ struct server_test st;
+ char chunk[256];
+ const void *ptr;
+ nng_iov iov;
+ nng_http_handler *h;
+
+ NUTS_PASS(nng_http_handler_alloc_static(
+ &h, "/home.html", doc1, strlen(doc1), "text/html"));
+
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/home.html"));
+ nng_http_conn_write_req(st.conn, st.req, st.aio);
+
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ NUTS_TRUE(nng_http_res_get_status(st.res) == NNG_HTTP_STATUS_OK);
+
+ ptr = nng_http_res_get_header(st.res, "Content-Length");
+ NUTS_TRUE(ptr != NULL);
+ NUTS_TRUE(atoi(ptr) == (int) strlen(doc1));
+
+ iov.iov_len = strlen(doc1);
+ iov.iov_buf = chunk;
+ NUTS_PASS(nng_aio_set_iov(st.aio, 1, &iov));
+ nng_http_conn_read_all(st.conn, st.aio);
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+ NUTS_TRUE(nng_aio_count(st.aio) == strlen(doc1));
+ NUTS_TRUE(memcmp(chunk, doc1, strlen(doc1)) == 0);
+
+ server_free(&st);
+}
+
+static void
+test_server_404(void)
+{
+ struct server_test st;
+
+ server_setup(&st, NULL);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/bogus"));
+ nng_http_conn_write_req(st.conn, st.req, st.aio);
+
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ NUTS_TRUE(
+ nng_http_res_get_status(st.res) == NNG_HTTP_STATUS_NOT_FOUND);
+
+ server_free(&st);
+}
+
+static void
+test_server_bad_version(void)
+{
+ struct server_test st;
+
+ server_setup(&st, NULL);
+
+ NUTS_PASS(nng_http_req_set_version(st.req, "HTTP/0.9"));
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/bogus"));
+ nng_http_conn_write_req(st.conn, st.req, st.aio);
+
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ NUTS_TRUE(nng_http_res_get_status(st.res) == 505);
+
+ server_free(&st);
+}
+
+void
+test_server_missing_host(void)
+{
+ struct server_test st;
+ server_setup(&st, NULL);
+
+ nng_http_req_del_header(st.req, "Host");
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/bogus"));
+ nng_http_conn_write_req(st.conn, st.req, st.aio);
+
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ NUTS_TRUE(nng_http_res_get_status(st.res) == 400);
+
+ server_free(&st);
+}
+
+void
+test_server_wrong_method(void)
+{
+ struct server_test st;
+ nng_http_handler *h;
+
+ NUTS_PASS(nng_http_handler_alloc_static(
+ &h, "/home.html", doc1, strlen(doc1), "text/html"));
+
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_method(st.req, "POST"));
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/home.html"));
+ nng_http_conn_write_req(st.conn, st.req, st.aio);
+
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_aio_wait(st.aio);
+ NUTS_PASS(nng_aio_result(st.aio));
+
+ NUTS_TRUE(nng_http_res_get_status(st.res) ==
+ NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
+ NUTS_MSG("Got result %d: %s", nng_http_res_get_status(st.res),
+ nng_http_res_get_reason(st.res));
+
+ server_free(&st);
+}
+
+static void
+test_server_post_handler(void)
+{
+ struct server_test st;
+ nng_http_handler *h;
+ char txdata[5];
+ char *rxdata;
+ size_t size;
+ void *data;
+
+ NUTS_PASS(nng_http_handler_alloc(&h, "/post", httpecho));
+ NUTS_PASS(nng_http_handler_set_method(h, "POST"));
+
+ server_setup(&st, h);
+
+ snprintf(txdata, sizeof(txdata), "1234");
+ nng_http_req_set_uri(st.req, "/post");
+ nng_http_req_set_data(st.req, txdata, strlen(txdata));
+ NUTS_PASS(nng_http_req_set_method(st.req, "POST"));
+ NUTS_PASS(httpdo(st.url, st.req, st.res, (void **) &rxdata, &size));
+ NUTS_TRUE(nng_http_res_get_status(st.res) == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(txdata));
+ NUTS_TRUE(strncmp(txdata, rxdata, size) == 0);
+ nng_free(rxdata, size);
+
+ server_reset(&st);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/post"));
+ NUTS_PASS(nng_http_req_set_method(st.req, "GET"));
+ NUTS_PASS(nng_http_req_set_data(st.req, txdata, strlen(txdata)));
+
+ NUTS_PASS(httpdo(st.url, st.req, st.res, &data, &size));
+ NUTS_TRUE(nng_http_res_get_status(st.res) ==
+ NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
+ NUTS_MSG("HTTP status was %u", nng_http_res_get_status(st.res));
+ nng_free(data, size);
+
+ server_free(&st);
+}
+
+static void
+test_server_get_redirect(void)
+{
+ char fullurl[256];
+ const char *dest;
+ void *data;
+ size_t size;
+ nng_http_handler *h;
+ struct server_test st;
+
+ // We'll use a 303 to ensure codes carry thru
+ NUTS_PASS(nng_http_handler_alloc_redirect(
+ &h, "/here", 303, "http://127.0.0.1/there"));
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/here"));
+ nng_http_req_set_method(st.req, "GET");
+
+ NUTS_PASS(httpdo(st.url, st.req, st.res, &data, &size));
+ NUTS_TRUE(nng_http_res_get_status(st.res) == 303);
+ NUTS_MSG("HTTP status got %d, expected %d (url %s)",
+ nng_http_res_get_status(st.res), 303, fullurl);
+ NUTS_TRUE(
+ (dest = nng_http_res_get_header(st.res, "Location")) != NULL);
+ NUTS_MATCH(dest, "http://127.0.0.1/there");
+ nng_free(data, size);
+
+ server_free(&st);
+}
+
+static void
+test_server_tree_redirect(void)
+{
+ char fullurl[256];
+ const char *dest;
+ void *data;
+ size_t size;
+ nng_http_handler *h;
+ struct server_test st;
+
+ // We'll use a 303 to ensure codes carry thru
+ NUTS_PASS(nng_http_handler_alloc_redirect(
+ &h, "/here", 303, "http://127.0.0.1/there"));
+ NUTS_PASS(nng_http_handler_set_tree(h));
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/here/i/go/again"));
+ nng_http_req_set_method(st.req, "GET");
+
+ NUTS_PASS(httpdo(st.url, st.req, st.res, &data, &size));
+ NUTS_TRUE(nng_http_res_get_status(st.res) == 303);
+ NUTS_MSG("HTTP status got %d, expected %d (url %s)",
+ nng_http_res_get_status(st.res), 303, fullurl);
+ NUTS_TRUE(
+ (dest = nng_http_res_get_header(st.res, "Location")) != NULL);
+ NUTS_MATCH(dest, "http://127.0.0.1/there/i/go/again");
+ nng_free(data, size);
+
+ server_free(&st);
+}
+
+static void
+test_server_post_redirect(void)
+{
+ size_t size;
+ char txdata[5];
+ const char *dest;
+ void *data;
+
+ struct server_test st;
+ nng_http_handler *h;
+
+ NUTS_PASS(nng_http_handler_alloc_redirect(
+ &h, "/here", 301, "http://127.0.0.1/there"));
+ server_setup(&st, h);
+
+ snprintf(txdata, sizeof(txdata), "1234");
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/here"));
+ nng_http_req_set_data(st.req, txdata, strlen(txdata));
+ NUTS_PASS(nng_http_req_set_method(st.req, "POST"));
+ NUTS_PASS(httpdo(st.url, st.req, st.res, (void **) &data, &size));
+ NUTS_TRUE(nng_http_res_get_status(st.res) == 301);
+ dest = nng_http_res_get_header(st.res, "Location");
+ NUTS_TRUE(dest != NULL);
+ NUTS_MATCH(dest, "http://127.0.0.1/there");
+ nng_free(data, size);
+ server_free(&st);
+}
+
+void
+test_server_post_echo_tree(void)
+{
+ struct server_test st;
+ nng_http_handler *h;
+ size_t size;
+ char txdata[5];
+ char *rxdata;
+
+ NUTS_PASS(nng_http_handler_alloc(&h, "/", httpecho));
+ NUTS_PASS(nng_http_handler_set_method(h, "POST"));
+ NUTS_PASS(nng_http_handler_set_tree(h));
+
+ server_setup(&st, h);
+
+ snprintf(txdata, sizeof(txdata), "1234");
+ nng_http_req_set_data(st.req, txdata, strlen(txdata));
+ NUTS_PASS(nng_http_req_set_method(st.req, "POST"));
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/some_sub/directory"));
+ NUTS_PASS(httpdo(st.url, st.req, st.res, (void **) &rxdata, &size));
+ NUTS_TRUE(nng_http_res_get_status(st.res) == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(txdata));
+ NUTS_TRUE(strncmp(txdata, rxdata, size) == 0);
+ nng_free(rxdata, size);
+
+ server_free(&st);
+}
+
+void
+test_server_error_page(void)
+{
+ struct server_test st;
+ void *data;
+ size_t size;
+ uint16_t stat;
+ char *ctype;
+
+ server_setup(&st, NULL);
+ NUTS_PASS(nng_http_server_set_error_page(
+ st.s, NNG_HTTP_STATUS_NOT_FOUND, doc4));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_NOT_FOUND);
+ NUTS_TRUE(size == strlen(doc4));
+ NUTS_TRUE(memcmp(data, doc4, size) == 0);
+ nng_strfree(ctype);
+ nng_free(data, size);
+ server_free(&st);
+}
+
+// internal functions we need for now
+extern char *nni_plat_temp_dir(void);
+extern char *nni_file_join(const char *, const char *);
+extern int nni_file_put(const char *, const void *, size_t);
+extern int nni_file_delete(const char *);
+
+void
+test_server_multiple_trees(void)
+{
+ char *tmpdir;
+ char *workdir;
+ char *workdir2;
+ char *file1;
+ char *file2;
+
+ struct server_test st;
+ nng_http_handler *h;
+
+ NUTS_TRUE((tmpdir = nni_plat_temp_dir()) != NULL);
+ NUTS_TRUE((workdir = nni_file_join(tmpdir, "httptest")) != NULL);
+ NUTS_TRUE((workdir2 = nni_file_join(tmpdir, "httptest2")) != NULL);
+ NUTS_TRUE((file1 = nni_file_join(workdir, "file1.txt")) != NULL);
+ NUTS_TRUE((file2 = nni_file_join(workdir2, "file2.txt")) != NULL);
+
+ NUTS_PASS(nni_file_put(file1, doc1, strlen(doc1)));
+ NUTS_PASS(nni_file_put(file2, doc2, strlen(doc2)));
+
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", workdir));
+ NUTS_PASS(nng_http_handler_set_tree(h));
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", workdir));
+ NUTS_PASS(nng_http_handler_set_tree(h));
+ NUTS_FAIL(nng_http_server_add_handler(st.s, h), NNG_EADDRINUSE);
+ nng_http_handler_free(h);
+
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/subdir", workdir2));
+ NUTS_PASS(nng_http_handler_set_tree(h));
+ NUTS_PASS(nng_http_server_add_handler(st.s, h));
+
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/subdir", workdir2));
+ NUTS_PASS(nng_http_handler_set_tree(h));
+ NUTS_FAIL(nng_http_server_add_handler(st.s, h), NNG_EADDRINUSE);
+ nng_http_handler_free(h);
+
+ nng_msleep(100);
+
+ void *data;
+ size_t size;
+ uint16_t stat;
+ char *ctype;
+
+ NUTS_CASE("Directory 1");
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/file1.txt"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(doc1));
+ NUTS_TRUE(memcmp(data, doc1, size) == 0);
+ NUTS_MATCH(ctype, "text/plain");
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_reset(&st);
+
+ NUTS_CASE("Directory 2");
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir/file2.txt"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(doc2));
+ NUTS_TRUE(memcmp(data, doc2, size) == 0);
+ NUTS_MATCH(ctype, "text/plain");
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_free(&st);
+ free(tmpdir);
+ nni_file_delete(file1);
+ nni_file_delete(file2);
+ nni_file_delete(workdir);
+ nni_file_delete(workdir2);
+ free(workdir2);
+ free(workdir);
+ free(file1);
+ free(file2);
+}
+
+struct serve_directory {
+ char *tmpdir;
+ char *workdir;
+ char *file1;
+ char *file2;
+ char *file3;
+ char *subdir1;
+ char *subdir2;
+};
+
+void
+setup_directory(struct serve_directory *sd)
+{
+ NUTS_TRUE((sd->tmpdir = nni_plat_temp_dir()) != NULL);
+ NUTS_TRUE(
+ (sd->workdir = nni_file_join(sd->tmpdir, "httptest")) != NULL);
+ NUTS_TRUE(
+ (sd->subdir1 = nni_file_join(sd->workdir, "subdir1")) != NULL);
+ NUTS_TRUE(
+ (sd->subdir2 = nni_file_join(sd->workdir, "subdir2")) != NULL);
+ NUTS_TRUE(
+ (sd->file1 = nni_file_join(sd->subdir1, "index.html")) != NULL);
+ NUTS_TRUE(
+ (sd->file2 = nni_file_join(sd->workdir, "file.txt")) != NULL);
+ NUTS_TRUE(
+ (sd->file3 = nni_file_join(sd->subdir2, "index.htm")) != NULL);
+ NUTS_PASS(nni_file_put(sd->file1, doc1, strlen(doc1)));
+ NUTS_PASS(nni_file_put(sd->file2, doc2, strlen(doc2)));
+ NUTS_PASS(nni_file_put(sd->file3, doc3, strlen(doc3)));
+}
+
+void
+clean_directory(struct serve_directory *sd)
+{
+ free(sd->tmpdir);
+ nni_file_delete(sd->file1);
+ nni_file_delete(sd->file2);
+ nni_file_delete(sd->file3);
+ nni_file_delete(sd->subdir1);
+ nni_file_delete(sd->subdir2);
+ nni_file_delete(sd->workdir);
+ free(sd->workdir);
+ free(sd->file1);
+ free(sd->file2);
+ free(sd->file3);
+ free(sd->subdir1);
+ free(sd->subdir2);
+}
+
+void
+test_serve_directory(void)
+{
+ void *data;
+ size_t size;
+ uint16_t stat;
+ char *ctype;
+ nng_http_handler *h;
+ struct server_test st;
+ struct serve_directory sd;
+
+ setup_directory(&sd);
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir1/index.html"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(doc1));
+ NUTS_TRUE(memcmp(data, doc1, size) == 0);
+ NUTS_MATCH(ctype, "text/html");
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_free(&st);
+
+ clean_directory(&sd);
+}
+
+void
+test_serve_directory_index(void)
+{
+ void *data;
+ size_t size;
+ uint16_t stat;
+ char *ctype;
+ nng_http_handler *h;
+ struct server_test st;
+ struct serve_directory sd;
+
+ setup_directory(&sd);
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
+ server_setup(&st, h);
+
+ NUTS_CASE("Directory 1: index.html");
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir1/"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(doc1));
+ NUTS_TRUE(memcmp(data, doc1, size) == 0);
+ NUTS_MATCH(ctype, "text/html");
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_reset(&st);
+
+ NUTS_CASE("Directory 2: index.htm");
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir2/"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(doc3));
+ NUTS_TRUE(memcmp(data, doc3, size) == 0);
+ NUTS_MATCH(ctype, "text/html");
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_free(&st);
+
+ clean_directory(&sd);
+}
+
+void
+test_serve_plain_text(void)
+{
+ void *data;
+ size_t size;
+ uint16_t stat;
+ char *ctype;
+ nng_http_handler *h;
+ struct server_test st;
+ struct serve_directory sd;
+
+ setup_directory(&sd);
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/file.txt"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(doc2));
+ NUTS_TRUE(memcmp(data, doc2, size) == 0);
+ NUTS_MATCH(ctype, "text/plain");
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_free(&st);
+
+ clean_directory(&sd);
+}
+
+void
+test_serve_file_parameters(void)
+{
+ void *data;
+ size_t size;
+ uint16_t stat;
+ char *ctype;
+ nng_http_handler *h;
+ struct server_test st;
+ struct serve_directory sd;
+
+ setup_directory(&sd);
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/file.txt?param=1234"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(doc2));
+ NUTS_TRUE(memcmp(data, doc2, size) == 0);
+ NUTS_MATCH(ctype, "text/plain");
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_free(&st);
+
+ clean_directory(&sd);
+}
+
+void
+test_serve_missing_index(void)
+{
+ void *data;
+ size_t size;
+ uint16_t stat;
+ char *ctype;
+ nng_http_handler *h;
+ struct server_test st;
+ struct serve_directory sd;
+
+ setup_directory(&sd);
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/index.html"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_NOT_FOUND);
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_free(&st);
+
+ clean_directory(&sd);
+}
+
+void
+test_serve_index_not_post(void)
+{
+ void *data;
+ size_t size;
+ uint16_t stat;
+ char *ctype;
+ nng_http_handler *h;
+ struct server_test st;
+ struct serve_directory sd;
+
+ setup_directory(&sd);
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir2/index.html"));
+ NUTS_PASS(nng_http_req_set_method(st.req, "POST"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_free(&st);
+
+ clean_directory(&sd);
+}
+
+void
+test_serve_subdir_index(void)
+{
+ void *data;
+ size_t size;
+ uint16_t stat;
+ char *ctype;
+ nng_http_handler *h;
+ struct server_test st;
+ struct serve_directory sd;
+
+ setup_directory(&sd);
+ NUTS_PASS(nng_http_handler_alloc_directory(&h, "/docs", sd.workdir));
+ server_setup(&st, h);
+
+ NUTS_PASS(nng_http_req_set_uri(st.req, "/docs/subdir1/"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(doc1));
+ NUTS_TRUE(memcmp(data, doc1, size) == 0);
+ NUTS_MATCH(ctype, "text/html");
+ nng_strfree(ctype);
+ nng_free(data, size);
+
+ server_free(&st);
+
+ clean_directory(&sd);
+}
+
+NUTS_TESTS = {
+ { "server basic", test_server_basic },
+ { "server 404", test_server_404 },
+ { "server bad version", test_server_bad_version },
+ { "server missing host", test_server_missing_host },
+ { "server wrong method", test_server_wrong_method },
+ { "server post handler", test_server_post_handler },
+ { "server get redirect", test_server_get_redirect },
+ { "server tree redirect", test_server_tree_redirect },
+ { "server post redirect", test_server_post_redirect },
+ { "server post echo tree", test_server_post_echo_tree },
+ { "server error page", test_server_error_page },
+ { "server multiple trees", test_server_multiple_trees },
+ { "server serve directory", test_serve_directory },
+ { "server serve index", test_serve_directory_index },
+ { "server plain text", test_serve_plain_text },
+ { "server file parameters", test_serve_file_parameters },
+ { "server index not post", test_serve_index_not_post },
+ { "server subdir index", test_serve_subdir_index },
+ { NULL, NULL },
+};
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 268b0808..9c05e270 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -125,7 +125,6 @@ else ()
endif ()
add_nng_test1(httpclient 60 NNG_SUPP_HTTP)
-add_nng_test1(httpserver 30 NNG_SUPP_HTTP)
add_nng_test(wss 30)
add_nng_test1(zt 60 NNG_TRANSPORT_ZEROTIER)
diff --git a/tests/httpserver.c b/tests/httpserver.c
deleted file mode 100644
index c3801148..00000000
--- a/tests/httpserver.c
+++ /dev/null
@@ -1,1044 +0,0 @@
-//
-// Copyright 2022 Staysail Systems, Inc. <info@staysail.tech>
-// Copyright 2018 Capitar IT Group BV <info@capitar.com>
-// 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
-// file was obtained (LICENSE.txt). A copy of the license may also be
-// found online at https://opensource.org/licenses/MIT.
-//
-
-#ifdef _WIN32
-#define strdup _strdup
-#else
-#include <arpa/inet.h>
-#endif
-
-#include "trantest.h"
-
-// Basic HTTP server tests.
-#include <nng/nng.h>
-#include <nng/supplemental/http/http.h>
-#include <nng/supplemental/tls/tls.h>
-
-#include "convey.h"
-#include "core/nng_impl.h"
-
-const char *doc1 = "<html><body>Someone <b>is</b> home!</body></html>";
-const char *doc2 = "This is a text file.";
-const char *doc3 = "<html><body>This is doc number 3.</body></html>";
-const char *doc4 = "<html><body>Whoops, Errored!</body></html>";
-
-static int
-httpdo(nng_url *url, nng_http_req *req, nng_http_res *res, void **datap,
- size_t *sizep)
-{
- int rv;
- nng_aio *aio = NULL;
- nng_http_client *cli = NULL;
- nng_http_conn *h = NULL;
- size_t clen = 0;
- void *data = NULL;
- const char *ptr;
-
- if (((rv = nng_aio_alloc(&aio, NULL, NULL)) != 0) ||
- ((rv = nng_http_client_alloc(&cli, url)) != 0)) {
- goto fail;
- }
- nng_http_client_connect(cli, aio);
- nng_aio_wait(aio);
- if ((rv = nng_aio_result(aio)) != 0) {
- goto fail;
- }
-
- h = nng_aio_get_output(aio, 0);
-
- nng_http_conn_write_req(h, req, aio);
- nng_aio_wait(aio);
- if ((rv = nng_aio_result(aio)) != 0) {
- goto fail;
- }
- nng_http_conn_read_res(h, res, aio);
- nng_aio_wait(aio);
- if ((rv = nng_aio_result(aio)) != 0) {
- goto fail;
- }
-
- clen = 0;
- if ((ptr = nng_http_res_get_header(res, "Content-Length")) != NULL) {
- clen = atoi(ptr);
- }
-
- if (clen > 0) {
- nng_iov iov;
- data = nng_alloc(clen);
- iov.iov_buf = data;
- iov.iov_len = clen;
- nng_aio_set_iov(aio, 1, &iov);
- nng_http_conn_read_all(h, aio);
- nng_aio_wait(aio);
- if ((rv = nng_aio_result(aio)) != 0) {
- goto fail;
- }
- }
-
- *datap = data;
- *sizep = clen;
-
-fail:
- if (aio != NULL) {
- nng_aio_free(aio);
- }
- if (h != NULL) {
- nng_http_conn_close(h);
- }
- if (cli != NULL) {
- nng_http_client_free(cli);
- }
-
- return (rv);
-}
-
-static int
-httpget(const char *addr, void **datap, size_t *sizep, uint16_t *statp,
- char **ctypep)
-{
- int rv;
- nng_http_req *req = NULL;
- nng_http_res *res = NULL;
- nng_url *url = NULL;
- size_t clen = 0;
- void *data = NULL;
- char *ctype = NULL;
- const char *ptr;
-
- if (((rv = nng_url_parse(&url, addr)) != 0) ||
- ((rv = nng_http_req_alloc(&req, url)) != 0) ||
- ((rv = nng_http_res_alloc(&res)) != 0)) {
- goto fail;
- }
- if ((rv = httpdo(url, req, res, &data, &clen)) != 0) {
- goto fail;
- }
-
- *statp = nng_http_res_get_status(res);
-
- if (clen > 0) {
- if ((ptr = nng_http_res_get_header(res, "Content-Type")) !=
- NULL) {
- ctype = strdup(ptr);
- }
- }
-
- *datap = data;
- *sizep = clen;
- *ctypep = ctype;
-
-fail:
- if (rv != 0) {
- if (data != NULL) {
- nng_free(data, clen);
- }
- free(ctype);
- }
- if (url != NULL) {
- nng_url_free(url);
- }
- if (req != NULL) {
- nng_http_req_free(req);
- }
- if (res != NULL) {
- nng_http_res_free(res);
- }
-
- return (rv);
-}
-
-static void
-httpecho(nng_aio *aio)
-{
- nng_http_req *req = nng_aio_get_input(aio, 0);
- nng_http_res *res;
- int rv;
- void *body;
- size_t len;
-
- nng_http_req_get_data(req, &body, &len);
-
- if (((rv = nng_http_res_alloc(&res)) != 0) ||
- ((rv = nng_http_res_copy_data(res, body, len)) != 0) ||
- ((rv = nng_http_res_set_header(
- res, "Content-type", "text/plain")) != 0) ||
- ((rv = nng_http_res_set_status(res, NNG_HTTP_STATUS_OK)) != 0)) {
- nng_http_res_free(res);
- nng_aio_finish(aio, rv);
- return;
- }
- nng_aio_set_output(aio, 0, res);
- nng_aio_finish(aio, 0);
-}
-
-TestMain("HTTP Server", {
- nng_http_server *s;
- nng_http_handler *h;
-
- Convey("We can start an HTTP server", {
- nng_aio *aio;
- char portbuf[16];
- char urlstr[48];
- nng_url *url;
-
- trantest_next_address(portbuf, "");
-
- snprintf(
- urlstr, sizeof(urlstr), "http://127.0.0.1:%s", portbuf);
-
- So(nng_url_parse(&url, urlstr) == 0);
- So(nng_aio_alloc(&aio, NULL, NULL) == 0);
-
- So(nng_http_server_hold(&s, url) == 0);
-
- Reset({
- nng_aio_free(aio);
- nng_http_server_release(s);
- nng_url_free(url);
- });
-
- So(nng_http_handler_alloc_static(&h, "/home.html", doc1,
- strlen(doc1), "text/html") == 0);
- So(nng_http_server_add_handler(s, h) == 0);
- So(nng_http_server_start(s) == 0);
-
- Convey("We can connect a client to it", {
- nng_http_client *cli;
- nng_http_conn *h;
- nng_http_req *req;
- nng_http_res *res;
-
- So(nng_http_client_alloc(&cli, url) == 0);
- nng_http_client_connect(cli, aio);
- nng_aio_wait(aio);
-
- So(nng_aio_result(aio) == 0);
- h = nng_aio_get_output(aio, 0);
- So(h != NULL);
- So(nng_http_req_alloc(&req, url) == 0);
- So(nng_http_res_alloc(&res) == 0);
-
- Reset({
- nng_http_client_free(cli);
- nng_http_conn_close(h);
- nng_http_req_free(req);
- nng_http_res_free(res);
- });
-
- Convey("404 works", {
- So(nng_http_req_set_uri(req, "/bogus") == 0);
- nng_http_conn_write_req(h, req, aio);
-
- nng_aio_wait(aio);
- So(nng_aio_result(aio) == 0);
-
- nng_http_conn_read_res(h, res, aio);
- nng_aio_wait(aio);
- So(nng_aio_result(aio) == 0);
-
- So(nng_http_res_get_status(res) == 404);
- });
-
- Convey("Valid data works", {
- char chunk[256];
- const void *ptr;
- nng_iov iov;
-
- So(nng_http_req_set_uri(req, "/home.html") ==
- 0);
- nng_http_conn_write_req(h, req, aio);
-
- nng_aio_wait(aio);
- So(nng_aio_result(aio) == 0);
-
- nng_http_conn_read_res(h, res, aio);
- nng_aio_wait(aio);
- So(nng_aio_result(aio) == 0);
-
- So(nng_http_res_get_status(res) == 200);
-
- ptr = nng_http_res_get_header(
- res, "Content-Length");
- So(ptr != NULL);
- So(atoi(ptr) == (int) strlen(doc1));
-
- iov.iov_len = strlen(doc1);
- iov.iov_buf = chunk;
- So(nng_aio_set_iov(aio, 1, &iov) == 0);
- nng_http_conn_read_all(h, aio);
- nng_aio_wait(aio);
- So(nng_aio_result(aio) == 0);
- So(nng_aio_count(aio) == strlen(doc1));
- So(memcmp(chunk, doc1, strlen(doc1)) == 0);
- });
- });
- });
-
- Convey("Directory serving works (root)", {
- char urlstr[32];
- nng_url *url;
- char *tmpdir;
- char *workdir;
- char *file1;
- char *file2;
- char *file3;
- char *subdir1;
- char *subdir2;
-
- trantest_next_address(urlstr, "http://127.0.0.1:");
- So(nng_url_parse(&url, urlstr) == 0);
- So(nng_http_server_hold(&s, url) == 0);
- So((tmpdir = nni_plat_temp_dir()) != NULL);
- So((workdir = nni_file_join(tmpdir, "httptest")) != NULL);
- So((subdir1 = nni_file_join(workdir, "subdir1")) != NULL);
- So((subdir2 = nni_file_join(workdir, "subdir2")) != NULL);
- So((file1 = nni_file_join(subdir1, "index.html")) != NULL);
- So((file2 = nni_file_join(workdir, "file.txt")) != NULL);
- So((file3 = nni_file_join(subdir2, "index.htm")) != NULL);
-
- So(nni_file_put(file1, doc1, strlen(doc1)) == 0);
- So(nni_file_put(file2, doc2, strlen(doc2)) == 0);
- So(nni_file_put(file3, doc3, strlen(doc3)) == 0);
-
- Reset({
- nng_http_server_release(s);
- free(tmpdir);
- nni_file_delete(file1);
- nni_file_delete(file2);
- nni_file_delete(file3);
- nni_file_delete(subdir1);
- nni_file_delete(subdir2);
- nni_file_delete(workdir);
- free(workdir);
- free(file1);
- free(file2);
- free(file3);
- free(subdir1);
- free(subdir2);
- nng_url_free(url);
- });
-
- So(nng_http_handler_alloc_directory(&h, "/", workdir) == 0);
- So(nng_http_server_add_handler(s, h) == 0);
- So(nng_http_server_start(s) == 0);
- nng_msleep(100);
-
- Convey("Index.html works", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(fullurl, sizeof(fullurl),
- "%s/subdir1/index.html", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_OK);
- So(size == strlen(doc1));
- So(memcmp(data, doc1, size) == 0);
- So(strcmp(ctype, "text/html") == 0);
- nng_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Index.htm works", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(
- fullurl, sizeof(fullurl), "%s/subdir2", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_OK);
- So(size == strlen(doc3));
- So(memcmp(data, doc3, size) == 0);
- So(strcmp(ctype, "text/html") == 0);
- nni_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Named file works", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(
- fullurl, sizeof(fullurl), "%s/file.txt", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_OK);
- So(size == strlen(doc2));
- So(memcmp(data, doc2, size) == 0);
- So(strcmp(ctype, "text/plain") == 0);
- nni_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Named file with URI parameters works", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(fullurl, sizeof(fullurl),
- "%s/file.txt?param=123456", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_OK);
- So(size == strlen(doc2));
- So(memcmp(data, doc2, size) == 0);
- So(strcmp(ctype, "text/plain") == 0);
- nni_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Missing index gives 404", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(fullurl, sizeof(fullurl), "%s/", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_NOT_FOUND);
- nng_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Custom error page works", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- So(nng_http_server_set_error_page(s, 404, doc4) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_NOT_FOUND);
- So(size == strlen(doc4));
- So(memcmp(data, doc4, size) == 0);
- nng_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Bad method gives 405", {
- char fullurl[256];
- void *data;
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
-
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- So(nng_http_req_set_method(req, "POST") == 0);
-
- So(httpdo(curl, req, res, &data, &size) == 0);
- So(nng_http_res_get_status(res) ==
- NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
-
- Convey("Version 0.9 gives 505", {
- char fullurl[256];
- void *data;
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
-
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- So(nng_http_req_set_version(req, "HTTP/0.9") == 0);
-
- So(httpdo(curl, req, res, &data, &size) == 0);
- So(nng_http_res_get_status(res) ==
- NNG_HTTP_STATUS_HTTP_VERSION_NOT_SUPP);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
-
- Convey("Missing Host gives 400", {
- char fullurl[256];
- void *data;
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
-
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- So(nng_http_req_del_header(req, "Host") == 0);
-
- So(httpdo(curl, req, res, &data, &size) == 0);
- So(nng_http_res_get_status(res) ==
- NNG_HTTP_STATUS_BAD_REQUEST);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
- });
-
- Convey("Directory serving works", {
- char urlstr[32];
- nng_url *url;
- char *tmpdir;
- char *workdir;
- char *file1;
- char *file2;
- char *file3;
- char *subdir1;
- char *subdir2;
-
- trantest_next_address(urlstr, "http://127.0.0.1:");
- So(nng_url_parse(&url, urlstr) == 0);
- So(nng_http_server_hold(&s, url) == 0);
- So((tmpdir = nni_plat_temp_dir()) != NULL);
- So((workdir = nni_file_join(tmpdir, "httptest")) != NULL);
- So((subdir1 = nni_file_join(workdir, "subdir1")) != NULL);
- So((subdir2 = nni_file_join(workdir, "subdir2")) != NULL);
- So((file1 = nni_file_join(subdir1, "index.html")) != NULL);
- So((file2 = nni_file_join(workdir, "file.txt")) != NULL);
- So((file3 = nni_file_join(subdir2, "index.htm")) != NULL);
-
- So(nni_file_put(file1, doc1, strlen(doc1)) == 0);
- So(nni_file_put(file2, doc2, strlen(doc2)) == 0);
- So(nni_file_put(file3, doc3, strlen(doc3)) == 0);
-
- Reset({
- nng_http_server_release(s);
- free(tmpdir);
- nni_file_delete(file1);
- nni_file_delete(file2);
- nni_file_delete(file3);
- nni_file_delete(subdir1);
- nni_file_delete(subdir2);
- nni_file_delete(workdir);
- free(workdir);
- free(file1);
- free(file2);
- free(file3);
- free(subdir1);
- free(subdir2);
- nng_url_free(url);
- });
-
- So(nng_http_handler_alloc_directory(&h, "/docs", workdir) ==
- 0);
- So(nng_http_server_add_handler(s, h) == 0);
- So(nng_http_server_start(s) == 0);
- nng_msleep(100);
-
- Convey("Index.html works", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(fullurl, sizeof(fullurl),
- "%s/docs/subdir1/index.html", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_OK);
- So(size == strlen(doc1));
- So(memcmp(data, doc1, size) == 0);
- So(strcmp(ctype, "text/html") == 0);
- nng_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Index.htm works", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(fullurl, sizeof(fullurl), "%s/docs/subdir2",
- urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_OK);
- So(size == strlen(doc3));
- So(memcmp(data, doc3, size) == 0);
- So(strcmp(ctype, "text/html") == 0);
- nni_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Named file works", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(fullurl, sizeof(fullurl), "%s/docs/file.txt",
- urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_OK);
- So(size == strlen(doc2));
- So(memcmp(data, doc2, size) == 0);
- So(strcmp(ctype, "text/plain") == 0);
- nni_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Missing index gives 404", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(fullurl, sizeof(fullurl), "%s/docs/", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_NOT_FOUND);
- nng_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Custom error page works", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- So(nng_http_server_set_error_page(s, 404, doc4) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/docs/", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_NOT_FOUND);
- So(size == strlen(doc4));
- So(memcmp(data, doc4, size) == 0);
- nng_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Bad method gives 405", {
- char fullurl[256];
- void *data;
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
-
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/docs/", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- So(nng_http_req_set_method(req, "POST") == 0);
-
- So(httpdo(curl, req, res, &data, &size) == 0);
- So(nng_http_res_get_status(res) ==
- NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
-
- Convey("Version 0.9 gives 505", {
- char fullurl[256];
- void *data;
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
-
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/docs/", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- So(nng_http_req_set_version(req, "HTTP/0.9") == 0);
-
- So(httpdo(curl, req, res, &data, &size) == 0);
- So(nng_http_res_get_status(res) ==
- NNG_HTTP_STATUS_HTTP_VERSION_NOT_SUPP);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
-
- Convey("Missing Host gives 400", {
- char fullurl[256];
- void *data;
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
-
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/docs/", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- So(nng_http_req_del_header(req, "Host") == 0);
-
- So(httpdo(curl, req, res, &data, &size) == 0);
- So(nng_http_res_get_status(res) ==
- NNG_HTTP_STATUS_BAD_REQUEST);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
- });
-
- Convey("Multiple tree handlers works", {
- char urlstr[32];
- nng_url *url;
- char *tmpdir;
- char *workdir;
- char *workdir2;
- char *file1;
- char *file2;
-
- trantest_next_address(urlstr, "http://127.0.0.1:");
- So(nng_url_parse(&url, urlstr) == 0);
- So(nng_http_server_hold(&s, url) == 0);
- So((tmpdir = nni_plat_temp_dir()) != NULL);
- So((workdir = nni_file_join(tmpdir, "httptest")) != NULL);
- So((workdir2 = nni_file_join(tmpdir, "httptest2")) != NULL);
- So((file1 = nni_file_join(workdir, "file1.txt")) != NULL);
- So((file2 = nni_file_join(workdir2, "file2.txt")) != NULL);
-
- So(nni_file_put(file1, doc1, strlen(doc1)) == 0);
- So(nni_file_put(file2, doc2, strlen(doc2)) == 0);
-
- Reset({
- nng_http_server_release(s);
- free(tmpdir);
- nni_file_delete(file1);
- nni_file_delete(file2);
- nni_file_delete(workdir);
- nni_file_delete(workdir2);
- free(workdir2);
- free(workdir);
- free(file1);
- free(file2);
- nng_url_free(url);
- });
-
- So(nng_http_handler_alloc_directory(&h, "/", workdir) == 0);
- So(nng_http_handler_set_tree(h) == 0);
- So(nng_http_server_add_handler(s, h) == 0);
-
- So(nng_http_handler_alloc_directory(&h, "/", workdir) == 0);
- So(nng_http_handler_set_tree(h) == 0);
- So(nng_http_server_add_handler(s, h) == NNG_EADDRINUSE);
- nng_http_handler_free(h);
-
- So(nng_http_handler_alloc_directory(&h, "/subdir", workdir2) ==
- 0);
- So(nng_http_handler_set_tree(h) == 0);
- So(nng_http_server_add_handler(s, h) == 0);
-
- So(nng_http_handler_alloc_directory(&h, "/subdir", workdir2) ==
- 0);
- So(nng_http_handler_set_tree(h) == 0);
- So(nng_http_server_add_handler(s, h) == NNG_EADDRINUSE);
- nng_http_handler_free(h);
-
- So(nng_http_server_start(s) == 0);
- nng_msleep(100);
-
- Convey("Named file works (1)", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(
- fullurl, sizeof(fullurl), "%s/file1.txt", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_OK);
- So(size == strlen(doc1));
- So(memcmp(data, doc1, size) == 0);
- So(strcmp(ctype, "text/plain") == 0);
- nni_strfree(ctype);
- nng_free(data, size);
- });
-
- Convey("Named file works (2)", {
- char fullurl[256];
- void *data;
- size_t size;
- uint16_t stat;
- char *ctype;
-
- snprintf(fullurl, sizeof(fullurl),
- "%s/subdir/file2.txt", urlstr);
- So(httpget(fullurl, &data, &size, &stat, &ctype) == 0);
- So(stat == NNG_HTTP_STATUS_OK);
- So(size == strlen(doc2));
- So(memcmp(data, doc2, size) == 0);
- So(strcmp(ctype, "text/plain") == 0);
- nni_strfree(ctype);
- nng_free(data, size);
- });
- });
-
- Convey("Custom POST handler works", {
- char urlstr[32];
- nng_url *url;
-
- trantest_next_address(urlstr, "http://127.0.0.1:");
- So(nng_url_parse(&url, urlstr) == 0);
- So(nng_http_server_hold(&s, url) == 0);
-
- Reset({
- nng_http_server_release(s);
- nng_url_free(url);
- });
-
- So(nng_http_handler_alloc(&h, "/post", httpecho) == 0);
- So(nng_http_handler_set_method(h, "POST") == 0);
- So(nng_http_server_add_handler(s, h) == 0);
- So(nng_http_server_start(s) == 0);
-
- nng_msleep(100);
-
- Convey("Echo POST works", {
- char fullurl[256];
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
- char txdata[5];
- char *rxdata;
-
- snprintf(txdata, sizeof(txdata), "1234");
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/post", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- nng_http_req_set_data(req, txdata, strlen(txdata));
- So(nng_http_req_set_method(req, "POST") == 0);
- So(httpdo(curl, req, res, (void **) &rxdata, &size) ==
- 0);
- So(nng_http_res_get_status(res) == NNG_HTTP_STATUS_OK);
- So(size == strlen(txdata));
- So(strncmp(txdata, rxdata, size) == 0);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(rxdata, size);
- });
-
- Convey("Get method gives 405", {
- char fullurl[256];
- void *data;
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
-
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/post", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- So(nng_http_req_set_method(req, "GET") == 0);
-
- So(httpdo(curl, req, res, &data, &size) == 0);
- So(nng_http_res_get_status(res) ==
- NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
- });
-
- Convey("Redirect handler works", {
- char urlstr[32];
- nng_url *url;
-
- trantest_next_address(urlstr, "http://127.0.0.1:");
- So(nng_url_parse(&url, urlstr) == 0);
- So(nng_http_server_hold(&s, url) == 0);
-
- Reset({
- nng_http_server_release(s);
- nng_url_free(url);
- });
-
- Convey("GET redirect works", {
- char fullurl[256];
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
- const char *dest;
- void *data;
- size_t size;
-
- So(nng_http_handler_alloc_redirect(&h, "/here", 301,
- "http://127.0.0.1/there") == 0);
- So(nng_http_server_add_handler(s, h) == 0);
- So(nng_http_server_start(s) == 0);
- nng_msleep(100);
-
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/here", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- So(nng_http_req_set_method(req, "GET") == 0);
-
- So(httpdo(curl, req, res, &data, &size) == 0);
- So(nng_http_res_get_status(res) == 301);
- So((dest = nng_http_res_get_header(res, "Location")) !=
- NULL);
- So(strcmp(dest, "http://127.0.0.1/there") == 0);
- So(data != NULL);
- So(size > 0);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
-
- Convey("Tree redirect works", {
- char fullurl[256];
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
- const char *dest;
- void *data;
- size_t size;
-
- // We'll use a 303 to ensure codes carry thru
- So(nng_http_handler_alloc_redirect(&h, "/here", 303,
- "http://127.0.0.1/there") == 0);
- So(nng_http_handler_set_tree(h) == 0);
- So(nng_http_server_add_handler(s, h) == 0);
- So(nng_http_server_start(s) == 0);
- nng_msleep(100);
-
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl),
- "%s/here/i/go/again", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- So(nng_http_req_set_method(req, "GET") == 0);
-
- So(httpdo(curl, req, res, &data, &size) == 0);
- So(nng_http_res_get_status(res) == 303);
- So((dest = nng_http_res_get_header(res, "Location")) !=
- NULL);
- So(strcmp(dest, "http://127.0.0.1/there/i/go/again") ==
- 0);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
-
- Convey("POST Redirect works", {
- char fullurl[256];
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
- char txdata[5];
- const char *dest;
- void *data;
-
- So(nng_http_handler_alloc_redirect(&h, "/here", 301,
- "http://127.0.0.1/there") == 0);
- So(nng_http_server_add_handler(s, h) == 0);
- So(nng_http_server_start(s) == 0);
- nng_msleep(100);
-
- snprintf(txdata, sizeof(txdata), "1234");
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl), "%s/here", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- nng_http_req_set_data(req, txdata, strlen(txdata));
- So(nng_http_req_set_method(req, "POST") == 0);
- So(httpdo(curl, req, res, (void **) &data, &size) ==
- 0);
- So(nng_http_res_get_status(res) == 301);
- So((dest = nng_http_res_get_header(res, "Location")) !=
- NULL);
- So(strcmp(dest, "http://127.0.0.1/there") == 0);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(data, size);
- });
- });
-
- Convey("Root tree handler works", {
- char urlstr[32];
- nng_url *url;
-
- trantest_next_address(urlstr, "http://127.0.0.1:");
- So(nng_url_parse(&url, urlstr) == 0);
- So(nng_http_server_hold(&s, url) == 0);
-
- Reset({
- nng_http_server_release(s);
- nng_url_free(url);
- });
-
- So(nng_http_handler_alloc(&h, "/", httpecho) == 0);
- So(nng_http_handler_set_method(h, "POST") == 0);
- So(nng_http_handler_set_tree(h) == 0);
- So(nng_http_server_add_handler(s, h) == 0);
- So(nng_http_server_start(s) == 0);
-
- Convey("Echo POST works", {
- char fullurl[256];
- size_t size;
- nng_http_req *req;
- nng_http_res *res;
- nng_url *curl;
- char txdata[5];
- char *rxdata;
-
- snprintf(txdata, sizeof(txdata), "1234");
- So(nng_http_res_alloc(&res) == 0);
- snprintf(fullurl, sizeof(fullurl),
- "%s/some_sub/directory", urlstr);
- So(nng_url_parse(&curl, fullurl) == 0);
- So(nng_http_req_alloc(&req, curl) == 0);
- nng_http_req_set_data(req, txdata, strlen(txdata));
- So(nng_http_req_set_method(req, "POST") == 0);
- So(httpdo(curl, req, res, (void **) &rxdata, &size) ==
- 0);
- So(nng_http_res_get_status(res) == NNG_HTTP_STATUS_OK);
- So(size == strlen(txdata));
- So(strncmp(txdata, rxdata, size) == 0);
- nng_http_req_free(req);
- nng_http_res_free(res);
- nng_url_free(curl);
- nng_free(rxdata, size);
- });
- });
-});