doompanning/external/nng/demo/http_client/http_client.c
2023-02-03 21:18:59 +01:00

147 lines
3.7 KiB
C

//
// 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 <nng/nng.h>
#include <nng/supplemental/http/http.h>
#include <stdio.h>
#include <stdlib.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);
}
len = atoi(hdr);
if (len == 0) {
return (0);
}
// Allocate a buffer to receive the body data.
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);
}