diff options
| -rw-r--r-- | src/supplemental/http/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | src/supplemental/http/http_server_test.c | 915 | ||||
| -rw-r--r-- | tests/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | tests/httpserver.c | 1044 |
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); - }); - }); -}); |
