HTTP Support
+NNG offers support for creation of HTTP clients, and servers. NNG supports HTTP/1.1 at present, and supports +a subset of functionality, but the support should be sufficient for simple clients, REST API servers, static content servers, +and gateways between HTTP and and other protocols. It also provides support for WebSocket based connections.
+HTTP follows a request/reply model, where a client issues a request, and the server is expected to reply. +Every request is answered with a single reply.
+Header File
+#include <nng/http.h>
+
+Unlike the rest of NNG, the HTTP API in NNG requires including nng/http.h. It is not necessary to include
+the main nng/nng.h header, it will be included transitively by nng/http.h.
Connection Object
+typedef struct nng_http nng_http;
+
+The nng_http object represents a single logical HTTP connection to the server.
+For HTTP/1.1 and earlier, this will correspond to a single TCP connection, but the object
+also contains state relating to the transaction, such as the hostname used, HTTP method used,
+request headers, response status, response headers, and so forth.
An nng_http object can be reused, unless closed, so that additional transactions can be
+performed after the first transaction is complete.
At any given point in time, an nng_http object can only refer to a single HTTP transaction.
+In NNG, these nng_http objects are used in both the client and server APIs.
The nng_http object is created by either nng_http_client_connect or by an HTTP server
+object which then passes it to an [nng_http_handler] callback function.
HTTP Method
+void nng_http_set_method(nng_http *conn, const char *method);
+const char *nng_http_get_method(nng_http *conn);
+
+Each HTTP transaction has a single verb, or method, that is used. The most common methods are “GET”, “HEAD”, and “POST”, +but a number of others are possible.
+The nng_http_set_method function specifies the HTTP method to use for the transaction.
+The default is “GET”. HTTP methods are case sensitive, and generally upper-case, such as “GET”, “POST”, “HEAD”,
+and so forth. This function silently truncates any method to 32-characters. (There are no defined methods longer than this.)
The nng_http_get_method function is used, typically on a server, to retrieve the method the client
+set when issuing the transaction.
HTTP URI
+int nng_http_set_uri(nng_http *conn, const char *uri, const char *query);
+const char *nng_http_get_uri(nng_http *conn);
+
+The nng_http_set_uri function sets the URI, which normally appears like a path such as “/docs/index.html”,
+for the next transaction on conn. It sets the URI to uri, and, if query is not NULL, also appends the
+contents of query, separated by either the ‘?’ or ‘&’ character, depending on whether uri already
+contains a query string. It may return NNG_ENOMEM, or NNG_EMSGSIZE if the the result is too long,
+or NNG_EINVAL if there is some other problem with the URI.
+ + note +
+The uri and query must be already percent-encoded if necessary.
+The nni_http_get_uri function is used to obtain the URI that was previously set by nng_http_set_uri.
+If the URI is unset (such as for a freshly created connection), then it returns NULL. The returned value
+will have any query concentated, for example “/api/get_user.cgi?name=garrett”.
HTTP Version
+int nng_http_set_version(nng_http *conn, const char *version);
+const char *nng_http_get_version(nng_http *conn);
+
+The nng_http_set_version function is used to select the HTTP protocol version to use for the
+exchange. At present, only the values NNG_HTTP_VERSION_1_0 and NNG_HTTP_VERSION_1_1 (corresponding to
+“HTTP/1.0” and “HTTP/1.1”) are supported. NNG will default to using “HTTP/1.1” if this function is not called.
+If an unsupported version is supplied, NNG_ENOTSUP will be returned, otherwise zero.
The nng_http_get_version function is used to determine the version the client selected. Normally
+there is little need to use this, but there are some subtle semantic differences between HTTP/1.0 and HTTP/1.1.
+ + tip +
+There are few, if any, remaining HTTP/1.0 implementations that are not also capable of HTTP/1.1. +It might be easiest to just fail any request coming in that is not HTTP/1.1.
++ + note +
+NNG does not support HTTP/2 or HTTP/3 at this time.
+HTTP Status
+uint16_t nng_http_get_status(nng_http *conn);
+const char *nng_http_get_reason(nng_http_conn *conn);
+void nng_http_set_status(nng_http *conn, uint16_t status, const char *reason);
+
+The nng_http_get_status function obtains the numeric code (typipcally numbered from 100 through 599) returned
+by the server in the last exchange on conn. (If no exchange has been performed yet, the result is undefined.)
A descriptive message matching the status code is returned by nng_http_get_reason.
The nng_http_set_status function is used on a server in a handler callback to set the status codethat will be
+reported to the client to status, and the associated text (reason) to reason. If reason is NULL,
+then a built in reason based on the status will be used instead.
+ + tip +
+Callbacks used on the server may wish to use nng_http_server_set_error or nng_http_server_set_redirect instead of
+nng_http_set_status, because those functions will also set the response body to a suitable HTML document
+for display to users.
Status codes are defined by the IETF. Here are defininitions that NNG provides for convenience:
+Retrieving Headers
+const char *nng_http_get_header(nng_http *conn, const char *key);
+bool nng_next_header(nng_http *conn, const char **keyp, const char **valuep, void **next);
+
+The nng_http_get_header returns the header value matching key that was received over conn,
+or NULL if no such header exists.
Thus, if conn is a client connection, then this function returns the the header value +sent by the server as part of a response, whereas if it is a server connection, it returns +the header value sent by the client as part of the request.
+If multiple headers are present with the same key, they may be returned as a combined value, +with individual values separated by commas, but this behavior is not guaranteed.
+The nng_http_next_header function iterates over all the headers, using the same list
+that nng_http_get_header uses. To start, it is called with next initialized to NULL.
+If a header was found, then it returns true, and sets keyp and valuep to values containing
+the header name and value. It also updates next, which should be used for the next iteration.
Once nng_http_next_header returns false, further calls with the same parameters will continue to do so.
+The scan can be rest by setting next to NULL.
Modifying Headers
+int nng_http_add_header(nng_http *conn, const char *key, const char *val);
+int nng_http_set_header(nng_http *conn, const char *key, const char *val);
+void nng_http_del_header(nng_http *conn, const char *key);
+
+The nng_http_add_header, nng_http_set_header, and nng_http_del_header functions are
+used to add a modify either the request or response headers for conn prior to sending to the connected peer on conn.
Thus, if the conn is a client connection created by nng_http_client_connect, then the request headers are modified.
+Conversely, if it is a connection created by an HTTP server and used in a callback function, then the response headers are modified.
The nng_http_add_header function adds a header with the name key, and the value val, to the list of headers.
+In so doing, it may bring collapse multiple headers with the same name into a comma separated list, following
+the syntax specified in RFC 9110. The function may return NNG_ENOMEM, NNG_EMSGSIZE, or NNG_EINVAL.
The nng_http_set_header function adds the header if it does not already exist, but replaces any and all previously existing
+headers with the same name key, if they exist. In all other respects it behaves similarly to nng_http_add_header.
The nng_http_del_header removes all headers with name key.
+ + note +
+Some HTTP headers have special semantics, such as the “Host”, “Content-Length”, and “Content-Type” headers. +This implementation may apply those semantics, in order to conform to the specifications for HTTP, such +as by guaranting that only a single instance of one of these headers is present.
+Retrieving Body Content
+void nng_http_get_body(nng_http_conn *conn, void **datap, size_t *sizep);
+
+The nng_http_get_data obtains the most recently received request or
+response body. This will be NULL if the content has not been retrieved
+properly yet, or if the peer did not any content. (Some requests are defined
+to never have body content, such as “HEAD”.)
Storing Body Content
+void nng_http_set_body(nng_http_conn *conn, void *data, size_t size);
+void nng_http_copy_body(nng_http_conn *conn, const void *data, size_t size);
+
+The nng_http_set_data function sets the outgoing body content to data,
+which must be size bytes long. The caller must ensure that data remains
+valid for the duration of the transaction.
The nng_http_copy_data function makes a copy of data, which
+will be freed automatically when the transaction is finished, but otherwise
+behaves like nng_http_set_data.
On client conn objects, these functions update the request object, but on server +conn objects, they update the response object.
+These functions also update the relevant “Content-Length” header.
++ + note +
+The current framework does not support sending data via chunked +transfer-encoding.
++ + tip +
+It is a good idea to also set the Content-Type header.
Closing the Connection
+void nng_http_close(nng_http *conn);
+
+The nng_http_close function closes the supplied HTTP connection conn,
+including any disposing of any underlying file descriptors or related resources.
Once this function, no further access to the conn structure may be made.
+Reset Connection State
+void nng_http_reset(nng_http *conn);
+
+The nng_http_reset function resets the request and response state of the
+the connection conn, so that it is just as if it had been freshly created with
+nng_http_client_connect or passed into a handler function for a server callback.
The intended purpose of this function is to clear the object state before reusing the conn for +subsequent transactions.
+Direct Read and Write
+void nng_http_read(nng_http *conn, nng_aio *aio);
+void nng_http_write(nng_http *conn, nng_aio *aio);
+void nng_http_read_all(nng_http *conn, nng_aio *aio);
+void nng_http_write_all(nng_http *conn, nng_aio *aio);
+
+The nng_http_read and nng_http_write functions read or write data asynchronously from or to the
+connection conn, using the nng_iov that is set in aio with nng_aio_set_iov.
+These functions will complete as soon as any data is transferred.
+Use nng_aio_count to determine how much data was actually transferred.
The nng_http_read_all and nng_http_write_all functions perform the same task, but will keep resubmitting
+operations until the the entire amount of data requested by the nng_iov is transferred.
+ + note +
+These functions perform no special handling for chunked transfers.
+These functions are most likely to be useful after hijacking the connection with nng_http_hijack.
+They can be used to transfer request or response body data as well.
Hijacking Connections
+void nng_http_hijack(nng_http_conn *conn);
+
+TODO: This API will change to convert the conn into a stream object.
+The nng_http_hijack function hijacks the connection conn, causing it
+to be disassociated from the HTTP server where it was created.
The purpose of this function is the creation of HTTP upgraders (such as +WebSocket), where the underlying HTTP connection will be taken over for +some other purpose, and should not be used any further by the server.
+This function is most useful when called from a handler function.
+(See [nng_http_handler_alloc].)
+ + note +
+It is the responsibility of the caller to dispose of the underlying connection when it is no longer needed.
+Furthermore, the HTTP server will no longer send any responses to the hijacked connection, so the caller should do that as well if appropriate.
+(See [nng_http_write_response].)
+ + tip +
+This function is intended to facilitate uses cases that involve changing the protocol from HTTP, such as WebSocket. +Most applications will never need to use this function.
+Client API
+The NNG client API consists of an API for creating connections, and an API for performing +transactions on those connections.
+Client Object
+typedef struct nng_http_client nng_http_client;
+
+The nng_http_client object is the client side creator for nng_http objects.
+It is analogous to a dialer object used elsewhere in NNG, but it specifically is only for HTTP.
Create a Client
+void nng_http_client_alloc(nng_http_client *clientp, const nng_url *url);
+
+The nng_http_client_alloc allocates an HTTP client suitable for
+connecting to the server identified by url and stores a pointer to
+it in the location referenced by clientp.
Destroy a Client
+void nng_http_client_free(nng_http_client *client);
+
+The nng_http_client_free connection destroys the client object and any
+of its resources.
+ + note +
+Any connections created by nng_http_client_connect are not affected by this function,
+and must be closed explicitly as needed.
Client TLS
+int nng_http_client_get_tls(nng_http_client *client, nng_tls_config **tlsp);
+int nng_http_client_set_tls(nng_http_client *client, nng_tls_config *tls);
+
+The nng_http_client_get_tls and nng_http_client_set_tls functions are used to
+retrieve or change the TLS configuration used when making outbound connections, enabling
+TLS as a result.
If TLS has not been previously configured on client, then nng_http_client_get_tls will return NNG_EINVAL.
+Both functions will return NNG_ENOTSUP if either HTTP or TLS is not supported.
Calling nng_http_client_set_tls invalidates any client previously obtained with
+nng_http_client_get_tls, unless a separate hold on the object was obtained.
Once TLS is enabled for an nng_http_client, it is not possible to disable TLS.
+ + note +
+The TLS configuration itself cannnot be changed once it has been used to create a connection,
+such as by calling nng_http_client_connect, but a new one can be installed in the client.
+Existing connections will use the TLS configuration that there were created with.
Creating Connections
+#include <nng/http.h>
+
+void nng_http_client_connect(nng_http_client *client, nng_aio *aio);
+
+The nng_http_client_connect function makes an outgoing connection to the
+server configured for client, and creates an nng_http object for the connection.
This is done asynchronously, and when the operation succeseds the connection may be
+retried from the aio using nng_aio_get_output with index 0.
Example 1: Connecting to Google
+nng_aio *aio;
+nng_url *url;
+nng_http_client *client;
+nng_http *conn;
+int rv;
+
+// Error checks elided for clarity.
+nng_url_parse(&url, "http://www.google.com");
+nng_aio_alloc(&aio, NULL, NULL);
+nng_http_client_alloc(&client, url);
+
+nng_http_client_connect(client, aio);
+
+// Wait for connection to establish (or attempt to fail).
+nng_aio_wait(aio);
+
+if ((rv = nng_aio_result(aio)) != 0) {
+ printf("Connection failed: %s\n", nng_strerror(rv));
+} else {
+ // Connection established, get it.
+ conn = nng_aio_get_output(aio, 0);
+
+ // ... do something with it here
+
+ // Close the connection when done to avoid leaking it.
+ nng_http_close(conn);
+}
+
+Preparing a Transaction
+int nng_http_set_version(nng_http *conn, const char *version);
+int nng_http_set_uri(nng_http *conn, const char *uri);
+
+The nng_http_set_uri function provides a URI for the transaction. This will be used to
+set the URI for the request. The URI typically appears like a path, starting with “/”, although
+it may also contain a query string.
Request Body
+Sending the Request
+void nng_http_write_request(nng_http *conn, nng_aio *aio);
+
+The nng_http_write_request function starts an asynchronous write of
+the HTTP request associated with conn.
+The entire request is sent,
+including headers, and if present, the request body data.
+(The request body can be set with
+[nng_http_set_data] or [nng_http_copy_data].)
This function returns immediately, with no return value.
+Completion of the operation is signaled via the aio, and the final result
+may be obtained via nng_aio_result.
+ + tip +
+Consider using the [nng_http_transact] function,
+which provides a simpler interface for performing a complete HTTP client transaction.
Obtaining the Response
+void nng_http_read_response(nng_http *conn, nng_aio *aio);
+
+The nng_http_read_response function starts an asynchronous read from the
+HTTP connection conn, reading an HTTP response into the response associated with conn, including all
+of the related headers.
It does not transfer any response body. To do that, use nng_http_read_all or nng_http_read.
+ + note +
+At this time we have no API support for reading chunked transfers directly. Applications that +need to do so may use the direct read functions.
++ + tip +
+An easier one-shot method for many use cases might be [nng_http_transact].
Submitting the Transaction
+int nng_http_transact(nng_http *conn, nng_aio *aio);
+
+The HTTP request is issued, and the response processed, asynchronously by the nng_http_transact function.
+When the function is complete, the aio will be notified.
The nng_http_transact function is used to perform a complete
+HTTP exchange over the connection conn, sending the request
+and attached body data to the remote server, and reading the response.
The entire response is read, including any associated body, which can
+subsequently be obtained using [nng_http_get_data].
This function is intended to make creation of client applications easier, +by performing multiple asynchronous operations required to complete an +entire HTTP transaction.
+If an error occurs, the caller should close conn with nng_http_close, as it may not
+necessarily be usable with other transactions.
+ + warning +
+If the remote server tries to send an extremely large buffer, +then a corresponding allocation will be made, which can lead to denial +of service attacks. +Client applications should take care to use this only with reasonably +trust-worthy servers.
++ + note +
+A given connection conn should be used with only one +operation or transaction at a time as HTTP/1.1 has no support for +request interleaving.
+This function returns immediately, with no return value.
+Completion of the operation is signaled via the aio, and the final result
+may be obtained via [nng_aio_result()].
Response Body
+Server API
+Handlers
+Sending the Response
+void nng_http_write_response(nng_http *conn, nng_aio *aio);
+
+Normally the server will send any attached response, but there are circumstances where +a response must be sent manually, such as when hijacking a connection.
+In such a case, nng_http_write_response can be called, which will send the response and any attached data, asynchronously
+using the nng_aio aio.
By default, for HTTP/1.1 connections, the connection is kept open, and
+will be reused to receive new requests. For HTTP/1.0, or if the client has requested
+explicitly by setting the “Connection: close” header, the connection will be closed after the
+response is fully sent.