diff options
| author | Garrett D'Amore <garrett@damore.org> | 2018-02-07 15:00:55 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2018-02-07 15:00:55 -0800 |
| commit | a2cb5607702bb503e634261742602881026e3b5d (patch) | |
| tree | 89c8bb13ff587abe7741aac0ff758b57ca028b4b /demo/http_client | |
| parent | a99cf87099c413cc42642fc7d113092b2f390d91 (diff) | |
| download | nng-a2cb5607702bb503e634261742602881026e3b5d.tar.gz nng-a2cb5607702bb503e634261742602881026e3b5d.tar.bz2 nng-a2cb5607702bb503e634261742602881026e3b5d.zip | |
Introduce simple HTTP client demo application.
Diffstat (limited to 'demo/http_client')
| -rw-r--r-- | demo/http_client/README.adoc | 42 | ||||
| -rw-r--r-- | demo/http_client/http_client.c | 146 |
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); +} |
