aboutsummaryrefslogtreecommitdiff
path: root/demo
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2018-02-07 15:00:55 -0800
committerGarrett D'Amore <garrett@damore.org>2018-02-07 15:00:55 -0800
commita2cb5607702bb503e634261742602881026e3b5d (patch)
tree89c8bb13ff587abe7741aac0ff758b57ca028b4b /demo
parenta99cf87099c413cc42642fc7d113092b2f390d91 (diff)
downloadnng-a2cb5607702bb503e634261742602881026e3b5d.tar.gz
nng-a2cb5607702bb503e634261742602881026e3b5d.tar.bz2
nng-a2cb5607702bb503e634261742602881026e3b5d.zip
Introduce simple HTTP client demo application.
Diffstat (limited to 'demo')
-rw-r--r--demo/http_client/README.adoc42
-rw-r--r--demo/http_client/http_client.c146
2 files changed, 188 insertions, 0 deletions
diff --git a/demo/http_client/README.adoc b/demo/http_client/README.adoc
new file mode 100644
index 00000000..a0fb54e4
--- /dev/null
+++ b/demo/http_client/README.adoc
@@ -0,0 +1,42 @@
+= http_client
+
+This is a very simple HTTP client. It only performs HTTP GET
+operations, and does not follow HTTP redirects. Think of it as
+a trivialized version of cURL. It is super simple, taking the
+URL on the command line, and emitting the results to stdout.
+
+For clarity, we are eliding TLS support.
+
+It may not work on all systems, but it should work anywhere that
+ both the standard C library and nng itself are available.
+
+We check for errors, but no effort is made to clean up resources,
+since this program just exits. In longer running programs or libraries,
+callers should take care to clean up things that they allocate.
+
+Unfortunately many famous sites use redirects (usually to HTTPS
+sites), so it's not a very useful replacement for cURL.
+
+== Compiling
+
+The following is an example typical of UNIX and similar systems like
+Linux and macOS:
+
+[source, bash]
+----
+% export CPPFLAGS="-I /usr/local/include"
+% export LDFLAGS="-L /usr/local/lib -lnng"
+% export CC="cc"
+% ${CC} ${CPPFLAGS} http_client.c -o http_client ${LDFLAGS}
+----
+
+== Running
+
+Make sure you specify the full URL (if the root page include
+the simple "/". The URL parser does not add it for you automatically.)
+
+[source, bash]
+----
+% ./http_client http://httpbin.org/ip
+----
+
diff --git a/demo/http_client/http_client.c b/demo/http_client/http_client.c
new file mode 100644
index 00000000..e3353ef8
--- /dev/null
+++ b/demo/http_client/http_client.c
@@ -0,0 +1,146 @@
+//
+// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2018 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.
+//
+
+// This is a very simple HTTP client. It only performs HTTP GET
+// operations, and does not follow HTTP redirects. Think of it as
+// a trivialized version of CURL. It is super simple, taking the
+// URL on the command line, and emitting the results to stdout.
+// For clarity, we are eliding TLS support.
+
+// It may not work on all systems, but it should work anywhere that
+// both the standard C library and nng itself are available.
+
+// We check for errors, but no effort is made to clean up resources,
+// since this program just exits. In longer running programs or libraries,
+// callers should take care to clean up things that they allocate.
+
+// Unfortunately many famous sites use redirects, so you won't see that
+// emitted.
+
+// Example usage:
+//
+// % export CPPFLAGS="-I /usr/local/include"
+// % export LDFLAGS="-L /usr/local/lib -lnng"
+// % export CC="cc"
+// % ${CC} ${CPPFLAGS} http_client.c -o http_client ${LDFLAGS}
+// % ./http_client http://httpbin.org/ip
+//
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <nng/nng.h>
+
+void
+fatal(int rv)
+{
+ fprintf(stderr, "%s\n", nng_strerror(rv));
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ nng_http_client *client;
+ nng_http_conn *conn;
+ nng_url *url;
+ nng_aio *aio;
+ nng_http_req *req;
+ nng_http_res *res;
+ const char *hdr;
+ int rv;
+ int len;
+ void *data;
+ nng_iov iov;
+
+ if (argc < 2) {
+ fprintf(stderr, "No URL supplied!\n");
+ exit(1);
+ }
+
+ if (((rv = nng_url_parse(&url, argv[1])) != 0) ||
+ ((rv = nng_http_client_alloc(&client, url)) != 0) ||
+ ((rv = nng_http_req_alloc(&req, url)) != 0) ||
+ ((rv = nng_http_res_alloc(&res)) != 0) ||
+ ((rv = nng_aio_alloc(&aio, NULL, NULL)) != 0)) {
+ fatal(rv);
+ }
+
+ // Start connection process...
+ nng_http_client_connect(client, aio);
+
+ // Wait for it to finish.
+ nng_aio_wait(aio);
+ if ((rv = nng_aio_result(aio)) != 0) {
+ fatal(rv);
+ }
+
+ // Get the connection, at the 0th output.
+ conn = nng_aio_get_output(aio, 0);
+
+ // Request is already set up with URL, and for GET via HTTP/1.1.
+ // The Host: header is already set up too.
+
+ // Send the request, and wait for that to finish.
+ nng_http_conn_write_req(conn, req, aio);
+ nng_aio_wait(aio);
+
+ if ((rv = nng_aio_result(aio)) != 0) {
+ fatal(rv);
+ }
+
+ // Read a response.
+ nng_http_conn_read_res(conn, res, aio);
+ nng_aio_wait(aio);
+
+ if ((rv = nng_aio_result(aio)) != 0) {
+ fatal(rv);
+ }
+
+ if (nng_http_res_get_status(res) != NNG_HTTP_STATUS_OK) {
+ fprintf(stderr, "HTTP Server Responded: %d %s\n",
+ nng_http_res_get_status(res),
+ nng_http_res_get_reason(res));
+ }
+
+ // This only supports regular transfer encoding (no Chunked-Encoding,
+ // and a Content-Length header is required.)
+ if ((hdr = nng_http_res_get_header(res, "Content-Length")) == NULL) {
+ fprintf(stderr, "Missing Content-Length header.\n");
+ exit(1);
+ }
+
+ if (len == 0) {
+ return (0);
+ }
+
+ // Allocate a buffer to receive the body data.
+ len = atoi(hdr);
+ data = malloc(len);
+
+ // Set up a single iov to point to the buffer.
+ iov.iov_len = len;
+ iov.iov_buf = data;
+
+ // Following never fails with fewer than 5 elements.
+ nng_aio_set_iov(aio, 1, &iov);
+
+ // Now attempt to receive the data.
+ nng_http_conn_read_all(conn, aio);
+
+ // Wait for it to complete.
+ nng_aio_wait(aio);
+
+ if ((rv = nng_aio_result(aio)) != 0) {
+ fatal(rv);
+ }
+
+ fwrite(data, 1, len, stdout);
+ return (0);
+}