aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2017-11-27 14:21:20 -0800
committerGarrett D'Amore <garrett@damore.org>2017-12-26 15:31:53 -0800
commit93db6fe3aaff421d61a15993ba6827b742ab00d1 (patch)
treed4d6372cb5d606ba9bcdb60b88b6271086940895 /tests
parentc9bf5a76b0d6aead6ae91af71ada51a17881ac0a (diff)
downloadnng-93db6fe3aaff421d61a15993ba6827b742ab00d1.tar.gz
nng-93db6fe3aaff421d61a15993ba6827b742ab00d1.tar.bz2
nng-93db6fe3aaff421d61a15993ba6827b742ab00d1.zip
fixes #2 Websocket transport
This is a rather large changeset -- it fundamentally adds websocket transport, but as part of this changeset we added a generic framework for both HTTP and websocket. We also made some supporting changes to the core, such as changing the way timeouts work for AIOs and adding additional state keeping for AIOs, and adding a common framework for deferred finalization (to avoid certain kinds of circular deadlocks during resource cleanup). We also invented a new initialization framework so that we can avoid wiring in knowledge about them into the master initialization framework. The HTTP framework is not yet complete, but it is good enough for simple static serving and building additional services on top of -- including websocket. We expect both websocket and HTTP support to evolve considerably, and so these are not part of the public API yet. Property support for the websocket transport (in particular address properties) is still missing, as is support for TLS. The websocket transport here is a bit more robust than the original nanomsg implementation, as it supports multiple sockets listening at the same port sharing the same HTTP server instance, discriminating between them based on URI (and possibly the virtual host). Websocket is enabled by default at present, and work to conditionalize HTTP and websocket further (to minimize bloat) is still pending.
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt3
-rw-r--r--tests/base64.c2
-rw-r--r--tests/httpclient.c120
-rw-r--r--tests/httpserver.c146
-rw-r--r--tests/platform.c6
-rw-r--r--tests/sha1.c2
-rw-r--r--tests/tls.c5
-rw-r--r--tests/trantest.h17
-rw-r--r--tests/ws.c97
-rw-r--r--tests/zt.c2
10 files changed, 388 insertions, 12 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 77eb0bc5..921b5925 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -152,6 +152,9 @@ add_nng_test(pair1 5)
add_nng_test(udp 5)
add_nng_test(zt 60)
add_nng_test(multistress 60)
+add_nng_test(httpclient 30)
+add_nng_test(httpserver 30)
+add_nng_test(ws 30)
# compatbility tests
# We only support these if ALL the legacy protocols are supported. This
diff --git a/tests/base64.c b/tests/base64.c
index 9682475c..714bd4b2 100644
--- a/tests/base64.c
+++ b/tests/base64.c
@@ -8,7 +8,7 @@
// found online at https://opensource.org/licenses/MIT.
//
-#include <strings.h>
+#include <string.h>
#include "convey.h"
diff --git a/tests/httpclient.c b/tests/httpclient.c
new file mode 100644
index 00000000..ab4f46a2
--- /dev/null
+++ b/tests/httpclient.c
@@ -0,0 +1,120 @@
+//
+// Copyright 2017 Garrett D'Amore <garrett@damore.org>
+// Copyright 2017 Capitar IT Group BV <info@capitar.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.
+//
+
+#include "convey.h"
+#include "trantest.h"
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#endif
+
+// Basic HTTP client tests.
+#include "core/nng_impl.h"
+#include "supplemental/http/http.h"
+#include "supplemental/sha1/sha1.h"
+
+const uint8_t utf8_sha1sum[20] = { 0x54, 0xf3, 0xb8, 0xbb, 0xfe, 0xda, 0x6f,
+ 0xb4, 0x96, 0xdd, 0xc9, 0x8b, 0x8c, 0x41, 0xf4, 0xfe, 0xe5, 0xa9, 0x7d,
+ 0xa9 };
+
+TestMain("HTTP Client", {
+
+ nni_init();
+ atexit(nng_fini);
+
+ Convey("Given a TCP connection to httpbin.org", {
+ nni_plat_tcp_ep * ep;
+ nni_plat_tcp_pipe *p;
+ nng_aio * aio;
+ nni_aio * iaio;
+ nng_sockaddr rsa;
+ nni_http_client * cli;
+ nni_http * http;
+
+ So(nng_aio_alloc(&aio, NULL, NULL) == 0);
+ iaio = (nni_aio *) aio;
+ iaio->a_addr = &rsa;
+
+ nng_aio_set_timeout(aio, 1000);
+ nni_plat_tcp_resolv("httpbin.org", "80", NNG_AF_INET, 0, iaio);
+ nng_aio_wait(aio);
+ So(nng_aio_result(aio) == 0);
+ So(rsa.s_un.s_in.sa_port == htons(80));
+
+ So(nni_http_client_init(&cli, &rsa) == 0);
+ nni_http_client_connect(cli, iaio);
+ nng_aio_wait(aio);
+ So(nng_aio_result(aio) == 0);
+ http = nni_aio_get_output(iaio, 0);
+ Reset({
+ nni_http_client_fini(cli);
+ nni_http_fini(http);
+ nng_aio_free(aio);
+ });
+
+ Convey("We can initiate a message", {
+ nni_http_req *req;
+ nni_http_res *res;
+ So(http != NULL);
+
+ So(nni_http_req_init(&req) == 0);
+ So(nni_http_res_init(&res) == 0);
+ Reset({
+ nni_http_close(http);
+ nni_http_req_fini(req);
+ nni_http_res_fini(res);
+ });
+ So(nni_http_req_set_method(req, "GET") == 0);
+ So(nni_http_req_set_version(req, "HTTP/1.1") == 0);
+ So(nni_http_req_set_uri(req, "/encoding/utf8") == 0);
+ So(nni_http_req_set_header(
+ req, "Host", "httpbin.org") == 0);
+ nni_http_write_req(http, req, iaio);
+
+ nng_aio_wait(aio);
+ So(nng_aio_result(aio) == 0);
+ nni_http_read_res(http, res, iaio);
+ nng_aio_wait(aio);
+ So(nng_aio_result(aio) == 0);
+ So(nni_http_res_get_status(res) == 200);
+
+ Convey("The message contents are correct", {
+ uint8_t digest[20];
+ void * data;
+ const char *cstr;
+ size_t sz;
+
+ cstr = nni_http_res_get_header(
+ res, "Content-Length");
+ So(cstr != NULL);
+ sz = atoi(cstr);
+ So(sz > 0);
+
+ data = nni_alloc(sz);
+ So(data != NULL);
+ Reset({ nni_free(data, sz); });
+
+ iaio->a_niov = 1;
+ iaio->a_iov[0].iov_len = sz;
+ iaio->a_iov[0].iov_buf = data;
+
+ nni_aio_wait(iaio);
+ So(nng_aio_result(aio) == 0);
+
+ nni_http_read_full(http, iaio);
+ nni_aio_wait(iaio);
+ So(nni_aio_result(iaio) == 0);
+
+ nni_sha1(data, sz, digest);
+ So(memcmp(digest, utf8_sha1sum, 20) == 0);
+ });
+ });
+ });
+});
diff --git a/tests/httpserver.c b/tests/httpserver.c
new file mode 100644
index 00000000..fa2753ea
--- /dev/null
+++ b/tests/httpserver.c
@@ -0,0 +1,146 @@
+//
+// Copyright 2017 Garrett D'Amore <garrett@damore.org>
+// Copyright 2017 Capitar IT Group BV <info@capitar.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.
+//
+
+#include "convey.h"
+#include "trantest.h"
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#endif
+
+// Basic HTTP server tests.
+#include "core/nng_impl.h"
+#include "supplemental/http/http.h"
+#include "supplemental/sha1/sha1.h"
+
+const uint8_t utf8_sha1sum[20] = { 0x54, 0xf3, 0xb8, 0xbb, 0xfe, 0xda, 0x6f,
+ 0xb4, 0x96, 0xdd, 0xc9, 0x8b, 0x8c, 0x41, 0xf4, 0xfe, 0xe5, 0xa9, 0x7d,
+ 0xa9 };
+
+void
+cleanup(void)
+{
+ nng_fini();
+}
+
+TestMain("HTTP Client", {
+
+ nni_http_server *s;
+
+ nni_init();
+ atexit(cleanup);
+
+ Convey("We can start an HTTP server", {
+ nng_sockaddr sa;
+ nni_aio * aio;
+ char portbuf[16];
+ char *doc = "<html><body>Someone <b>is</b> home!</body</html>";
+
+ trantest_next_address(portbuf, "%u");
+
+ So(nni_aio_init(&aio, NULL, NULL) == 0);
+ aio->a_addr = &sa;
+ nni_plat_tcp_resolv("127.0.0.1", portbuf, NNG_AF_INET, 0, aio);
+ nni_aio_wait(aio);
+ So(nni_aio_result(aio) == 0);
+
+ So(nni_http_server_init(&s, &sa) == 0);
+
+ Reset({
+ nni_aio_fini(aio);
+ nni_http_server_fini(s);
+ });
+
+ So(nni_http_server_add_static(s, NULL, "text/html",
+ "/home.html", doc, strlen(doc)) == 0);
+ So(nni_http_server_start(s) == 0);
+
+ Convey("We can connect a client to it", {
+ nni_http_client *cli;
+ nni_http * h;
+ nni_http_req * req;
+ nni_http_res * res;
+
+ So(nni_http_client_init(&cli, &sa) == 0);
+ nni_http_client_connect(cli, aio);
+ nni_aio_wait(aio);
+
+ So(nni_aio_result(aio) == 0);
+ h = nni_aio_get_output(aio, 0);
+ So(h != NULL);
+ So(nni_http_req_init(&req) == 0);
+ So(nni_http_res_init(&res) == 0);
+
+ Reset({
+ nni_http_client_fini(cli);
+ nni_http_fini(h);
+ nni_http_req_fini(req);
+ nni_http_res_fini(res);
+ });
+
+ Convey("404 works", {
+ So(nni_http_req_set_method(req, "GET") == 0);
+ So(nni_http_req_set_version(req, "HTTP/1.1") ==
+ 0);
+ So(nni_http_req_set_uri(req, "/bogus") == 0);
+ So(nni_http_req_set_header(
+ req, "Host", "localhost") == 0);
+ nni_http_write_req(h, req, aio);
+
+ nni_aio_wait(aio);
+ So(nni_aio_result(aio) == 0);
+
+ nni_http_read_res(h, res, aio);
+ nni_aio_wait(aio);
+ So(nni_aio_result(aio) == 0);
+
+ So(nni_http_res_get_status(res) == 404);
+ });
+
+ Convey("Valid data works", {
+ char chunk[256];
+ const void *ptr;
+
+ So(nni_http_req_set_method(req, "GET") == 0);
+ So(nni_http_req_set_version(req, "HTTP/1.1") ==
+ 0);
+ So(nni_http_req_set_uri(req, "/home.html") ==
+ 0);
+ So(nni_http_req_set_header(
+ req, "Host", "localhost") == 0);
+ nni_http_write_req(h, req, aio);
+
+ nni_aio_wait(aio);
+ So(nni_aio_result(aio) == 0);
+
+ nni_http_read_res(h, res, aio);
+ nni_aio_wait(aio);
+ So(nni_aio_result(aio) == 0);
+
+ So(nni_http_res_get_status(res) == 200);
+
+ ptr = nni_http_res_get_header(
+ res, "Content-Length");
+ So(ptr != NULL);
+ So(atoi(ptr) == strlen(doc));
+
+ aio->a_niov = 1;
+ aio->a_iov[0].iov_len = strlen(doc);
+ aio->a_iov[0].iov_buf = (void *) chunk;
+ nni_http_read_full(h, aio);
+ nni_aio_wait(aio);
+ So(nni_aio_result(aio) == 0);
+ So(nni_aio_count(aio) == strlen(doc));
+ So(memcmp(chunk, doc, strlen(doc)) == 0);
+ });
+
+ });
+ });
+});
diff --git a/tests/platform.c b/tests/platform.c
index 74dbab50..10278e7d 100644
--- a/tests/platform.c
+++ b/tests/platform.c
@@ -52,7 +52,7 @@ TestMain("Platform Operations", {
nng_msleep(100);
So((getms() - now) >= 100); // cannot be *shorter*!!
- So((getms() - now) < 150); // crummy clock resolution?
+ So((getms() - now) < 200); // crummy clock resolution?
});
Convey("times work", {
uint64_t msend;
@@ -69,8 +69,8 @@ TestMain("Platform Operations", {
usdelta = (int) (usend - usnow);
msdelta = (int) (msend - now);
So(usdelta >= 200);
- So(usdelta < 220);
- So(abs(msdelta - usdelta) < 20);
+ So(usdelta < 250); // increased tolerance for CIs
+ So(abs(msdelta - usdelta) < 50);
});
});
Convey("Mutexes work", {
diff --git a/tests/sha1.c b/tests/sha1.c
index f26a5b9c..9f1901e0 100644
--- a/tests/sha1.c
+++ b/tests/sha1.c
@@ -9,7 +9,7 @@
//
#include <stdint.h>
-#include <strings.h>
+#include <string.h>
#include "convey.h"
diff --git a/tests/tls.c b/tests/tls.c
index 6ec249cf..fa44d9c9 100644
--- a/tests/tls.c
+++ b/tests/tls.c
@@ -128,6 +128,7 @@ TestMain("TLS Transport", {
tt.init = init_tls;
tt.tmpl = "tls+tcp://127.0.0.1:%u";
+ atexit(nng_fini);
trantest_test(&tt);
@@ -170,7 +171,8 @@ TestMain("TLS Transport", {
So(nng_tls_register() == 0);
So(nng_pair_open(&s1) == 0);
Reset({ nng_close(s1); });
- So(nng_dial(s1, "tls+tcp://127.0.0.1", NULL, 0) == NNG_EADDRINVAL);
+ So(nng_dial(s1, "tls+tcp://127.0.0.1", NULL, 0) ==
+ NNG_EADDRINVAL);
So(nng_dial(s1, "tls+tcp://127.0.0.1.32", NULL, 0) ==
NNG_EADDRINVAL);
So(nng_dial(s1, "tls+tcp://127.0.x.1.32", NULL, 0) ==
@@ -183,5 +185,4 @@ TestMain("TLS Transport", {
NNG_EADDRINVAL);
});
- nng_fini();
})
diff --git a/tests/trantest.h b/tests/trantest.h
index 4f6dfe7f..b1b0ff80 100644
--- a/tests/trantest.h
+++ b/tests/trantest.h
@@ -58,6 +58,9 @@ unsigned trantest_port = 0;
#ifndef NNG_HAVE_TLS
#define nng_tls_register notransport
#endif
+#ifndef NNG_HAVE_WEBSOCKET
+#define nng_ws_register notransport
+#endif
int
notransport(void)
@@ -88,6 +91,9 @@ trantest_checktran(const char *url)
#ifndef NNG_HAVE_TLS
CHKTRAN(url, "tls+tcp:");
#endif
+#ifndef NNG_HAVE_WEBSOCKET
+ CHKTRAN(url, "ws:");
+#endif
(void) url;
}
@@ -99,7 +105,10 @@ trantest_next_address(char *out, const char *template)
if (trantest_port == 0) {
char *pstr;
- trantest_port = 5555;
+
+ // start at a different port each time -- 5000 - 10000 --
+ // unless a specific port is given.
+ trantest_port = nni_clock() % 5000 + 5000;
if (((pstr = ConveyGetEnv("TEST_PORT")) != NULL) &&
(atoi(pstr) != 0)) {
trantest_port = atoi(pstr);
@@ -218,7 +227,7 @@ trantest_send_recv(trantest *tt)
So(l != 0);
So(trantest_dial(tt) == 0);
- nng_msleep(20); // listener may be behind slightly
+ nng_msleep(200); // listener may be behind slightly
send = NULL;
So(nng_msg_alloc(&send, 0) == 0);
@@ -265,7 +274,7 @@ trantest_check_properties(trantest *tt, trantest_proptest_t f)
So(nng_dial(tt->reqsock, tt->addr, &d, 0) == 0);
So(d != 0);
- nng_msleep(10); // listener may be behind slightly
+ nng_msleep(200); // listener may be behind slightly
send = NULL;
So(nng_msg_alloc(&send, 0) == 0);
@@ -307,7 +316,7 @@ trantest_send_recv_large(trantest *tt)
So(nng_dial(tt->reqsock, tt->addr, &d, 0) == 0);
So(d != 0);
- nng_msleep(10); // listener may be behind slightly
+ nng_msleep(200); // listener may be behind slightly
send = NULL;
So(nng_msg_alloc(&send, size) == 0);
diff --git a/tests/ws.c b/tests/ws.c
new file mode 100644
index 00000000..f6eac685
--- /dev/null
+++ b/tests/ws.c
@@ -0,0 +1,97 @@
+//
+// Copyright 2017 Garrett D'Amore <garrett@damore.org>
+// Copyright 2017 Capitar IT Group BV <info@capitar.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.
+//
+
+#include "convey.h"
+#include "nng.h"
+#include "protocol/pair1/pair.h"
+#include "trantest.h"
+
+#include "stubs.h"
+// TCP tests.
+
+#ifndef _WIN32
+#include <arpa/inet.h>
+#endif
+
+static int
+check_props_v4(nng_msg *msg, nng_listener l, nng_dialer d)
+{
+#if 0
+ nng_pipe p;
+ size_t z;
+ p = nng_msg_get_pipe(msg);
+ So(p > 0);
+
+ Convey("Local address property works", {
+ nng_sockaddr la;
+ z = sizeof(nng_sockaddr);
+ So(nng_pipe_getopt(p, NNG_OPT_LOCADDR, &la, &z) == 0);
+ So(z == sizeof(la));
+ So(la.s_un.s_family == NNG_AF_INET);
+ So(la.s_un.s_in.sa_port == htons(trantest_port - 1));
+ So(la.s_un.s_in.sa_port != 0);
+ So(la.s_un.s_in.sa_addr == htonl(0x7f000001));
+ });
+
+ Convey("Remote address property works", {
+ nng_sockaddr ra;
+ z = sizeof(nng_sockaddr);
+ So(nng_pipe_getopt(p, NNG_OPT_REMADDR, &ra, &z) == 0);
+ So(z == sizeof(ra));
+ So(ra.s_un.s_family == NNG_AF_INET);
+ So(ra.s_un.s_in.sa_port != 0);
+ So(ra.s_un.s_in.sa_addr == htonl(0x7f000001));
+ });
+#endif
+ return (0);
+}
+
+TestMain("WebSocket Transport", {
+
+ trantest_test_extended("ws://127.0.0.1:%u/test", check_props_v4);
+
+ Convey("Wild cards work", {
+ nng_socket s1;
+ nng_socket s2;
+ char addr[NNG_MAXADDRLEN];
+
+ So(nng_pair_open(&s1) == 0);
+ So(nng_pair_open(&s2) == 0);
+ Reset({
+ nng_close(s2);
+ nng_close(s1);
+ });
+ trantest_next_address(addr, "ws://*:%u/test");
+ So(nng_listen(s1, addr, NULL, 0) == 0);
+ // reset port back one
+ trantest_prev_address(addr, "ws://127.0.0.1:%u/test");
+ So(nng_dial(s2, addr, NULL, 0) == 0);
+ });
+
+ Convey("Incorrect URL paths do not work", {
+ nng_socket s1;
+ nng_socket s2;
+ char addr[NNG_MAXADDRLEN];
+
+ So(nng_pair_open(&s1) == 0);
+ So(nng_pair_open(&s2) == 0);
+ Reset({
+ nng_close(s2);
+ nng_close(s1);
+ });
+ trantest_next_address(addr, "ws://*:%u/test");
+ So(nng_listen(s1, addr, NULL, 0) == 0);
+ // reset port back one
+ trantest_prev_address(addr, "ws://127.0.0.1:%u/nothere");
+ So(nng_dial(s2, addr, NULL, 0) == NNG_ECONNREFUSED);
+ });
+
+ nng_fini();
+})
diff --git a/tests/zt.c b/tests/zt.c
index 9680f82d..bf423df0 100644
--- a/tests/zt.c
+++ b/tests/zt.c
@@ -194,6 +194,7 @@ TestMain("ZeroTier Transport", {
unsigned port;
port = 5555;
+ atexit(nng_fini);
Convey("We can register the zero tier transport",
{ So(nng_zt_register() == 0); });
@@ -353,5 +354,4 @@ TestMain("ZeroTier Transport", {
trantest_test_extended("zt://" NWID "/*:%u", check_props);
- nng_fini();
})