aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/http
diff options
context:
space:
mode:
Diffstat (limited to 'src/supplemental/http')
-rw-r--r--src/supplemental/http/CMakeLists.txt3
-rw-r--r--src/supplemental/http/http_api.h131
-rw-r--r--src/supplemental/http/http_client.c139
-rw-r--r--src/supplemental/http/http_conn.c744
-rw-r--r--src/supplemental/http/http_msg.c778
-rw-r--r--src/supplemental/http/http_msg.h33
-rw-r--r--src/supplemental/http/http_public.c411
-rw-r--r--src/supplemental/http/http_server.c260
-rw-r--r--src/supplemental/http/http_server_test.c284
9 files changed, 1266 insertions, 1517 deletions
diff --git a/src/supplemental/http/CMakeLists.txt b/src/supplemental/http/CMakeLists.txt
index 0e4cc808..31a544a9 100644
--- a/src/supplemental/http/CMakeLists.txt
+++ b/src/supplemental/http/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
+# Copyright 2025 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
@@ -15,7 +15,6 @@ endif()
mark_as_advanced(NNG_ENABLE_HTTP)
nng_sources(http_public.c http_api.h)
-nng_headers(nng/supplemental/http/http.h)
nng_defines_if(NNG_SUPP_HTTP NNG_SUPP_HTTP)
nng_sources_if(NNG_SUPP_HTTP
diff --git a/src/supplemental/http/http_api.h b/src/supplemental/http/http_api.h
index 1630198d..b2b07b99 100644
--- a/src/supplemental/http/http_api.h
+++ b/src/supplemental/http/http_api.h
@@ -13,7 +13,9 @@
#define NNG_SUPPLEMENTAL_HTTP_HTTP_API_H
#include "core/nng_impl.h"
-#include <nng/supplemental/http/http.h>
+#include "nng/http.h"
+
+#include "http_msg.h"
// This represents the "internal" HTTP API. It should not be used
// or exposed to applications directly.
@@ -32,21 +34,15 @@ typedef struct nng_http_chunks nni_http_chunks;
// These functions are private to the internal framework, and really should
// not be used elsewhere.
-extern const char *nni_http_reason(uint16_t);
-
-extern void nni_http_req_init(nni_http_req *);
-extern void nni_http_req_reset(nni_http_req *);
-extern int nni_http_req_get_buf(nni_http_req *, void **, size_t *);
-extern int nni_http_req_parse(nni_http_req *, void *, size_t, size_t *);
-extern char *nni_http_req_headers(nni_http_req *);
-extern void nni_http_req_get_data(nni_http_req *, void **, size_t *);
+extern void nni_http_req_init(nni_http_req *);
+extern void nni_http_req_reset(nni_http_req *);
+extern int nni_http_req_get_buf(nni_http_req *, void **, size_t *);
+extern int nni_http_req_parse(nng_http *, void *, size_t, size_t *);
-extern void nni_http_res_init(nni_http_res *);
-extern void nni_http_res_reset(nni_http_res *);
-extern int nni_http_res_get_buf(nni_http_res *, void **, size_t *);
-extern int nni_http_res_parse(nni_http_res *, void *, size_t, size_t *);
-extern void nni_http_res_get_data(nni_http_res *, void **, size_t *);
-extern char *nni_http_res_headers(nni_http_res *);
+extern void nni_http_res_init(nni_http_res *);
+extern void nni_http_res_reset(nni_http_res *);
+extern int nni_http_res_get_buf(nni_http_conn *, void **, size_t *);
+extern int nni_http_res_parse(nng_http *, void *, size_t, size_t *);
// Chunked transfer encoding. For the moment this is not part of our public
// API. We can change that later.
@@ -99,12 +95,12 @@ extern void *nni_http_conn_get_ctx(nni_http_conn *);
// These initialization functions create stream for HTTP transactions.
// They should only be used by the server or client HTTP implementations,
// and are not for use by other code.
-extern int nni_http_conn_init(nni_http_conn **, nng_stream *);
+extern int nni_http_conn_init(nng_http **, nng_stream *, bool);
-extern void nni_http_conn_close(nni_http_conn *);
+extern void nni_http_conn_close(nng_http *);
extern void nni_http_conn_fini(nni_http_conn *);
extern int nni_http_conn_getopt(
- nni_http_conn *, const char *, void *, size_t *, nni_type);
+ nng_http *, const char *, void *, size_t *, nni_type);
// Reading messages -- the caller must supply a preinitialized (but otherwise
// idle) message. We recommend the caller store this in the aio's user data.
@@ -112,61 +108,40 @@ extern int nni_http_conn_getopt(
// must not use them for any other purpose.
extern int nni_http_req_alloc(nni_http_req **, const nng_url *);
-extern int nni_http_res_alloc(nni_http_res **);
-extern int nni_http_res_alloc_error(nni_http_res **, uint16_t);
extern int nni_http_res_set_error(nni_http_res *, uint16_t);
extern void nni_http_req_free(nni_http_req *);
extern void nni_http_res_free(nni_http_res *);
-extern void nni_http_write_req(nni_http_conn *, nni_http_req *, nni_aio *);
-extern void nni_http_read_res(nni_http_conn *, nni_http_res *, nni_aio *);
+extern void nni_http_write_req(nni_http_conn *, nni_aio *);
+extern void nni_http_read_res(nni_http_conn *, nni_aio *);
extern void nni_http_read_req(nni_http_conn *, nni_aio *);
extern void nni_http_write_res(nni_http_conn *, nni_aio *);
-extern const char *nni_http_req_get_header(const nni_http_req *, const char *);
-extern const char *nni_http_res_get_header(const nni_http_res *, const char *);
extern int nni_http_req_add_header(nni_http_req *, const char *, const char *);
-extern int nni_http_res_add_header(nni_http_res *, const char *, const char *);
-extern int nni_http_req_set_header(nni_http_req *, const char *, const char *);
-extern int nni_http_res_set_header(nni_http_res *, const char *, const char *);
extern int nni_http_req_del_header(nni_http_req *, const char *);
extern int nni_http_res_del_header(nni_http_res *, const char *);
-extern int nni_http_req_copy_data(nni_http_req *, const void *, size_t);
-extern int nni_http_res_copy_data(nni_http_res *, const void *, size_t);
-extern int nni_http_req_set_data(nni_http_req *, const void *, size_t);
-extern int nni_http_res_set_data(nni_http_res *, const void *, size_t);
extern int nni_http_req_alloc_data(nni_http_req *, size_t);
extern int nni_http_res_alloc_data(nni_http_res *, size_t);
-extern const char *nni_http_req_get_method(const nni_http_req *);
-extern const char *nni_http_req_get_version(const nni_http_req *);
-extern const char *nni_http_req_get_uri(const nni_http_req *);
-extern void nni_http_req_set_method(nni_http_req *, const char *);
-extern int nni_http_req_set_version(nni_http_req *, const char *);
-extern int nni_http_req_set_uri(nni_http_req *, const char *);
-extern int nni_http_req_set_url(nni_http_req *, const nng_url *);
-extern uint16_t nni_http_res_get_status(const nni_http_res *);
-extern void nni_http_res_set_status(nni_http_res *, uint16_t);
-extern const char *nni_http_res_get_version(const nni_http_res *);
-extern int nni_http_res_set_version(nni_http_res *, const char *);
-extern const char *nni_http_res_get_reason(const nni_http_res *);
-extern int nni_http_res_set_reason(nni_http_res *, const char *);
-
-// nni_http_res_is_error is true if the status was allocated as part of
-// nni_http_res_alloc_error(). This is a hint to the server to replace
-// the HTML body with customized content if it exists.
-extern bool nni_http_res_is_error(nni_http_res *);
-// nni_http_alloc_html_error allocates a string corresponding to an
-// HTML error. This can be set as the body of the res. The status
-// will be looked up using HTTP status code lookups, but the details
-// will be added if present as further body text. The result can
-// be freed with nni_strfree() later.
-extern int nni_http_alloc_html_error(char **, uint16_t, const char *);
+extern bool nni_http_res_is_error(nni_http_res *);
extern void nni_http_read(nni_http_conn *, nni_aio *);
extern void nni_http_read_full(nni_http_conn *, nni_aio *);
extern void nni_http_write(nni_http_conn *, nni_aio *);
extern void nni_http_write_full(nni_http_conn *, nni_aio *);
+extern int nni_http_add_header(nng_http *, const char *, const char *);
+extern int nni_http_set_header(nng_http *, const char *, const char *);
+extern void nni_http_del_header(nng_http *, const char *);
+extern const char *nni_http_get_header(nng_http *, const char *);
+
+extern void nni_http_get_body(nng_http *, void **, size_t *);
+extern void nni_http_set_body(nng_http *, void *, size_t);
+extern int nni_http_copy_body(nng_http *, const void *, size_t);
+
+// prune body clears the outgoing body (0 bytes), but leaves content-length
+// intact if present for the benefit of HEAD.
+extern void nni_http_prune_body(nng_http *);
+
// nni_http_server will look for an existing server with the same
// name and port, or create one if one does not exist. The servers
// are reference counted to permit sharing the server object across
@@ -232,14 +207,10 @@ extern void nni_http_server_close(nni_http_server *);
extern int nni_http_server_set_error_page(
nni_http_server *, uint16_t, const char *);
-// nni_http_server_set_error_page sets an error file for the named status.
-extern int nni_http_server_set_error_file(
- nni_http_server *, uint16_t, const char *);
-
// nni_http_server_res_error takes replaces the body of the res with
// a custom error page previously set for the server, using the status
// of the res. The res must have the status set first.
-extern int nni_http_server_res_error(nni_http_server *, nni_http_res *);
+extern int nni_http_server_error(nni_http_server *, nng_http *);
// nni_http_hijack is intended to be called by a handler that wishes to
// take over the processing of the HTTP session -- usually to change protocols
@@ -390,6 +361,44 @@ extern void nni_http_transact(
extern const char *nni_http_stream_scheme(const char *);
// Private method used for the server.
-extern bool nni_http_conn_res_sent(nni_http_conn *conn);
+extern bool nni_http_res_sent(nni_http_conn *conn);
+
+extern const char *nni_http_get_version(nng_http *conn);
+extern int nni_http_set_version(nng_http *conn, const char *vers);
+
+extern void nni_http_set_method(nng_http *conn, const char *method);
+extern const char *nni_http_get_method(nng_http *conn);
+
+extern int nni_http_set_status(
+ nng_http *conn, uint16_t status, const char *reason);
+
+extern uint16_t nni_http_get_status(nng_http *);
+extern const char *nni_http_get_reason(nng_http *);
+
+// nni_http_set_error flags an error using the built in HTML page.
+// unless body is not NULL. To pass no content, pass an empty string for body.
+extern int nni_http_set_error(
+ nng_http *conn, uint16_t status, const char *reason, const char *body);
+
+// nni_http_set_redirect is used to set the redirection.
+// It uses a built-in error page, with a message about the redirection, and
+// sets the response Location: header accordingly.
+extern int nni_http_set_redirect(
+ nng_http *conn, uint16_t status, const char *reason, const char *dest);
+
+extern int nni_http_set_uri(
+ nng_http *conn, const char *uri, const char *query);
+extern const char *nni_http_get_uri(nng_http *conn);
+
+extern void nni_http_set_host(nng_http *conn, const char *);
+extern void nni_http_set_content_type(nng_http *conn, const char *);
+extern void nni_http_conn_reset(nng_http *conn);
+extern int nni_http_add_header(
+ nng_http *conn, const char *key, const char *val);
+
+extern void nni_http_set_static_header(
+ nng_http *conn, nni_http_header *header, const char *key, const char *val);
+
+extern bool nni_http_parsed(nng_http *conn);
#endif // NNG_SUPPLEMENTAL_HTTP_HTTP_API_H
diff --git a/src/supplemental/http/http_client.c b/src/supplemental/http/http_client.c
index 8701a96b..7062ae3c 100644
--- a/src/supplemental/http/http_client.c
+++ b/src/supplemental/http/http_client.c
@@ -10,12 +10,14 @@
//
#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "core/nng_impl.h"
#include "http_api.h"
+#include "http_msg.h"
static nni_mtx http_txn_lk = NNI_MTX_INITIALIZER;
@@ -24,6 +26,7 @@ struct nng_http_client {
nni_mtx mtx;
bool closed;
nni_aio aio;
+ char host[260];
nng_stream_dialer *dialer;
};
@@ -70,7 +73,9 @@ http_dial_cb(void *arg)
stream = nni_aio_get_output(&c->aio, 0);
NNI_ASSERT(stream != NULL);
- rv = nni_http_conn_init(&conn, stream);
+ rv = nni_http_conn_init(&conn, stream, true);
+
+ // set up the host header
http_dial_start(c);
nni_mtx_unlock(&c->mtx);
@@ -79,7 +84,7 @@ http_dial_cb(void *arg)
nni_aio_finish_error(aio, rv);
return;
}
-
+ nni_http_set_host(conn, c->host);
nni_aio_set_output(aio, 0, conn);
nni_aio_finish(aio, 0, 0);
}
@@ -110,7 +115,8 @@ nni_http_client_init(nni_http_client **cp, const nng_url *url)
memcpy(&my_url, url, sizeof(my_url));
my_url.u_scheme = (char *) scheme;
- if (strlen(url->u_hostname) == 0) {
+ if ((strlen(url->u_hostname) == 0) ||
+ (strlen(url->u_hostname) > 253)) {
// We require a valid hostname.
return (NNG_EADDRINVAL);
}
@@ -122,6 +128,16 @@ nni_http_client_init(nni_http_client **cp, const nng_url *url)
nni_aio_list_init(&c->aios);
nni_aio_init(&c->aio, http_dial_cb, c);
+ if (nni_url_default_port(url->u_scheme) == url->u_port) {
+ snprintf(c->host, sizeof(c->host), "%s", url->u_hostname);
+ } else if (strchr(url->u_hostname, ':') != NULL) {
+ // IPv6 address, needs [wrapping]
+ snprintf(c->host, sizeof(c->host), "[%s]:%d", url->u_hostname,
+ url->u_port);
+ } else {
+ snprintf(c->host, sizeof(c->host), "%s:%d", url->u_hostname,
+ url->u_port);
+ }
if ((rv = nng_stream_dialer_alloc_url(&c->dialer, &my_url)) != 0) {
nni_http_client_fini(c);
return (rv);
@@ -190,7 +206,6 @@ nni_http_client_connect(nni_http_client *c, nni_aio *aio)
}
typedef enum http_txn_state {
- HTTP_CONNECTING,
HTTP_SENDING,
HTTP_RECVING,
HTTP_RECVING_BODY,
@@ -198,7 +213,7 @@ typedef enum http_txn_state {
} http_txn_state;
typedef struct http_txn {
- nni_aio *aio; // lower level aio
+ nni_aio aio; // lower level aio
nni_list aios; // upper level aio(s) -- maximum one
nni_http_client *client;
nni_http_conn *conn;
@@ -206,12 +221,15 @@ typedef struct http_txn {
nni_http_res *res;
nni_http_chunks *chunks;
http_txn_state state;
+ nni_reap_node reap;
} http_txn;
static void
-http_txn_fini(void *arg)
+http_txn_reap(void *arg)
{
http_txn *txn = arg;
+
+ nni_aio_stop(&txn->aio);
if (txn->client != NULL) {
// We only close the connection if we created it.
if (txn->conn != NULL) {
@@ -220,10 +238,21 @@ http_txn_fini(void *arg)
}
}
nni_http_chunks_free(txn->chunks);
- nni_aio_reap(txn->aio);
+ nni_aio_fini(&txn->aio);
NNI_FREE_STRUCT(txn);
}
+static nni_reap_list http_txn_reaplist = {
+ .rl_offset = offsetof(http_txn, reap),
+ .rl_func = (nni_cb) http_txn_reap,
+};
+
+static void
+http_txn_fini(http_txn *txn)
+{
+ nni_reap(&http_txn_reaplist, txn);
+}
+
static void
http_txn_finish_aios(http_txn *txn, int rv)
{
@@ -248,31 +277,26 @@ http_txn_cb(void *arg)
nni_http_chunk *chunk = NULL;
nni_mtx_lock(&http_txn_lk);
- if ((rv = nni_aio_result(txn->aio)) != 0) {
+ if ((rv = nni_aio_result(&txn->aio)) != 0) {
http_txn_finish_aios(txn, rv);
nni_mtx_unlock(&http_txn_lk);
http_txn_fini(txn);
return;
}
switch (txn->state) {
- case HTTP_CONNECTING:
- txn->conn = nni_aio_get_output(txn->aio, 0);
- txn->state = HTTP_SENDING;
- nni_http_write_req(txn->conn, txn->req, txn->aio);
- nni_mtx_unlock(&http_txn_lk);
- return;
-
case HTTP_SENDING:
txn->state = HTTP_RECVING;
- nni_http_read_res(txn->conn, txn->res, txn->aio);
+ nni_http_read_res(txn->conn, &txn->aio);
nni_mtx_unlock(&http_txn_lk);
return;
case HTTP_RECVING:
- // Detect chunked encoding. You poor bastard.
- if (((str = nni_http_res_get_header(
- txn->res, "Transfer-Encoding")) != NULL) &&
+ // Detect chunked encoding. You poor bastard. (Only if not
+ // HEAD.)
+ if ((strcmp(nni_http_get_method(txn->conn), "HEAD") != 0) &&
+ ((str = nni_http_get_header(
+ txn->conn, "Transfer-Encoding")) != NULL) &&
(strstr(str, "chunked") != NULL)) {
if ((rv = nni_http_chunks_init(&txn->chunks, 0)) !=
@@ -280,15 +304,15 @@ http_txn_cb(void *arg)
goto error;
}
txn->state = HTTP_RECVING_CHUNKS;
- nni_http_read_chunks(txn->conn, txn->chunks, txn->aio);
+ nni_http_read_chunks(
+ txn->conn, txn->chunks, &txn->aio);
nni_mtx_unlock(&http_txn_lk);
return;
}
- str = nni_http_req_get_method(txn->req);
- if ((nni_strcasecmp(str, "HEAD") == 0) ||
- ((str = nni_http_res_get_header(
- txn->res, "Content-Length")) == NULL) ||
+ if ((strcmp(nni_http_get_method(txn->conn), "HEAD") == 0) ||
+ ((str = nni_http_get_header(
+ txn->conn, "Content-Length")) == NULL) ||
((len = (uint64_t) strtoull(str, &end, 10)) == 0) ||
(end == NULL) || (*end != '\0')) {
// If no content-length, or HEAD (which per RFC
@@ -303,10 +327,10 @@ http_txn_cb(void *arg)
0) {
goto error;
}
- nni_http_res_get_data(txn->res, &iov.iov_buf, &iov.iov_len);
- nni_aio_set_iov(txn->aio, 1, &iov);
+ nni_http_get_body(txn->conn, &iov.iov_buf, &iov.iov_len);
+ nni_aio_set_iov(&txn->aio, 1, &iov);
txn->state = HTTP_RECVING_BODY;
- nni_http_read_full(txn->conn, txn->aio);
+ nni_http_read_full(txn->conn, &txn->aio);
nni_mtx_unlock(&http_txn_lk);
return;
@@ -324,7 +348,7 @@ http_txn_cb(void *arg)
if ((rv = nni_http_res_alloc_data(txn->res, sz)) != 0) {
goto error;
}
- nni_http_res_get_data(txn->res, (void **) &dst, &sz);
+ nni_http_get_body(txn->conn, (void **) &dst, &sz);
while ((chunk = nni_http_chunks_iter(txn->chunks, chunk)) !=
NULL) {
memcpy(dst, nni_http_chunk_data(chunk),
@@ -350,7 +374,7 @@ http_txn_cancel(nni_aio *aio, void *arg, int rv)
http_txn *txn = arg;
nni_mtx_lock(&http_txn_lk);
if (nni_aio_list_active(aio)) {
- nni_aio_abort(txn->aio, rv);
+ nni_aio_abort(&txn->aio, rv);
}
nni_mtx_unlock(&http_txn_lk);
}
@@ -364,18 +388,13 @@ void
nni_http_transact_conn(nni_http_conn *conn, nni_aio *aio)
{
http_txn *txn;
- int rv;
nni_aio_reset(aio);
if ((txn = NNI_ALLOC_STRUCT(txn)) == NULL) {
nni_aio_finish_error(aio, NNG_ENOMEM);
return;
}
- if ((rv = nni_aio_alloc(&txn->aio, http_txn_cb, txn)) != 0) {
- NNI_FREE_STRUCT(txn);
- nni_aio_finish_error(aio, rv);
- return;
- }
+ nni_aio_init(&txn->aio, http_txn_cb, txn);
nni_aio_list_init(&txn->aios);
txn->client = NULL;
txn->conn = conn;
@@ -391,55 +410,7 @@ nni_http_transact_conn(nni_http_conn *conn, nni_aio *aio)
http_txn_fini(txn);
return;
}
- nni_http_res_reset(txn->res);
- nni_list_append(&txn->aios, aio);
- nni_http_write_req(conn, txn->req, txn->aio);
- nni_mtx_unlock(&http_txn_lk);
-}
-
-// nni_http_transact_simple does a single transaction, creating a connection
-// just for the purpose, and closing it when done. (No connection caching.)
-// The reason we require a client to be created first is to deal with TLS
-// settings. A single global client (per server) may be used.
-void
-nni_http_transact(nni_http_client *client, nni_http_req *req,
- nni_http_res *res, nni_aio *aio)
-{
- http_txn *txn;
- int rv;
-
- nni_aio_reset(aio);
- if ((txn = NNI_ALLOC_STRUCT(txn)) == NULL) {
- nni_aio_finish_error(aio, NNG_ENOMEM);
- return;
- }
- if ((rv = nni_aio_alloc(&txn->aio, http_txn_cb, txn)) != 0) {
- NNI_FREE_STRUCT(txn);
- nni_aio_finish_error(aio, rv);
- return;
- }
-
- if ((rv = nni_http_req_set_header(req, "Connection", "close")) != 0) {
- nni_aio_finish_error(aio, rv);
- http_txn_fini(txn);
- return;
- }
-
- nni_aio_list_init(&txn->aios);
- txn->client = client;
- txn->conn = NULL;
- txn->req = req;
- txn->res = res;
- txn->state = HTTP_CONNECTING;
-
- nni_mtx_lock(&http_txn_lk);
- if (!nni_aio_start(aio, http_txn_cancel, txn)) {
- nni_mtx_unlock(&http_txn_lk);
- http_txn_fini(txn);
- return;
- }
- nni_http_res_reset(txn->res);
nni_list_append(&txn->aios, aio);
- nni_http_client_connect(client, txn->aio);
+ nni_http_write_req(conn, &txn->aio);
nni_mtx_unlock(&http_txn_lk);
}
diff --git a/src/supplemental/http/http_conn.c b/src/supplemental/http/http_conn.c
index 9ed882f1..20ae2f05 100644
--- a/src/supplemental/http/http_conn.c
+++ b/src/supplemental/http/http_conn.c
@@ -1,5 +1,5 @@
//
-// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
//
@@ -11,9 +11,12 @@
#include <ctype.h>
#include <stdbool.h>
+#include <stdio.h>
#include <string.h>
+#include "core/list.h"
#include "core/nng_impl.h"
+#include "nng/http.h"
#include "supplemental/tls/tls_api.h"
#include "http_api.h"
@@ -62,20 +65,20 @@ struct nng_http_conn {
size_t rd_put;
size_t rd_bufsz;
bool rd_buffered;
-
- bool res_sent;
+ bool client; // true if this is a client's connection
+ bool res_sent;
enum write_flavor wr_flavor;
};
nng_http_req *
-nni_http_conn_req(nng_http_conn *conn)
+nni_http_conn_req(nng_http *conn)
{
return (&conn->req);
}
nng_http_res *
-nni_http_conn_res(nng_http_conn *conn)
+nni_http_conn_res(nng_http *conn)
{
return (&conn->res);
}
@@ -138,6 +141,20 @@ nni_http_conn_close(nni_http_conn *conn)
nni_mtx_unlock(&conn->mtx);
}
+// http_rd_buf_pull_up pulls the content of the read buffer back to the
+// beginning, so that the next read can go at the end. This avoids the problem
+// of dealing with a read that might wrap.
+static void
+http_rd_buf_pull_up(nni_http_conn *conn)
+{
+ if (conn->rd_get != 0) {
+ memmove(conn->rd_buf, conn->rd_buf + conn->rd_get,
+ conn->rd_put - conn->rd_get);
+ conn->rd_put -= conn->rd_get;
+ conn->rd_get = 0;
+ }
+}
+
// http_rd_buf attempts to satisfy the read from data in the buffer.
static int
http_rd_buf(nni_http_conn *conn, nni_aio *aio)
@@ -196,34 +213,44 @@ http_rd_buf(nni_http_conn *conn, nni_aio *aio)
return (NNG_EAGAIN);
case HTTP_RD_REQ:
- rv = nni_http_req_parse(
- nni_aio_get_prov_data(aio), rbuf, cnt, &n);
+ conn->client = true;
+ rv = nni_http_req_parse(conn, rbuf, cnt, &n);
+ conn->client = false;
conn->rd_get += n;
if (conn->rd_get == conn->rd_put) {
conn->rd_get = conn->rd_put = 0;
}
if (rv == NNG_EAGAIN) {
nni_iov iov1;
+ http_rd_buf_pull_up(conn);
iov1.iov_buf = conn->rd_buf + conn->rd_put;
iov1.iov_len = conn->rd_bufsz - conn->rd_put;
conn->rd_buffered = true;
+ if (iov1.iov_len == 0) {
+ return (NNG_EMSGSIZE);
+ }
nni_aio_set_iov(&conn->rd_aio, 1, &iov1);
nng_stream_recv(conn->sock, &conn->rd_aio);
}
return (rv);
case HTTP_RD_RES:
- rv = nni_http_res_parse(
- nni_aio_get_prov_data(aio), rbuf, cnt, &n);
+ conn->client = false;
+ rv = nni_http_res_parse(conn, rbuf, cnt, &n);
+ conn->client = true;
conn->rd_get += n;
if (conn->rd_get == conn->rd_put) {
conn->rd_get = conn->rd_put = 0;
}
if (rv == NNG_EAGAIN) {
nni_iov iov1;
+ http_rd_buf_pull_up(conn);
iov1.iov_buf = conn->rd_buf + conn->rd_put;
iov1.iov_len = conn->rd_bufsz - conn->rd_put;
conn->rd_buffered = true;
+ if (iov1.iov_len == 0) {
+ return (NNG_EMSGSIZE);
+ }
nni_aio_set_iov(&conn->rd_aio, 1, &iov1);
nng_stream_recv(conn->sock, &conn->rd_aio);
}
@@ -511,23 +538,29 @@ http_wr_submit(nni_http_conn *conn, nni_aio *aio, enum write_flavor flavor)
}
void
-nni_http_read_req(nni_http_conn *conn, nni_aio *aio)
+nni_http_conn_reset(nng_http *conn)
{
- nni_aio_set_prov_data(aio, &conn->req);
+ nni_http_req_reset(&conn->req);
+ nni_http_res_reset(&conn->res);
+ if (strlen(conn->req.host)) {
+ nni_http_set_host(conn, conn->req.host);
+ }
+}
+void
+nni_http_read_req(nni_http_conn *conn, nni_aio *aio)
+{
// clear the sent flag (used for the server)
conn->res_sent = false;
- nni_http_req_reset(&conn->req);
+ nni_http_conn_reset(conn);
nni_mtx_lock(&conn->mtx);
http_rd_submit(conn, aio, HTTP_RD_REQ);
nni_mtx_unlock(&conn->mtx);
}
void
-nni_http_read_res(nni_http_conn *conn, nni_http_res *res, nni_aio *aio)
+nni_http_read_res(nni_http_conn *conn, nni_aio *aio)
{
- nni_aio_set_prov_data(aio, res);
-
nni_mtx_lock(&conn->mtx);
http_rd_submit(conn, aio, HTTP_RD_RES);
nni_mtx_unlock(&conn->mtx);
@@ -564,28 +597,25 @@ nni_http_read(nni_http_conn *conn, nni_aio *aio)
}
void
-nni_http_write_req(nni_http_conn *conn, nni_http_req *req, nni_aio *aio)
+nni_http_write_req(nni_http_conn *conn, nni_aio *aio)
{
int rv;
void *buf;
size_t bufsz;
- void *data;
- size_t size;
nni_iov iov[2];
int niov;
- if ((rv = nni_http_req_get_buf(req, &buf, &bufsz)) != 0) {
+ if ((rv = nni_http_req_get_buf(&conn->req, &buf, &bufsz)) != 0) {
nni_aio_finish_error(aio, rv);
return;
}
- nni_http_req_get_data(req, &data, &size);
niov = 1;
iov[0].iov_len = bufsz;
iov[0].iov_buf = buf;
- if ((size > 0) && (data != NULL)) {
+ iov[1].iov_len = conn->req.data.size;
+ iov[1].iov_buf = conn->req.data.data;
+ if ((iov[1].iov_len > 0) && (iov[1].iov_buf != NULL)) {
niov++;
- iov[1].iov_len = size;
- iov[1].iov_buf = data;
}
nni_aio_set_iov(aio, niov, iov);
@@ -597,28 +627,24 @@ nni_http_write_req(nni_http_conn *conn, nni_http_req *req, nni_aio *aio)
void
nni_http_write_res(nni_http_conn *conn, nni_aio *aio)
{
- int rv;
- void *buf;
- size_t bufsz;
- void *data;
- size_t size;
- nni_iov iov[2];
- int nio;
- nng_http_res *res = nng_http_conn_res(conn);
+ int rv;
+ void *buf;
+ size_t bufsz;
+ nni_iov iov[2];
+ int nio;
conn->res_sent = true;
- if ((rv = nni_http_res_get_buf(res, &buf, &bufsz)) != 0) {
+ if ((rv = nni_http_res_get_buf(conn, &buf, &bufsz)) != 0) {
nni_aio_finish_error(aio, rv);
return;
}
- nni_http_res_get_data(res, &data, &size);
nio = 1;
iov[0].iov_len = bufsz;
iov[0].iov_buf = buf;
- if ((size > 0) && (data != NULL)) {
+ iov[1].iov_len = conn->res.data.size;
+ iov[1].iov_buf = conn->res.data.data;
+ if ((iov[1].iov_len > 0) && (iov[1].iov_buf != NULL)) {
nio++;
- iov[1].iov_len = size;
- iov[1].iov_buf = data;
}
nni_aio_set_iov(aio, nio, iov);
@@ -643,6 +669,632 @@ nni_http_write_full(nni_http_conn *conn, nni_aio *aio)
nni_mtx_unlock(&conn->mtx);
}
+const char *
+nni_http_get_version(nng_http *conn)
+{
+ return (conn->req.vers);
+}
+
+int
+nni_http_set_version(nng_http *conn, const char *vers)
+{
+ static const char *http_versions[] = {
+ // for efficiency, we order in most likely first
+ "HTTP/1.1",
+ "HTTP/2",
+ "HTTP/3",
+ "HTTP/1.0",
+ "HTTP/0.9",
+ NULL,
+ };
+
+ vers = vers != NULL ? vers : NNG_HTTP_VERSION_1_1;
+ for (int i = 0; http_versions[i] != NULL; i++) {
+ if (strcmp(vers, http_versions[i]) == 0) {
+ conn->req.vers = http_versions[i];
+ conn->res.vers = http_versions[i];
+ return (0);
+ }
+ }
+ return (NNG_ENOTSUP);
+}
+
+void
+nni_http_set_method(nng_http *conn, const char *method)
+{
+ if (method == NULL) {
+ method = "GET";
+ }
+ // this may truncate the method, but nobody should be sending
+ // methods so long.
+ (void) snprintf(conn->req.meth, sizeof(conn->req.meth), "%s", method);
+}
+
+const char *
+nni_http_get_method(nng_http *conn)
+{
+ return (conn->req.meth);
+}
+
+uint16_t
+nni_http_get_status(nng_http *conn)
+{
+ return (conn->res.code);
+}
+
+const char *
+nni_http_reason(uint16_t code)
+{
+ static struct {
+ uint16_t code;
+ const char *mesg;
+ } http_status[] = {
+ // 200, listed first because most likely
+ { NNG_HTTP_STATUS_OK, "OK" },
+
+ // 100 series -- informational
+ { NNG_HTTP_STATUS_CONTINUE, "Continue" },
+ { NNG_HTTP_STATUS_SWITCHING, "Switching Protocols" },
+ { NNG_HTTP_STATUS_PROCESSING, "Processing" },
+
+ // 200 series -- successful
+ { NNG_HTTP_STATUS_CREATED, "Created" },
+ { NNG_HTTP_STATUS_ACCEPTED, "Accepted" },
+ { NNG_HTTP_STATUS_NOT_AUTHORITATIVE, "Not Authoritative" },
+ { NNG_HTTP_STATUS_NO_CONTENT, "No Content" },
+ { NNG_HTTP_STATUS_RESET_CONTENT, "Reset Content" },
+ { NNG_HTTP_STATUS_PARTIAL_CONTENT, "Partial Content" },
+ { NNG_HTTP_STATUS_MULTI_STATUS, "Multi-Status" },
+ { NNG_HTTP_STATUS_ALREADY_REPORTED, "Already Reported" },
+ { NNG_HTTP_STATUS_IM_USED, "IM Used" },
+
+ // 300 series -- redirection
+ { NNG_HTTP_STATUS_MULTIPLE_CHOICES, "Multiple Choices" },
+ { NNG_HTTP_STATUS_STATUS_MOVED_PERMANENTLY,
+ "Moved Permanently" },
+ { NNG_HTTP_STATUS_FOUND, "Found" },
+ { NNG_HTTP_STATUS_SEE_OTHER, "See Other" },
+ { NNG_HTTP_STATUS_NOT_MODIFIED, "Not Modified" },
+ { NNG_HTTP_STATUS_USE_PROXY, "Use Proxy" },
+ { NNG_HTTP_STATUS_TEMPORARY_REDIRECT, "Temporary Redirect" },
+ { NNG_HTTP_STATUS_PERMANENT_REDIRECT, "Permanent Redirect" },
+
+ // 400 series -- client errors
+ { NNG_HTTP_STATUS_BAD_REQUEST, "Bad Request" },
+ { NNG_HTTP_STATUS_UNAUTHORIZED, "Unauthorized" },
+ { NNG_HTTP_STATUS_PAYMENT_REQUIRED, "Payment Required" },
+ { NNG_HTTP_STATUS_FORBIDDEN, "Forbidden" },
+ { NNG_HTTP_STATUS_NOT_FOUND, "Not Found" },
+ { NNG_HTTP_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed" },
+ { NNG_HTTP_STATUS_NOT_ACCEPTABLE, "Not Acceptable" },
+ { NNG_HTTP_STATUS_PROXY_AUTH_REQUIRED,
+ "Proxy Authentication Required" },
+ { NNG_HTTP_STATUS_REQUEST_TIMEOUT, "Request Timeout" },
+ { NNG_HTTP_STATUS_CONFLICT, "Conflict" },
+ { NNG_HTTP_STATUS_GONE, "Gone" },
+ { NNG_HTTP_STATUS_LENGTH_REQUIRED, "Length Required" },
+ { NNG_HTTP_STATUS_PRECONDITION_FAILED, "Precondition Failed" },
+ { NNG_HTTP_STATUS_CONTENT_TOO_LARGE, "Content Too Large" },
+ { NNG_HTTP_STATUS_URI_TOO_LONG, "URI Too Long" },
+ { NNG_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE,
+ "Unsupported Media Type" },
+ { NNG_HTTP_STATUS_RANGE_NOT_SATISFIABLE,
+ "Range Not Satisfiable" },
+ { NNG_HTTP_STATUS_EXPECTATION_FAILED, "Expectation Failed" },
+ { NNG_HTTP_STATUS_TEAPOT, "I Am A Teapot" },
+ { NNG_HTTP_STATUS_LOCKED, "Locked" },
+ { NNG_HTTP_STATUS_FAILED_DEPENDENCY, "Failed Dependency" },
+ { NNG_HTTP_STATUS_UPGRADE_REQUIRED, "Upgrade Required" },
+ { NNG_HTTP_STATUS_PRECONDITION_REQUIRED,
+ "Precondition Required" },
+ { NNG_HTTP_STATUS_TOO_MANY_REQUESTS, "Too Many Requests" },
+ { NNG_HTTP_STATUS_HEADERS_TOO_LARGE, "Headers Too Large" },
+ { NNG_HTTP_STATUS_UNAVAIL_LEGAL_REASONS,
+ "Unavailable For Legal Reasons" },
+
+ // 500 series -- server errors
+ { NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR,
+ "Internal Server Error" },
+ { NNG_HTTP_STATUS_NOT_IMPLEMENTED, "Not Implemented" },
+ { NNG_HTTP_STATUS_BAD_REQUEST, "Bad Gateway" },
+ { NNG_HTTP_STATUS_SERVICE_UNAVAILABLE, "Service Unavailable" },
+ { NNG_HTTP_STATUS_GATEWAY_TIMEOUT, "Gateway Timeout" },
+ { NNG_HTTP_STATUS_HTTP_VERSION_NOT_SUPP,
+ "HTTP Version Not Supported" },
+ { NNG_HTTP_STATUS_VARIANT_ALSO_NEGOTIATES,
+ "Variant Also Negotiates" },
+ { NNG_HTTP_STATUS_INSUFFICIENT_STORAGE,
+ "Insufficient Storage" },
+ { NNG_HTTP_STATUS_LOOP_DETECTED, "Loop Detected" },
+ { NNG_HTTP_STATUS_NOT_EXTENDED, "Not Extended" },
+ { NNG_HTTP_STATUS_NETWORK_AUTH_REQUIRED,
+ "Network Authentication Required" },
+
+ // Terminator
+ { 0, NULL },
+ };
+
+ for (int i = 0; http_status[i].code != 0; i++) {
+ if (http_status[i].code == code) {
+ return (http_status[i].mesg);
+ }
+ }
+ return ("Unknown HTTP Status");
+}
+
+const char *
+nni_http_get_reason(nng_http *conn)
+{
+#ifdef NNG_SUPP_HTTP
+ return (
+ conn->res.rsn ? conn->res.rsn : nni_http_reason(conn->res.code));
+#else
+ return (NULL);
+#endif
+}
+
+int
+nni_http_set_status(nng_http *conn, uint16_t status, const char *reason)
+{
+ conn->res.code = status;
+ char *dup = NULL;
+ if ((reason != NULL) &&
+ (strcmp(reason, nni_http_reason(conn->res.code)) == 0)) {
+ reason = NULL;
+ return (0);
+ }
+ if ((reason != NULL) && (dup = nni_strdup(reason)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ if (conn->res.rsn != NULL) {
+ nni_strfree(conn->res.rsn);
+ }
+ conn->res.rsn = dup;
+ return (0);
+}
+
+static int
+http_conn_set_error(nng_http *conn, uint16_t status, const char *reason,
+ const char *body, const char *redirect)
+{
+ int rv;
+ char content[1024];
+ const char *prefix = "<!DOCTYPE html>\n"
+ "<html><head><title>%d %s</title>\n"
+ "<style>"
+ "body { font-family: Arial, sans serif; "
+ "text-align: center }\n"
+ "h1 { font-size: 36px; }"
+ "span { background-color: gray; color: white; "
+ "padding: 7px; "
+ "border-radius: 5px }"
+ "h2 { font-size: 24px; }"
+ "p { font-size: 20px; }"
+ "</style></head>"
+ "<body><p>&nbsp;</p>"
+ "<h1><span>%d</span></h1>"
+ "<h2>%s</h2><p>";
+ const char *suffix = "</p></body></html>";
+
+ conn->res.iserr = true;
+
+ if ((rv = nni_http_set_status(conn, status, reason)) != 0) {
+ return (rv);
+ }
+ reason = nni_http_get_reason(conn);
+
+ if (body == NULL) {
+ snprintf(content, sizeof(content), prefix, status, reason,
+ status, reason);
+ size_t avail = sizeof(content) - strlen(content);
+
+ if (redirect != NULL && strlen(redirect) > 200 &&
+ strlen(reason) < 40) {
+ // URL is too long for buffer and unlikely to be useful
+ // to humans anyway. 600 bytes will fit in the 1K
+ // buffer without issue. (Our prelude and trailer are
+ // less than 400 bytes.)
+ snprintf(content + strlen(content), avail,
+ "You should be automatically redirected.");
+ avail = sizeof(content) - strlen(content);
+ } else if (redirect != NULL) {
+ // TODO: redirect should be URL encoded.
+ snprintf(content + strlen(content), avail,
+ "You should be automatically redirected to <a "
+ "href=\"%s\">%s</a>.",
+ redirect, redirect);
+ avail = sizeof(content) - strlen(content);
+ }
+ snprintf(content + strlen(content), avail, "%s", suffix);
+ body = content;
+ }
+ if (strlen(body) > 0) {
+ nni_http_set_content_type(conn, "text/html; charset=UTF-8");
+ return (nni_http_copy_body(conn, body, strlen(body)));
+ }
+ return (0);
+}
+
+int
+nni_http_set_error(
+ nng_http *conn, uint16_t status, const char *reason, const char *body)
+{
+ return (http_conn_set_error(conn, status, reason, body, NULL));
+}
+
+int
+nni_http_set_redirect(
+ nng_http *conn, uint16_t status, const char *reason, const char *redirect)
+{
+ char *loc;
+ if ((loc = nni_strdup(redirect)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ (void) nni_http_del_header(conn, "Location");
+ nni_list_node_remove(&conn->res.location.node);
+ nni_http_free_header(&conn->res.location);
+ conn->res.location.name = "Location";
+ conn->res.location.value = loc;
+ conn->res.location.static_name = true;
+ conn->res.location.static_value = false;
+ nni_list_prepend(&conn->res.data.hdrs, &conn->res.location);
+ return (http_conn_set_error(conn, status, reason, NULL, redirect));
+}
+
+void
+nni_http_set_host(nng_http *conn, const char *host)
+{
+ if (host != conn->req.host) {
+ snprintf(conn->req.host, sizeof(conn->req.host), "%s", host);
+ }
+ nni_list_node_remove(&conn->req.host_header.node);
+ conn->req.host_header.name = "Host";
+ conn->req.host_header.value = conn->req.host;
+ conn->req.host_header.static_name = true;
+ conn->req.host_header.static_value = true;
+ conn->req.host_header.alloc_header = false;
+ nni_list_prepend(&conn->req.data.hdrs, &conn->req.host_header);
+}
+
+void
+nni_http_set_content_length(nng_http *conn, size_t size)
+{
+ nni_http_entity *data =
+ conn->client ? &conn->req.data : &conn->res.data;
+
+ snprintf(data->clen, sizeof(data->clen), "%lu", (unsigned long) size);
+ nni_http_set_static_header(
+ conn, &data->content_length, "Content-Length", data->clen);
+}
+
+void
+nni_http_set_content_type(nng_http *conn, const char *ctype)
+{
+ nni_http_entity *data =
+ conn->client ? &conn->req.data : &conn->res.data;
+ snprintf(data->ctype, sizeof(data->ctype), "%s", ctype);
+ nni_http_set_static_header(
+ conn, &data->content_type, "Content-Type", data->ctype);
+}
+
+const char *
+nni_http_get_uri(nng_http *conn)
+{
+ return (conn->req.uri);
+}
+
+int
+nni_http_set_uri(nng_http *conn, const char *uri, const char *query)
+{
+ size_t needed;
+ const char *fmt;
+
+ if (query != NULL) {
+ fmt = strchr(uri, '?') != NULL ? "%s&%s" : "%s?%s";
+ needed = strlen(uri) + strlen(query) + 1;
+ } else {
+ fmt = "%s%s";
+ query = "";
+ needed = strlen(uri);
+ }
+
+ if (conn->req.uri != NULL && (strcmp(uri, conn->req.uri) == 0) &&
+ strlen(query) == 0) {
+ // no change, do nothing
+ return (0);
+ }
+ if (conn->req.uri != NULL && conn->req.uri != conn->req.ubuf) {
+ nni_strfree(conn->req.uri);
+ }
+
+ // fast path, small size URI fits in our buffer
+ if (needed < sizeof(conn->req.ubuf)) {
+ snprintf(
+ conn->req.ubuf, sizeof(conn->req.ubuf), fmt, uri, query);
+ conn->req.uri = conn->req.ubuf;
+ return (0);
+ }
+
+ // too big, we have to allocate it (slow path)
+ if (nni_asprintf(&conn->req.uri, fmt, uri, query) != 0) {
+ return (NNG_ENOMEM);
+ }
+ return (0);
+}
+
+static int
+http_set_header(nng_http *conn, const char *key, const char *val)
+{
+ nni_http_entity *data =
+ conn->client ? &conn->req.data : &conn->res.data;
+ http_header *h;
+
+ NNI_LIST_FOREACH (&data->hdrs, h) {
+ if (nni_strcasecmp(key, h->name) == 0) {
+ char *news;
+ if ((news = nni_strdup(val)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ if (!h->static_value) {
+ nni_strfree(h->value);
+ h->value = NULL;
+ }
+ h->value = news;
+ return (0);
+ }
+ }
+
+ if ((h = NNI_ALLOC_STRUCT(h)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ h->alloc_header = true;
+ if ((h->name = nni_strdup(key)) == NULL) {
+ NNI_FREE_STRUCT(h);
+ return (NNG_ENOMEM);
+ }
+ if ((h->value = nni_strdup(val)) == NULL) {
+ nni_strfree(h->name);
+ NNI_FREE_STRUCT(h);
+ return (NNG_ENOMEM);
+ }
+ nni_list_append(&data->hdrs, h);
+ return (0);
+}
+
+static int
+http_add_header(nng_http *conn, const char *key, const char *val)
+{
+ nni_http_entity *data =
+ conn->client ? &conn->req.data : &conn->res.data;
+ http_header *h;
+ NNI_LIST_FOREACH (&data->hdrs, h) {
+ if (nni_strcasecmp(key, h->name) == 0) {
+ char *news;
+ int rv;
+ rv = nni_asprintf(&news, "%s, %s", h->value, val);
+ if (rv != 0) {
+ return (rv);
+ }
+ if (!h->static_value) {
+ nni_strfree(h->value);
+ }
+ h->value = news;
+ return (0);
+ }
+ }
+
+ if ((h = NNI_ALLOC_STRUCT(h)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ h->alloc_header = true;
+ if ((h->name = nni_strdup(key)) == NULL) {
+ NNI_FREE_STRUCT(h);
+ return (NNG_ENOMEM);
+ }
+ if ((h->value = nni_strdup(val)) == NULL) {
+ nni_strfree(h->name);
+ NNI_FREE_STRUCT(h);
+ return (NNG_ENOMEM);
+ }
+ nni_list_append(&data->hdrs, h);
+ return (0);
+}
+
+static bool
+http_set_known_header(nng_http *conn, const char *key, const char *val)
+{
+ if (nni_strcasecmp(key, "Content-Type") == 0) {
+ nni_http_set_content_type(conn, val);
+ return (true);
+ }
+ if (nni_strcasecmp(key, "Content-Length") == 0) {
+ nni_http_entity *data =
+ conn->client ? &conn->req.data : &conn->res.data;
+ snprintf(data->clen, sizeof(data->clen), "%s", val);
+ nni_http_set_static_header(
+ conn, &data->content_length, "Content-Length", data->clen);
+ return (true);
+ }
+
+ if (conn->client) {
+ if (nni_strcasecmp(key, "Host") == 0) {
+ nni_http_set_host(conn, val);
+ return (true);
+ }
+ }
+ return (false);
+}
+
+int
+nni_http_add_header(nng_http *conn, const char *key, const char *val)
+{
+ if (http_set_known_header(conn, key, val)) {
+ return (0);
+ }
+
+ return (http_add_header(conn, key, val));
+}
+
+void
+nni_http_set_static_header(
+ nng_http *conn, nni_http_header *h, const char *key, const char *val)
+{
+ nni_list *headers;
+ if (conn->client) {
+ headers = &conn->req.data.hdrs;
+ } else {
+ headers = &conn->res.data.hdrs;
+ }
+
+ nni_http_del_header(conn, key);
+ nni_list_node_remove(&h->node);
+ h->alloc_header = false;
+ h->static_name = true;
+ h->static_value = true;
+ h->name = (char *) key;
+ h->value = (char *) val;
+ nni_list_append(headers, h);
+}
+
+int
+nni_http_set_header(nng_http *conn, const char *key, const char *val)
+{
+ if (http_set_known_header(conn, key, val)) {
+ return (0);
+ }
+ return (http_set_header(conn, key, val));
+}
+
+static bool
+http_del_header_one(nni_list *hdrs, const char *key)
+{
+ http_header *h;
+ NNI_LIST_FOREACH (hdrs, h) {
+ if (nni_strcasecmp(key, h->name) == 0) {
+ nni_http_free_header(h);
+ return (true);
+ }
+ }
+ return (false);
+}
+
+void
+nni_http_del_header(nng_http *conn, const char *key)
+{
+ nni_list *hdrs =
+ conn->client ? &conn->req.data.hdrs : &conn->res.data.hdrs;
+ while (http_del_header_one(hdrs, key)) {
+ continue;
+ }
+}
+
+static const char *
+http_get_header(const nni_list *hdrs, const char *key)
+{
+ http_header *h;
+ NNI_LIST_FOREACH (hdrs, h) {
+ if (nni_strcasecmp(h->name, key) == 0) {
+ return (h->value);
+ }
+ }
+ return (NULL);
+}
+
+const char *
+nni_http_get_header(nng_http *conn, const char *key)
+{
+ if (conn->client) {
+ return (http_get_header(&conn->res.data.hdrs, key));
+ } else {
+ return (http_get_header(&conn->req.data.hdrs, key));
+ }
+}
+
+void
+nni_http_get_body(nng_http *conn, void **datap, size_t *sizep)
+{
+ if (conn->client) {
+ *datap = conn->res.data.data;
+ *sizep = conn->res.data.size;
+ } else {
+ *datap = conn->req.data.data;
+ *sizep = conn->req.data.size;
+ }
+}
+
+static void
+http_set_data(nni_http_entity *entity, const void *data, size_t size)
+{
+ if (entity->own) {
+ nni_free(entity->data, entity->size);
+ }
+ entity->data = (void *) data;
+ entity->size = size;
+ entity->own = false;
+}
+
+static int
+http_alloc_data(nni_http_entity *entity, size_t size)
+{
+ void *newdata;
+ if (size != 0) {
+ if ((newdata = nni_zalloc(size)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ }
+ http_set_data(entity, newdata, size);
+ entity->own = true;
+ return (0);
+}
+
+static int
+http_copy_data(nni_http_entity *entity, const void *data, size_t size)
+{
+ int rv;
+ if ((rv = http_alloc_data(entity, size)) == 0) {
+ memcpy(entity->data, data, size);
+ }
+ return (rv);
+}
+
+void
+nni_http_set_body(nng_http *conn, void *data, size_t size)
+{
+ if (conn->client) {
+ http_set_data(&conn->req.data, data, size);
+ } else {
+ http_set_data(&conn->res.data, data, size);
+ }
+ nni_http_set_content_length(conn, size);
+}
+
+void
+nni_http_prune_body(nng_http *conn)
+{
+ // prune body but leave content-length header intact.
+ // This is for HEAD.
+ if (conn->client) {
+ http_set_data(&conn->req.data, NULL, 0);
+ } else {
+ http_set_data(&conn->res.data, NULL, 0);
+ }
+}
+
+int
+nni_http_copy_body(nng_http *conn, const void *data, size_t size)
+{
+ int rv;
+ if (conn->client) {
+ rv = http_copy_data(&conn->req.data, data, size);
+ } else {
+ rv = http_copy_data(&conn->res.data, data, size);
+ }
+ if (rv == 0) {
+ nni_http_set_content_length(conn, size);
+ }
+ return (rv);
+}
+
int
nni_http_conn_getopt(
nni_http_conn *conn, const char *name, void *buf, size_t *szp, nni_type t)
@@ -674,26 +1326,28 @@ nni_http_conn_fini(nni_http_conn *conn)
nni_aio_fini(&conn->wr_aio);
nni_aio_fini(&conn->rd_aio);
- nni_http_req_reset(&conn->req);
- nni_http_res_reset(&conn->res);
+ nni_http_conn_reset(conn);
nni_free(conn->rd_buf, conn->rd_bufsz);
nni_mtx_fini(&conn->mtx);
NNI_FREE_STRUCT(conn);
}
static int
-http_init(nni_http_conn **connp, nng_stream *data)
+http_init(nni_http_conn **connp, nng_stream *data, bool client)
{
nni_http_conn *conn;
if ((conn = NNI_ALLOC_STRUCT(conn)) == NULL) {
return (NNG_ENOMEM);
}
+ conn->client = client;
nni_mtx_init(&conn->mtx);
nni_aio_list_init(&conn->rdq);
nni_aio_list_init(&conn->wrq);
nni_http_req_init(&conn->req);
nni_http_res_init(&conn->res);
+ nni_http_set_version(conn, NNG_HTTP_VERSION_1_1);
+ nni_http_set_method(conn, NULL);
if ((conn->rd_buf = nni_alloc(HTTP_BUFSIZE)) == NULL) {
nni_http_conn_fini(conn);
@@ -712,10 +1366,10 @@ http_init(nni_http_conn **connp, nng_stream *data)
}
int
-nni_http_conn_init(nni_http_conn **connp, nng_stream *stream)
+nni_http_conn_init(nni_http_conn **connp, nng_stream *stream, bool client)
{
int rv;
- if ((rv = http_init(connp, stream)) != 0) {
+ if ((rv = http_init(connp, stream, client)) != 0) {
nng_stream_free(stream);
}
return (rv);
@@ -723,7 +1377,13 @@ nni_http_conn_init(nni_http_conn **connp, nng_stream *stream)
// private to the HTTP framework, used on the server
bool
-nni_http_conn_res_sent(nni_http_conn *conn)
+nni_http_res_sent(nni_http_conn *conn)
{
return (conn->res_sent);
}
+
+bool
+nni_http_parsed(nng_http *conn)
+{
+ return (conn->client ? conn->res.data.parsed : conn->req.data.parsed);
+}
diff --git a/src/supplemental/http/http_msg.c b/src/supplemental/http/http_msg.c
index 6b12496c..988bb37e 100644
--- a/src/supplemental/http/http_msg.c
+++ b/src/supplemental/http/http_msg.c
@@ -14,23 +14,27 @@
#include <stdlib.h>
#include <string.h>
+#include "core/list.h"
#include "core/nng_impl.h"
#include "http_api.h"
#include "http_msg.h"
-#include "nng/supplemental/http/http.h"
+#include "nng/http.h"
-static int
-http_set_string(char **strp, const char *val)
+void
+nni_http_free_header(http_header *h)
{
- char *news;
- if (val == NULL) {
- news = NULL;
- } else if ((news = nni_strdup(val)) == NULL) {
- return (NNG_ENOMEM);
+ nni_list_node_remove(&h->node);
+ if (!h->static_name) {
+ nni_strfree(h->name);
+ h->name = NULL;
+ }
+ if (!h->static_value) {
+ nni_strfree(h->value);
+ h->value = NULL;
+ }
+ if (h->alloc_header) {
+ NNI_FREE_STRUCT(h);
}
- nni_strfree(*strp);
- *strp = news;
- return (0);
}
static void
@@ -38,10 +42,7 @@ http_headers_reset(nni_list *hdrs)
{
http_header *h;
while ((h = nni_list_first(hdrs)) != NULL) {
- nni_list_remove(hdrs, h);
- nni_strfree(h->name);
- nni_strfree(h->value);
- NNI_FREE_STRUCT(h);
+ nni_http_free_header(h);
}
}
@@ -51,57 +52,35 @@ http_entity_reset(nni_http_entity *entity)
if (entity->own && entity->size) {
nni_free(entity->data, entity->size);
}
- entity->data = NULL;
- entity->size = 0;
- entity->own = false;
+ http_headers_reset(&entity->hdrs);
+ nni_free(entity->buf, entity->bufsz);
+ entity->data = NULL;
+ entity->size = 0;
+ entity->own = false;
+ entity->parsed = false;
+ entity->buf = NULL;
+ entity->bufsz = 0;
}
void
nni_http_req_reset(nni_http_req *req)
{
- http_headers_reset(&req->hdrs);
http_entity_reset(&req->data);
- nni_strfree(req->uri);
+ if (req->uri != req->ubuf) {
+ nni_strfree(req->uri);
+ }
req->uri = NULL;
- nni_free(req->buf, req->bufsz);
- nni_http_req_set_method(req, NULL);
- nni_http_req_set_version(req, NNG_HTTP_VERSION_1_1);
- req->bufsz = 0;
- req->buf = NULL;
- req->parsed = false;
+ (void) snprintf(req->meth, sizeof(req->meth), "GET");
}
void
nni_http_res_reset(nni_http_res *res)
{
- http_headers_reset(&res->hdrs);
http_entity_reset(&res->data);
nni_strfree(res->rsn);
- res->vers = NNG_HTTP_VERSION_1_1;
- res->rsn = NULL;
- res->code = 0;
- res->parsed = false;
- nni_free(res->buf, res->bufsz);
- res->buf = NULL;
- res->bufsz = 0;
-}
-
-void
-nni_http_req_free(nni_http_req *req)
-{
- if (req != NULL) {
- nni_http_req_reset(req);
- NNI_FREE_STRUCT(req);
- }
-}
-
-void
-nni_http_res_free(nni_http_res *res)
-{
- if (res != NULL) {
- nni_http_res_reset(res);
- NNI_FREE_STRUCT(res);
- }
+ res->vers = NNG_HTTP_VERSION_1_1;
+ res->rsn = NULL;
+ res->code = 0;
}
static int
@@ -110,10 +89,7 @@ http_del_header(nni_list *hdrs, const char *key)
http_header *h;
NNI_LIST_FOREACH (hdrs, h) {
if (nni_strcasecmp(key, h->name) == 0) {
- nni_list_remove(hdrs, h);
- nni_strfree(h->name);
- nni_free(h->value, strlen(h->value) + 1);
- NNI_FREE_STRUCT(h);
+ nni_http_free_header(h);
return (0);
}
}
@@ -123,127 +99,21 @@ http_del_header(nni_list *hdrs, const char *key)
int
nni_http_req_del_header(nni_http_req *req, const char *key)
{
- return (http_del_header(&req->hdrs, key));
-}
-
-int
-nni_http_res_del_header(nni_http_res *res, const char *key)
-{
- return (http_del_header(&res->hdrs, key));
-}
-
-static int
-http_set_header(nni_list *hdrs, const char *key, const char *val)
-{
- http_header *h;
- NNI_LIST_FOREACH (hdrs, h) {
- if (nni_strcasecmp(key, h->name) == 0) {
- char *news;
- if ((news = nni_strdup(val)) == NULL) {
- return (NNG_ENOMEM);
- }
- nni_strfree(h->value);
- h->value = news;
- return (0);
- }
- }
-
- if ((h = NNI_ALLOC_STRUCT(h)) == NULL) {
- return (NNG_ENOMEM);
- }
- if ((h->name = nni_strdup(key)) == NULL) {
- NNI_FREE_STRUCT(h);
- return (NNG_ENOMEM);
- }
- if ((h->value = nni_strdup(val)) == NULL) {
- nni_strfree(h->name);
- NNI_FREE_STRUCT(h);
- return (NNG_ENOMEM);
- }
- nni_list_append(hdrs, h);
- return (0);
-}
-
-int
-nni_http_req_set_header(nni_http_req *req, const char *key, const char *val)
-{
- return (http_set_header(&req->hdrs, key, val));
-}
-
-int
-nni_http_res_set_header(nni_http_res *res, const char *key, const char *val)
-{
- return (http_set_header(&res->hdrs, key, val));
-}
-
-static int
-http_add_header(nni_list *hdrs, const char *key, const char *val)
-{
- http_header *h;
- NNI_LIST_FOREACH (hdrs, h) {
- if (nni_strcasecmp(key, h->name) == 0) {
- char *news;
- int rv;
- rv = nni_asprintf(&news, "%s, %s", h->value, val);
- if (rv != 0) {
- return (rv);
- }
- nni_strfree(h->value);
- h->value = news;
- return (0);
- }
- }
-
- if ((h = NNI_ALLOC_STRUCT(h)) == NULL) {
- return (NNG_ENOMEM);
- }
- if ((h->name = nni_strdup(key)) == NULL) {
- NNI_FREE_STRUCT(h);
- return (NNG_ENOMEM);
+ int rv = NNG_ENOENT;
+ while (http_del_header(&req->data.hdrs, key) == 0) {
+ rv = 0;
}
- if ((h->value = nni_strdup(val)) == NULL) {
- nni_strfree(h->name);
- NNI_FREE_STRUCT(h);
- return (NNG_ENOMEM);
- }
- nni_list_append(hdrs, h);
- return (0);
-}
-
-int
-nni_http_req_add_header(nni_http_req *req, const char *key, const char *val)
-{
- return (http_add_header(&req->hdrs, key, val));
+ return (rv);
}
int
-nni_http_res_add_header(nni_http_res *res, const char *key, const char *val)
-{
- return (http_add_header(&res->hdrs, key, val));
-}
-
-static const char *
-http_get_header(const nni_list *hdrs, const char *key)
+nni_http_res_del_header(nni_http_res *res, const char *key)
{
- http_header *h;
- NNI_LIST_FOREACH (hdrs, h) {
- if (nni_strcasecmp(h->name, key) == 0) {
- return (h->value);
- }
+ int rv = NNG_ENOENT;
+ while (http_del_header(&res->data.hdrs, key) == 0) {
+ rv = 0;
}
- return (NULL);
-}
-
-const char *
-nni_http_req_get_header(const nni_http_req *req, const char *key)
-{
- return (http_get_header(&req->hdrs, key));
-}
-
-const char *
-nni_http_res_get_header(const nni_http_res *res, const char *key)
-{
- return (http_get_header(&res->hdrs, key));
+ return (rv);
}
// http_entity_set_data sets the entity, but does not update the
@@ -273,81 +143,6 @@ http_entity_alloc_data(nni_http_entity *entity, size_t size)
return (0);
}
-static int
-http_entity_copy_data(nni_http_entity *entity, const void *data, size_t size)
-{
- int rv;
- if ((rv = http_entity_alloc_data(entity, size)) == 0) {
- memcpy(entity->data, data, size);
- }
- return (rv);
-}
-
-static int
-http_set_content_length(nni_http_entity *entity, nni_list *hdrs)
-{
- char buf[16];
- (void) snprintf(buf, sizeof(buf), "%u", (unsigned) entity->size);
- return (http_set_header(hdrs, "Content-Length", buf));
-}
-
-static void
-http_entity_get_data(nni_http_entity *entity, void **datap, size_t *sizep)
-{
- *datap = entity->data;
- *sizep = entity->size;
-}
-
-void
-nni_http_req_get_data(nni_http_req *req, void **datap, size_t *sizep)
-{
- http_entity_get_data(&req->data, datap, sizep);
-}
-
-void
-nni_http_res_get_data(nni_http_res *res, void **datap, size_t *sizep)
-{
- http_entity_get_data(&res->data, datap, sizep);
-}
-
-int
-nni_http_req_set_data(nni_http_req *req, const void *data, size_t size)
-{
- int rv;
-
- http_entity_set_data(&req->data, data, size);
- if ((rv = http_set_content_length(&req->data, &req->hdrs)) != 0) {
- http_entity_set_data(&req->data, NULL, 0);
- }
- return (rv);
-}
-
-int
-nni_http_res_set_data(nni_http_res *res, const void *data, size_t size)
-{
- int rv;
-
- http_entity_set_data(&res->data, data, size);
- if ((rv = http_set_content_length(&res->data, &res->hdrs)) != 0) {
- http_entity_set_data(&res->data, NULL, 0);
- }
- res->iserr = false;
- return (rv);
-}
-
-int
-nni_http_req_copy_data(nni_http_req *req, const void *data, size_t size)
-{
- int rv;
-
- if (((rv = http_entity_copy_data(&req->data, data, size)) != 0) ||
- ((rv = http_set_content_length(&req->data, &req->hdrs)) != 0)) {
- http_entity_set_data(&req->data, NULL, 0);
- return (rv);
- }
- return (0);
-}
-
int
nni_http_req_alloc_data(nni_http_req *req, size_t size)
{
@@ -359,20 +154,6 @@ nni_http_req_alloc_data(nni_http_req *req, size_t size)
return (0);
}
-int
-nni_http_res_copy_data(nni_http_res *res, const void *data, size_t size)
-{
- int rv;
-
- if (((rv = http_entity_copy_data(&res->data, data, size)) != 0) ||
- ((rv = http_set_content_length(&res->data, &res->hdrs)) != 0)) {
- http_entity_set_data(&res->data, NULL, 0);
- return (rv);
- }
- res->iserr = false;
- return (0);
-}
-
// nni_http_res_alloc_data allocates the data region, but does not update any
// headers. The intended use is for client implementations that want to
// allocate a buffer to receive the entity into.
@@ -394,7 +175,7 @@ nni_http_res_is_error(nni_http_res *res)
}
static int
-http_parse_header(nni_list *hdrs, void *line)
+http_parse_header(nng_http *conn, void *line)
{
char *key = line;
char *val;
@@ -418,7 +199,7 @@ http_parse_header(nni_list *hdrs, void *line)
end--;
}
- return (http_add_header(hdrs, key, val));
+ return (nni_http_add_header(conn, key, val));
}
// http_sprintf_headers makes headers for an HTTP request or an HTTP response
@@ -494,258 +275,77 @@ http_req_prepare(nni_http_req *req)
if (req->uri == NULL) {
return (NNG_EINVAL);
}
- rv = http_asprintf(&req->buf, &req->bufsz, &req->hdrs, "%s %s %s\r\n",
- req->meth, req->uri, req->vers);
+ rv = http_asprintf(&req->data.buf, &req->data.bufsz, &req->data.hdrs,
+ "%s %s %s\r\n", req->meth, req->uri, req->vers);
return (rv);
}
static int
-http_res_prepare(nni_http_res *res)
+http_res_prepare(nng_http *conn)
{
- int rv;
+ int rv;
+ nng_http_res *res = nni_http_conn_res(conn);
+
if (res->code == 0) {
res->code = NNG_HTTP_STATUS_OK;
}
- rv = http_asprintf(&res->buf, &res->bufsz, &res->hdrs, "%s %d %s\r\n",
- res->vers, nni_http_res_get_status(res),
- nni_http_res_get_reason(res));
+ rv = http_asprintf(&res->data.buf, &res->data.bufsz, &res->data.hdrs,
+ "%s %d %s\r\n", res->vers, res->code, nni_http_get_reason(conn));
return (rv);
}
-char *
-nni_http_req_headers(nni_http_req *req)
-{
- char *s;
- size_t len;
-
- len = http_sprintf_headers(NULL, 0, &req->hdrs) + 1;
- if ((s = nni_alloc(len)) != NULL) {
- http_sprintf_headers(s, len, &req->hdrs);
- }
- return (s);
-}
-
-char *
-nni_http_res_headers(nni_http_res *res)
-{
- char *s;
- size_t len;
-
- len = http_sprintf_headers(NULL, 0, &res->hdrs) + 1;
- if ((s = nni_alloc(len)) != NULL) {
- http_sprintf_headers(s, len, &res->hdrs);
- }
- return (s);
-}
-
int
nni_http_req_get_buf(nni_http_req *req, void **data, size_t *szp)
{
int rv;
- if ((req->buf == NULL) && (rv = http_req_prepare(req)) != 0) {
+ if ((req->data.buf == NULL) && (rv = http_req_prepare(req)) != 0) {
return (rv);
}
- *data = req->buf;
- *szp = req->bufsz - 1; // exclude terminating NUL
+ *data = req->data.buf;
+ *szp = req->data.bufsz - 1; // exclude terminating NUL
return (0);
}
int
-nni_http_res_get_buf(nni_http_res *res, void **data, size_t *szp)
+nni_http_res_get_buf(nng_http *conn, void **data, size_t *szp)
{
- int rv;
+ int rv;
+ nni_http_res *res = nni_http_conn_res(conn);
- if ((res->buf == NULL) && (rv = http_res_prepare(res)) != 0) {
+ if ((res->data.buf == NULL) && (rv = http_res_prepare(conn)) != 0) {
return (rv);
}
- *data = res->buf;
- *szp = res->bufsz - 1; // exclude terminating NUL
+ *data = res->data.buf;
+ *szp = res->data.bufsz - 1; // exclude terminating NUL
return (0);
}
void
nni_http_req_init(nni_http_req *req)
{
- NNI_LIST_INIT(&req->hdrs, http_header, node);
- req->buf = NULL;
- req->bufsz = 0;
- req->data.data = NULL;
- req->data.size = 0;
- req->data.own = false;
- req->uri = NULL;
- nni_http_req_set_version(req, NNG_HTTP_VERSION_1_1);
- nni_http_req_set_method(req, "GET");
-}
-
-int
-nni_http_req_set_url(nni_http_req *req, const nng_url *url)
-{
- if (url == NULL) {
- return (0);
- }
- const char *host;
- char host_buf[264]; // 256 + 8 for port
- int rv;
- rv = nni_asprintf(&req->uri, "%s%s%s%s%s", url->u_path,
- url->u_query ? "?" : "", url->u_query ? url->u_query : "",
- url->u_fragment ? "#" : "",
- url->u_fragment ? url->u_fragment : "");
- if (rv != 0) {
- return (NNG_ENOMEM);
- }
-
- // Add a Host: header since we know that from the URL. Also,
- // only include the :port portion if it isn't the default port.
- if (nni_url_default_port(url->u_scheme) == url->u_port) {
- host = url->u_hostname;
- } else {
- if (strchr(url->u_hostname, ':')) {
- snprintf(host_buf, sizeof(host_buf), "[%s]:%u",
- url->u_hostname, url->u_port);
- } else {
- snprintf(host_buf, sizeof(host_buf), "%s:%u",
- url->u_hostname, url->u_port);
- }
- host = host_buf;
- }
- if ((rv = nni_http_req_set_header(req, "Host", host)) != 0) {
- return (rv);
- }
- return (0);
-}
-
-int
-nni_http_req_alloc(nni_http_req **reqp, const nng_url *url)
-{
- nni_http_req *req;
- if ((req = NNI_ALLOC_STRUCT(req)) == NULL) {
- return (NNG_ENOMEM);
- }
- nni_http_req_init(req);
- if (url != NULL) {
- int rv;
- if ((rv = nni_http_req_set_url(req, url)) != 0) {
- nni_http_req_free(req);
- return (rv);
- }
- }
- *reqp = req;
- return (0);
+ NNI_LIST_INIT(&req->data.hdrs, http_header, node);
+ req->data.buf = NULL;
+ req->data.bufsz = 0;
+ req->data.data = NULL;
+ req->data.size = 0;
+ req->data.own = false;
+ req->uri = NULL;
+ (void) snprintf(req->meth, sizeof(req->meth), "GET");
}
void
nni_http_res_init(nni_http_res *res)
{
- NNI_LIST_INIT(&res->hdrs, http_header, node);
- res->buf = NULL;
- res->bufsz = 0;
- res->data.data = NULL;
- res->data.size = 0;
- res->data.own = false;
- res->vers = NNG_HTTP_VERSION_1_1;
- res->rsn = NULL;
- res->code = 0;
-}
-
-int
-nni_http_res_alloc(nni_http_res **resp)
-{
- nni_http_res *res;
- if ((res = NNI_ALLOC_STRUCT(res)) == NULL) {
- return (NNG_ENOMEM);
- }
- nni_http_res_init(res);
- *resp = res;
- return (0);
-}
-
-const char *
-nni_http_req_get_method(const nni_http_req *req)
-{
- return (req->meth);
-}
-
-const char *
-nni_http_req_get_uri(const nni_http_req *req)
-{
- return (req->uri != NULL ? req->uri : "");
-}
-
-const char *
-nni_http_req_get_version(const nni_http_req *req)
-{
- return (req->vers);
-}
-
-const char *
-nni_http_res_get_version(const nni_http_res *res)
-{
- return (res->vers);
-}
-
-static const char *http_versions[] = {
- // for efficiency, we order in most likely first
- "HTTP/1.1",
- "HTTP/2",
- "HTTP/3",
- "HTTP/1.0",
- "HTTP/0.9",
- NULL,
-};
-
-static int
-http_set_version(const char **ptr, const char *vers)
-{
- vers = vers != NULL ? vers : NNG_HTTP_VERSION_1_1;
- for (int i = 0; http_versions[i] != NULL; i++) {
- if (strcmp(vers, http_versions[i]) == 0) {
- *ptr = http_versions[i];
- return (0);
- }
- }
- return (NNG_ENOTSUP);
-}
-
-int
-nni_http_req_set_version(nni_http_req *req, const char *vers)
-{
- return (http_set_version(&req->vers, vers));
-}
-
-int
-nni_http_res_set_version(nni_http_res *res, const char *vers)
-{
- return (http_set_version(&res->vers, vers));
-}
-
-int
-nni_http_req_set_uri(nni_http_req *req, const char *uri)
-{
- return (http_set_string(&req->uri, uri));
-}
-
-void
-nni_http_req_set_method(nni_http_req *req, const char *meth)
-{
- if (meth == NULL) {
- meth = "GET";
- }
- // this may truncate the method, but nobody should be sending
- // methods so long.
- (void) snprintf(req->meth, sizeof(req->meth), "%s", meth);
-}
-
-void
-nni_http_res_set_status(nni_http_res *res, uint16_t status)
-{
- res->code = status;
-}
-
-uint16_t
-nni_http_res_get_status(const nni_http_res *res)
-{
- return (res->code);
+ NNI_LIST_INIT(&res->data.hdrs, http_header, node);
+ res->data.buf = NULL;
+ res->data.bufsz = 0;
+ res->data.data = NULL;
+ res->data.size = 0;
+ res->data.own = false;
+ res->vers = NNG_HTTP_VERSION_1_1;
+ res->rsn = NULL;
+ res->code = 0;
}
static int
@@ -782,7 +382,7 @@ http_scan_line(void *vbuf, size_t n, size_t *lenp)
}
static int
-http_req_parse_line(nni_http_req *req, void *line)
+http_req_parse_line(nng_http *conn, void *line)
{
int rv;
char *method;
@@ -802,17 +402,16 @@ http_req_parse_line(nni_http_req *req, void *line)
*version = '\0';
version++;
- nni_http_req_set_method(req, method);
- if (((rv = nni_http_req_set_uri(req, uri)) != 0) ||
- ((rv = nni_http_req_set_version(req, version)) != 0)) {
+ nni_http_set_method(conn, method);
+ if (((rv = nni_http_set_uri(conn, uri, NULL)) != 0) ||
+ ((rv = nni_http_set_version(conn, version)) != 0)) {
return (rv);
}
- req->parsed = true;
return (0);
}
static int
-http_res_parse_line(nni_http_res *res, uint8_t *line)
+http_res_parse_line(nng_http *conn, uint8_t *line)
{
int rv;
char *reason;
@@ -838,13 +437,13 @@ http_res_parse_line(nni_http_res *res, uint8_t *line)
return (NNG_EPROTO);
}
- nni_http_res_set_status(res, (uint16_t) status);
+ if ((rv = nni_http_set_status(conn, (uint16_t) status, reason)) != 0) {
+ return (rv);
+ }
- if (((rv = nni_http_res_set_version(res, version)) != 0) ||
- ((rv = nni_http_res_set_reason(res, reason)) != 0)) {
+ if ((rv = nni_http_set_version(conn, version)) != 0) {
return (rv);
}
- res->parsed = true;
return (0);
}
@@ -855,12 +454,13 @@ http_res_parse_line(nni_http_res *res, uint8_t *line)
// be updated even in the face of errors (esp. NNG_EAGAIN, which is
// not an error so much as a request for more data.)
int
-nni_http_req_parse(nni_http_req *req, void *buf, size_t n, size_t *lenp)
+nni_http_req_parse(nng_http *conn, void *buf, size_t n, size_t *lenp)
{
- size_t len = 0;
- size_t cnt;
- int rv = 0;
+ size_t len = 0;
+ size_t cnt;
+ int rv = 0;
+ nni_http_req *req = nni_http_conn_req(conn);
for (;;) {
uint8_t *line;
@@ -877,10 +477,10 @@ nni_http_req_parse(nni_http_req *req, void *buf, size_t n, size_t *lenp)
break;
}
- if (req->parsed) {
- rv = http_parse_header(&req->hdrs, line);
- } else {
- rv = http_req_parse_line(req, line);
+ if (req->data.parsed) {
+ rv = http_parse_header(conn, line);
+ } else if ((rv = http_req_parse_line(conn, line)) == 0) {
+ req->data.parsed = true;
}
if (rv != 0) {
@@ -888,17 +488,21 @@ nni_http_req_parse(nni_http_req *req, void *buf, size_t n, size_t *lenp)
}
}
+ if (rv == 0) {
+ req->data.parsed = false;
+ }
*lenp = len;
return (rv);
}
int
-nni_http_res_parse(nni_http_res *res, void *buf, size_t n, size_t *lenp)
+nni_http_res_parse(nng_http *conn, void *buf, size_t n, size_t *lenp)
{
- size_t len = 0;
- size_t cnt;
- int rv = 0;
+ size_t len = 0;
+ size_t cnt;
+ int rv = 0;
+ nng_http_res *res = nni_http_conn_res(conn);
for (;;) {
uint8_t *line;
if ((rv = http_scan_line(buf, n, &cnt)) != 0) {
@@ -914,10 +518,10 @@ nni_http_res_parse(nni_http_res *res, void *buf, size_t n, size_t *lenp)
break;
}
- if (res->parsed) {
- rv = http_parse_header(&res->hdrs, line);
- } else {
- rv = http_res_parse_line(res, line);
+ if (res->data.parsed) {
+ rv = http_parse_header(conn, line);
+ } else if ((rv = http_res_parse_line(conn, line)) == 0) {
+ res->data.parsed = true;
}
if (rv != 0) {
@@ -925,171 +529,9 @@ nni_http_res_parse(nni_http_res *res, void *buf, size_t n, size_t *lenp)
}
}
+ if (rv == 0) {
+ res->data.parsed = false;
+ }
*lenp = len;
return (rv);
}
-
-static struct {
- uint16_t code;
- const char *mesg;
-} http_status[] = {
- // 200, listed first because most likely
- { NNG_HTTP_STATUS_OK, "OK" },
-
- // 100 series -- informational
- { NNG_HTTP_STATUS_CONTINUE, "Continue" },
- { NNG_HTTP_STATUS_SWITCHING, "Switching Protocols" },
- { NNG_HTTP_STATUS_PROCESSING, "Processing" },
-
- // 200 series -- successful
- { NNG_HTTP_STATUS_CREATED, "Created" },
- { NNG_HTTP_STATUS_ACCEPTED, "Accepted" },
- { NNG_HTTP_STATUS_NOT_AUTHORITATIVE, "Not Authoritative" },
- { NNG_HTTP_STATUS_NO_CONTENT, "No Content" },
- { NNG_HTTP_STATUS_RESET_CONTENT, "Reset Content" },
- { NNG_HTTP_STATUS_PARTIAL_CONTENT, "Partial Content" },
-
- // 300 series -- redirection
- { NNG_HTTP_STATUS_MULTIPLE_CHOICES, "Multiple Choices" },
- { NNG_HTTP_STATUS_STATUS_MOVED_PERMANENTLY, "Moved Permanently" },
- { NNG_HTTP_STATUS_FOUND, "Found" },
- { NNG_HTTP_STATUS_SEE_OTHER, "See Other" },
- { NNG_HTTP_STATUS_NOT_MODIFIED, "Not Modified" },
- { NNG_HTTP_STATUS_USE_PROXY, "Use Proxy" },
- { NNG_HTTP_STATUS_TEMPORARY_REDIRECT, "Temporary Redirect" },
-
- // 400 series -- client errors
- { NNG_HTTP_STATUS_BAD_REQUEST, "Bad Request" },
- { NNG_HTTP_STATUS_UNAUTHORIZED, "Unauthorized" },
- { NNG_HTTP_STATUS_PAYMENT_REQUIRED, "Payment Required" },
- { NNG_HTTP_STATUS_FORBIDDEN, "Forbidden" },
- { NNG_HTTP_STATUS_NOT_FOUND, "Not Found" },
- { NNG_HTTP_STATUS_METHOD_NOT_ALLOWED, "Method Not Allowed" },
- { NNG_HTTP_STATUS_NOT_ACCEPTABLE, "Not Acceptable" },
- { NNG_HTTP_STATUS_PROXY_AUTH_REQUIRED,
- "Proxy Authentication Required" },
- { NNG_HTTP_STATUS_REQUEST_TIMEOUT, "Request Timeout" },
- { NNG_HTTP_STATUS_CONFLICT, "Conflict" },
- { NNG_HTTP_STATUS_GONE, "Gone" },
- { NNG_HTTP_STATUS_LENGTH_REQUIRED, "Length Required" },
- { NNG_HTTP_STATUS_PRECONDITION_FAILED, "Precondition Failed" },
- { NNG_HTTP_STATUS_ENTITY_TOO_LONG, "Request Entity Too Long" },
- { NNG_HTTP_STATUS_UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type" },
- { NNG_HTTP_STATUS_RANGE_NOT_SATISFIABLE,
- "Requested Range Not Satisfiable" },
- { NNG_HTTP_STATUS_EXPECTATION_FAILED, "Expectation Failed" },
- { NNG_HTTP_STATUS_TEAPOT, "I Am A Teapot" },
- { NNG_HTTP_STATUS_LOCKED, "Locked" },
- { NNG_HTTP_STATUS_FAILED_DEPENDENCY, "Failed Dependency" },
- { NNG_HTTP_STATUS_UPGRADE_REQUIRED, "Upgrade Required" },
- { NNG_HTTP_STATUS_PRECONDITION_REQUIRED, "Precondition Required" },
- { NNG_HTTP_STATUS_TOO_MANY_REQUESTS, "Too Many Requests" },
- { NNG_HTTP_STATUS_HEADERS_TOO_LARGE, "Headers Too Large" },
- { NNG_HTTP_STATUS_UNAVAIL_LEGAL_REASONS,
- "Unavailable For Legal Reasons" },
-
- // 500 series -- server errors
- { NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR, "Internal Server Error" },
- { NNG_HTTP_STATUS_NOT_IMPLEMENTED, "Not Implemented" },
- { NNG_HTTP_STATUS_BAD_REQUEST, "Bad Gateway" },
- { NNG_HTTP_STATUS_SERVICE_UNAVAILABLE, "Service Unavailable" },
- { NNG_HTTP_STATUS_GATEWAY_TIMEOUT, "Gateway Timeout" },
- { NNG_HTTP_STATUS_HTTP_VERSION_NOT_SUPP,
- "HTTP Version Not Supported" },
- { NNG_HTTP_STATUS_VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates" },
- { NNG_HTTP_STATUS_INSUFFICIENT_STORAGE, "Insufficient Storage" },
- { NNG_HTTP_STATUS_LOOP_DETECTED, "Loop Detected" },
- { NNG_HTTP_STATUS_NOT_EXTENDED, "Not Extended" },
- { NNG_HTTP_STATUS_NETWORK_AUTH_REQUIRED,
- "Network Authentication Required" },
-
- // Terminator
- { 0, NULL },
-};
-
-const char *
-nni_http_reason(uint16_t code)
-{
- for (int i = 0; http_status[i].code != 0; i++) {
- if (http_status[i].code == code) {
- return (http_status[i].mesg);
- }
- }
- return ("Unknown HTTP Status");
-}
-
-const char *
-nni_http_res_get_reason(const nni_http_res *res)
-{
- return (res->rsn ? res->rsn : nni_http_reason(res->code));
-}
-
-int
-nni_http_res_set_reason(nni_http_res *res, const char *reason)
-{
- if ((reason != NULL) &&
- (strcmp(reason, nni_http_reason(res->code)) == 0)) {
- reason = NULL;
- }
- return (http_set_string(&res->rsn, reason));
-}
-
-int
-nni_http_alloc_html_error(char **html, uint16_t code, const char *details)
-{
- const char *rsn = nni_http_reason(code);
-
- return (nni_asprintf(html,
- "<!DOCTYPE html>\n"
- "<html><head><title>%d %s</title>\n"
- "<style>"
- "body { font-family: Arial, sans serif; text-align: center }\n"
- "h1 { font-size: 36px; }"
- "span { background-color: gray; color: white; padding: 7px; "
- "border-radius: 5px }"
- "h2 { font-size: 24px; }"
- "p { font-size: 20px; }"
- "</style></head>"
- "<body><p>&nbsp;</p>"
- "<h1><span>%d</span></h1>"
- "<h2>%s</h2>"
- "<p>%s</p>"
- "</body></html>",
- code, rsn, code, rsn, details != NULL ? details : ""));
-}
-
-int
-nni_http_res_set_error(nni_http_res *res, uint16_t err)
-{
- int rv;
- char *html = NULL;
- if (((rv = nni_http_alloc_html_error(&html, err, NULL)) != 0) ||
- ((rv = nni_http_res_set_header(
- res, "Content-Type", "text/html; charset=UTF-8")) != 0) ||
- ((rv = nni_http_res_copy_data(res, html, strlen(html))) != 0)) {
- nni_strfree(html);
- return (rv);
- }
- nni_strfree(html);
- res->code = err;
- res->iserr = true;
- return (0);
-}
-
-int
-nni_http_res_alloc_error(nni_http_res **resp, uint16_t err)
-{
- nni_http_res *res;
- int rv;
-
- if ((rv = nni_http_res_alloc(&res)) != 0) {
- return (rv);
- }
- rv = nni_http_res_set_error(res, err);
- if (rv != 0) {
- nni_http_res_free(res);
- return (rv);
- }
- *resp = res;
- return (0);
-}
diff --git a/src/supplemental/http/http_msg.h b/src/supplemental/http/http_msg.h
index 7d9e7dcf..e08dab8a 100644
--- a/src/supplemental/http/http_msg.h
+++ b/src/supplemental/http/http_msg.h
@@ -22,36 +22,45 @@ typedef struct http_header {
char *name;
char *value;
nni_list_node node;
+ bool static_name : 1; // name is static, do not free it
+ bool static_value : 1; // value is static, do not free it
+ bool alloc_header : 1; // header is heap allocated
} http_header;
+typedef struct http_header nni_http_header;
typedef struct nni_http_entity {
- char *data;
- size_t size; // allocated/expected size
- size_t len; // current length
- bool own; // if true, data is "ours", and should be freed
+ char *data;
+ size_t size;
+ bool own; // if true, data is "ours", and should be freed
+ char clen[24]; // 64-bit lengths, in decimal
+ char ctype[128]; // 63+63+; per RFC 6838
+ http_header content_type;
+ http_header content_length;
+ nni_list hdrs;
+ char *buf;
+ size_t bufsz;
+ bool parsed;
} nni_http_entity;
struct nng_http_req {
- nni_list hdrs;
nni_http_entity data;
char meth[32];
+ char host[260]; // 253 per IETF, plus 6 for :port plus null
+ char ubuf[200]; // Most URIs are smaller than this
char *uri;
const char *vers;
- char *buf;
- size_t bufsz;
- bool parsed;
+ http_header host_header;
};
struct nng_http_res {
- nni_list hdrs;
nni_http_entity data;
uint16_t code;
char *rsn;
const char *vers;
- char *buf;
- size_t bufsz;
- bool parsed;
bool iserr;
+ http_header location;
};
+extern void nni_http_free_header(http_header *);
+
#endif
diff --git a/src/supplemental/http/http_public.c b/src/supplemental/http/http_public.c
index dd35151a..ef000802 100644
--- a/src/supplemental/http/http_public.c
+++ b/src/supplemental/http/http_public.c
@@ -1,5 +1,5 @@
//
-// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2025 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
@@ -10,123 +10,30 @@
#include "core/nng_impl.h"
#include "http_api.h"
-#include "nng/supplemental/http/http.h"
+#include "nng/http.h"
// Symbols in this file are "public" versions of the HTTP API.
// These are suitable for exposure to applications.
-int
-nng_http_req_alloc(nng_http_req **reqp, const nng_url *url)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_req_alloc(reqp, url));
-#else
- NNI_ARG_UNUSED(reqp);
- NNI_ARG_UNUSED(url);
- return (NNG_ENOTSUP);
-#endif
-}
-
-void
-nng_http_req_free(nng_http_req *req)
-{
-#ifdef NNG_SUPP_HTTP
- nni_http_req_free(req);
-#else
- NNI_ARG_UNUSED(req);
-#endif
-}
-
-void
-nng_http_res_free(nng_http_res *res)
-{
-#ifdef NNG_SUPP_HTTP
- nni_http_res_free(res);
-#else
- NNI_ARG_UNUSED(res);
-#endif
-}
-
-int
-nng_http_res_alloc(nng_http_res **resp)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_res_alloc(resp));
-#else
- NNI_ARG_UNUSED(resp);
- return (NNG_ENOTSUP);
-#endif
-}
-
-int
-nng_http_res_alloc_error(nng_http_res **resp, uint16_t code)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_res_alloc_error(resp, code));
-#else
- NNI_ARG_UNUSED(resp);
- NNI_ARG_UNUSED(code);
- return (NNG_ENOTSUP);
-#endif
-}
-
const char *
-nng_http_req_get_header(const nng_http_req *req, const char *key)
+nng_http_get_header(nng_http *conn, const char *key)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_req_get_header(req, key));
+ return (nni_http_get_header(conn, key));
#else
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(key);
- return (NULL);
-#endif
-}
-
-const char *
-nng_http_res_get_header(const nng_http_res *res, const char *key)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_res_get_header(res, key));
-#else
- NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(conn);
NNI_ARG_UNUSED(key);
return (NULL);
#endif
}
int
-nng_http_req_add_header(nng_http_req *req, const char *key, const char *val)
+nng_http_set_header(nng_http *conn, const char *key, const char *val)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_req_add_header(req, key, val));
+ return (nni_http_set_header(conn, key, val));
#else
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(key);
- NNI_ARG_UNUSED(val);
- return (NNG_ENOTSUP);
-#endif
-}
-
-int
-nng_http_res_add_header(nng_http_res *res, const char *key, const char *val)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_res_add_header(res, key, val));
-#else
- NNI_ARG_UNUSED(res);
- NNI_ARG_UNUSED(key);
- NNI_ARG_UNUSED(val);
- return (NNG_ENOTSUP);
-#endif
-}
-
-int
-nng_http_req_set_header(nng_http_req *req, const char *key, const char *val)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_req_set_header(req, key, val));
-#else
- NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(conn);
NNI_ARG_UNUSED(key);
NNI_ARG_UNUSED(val);
return (NNG_ENOTSUP);
@@ -134,75 +41,37 @@ nng_http_req_set_header(nng_http_req *req, const char *key, const char *val)
}
int
-nng_http_res_set_header(nng_http_res *res, const char *key, const char *val)
+nng_http_add_header(nng_http *conn, const char *key, const char *val)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_res_set_header(res, key, val));
+ return (nni_http_add_header(conn, key, val));
#else
- NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(conn);
NNI_ARG_UNUSED(key);
NNI_ARG_UNUSED(val);
return (NNG_ENOTSUP);
#endif
}
-int
-nng_http_req_del_header(nng_http_req *req, const char *key)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_req_del_header(req, key));
-#else
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(key);
- return (NNG_ENOTSUP);
-#endif
-}
-
-int
-nng_http_res_del_header(nng_http_res *res, const char *key)
+void
+nng_http_del_header(nng_http *conn, const char *key)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_res_del_header(res, key));
+ nni_http_del_header(conn, key);
#else
- NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(conn);
NNI_ARG_UNUSED(key);
return (NNG_ENOTSUP);
#endif
}
-int
-nng_http_req_copy_data(nng_http_req *req, const void *data, size_t sz)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_req_copy_data(req, data, sz));
-#else
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(data);
- NNI_ARG_UNUSED(sz);
- return (NNG_ENOTSUP);
-#endif
-}
-
-int
-nng_http_res_copy_data(nng_http_res *res, const void *data, size_t sz)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_res_copy_data(res, data, sz));
-#else
- NNI_ARG_UNUSED(res);
- NNI_ARG_UNUSED(data);
- NNI_ARG_UNUSED(sz);
- return (NNG_ENOTSUP);
-#endif
-}
-
-int
-nng_http_req_set_data(nng_http_req *req, const void *data, size_t sz)
+void
+nng_http_set_body(nng_http *conn, void *data, size_t sz)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_req_set_data(req, data, sz));
+ nni_http_set_body(conn, data, sz);
#else
- NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(conn);
NNI_ARG_UNUSED(data);
NNI_ARG_UNUSED(sz);
return (NNG_ENOTSUP);
@@ -210,212 +79,146 @@ nng_http_req_set_data(nng_http_req *req, const void *data, size_t sz)
}
int
-nng_http_res_set_data(nng_http_res *res, const void *data, size_t sz)
+nng_http_copy_body(nng_http *conn, const void *data, size_t len)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_res_set_data(res, data, sz));
+ return (nni_http_copy_body(conn, data, len));
#else
- NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(conn);
NNI_ARG_UNUSED(data);
- NNI_ARG_UNUSED(sz);
+ NNI_ARG_UNUSED(len);
return (NNG_ENOTSUP);
#endif
}
void
-nng_http_req_get_data(nng_http_req *req, void **datap, size_t *lenp)
+nng_http_get_body(nng_http *conn, void **datap, size_t *lenp)
{
#ifdef NNG_SUPP_HTTP
- nni_http_req_get_data(req, datap, lenp);
+ nni_http_get_body(conn, datap, lenp);
#else
- NNI_ARG_UNUSED(req);
- *datap = NULL;
- *lenp = 0;
-#endif
-}
-
-void
-nng_http_res_get_data(nng_http_res *res, void **datap, size_t *lenp)
-{
-#ifdef NNG_SUPP_HTTP
- nni_http_res_get_data(res, datap, lenp);
-#else
- NNI_ARG_UNUSED(res);
- *datap = NULL;
- *lenp = 0;
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(datap);
+ NNI_ARG_UNUSED(lenp);
#endif
}
const char *
-nng_http_req_get_method(const nng_http_req *req)
+nng_http_get_uri(nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_req_get_method(req));
+ return (nni_http_get_uri(conn));
#else
NNI_ARG_UNUSED(req);
return (NULL);
#endif
}
-const char *
-nng_http_req_get_version(const nng_http_req *req)
+int
+nng_http_set_uri(nng_http *conn, const char *uri, const char *query)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_req_get_version(req));
+ return (nni_http_set_uri(conn, uri, query));
#else
- NNI_ARG_UNUSED(req);
- return (NULL);
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(uri);
+ NNI_ARG_UNUSED(query);
+ return (NNG_ENOTSUP);
#endif
}
const char *
-nng_http_req_get_uri(const nng_http_req *req)
+nng_http_get_version(nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_req_get_uri(req));
+ return (nni_http_get_version(conn));
#else
- NNI_ARG_UNUSED(req);
+ NNI_ARG_UNUSED(res);
return (NULL);
#endif
}
-void
-nng_http_req_set_method(nng_http_req *req, const char *meth)
-{
-#ifdef NNG_SUPP_HTTP
- nni_http_req_set_method(req, meth);
-#else
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(meth);
-#endif
-}
-
-int
-nng_http_req_set_version(nng_http_req *req, const char *vers)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_req_set_version(req, vers));
-#else
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(vers);
- return (NNG_ENOTSUP);
-#endif
-}
-
-int
-nng_http_req_set_url(nng_http_req *req, const nng_url *url)
+nng_http_res *
+nng_http_conn_res(nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_req_set_url(req, url));
+ return (nni_http_conn_res(conn));
#else
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(url);
- return (NNG_ENOTSUP);
+ return (NULL);
#endif
}
int
-nng_http_req_set_uri(nng_http_req *req, const char *uri)
+nng_http_set_status(nng_http *conn, uint16_t status, const char *reason)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_req_set_uri(req, uri));
+ return (nni_http_set_status(conn, status, reason));
#else
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(uri);
+ NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(status);
+ NNI_ARG_UNUSED(reason);
return (NNG_ENOTSUP);
#endif
}
uint16_t
-nng_http_res_get_status(const nng_http_res *res)
+nng_http_get_status(nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_res_get_status(res));
+ return (nni_http_get_status(conn));
#else
NNI_ARG_UNUSED(res);
+ NNI_ARG_UNUSED(status);
return (0);
#endif
}
const char *
-nng_http_res_get_version(const nng_http_res *res)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_res_get_version(res));
-#else
- NNI_ARG_UNUSED(res);
- return (NULL);
-#endif
-}
-
-const char *
-nng_http_res_get_reason(const nng_http_res *res)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_res_get_reason(res));
-#else
- NNI_ARG_UNUSED(res);
- return (NULL);
-#endif
-}
-
-void
-nng_http_res_set_status(nng_http_res *res, uint16_t status)
+nng_http_get_reason(nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
- nni_http_res_set_status(res, status);
+ return (nni_http_get_reason(conn));
#else
NNI_ARG_UNUSED(res);
NNI_ARG_UNUSED(status);
+ return (0);
#endif
}
int
-nng_http_res_set_version(nng_http_res *res, const char *vers)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_res_set_version(res, vers));
-#else
- NNI_ARG_UNUSED(res);
- NNI_ARG_UNUSED(vers);
- return (NNG_ENOTSUP);
-#endif
-}
-
-int
-nng_http_res_set_reason(nng_http_res *res, const char *rsn)
+nng_http_set_version(nng_http *conn, const char *version)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_res_set_reason(res, rsn));
+ return (nni_http_set_version(conn, version));
#else
- NNI_ARG_UNUSED(res);
- NNI_ARG_UNUSED(rsn);
return (NNG_ENOTSUP);
#endif
}
-nng_http_req *
-nng_http_conn_req(nng_http_conn *conn)
+void
+nng_http_set_method(nng_http *conn, const char *method)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_conn_req(conn));
+ nni_http_set_method(conn, method);
#else
- return (NULL);
+ NNI_ARG_UNUSED(conn);
+ NNI_ARG_UNUSED(method);
#endif
}
-nng_http_res *
-nng_http_conn_res(nng_http_conn *conn)
+const char *
+nng_http_get_method(nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_conn_res(conn));
+ return (nni_http_get_method(conn));
#else
+ NNI_ARG_UNUSED(conn);
return (NULL);
#endif
}
void
-nng_http_conn_close(nng_http_conn *conn)
+nng_http_close(nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
// API version of this closes *and* frees the structure.
@@ -426,7 +229,7 @@ nng_http_conn_close(nng_http_conn *conn)
}
void
-nng_http_conn_read(nng_http_conn *conn, nng_aio *aio)
+nng_http_read(nng_http *conn, nng_aio *aio)
{
#ifdef NNG_SUPP_HTTP
nni_http_read(conn, aio);
@@ -437,7 +240,7 @@ nng_http_conn_read(nng_http_conn *conn, nng_aio *aio)
}
void
-nng_http_conn_read_all(nng_http_conn *conn, nng_aio *aio)
+nng_http_read_all(nng_http *conn, nng_aio *aio)
{
#ifdef NNG_SUPP_HTTP
nni_http_read_full(conn, aio);
@@ -448,7 +251,7 @@ nng_http_conn_read_all(nng_http_conn *conn, nng_aio *aio)
}
void
-nng_http_conn_write(nng_http_conn *conn, nng_aio *aio)
+nng_http_write(nng_http *conn, nng_aio *aio)
{
#ifdef NNG_SUPP_HTTP
nni_http_write(conn, aio);
@@ -459,7 +262,7 @@ nng_http_conn_write(nng_http_conn *conn, nng_aio *aio)
}
void
-nng_http_conn_write_all(nng_http_conn *conn, nng_aio *aio)
+nng_http_write_all(nng_http *conn, nng_aio *aio)
{
#ifdef NNG_SUPP_HTTP
nni_http_write_full(conn, aio);
@@ -470,19 +273,18 @@ nng_http_conn_write_all(nng_http_conn *conn, nng_aio *aio)
}
void
-nng_http_conn_write_req(nng_http_conn *conn, nng_http_req *req, nng_aio *aio)
+nng_http_write_request(nng_http *conn, nng_aio *aio)
{
#ifdef NNG_SUPP_HTTP
- nni_http_write_req(conn, req, aio);
+ nni_http_write_req(conn, aio);
#else
NNI_ARG_UNUSED(conn);
- NNI_ARG_UNUSED(req);
nni_aio_finish_error(aio, NNG_ENOTSUP);
#endif
}
void
-nng_http_conn_write_res(nng_http_conn *conn, nng_aio *aio)
+nng_http_write_response(nng_http *conn, nng_aio *aio)
{
#ifdef NNG_SUPP_HTTP
nni_http_write_res(conn, aio);
@@ -493,21 +295,10 @@ nng_http_conn_write_res(nng_http_conn *conn, nng_aio *aio)
}
void
-nng_http_conn_read_req(nng_http_conn *conn, nng_aio *aio)
-{
-#ifdef NNG_SUPP_HTTP
- nni_http_read_req(conn, aio);
-#else
- NNI_ARG_UNUSED(conn);
- nni_aio_finish_error(aio, NNG_ENOTSUP);
-#endif
-}
-
-void
-nng_http_conn_read_res(nng_http_conn *conn, nng_http_res *res, nng_aio *aio)
+nng_http_read_response(nng_http *conn, nng_aio *aio)
{
#ifdef NNG_SUPP_HTTP
- nni_http_read_res(conn, res, aio);
+ nni_http_read_res(conn, aio);
#else
NNI_ARG_UNUSED(conn);
NNI_ARG_UNUSED(res);
@@ -757,20 +548,6 @@ nng_http_server_set_error_page(
}
int
-nng_http_server_set_error_file(
- nng_http_server *srv, uint16_t code, const char *path)
-{
-#ifdef NNG_SUPP_HTTP
- return (nni_http_server_set_error_file(srv, code, path));
-#else
- NNI_ARG_UNUSED(srv);
- NNI_ARG_UNUSED(code);
- NNI_ARG_UNUSED(path);
- return (NNG_ENOTSUP);
-#endif
-}
-
-int
nng_http_server_set_tls(nng_http_server *srv, nng_tls_config *cfg)
{
#if defined(NNG_SUPP_HTTP) && defined(NNG_SUPP_TLS)
@@ -811,10 +588,10 @@ nng_http_server_get_addr(nng_http_server *srv, nng_sockaddr *addr)
}
int
-nng_http_server_res_error(nng_http_server *srv, nng_http_res *res)
+nng_http_server_error(nng_http_server *srv, nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
- return (nni_http_server_res_error(srv, res));
+ return (nni_http_server_error(srv, conn));
#else
NNI_ARG_UNUSED(srv);
NNI_ARG_UNUSED(res);
@@ -823,7 +600,7 @@ nng_http_server_res_error(nng_http_server *srv, nng_http_res *res)
}
int
-nng_http_hijack(nng_http_conn *conn)
+nng_http_hijack(nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
return (nni_http_hijack(conn));
@@ -891,21 +668,7 @@ nng_http_client_connect(nng_http_client *cli, nng_aio *aio)
}
void
-nng_http_client_transact(
- nng_http_client *cli, nng_http_req *req, nng_http_res *res, nng_aio *aio)
-{
-#ifdef NNG_SUPP_HTTP
- nni_http_transact(cli, req, res, aio);
-#else
- NNI_ARG_UNUSED(cli);
- NNI_ARG_UNUSED(req);
- NNI_ARG_UNUSED(res);
- nni_aio_finish_error(aio, NNG_ENOTSUP);
-#endif
-}
-
-void
-nng_http_conn_transact(nng_http_conn *conn, nng_aio *aio)
+nng_http_transact(nng_http *conn, nng_aio *aio)
{
#ifdef NNG_SUPP_HTTP
nni_http_transact_conn(conn, aio);
@@ -916,21 +679,11 @@ nng_http_conn_transact(nng_http_conn *conn, nng_aio *aio)
}
void
-nng_http_req_reset(nng_http_req *req)
+nng_http_reset(nng_http *conn)
{
#ifdef NNG_SUPP_HTTP
- nni_http_req_reset(req);
+ nni_http_conn_reset(conn);
#else
NNI_ARG_UNUSED(req);
#endif
}
-
-void
-nng_http_res_reset(nng_http_res *res)
-{
-#ifdef NNG_SUPP_HTTP
- nni_http_res_reset(res);
-#else
- NNI_ARG_UNUSED(res);
-#endif
-}
diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c
index 87285417..7fbd7a56 100644
--- a/src/supplemental/http/http_server.c
+++ b/src/supplemental/http/http_server.c
@@ -20,7 +20,9 @@
#include "core/nng_impl.h"
#include "http_api.h"
-#include "nng/supplemental/http/http.h"
+#include "http_msg.h"
+#include "nng/http.h"
+#include "nng/nng.h"
#ifndef NNG_HTTP_MAX_URI
#define NNG_HTTP_MAX_URI 1024
@@ -59,13 +61,13 @@ typedef struct http_sconn {
nni_aio txdataio;
nni_reap_node reap;
nni_atomic_flag closed;
+ nni_http_header close_header;
} http_sconn;
typedef struct http_error {
nni_list_node node;
uint16_t code;
- void *body;
- size_t len;
+ char *body;
} http_error;
struct nng_http_server {
@@ -414,20 +416,15 @@ http_uri_canonify(char *path)
static void
http_sconn_error(http_sconn *sc, uint16_t err)
{
- nni_http_res *res;
-
- res = nng_http_conn_res(sc->conn);
- nni_http_res_set_status(res, err);
- if (nni_http_server_res_error(sc->server, res) != 0) {
+ nng_http_set_status(sc->conn, err, NULL);
+ if (nni_http_server_error(sc->server, sc->conn) != 0) {
http_sconn_close(sc);
return;
}
if (sc->close) {
- if (nni_http_res_set_header(res, "Connection", "close") != 0) {
- http_sconn_close(sc);
- return;
- }
+ nni_http_set_static_header(
+ sc->conn, &sc->close_header, "Connection", "close");
}
nni_http_write_res(sc->conn, &sc->txaio);
}
@@ -515,7 +512,7 @@ http_sconn_rxdone(void *arg)
nni_http_handler *h = NULL;
nni_http_handler *head = NULL;
const char *val;
- nni_http_req *req = nng_http_conn_req(sc->conn);
+ nni_http_req *req = nni_http_conn_req(sc->conn);
char *uri;
size_t urisz;
char *path;
@@ -525,7 +522,15 @@ http_sconn_rxdone(void *arg)
const char *cls;
if ((rv = nni_aio_result(aio)) != 0) {
- http_sconn_close(sc);
+ if (rv == NNG_EMSGSIZE) {
+ sc->close = true;
+ http_sconn_error(sc,
+ nni_http_parsed(sc->conn)
+ ? NNG_HTTP_STATUS_HEADERS_TOO_LARGE
+ : NNG_HTTP_STATUS_URI_TOO_LONG);
+ } else {
+ http_sconn_close(sc);
+ }
return;
}
@@ -537,7 +542,7 @@ http_sconn_rxdone(void *arg)
// Validate the request -- it has to at least look like HTTP
// 1.x. We flatly refuse to deal with HTTP 0.9, and we can't
// cope with HTTP/2.
- if ((val = nni_http_req_get_version(req)) == NULL) {
+ if ((val = nni_http_get_version(sc->conn)) == NULL) {
sc->close = true;
http_sconn_error(sc, NNG_HTTP_STATUS_BAD_REQUEST);
return;
@@ -557,7 +562,7 @@ http_sconn_rxdone(void *arg)
// If the connection was 1.0, or a connection: close was
// requested, then mark this close on our end.
- if ((val = nni_http_req_get_header(req, "Connection")) != NULL) {
+ if ((val = nni_http_get_header(sc->conn, "Connection")) != NULL) {
// HTTP 1.1 says these have to be case insensitive
if (nni_strcasestr(val, "close") != NULL) {
// In theory this could falsely match some other weird
@@ -569,7 +574,7 @@ http_sconn_rxdone(void *arg)
}
}
- val = nni_http_req_get_uri(req);
+ val = nni_http_get_uri(sc->conn);
urisz = strlen(val) + 1;
if ((uri = nni_alloc(urisz)) == NULL) {
http_sconn_close(sc); // out of memory
@@ -578,7 +583,7 @@ http_sconn_rxdone(void *arg)
strncpy(uri, val, urisz);
path = http_uri_canonify(uri);
- host = nni_http_req_get_header(req, "Host");
+ host = nni_http_get_header(sc->conn, "Host");
if ((host == NULL) && (needhost)) {
// Per RFC 2616 14.23 we have to send 400 status here.
http_sconn_error(sc, NNG_HTTP_STATUS_BAD_REQUEST);
@@ -616,7 +621,7 @@ http_sconn_rxdone(void *arg)
break;
}
// So, what about the method?
- val = nni_http_req_get_method(req);
+ val = nni_http_get_method(sc->conn);
if (strcmp(val, h->method) == 0) {
break;
}
@@ -646,7 +651,8 @@ http_sconn_rxdone(void *arg)
}
if ((h->getbody) &&
- ((cls = nni_http_req_get_header(req, "Content-Length")) != NULL)) {
+ ((cls = nni_http_get_header(sc->conn, "Content-Length")) !=
+ NULL)) {
uint64_t len;
char *end;
@@ -665,7 +671,8 @@ http_sconn_rxdone(void *arg)
sc, NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR);
return;
}
- nng_http_req_get_data(req, &iov.iov_buf, &iov.iov_len);
+ iov.iov_buf = req->data.data;
+ iov.iov_len = req->data.size;
sc->handler = h;
nni_mtx_unlock(&s->mtx);
nni_aio_set_iov(&sc->rxaio, 1, &iov);
@@ -725,28 +732,25 @@ http_sconn_cbdone(void *arg)
return;
}
res = nni_http_conn_res(sc->conn);
- if (!nni_http_conn_res_sent(sc->conn)) {
+ if (!nni_http_res_sent(sc->conn)) {
const char *val;
- val = nni_http_res_get_header(res, "Connection");
+ const char *method;
+ uint16_t status;
+ val = nni_http_get_header(sc->conn, "Connection");
+ status = nni_http_get_status(sc->conn);
+ method = nni_http_get_method(sc->conn);
if ((val != NULL) && (strstr(val, "close") != NULL)) {
sc->close = true;
}
if (sc->close) {
- nni_http_res_set_header(res, "Connection", "close");
+ nni_http_set_header(sc->conn, "Connection", "close");
}
- if (strcmp(
- nni_http_req_get_method(nng_http_conn_req(sc->conn)),
- "HEAD") == 0) {
- void *data;
- size_t size;
- // prune off the data, but preserve the content-length
- // header. By passing NULL here, we leave off the old
- // data, but the non-zero size means we don't clobber
- // the HTTP header.
- nni_http_res_get_data(res, &data, &size);
- nni_http_res_set_data(res, NULL, size);
+ if ((strcmp(method, "HEAD") == 0) && status >= 200 &&
+ status <= 299) {
+ // prune off data, preserving content-length header.
+ nni_http_prune_body(sc->conn);
} else if (nni_http_res_is_error(res)) {
- (void) nni_http_server_res_error(s, res);
+ (void) nni_http_server_error(s, sc->conn);
}
nni_http_write_res(sc->conn, &sc->txaio);
} else if (sc->close) {
@@ -775,7 +779,7 @@ http_sconn_init(http_sconn **scp, nng_stream *stream)
nni_aio_init(&sc->txdataio, http_sconn_txdatdone, sc);
nni_aio_init(&sc->cbaio, http_sconn_cbdone, sc);
- if ((rv = nni_http_conn_init(&sc->conn, stream)) != 0) {
+ if ((rv = nni_http_conn_init(&sc->conn, stream, false)) != 0) {
// Can't even accept the incoming request. Hard close.
http_sconn_close(sc);
return (rv);
@@ -847,7 +851,7 @@ http_server_fini(nni_http_server *s)
nni_mtx_lock(&s->errors_mtx);
while ((epage = nni_list_first(&s->errors)) != NULL) {
nni_list_remove(&s->errors, epage);
- nni_free(epage->body, epage->len);
+ nni_strfree(epage->body);
NNI_FREE_STRUCT(epage);
}
nni_mtx_unlock(&s->errors_mtx);
@@ -1026,7 +1030,7 @@ nni_http_server_close(nni_http_server *s)
}
static int
-http_server_set_err(nni_http_server *s, uint16_t code, void *body, size_t len)
+http_server_set_err(nni_http_server *s, uint16_t code, char *body)
{
http_error *epage;
@@ -1044,11 +1048,8 @@ http_server_set_err(nni_http_server *s, uint16_t code, void *body, size_t len)
epage->code = code;
nni_list_append(&s->errors, epage);
}
- if (epage->len != 0) {
- nni_free(epage->body, epage->len);
- }
+ nni_strfree(epage->body);
epage->body = body;
- epage->len = len;
nni_mtx_unlock(&s->errors_mtx);
return (0);
}
@@ -1057,75 +1058,36 @@ int
nni_http_server_set_error_page(
nni_http_server *s, uint16_t code, const char *html)
{
- char *body;
- int rv;
- size_t len;
+ char *body;
+ int rv;
// We copy the content, without the trailing NUL.
- len = strlen(html);
- if ((body = nni_alloc(len)) == NULL) {
+ if ((body = nni_strdup(html)) == NULL) {
return (NNG_ENOMEM);
}
- memcpy(body, html, len);
- if ((rv = http_server_set_err(s, code, body, len)) != 0) {
- nni_free(body, len);
- }
- return (rv);
-}
-
-int
-nni_http_server_set_error_file(
- nni_http_server *s, uint16_t code, const char *path)
-{
- void *body;
- size_t len;
- int rv;
- if ((rv = nni_file_get(path, &body, &len)) != 0) {
- return (rv);
- }
- if ((rv = http_server_set_err(s, code, body, len)) != 0) {
- nni_free(body, len);
+ if ((rv = http_server_set_err(s, code, body)) != 0) {
+ nni_strfree(body);
}
return (rv);
}
int
-nni_http_server_res_error(nni_http_server *s, nni_http_res *res)
+nni_http_server_error(nni_http_server *s, nng_http *conn)
{
http_error *epage;
char *body = NULL;
- char *html = NULL;
- size_t len = 0;
- uint16_t code = nni_http_res_get_status(res);
+ uint16_t code = nni_http_get_status(conn);
int rv;
nni_mtx_lock(&s->errors_mtx);
NNI_LIST_FOREACH (&s->errors, epage) {
if (epage->code == code) {
body = epage->body;
- len = epage->len;
break;
}
}
+ rv = nni_http_set_error(conn, code, NULL, body);
nni_mtx_unlock(&s->errors_mtx);
-
- if (body == NULL) {
- if ((rv = nni_http_alloc_html_error(&html, code, NULL)) != 0) {
- return (rv);
- }
- body = html;
- len = strlen(body);
- }
-
- // NB: The server lock has to be held here to guard against the
- // error page being tossed or changed.
- if (((rv = nni_http_res_copy_data(res, body, len)) == 0) &&
- ((rv = nni_http_res_set_header(
- res, "Content-Type", "text/html; charset=UTF-8")) == 0)) {
- nni_http_res_set_status(res, code);
- }
- nni_strfree(html);
-
return (rv);
}
@@ -1322,14 +1284,13 @@ typedef struct http_file {
} http_file;
static void
-http_handle_file(nni_http_conn *conn, void *arg, nni_aio *aio)
+http_handle_file(nng_http *conn, void *arg, nni_aio *aio)
{
- nni_http_res *res = nng_http_conn_res(conn);
- void *data;
- size_t size;
- int rv;
- http_file *hf = arg;
- const char *ctype;
+ void *data;
+ size_t size;
+ int rv;
+ http_file *hf = arg;
+ const char *ctype;
if ((ctype = hf->ctype) == NULL) {
ctype = "application/octet-stream";
@@ -1356,26 +1317,23 @@ http_handle_file(nni_http_conn *conn, void *arg, nni_aio *aio)
status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
break;
}
- if ((rv = nni_http_res_set_error(res, status)) != 0) {
+ if ((rv = nni_http_set_error(conn, status, NULL, NULL)) != 0) {
nni_aio_finish_error(aio, rv);
return;
}
- nni_aio_set_output(aio, 0, res);
nni_aio_finish(aio, 0, 0);
return;
}
- if (((rv = nni_http_res_set_header(res, "Content-Type", ctype)) !=
- 0) ||
- ((rv = nni_http_res_copy_data(res, data, size)) != 0)) {
+ if (((rv = nni_http_set_header(conn, "Content-Type", ctype)) != 0) ||
+ ((rv = nni_http_copy_body(conn, data, size)) != 0)) {
nni_free(data, size);
nni_aio_finish_error(aio, rv);
return;
}
- nni_http_res_set_status(res, NNG_HTTP_STATUS_OK);
+ nng_http_set_status(conn, NNG_HTTP_STATUS_OK, NULL);
nni_free(data, size);
- nni_aio_set_output(aio, 0, res);
nni_aio_finish(aio, 0, 0);
}
@@ -1439,22 +1397,20 @@ nni_http_handler_init_file(
}
static void
-http_handle_dir(nng_http_conn *conn, void *arg, nng_aio *aio)
+http_handle_dir(nng_http *conn, void *arg, nng_aio *aio)
{
- nni_http_req *req = nni_http_conn_req(conn);
- nni_http_res *res = nni_http_conn_res(conn);
- void *data;
- size_t size;
- int rv;
- http_file *hf = arg;
- const char *path = hf->path;
- const char *base = hf->base;
- const char *uri = nni_http_req_get_uri(req);
- const char *ctype;
- char *dst;
- size_t len;
- size_t pnsz;
- char *pn;
+ void *data;
+ size_t size;
+ int rv;
+ http_file *hf = arg;
+ const char *path = hf->path;
+ const char *base = hf->base;
+ const char *uri = nni_http_get_uri(conn);
+ const char *ctype;
+ char *dst;
+ size_t len;
+ size_t pnsz;
+ char *pn;
len = strlen(base);
if (base[1] != '\0' && // Allows "/" as base
@@ -1544,27 +1500,24 @@ http_handle_dir(nng_http_conn *conn, void *arg, nng_aio *aio)
status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
break;
}
- if ((rv = nni_http_res_set_error(res, status)) != 0) {
+ if ((rv = nni_http_set_error(conn, status, NULL, NULL)) != 0) {
nni_aio_finish_error(aio, rv);
return;
}
- nni_aio_set_output(aio, 0, res);
nni_aio_finish(aio, 0, 0);
return;
}
- if (((rv = nni_http_res_set_header(res, "Content-Type", ctype)) !=
- 0) ||
- ((rv = nni_http_res_copy_data(res, data, size)) != 0)) {
+ if (((rv = nng_http_set_header(conn, "Content-Type", ctype)) != 0) ||
+ ((rv = nng_http_copy_body(conn, data, size)) != 0)) {
nni_free(data, size);
nni_aio_finish_error(aio, rv);
return;
}
- nni_http_res_set_status(res, NNG_HTTP_STATUS_OK);
+ nng_http_set_status(conn, NNG_HTTP_STATUS_OK, NULL);
nni_free(data, size);
- nni_aio_set_output(aio, 0, res);
nni_aio_finish(aio, 0, 0);
}
@@ -1605,20 +1558,17 @@ typedef struct http_redirect {
} http_redirect;
static void
-http_handle_redirect(nng_http_conn *conn, void *data, nng_aio *aio)
+http_handle_redirect(nng_http *conn, void *data, nng_aio *aio)
{
- nni_http_res *res = nng_http_conn_res(conn);
- nni_http_req *req = nng_http_conn_req(conn);
- char *html = NULL;
- char *msg = NULL;
- char *loc = NULL;
- http_redirect *hr = data;
+ nni_http_res *res = nni_http_conn_res(conn);
+ char *loc = NULL;
+ http_redirect *hr = data;
int rv;
const char *base;
const char *uri;
base = hr->from; // base uri
- uri = nni_http_req_get_uri(req);
+ uri = nni_http_get_uri(conn);
// If we are doing a full tree, then include the entire suffix.
if (strncmp(uri, base, strlen(base)) == 0) {
@@ -1631,39 +1581,24 @@ http_handle_redirect(nng_http_conn *conn, void *data, nng_aio *aio)
loc = hr->where;
}
- // Builtin redirect page
- rv = nni_asprintf(&msg,
- "You should be automatically redirected to <a href=\"%s\">%s</a>.",
- loc, loc);
-
// Build a response. We always close the connection for redirects,
// because it is probably going to another server. This also
// keeps us from having to consume the entity body, we can just
// discard it.
- if ((rv != 0) ||
- ((rv = nni_http_alloc_html_error(&html, hr->code, msg)) != 0) ||
- ((rv = nni_http_res_set_header(res, "Connection", "close")) !=
- 0) ||
- ((rv = nni_http_res_set_header(
- res, "Content-Type", "text/html; charset=UTF-8")) != 0) ||
- ((rv = nni_http_res_set_header(res, "Location", loc)) != 0) ||
- ((rv = nni_http_res_copy_data(res, html, strlen(html))) != 0)) {
+ if (((rv = nni_http_set_redirect(conn, hr->code, NULL, loc)) != 0) ||
+ ((rv = nni_http_set_header(conn, "Connection", "close")) != 0)) {
if (loc != hr->where) {
nni_strfree(loc);
}
- nni_strfree(msg);
- nni_strfree(html);
nni_aio_finish_error(aio, rv);
return;
}
- nni_http_res_set_status(res, hr->code);
+ nng_http_set_status(conn, hr->code, NULL);
if (loc != hr->where) {
nni_strfree(loc);
}
- nni_strfree(msg);
- nni_strfree(html);
nni_aio_set_output(aio, 0, res);
nni_aio_finish(aio, 0, 0);
}
@@ -1725,28 +1660,21 @@ typedef struct http_static {
} http_static;
static void
-http_handle_static(nng_http_conn *conn, void *data, nni_aio *aio)
+http_handle_static(nng_http *conn, void *data, nni_aio *aio)
{
- http_static *hs = data;
- const char *ctype;
- nni_http_res *r = NULL;
- int rv;
+ http_static *hs = data;
+ const char *ctype;
if ((ctype = hs->ctype) == NULL) {
ctype = "application/octet-stream";
}
- r = nng_http_conn_res(conn);
- nng_http_res_reset(r);
- if (((rv = nni_http_res_set_header(r, "Content-Type", ctype)) != 0) ||
- ((rv = nni_http_res_set_data(r, hs->data, hs->size)) != 0)) {
- nni_aio_finish_error(aio, rv);
- return;
- }
+ // this cannot fail (no dynamic allocation)
+ (void) nni_http_set_header(conn, "Content-Type", ctype);
+ nni_http_set_body(conn, hs->data, hs->size);
- nni_http_res_set_status(r, NNG_HTTP_STATUS_OK);
+ nng_http_set_status(conn, NNG_HTTP_STATUS_OK, NULL);
- nni_aio_set_output(aio, 0, r);
nni_aio_finish(aio, 0, 0);
}
diff --git a/src/supplemental/http/http_server_test.c b/src/supplemental/http/http_server_test.c
index 91411499..7a38a396 100644
--- a/src/supplemental/http/http_server_test.c
+++ b/src/supplemental/http/http_server_test.c
@@ -11,8 +11,9 @@
// Basic HTTP server tests.
#include "core/defs.h"
+#include <complex.h>
+#include <nng/http.h>
#include <nng/nng.h>
-#include <nng/supplemental/http/http.h>
#include <nuts.h>
@@ -27,49 +28,31 @@ struct server_test {
nng_http_server *s;
nng_http_handler *h;
nng_http_client *cli;
- nng_http_conn *conn;
- nng_http_req *req;
- nng_http_res *res;
+ nng_http *conn;
char urlstr[2048];
};
static int
-httpdo(nng_url *url, nng_http_req *req, nng_http_res *res, void **datap,
- size_t *sizep)
+httpdo(struct server_test *st, void **datap, size_t *sizep)
{
- int rv;
- nng_aio *aio = NULL;
- nng_http_client *cli = NULL;
- nng_http_conn *h = NULL;
- size_t clen = 0;
- void *data = NULL;
- const char *ptr;
-
- if (((rv = nng_aio_alloc(&aio, NULL, NULL)) != 0) ||
- ((rv = nng_http_client_alloc(&cli, url)) != 0)) {
- goto fail;
- }
- nng_http_client_connect(cli, aio);
- nng_aio_wait(aio);
- if ((rv = nng_aio_result(aio)) != 0) {
- goto fail;
- }
-
- h = nng_aio_get_output(aio, 0);
+ int rv;
+ size_t clen = 0;
+ void *data = NULL;
+ const char *ptr;
- nng_http_conn_write_req(h, req, aio);
- nng_aio_wait(aio);
- if ((rv = nng_aio_result(aio)) != 0) {
- goto fail;
+ nng_http_write_request(st->conn, st->aio);
+ nng_aio_wait(st->aio);
+ if ((rv = nng_aio_result(st->aio)) != 0) {
+ return (rv);
}
- nng_http_conn_read_res(h, res, aio);
- nng_aio_wait(aio);
- if ((rv = nng_aio_result(aio)) != 0) {
- goto fail;
+ nng_http_read_response(st->conn, st->aio);
+ nng_aio_wait(st->aio);
+ if ((rv = nng_aio_result(st->aio)) != 0) {
+ return (rv);
}
clen = 0;
- if ((ptr = nng_http_res_get_header(res, "Content-Length")) != NULL) {
+ if ((ptr = nng_http_get_header(st->conn, "Content-Length")) != NULL) {
clen = atoi(ptr);
}
@@ -78,28 +61,17 @@ httpdo(nng_url *url, nng_http_req *req, nng_http_res *res, void **datap,
data = nng_alloc(clen);
iov.iov_buf = data;
iov.iov_len = clen;
- nng_aio_set_iov(aio, 1, &iov);
- nng_http_conn_read_all(h, aio);
- nng_aio_wait(aio);
- if ((rv = nng_aio_result(aio)) != 0) {
- goto fail;
+ nng_aio_set_iov(st->aio, 1, &iov);
+ nng_http_read_all(st->conn, st->aio);
+ nng_aio_wait(st->aio);
+ if ((rv = nng_aio_result(st->aio)) != 0) {
+ return (rv);
}
}
*datap = data;
*sizep = clen;
-fail:
- if (aio != NULL) {
- nng_aio_free(aio);
- }
- if (h != NULL) {
- nng_http_conn_close(h);
- }
- if (cli != NULL) {
- nng_http_client_free(cli);
- }
-
return (rv);
}
@@ -111,16 +83,17 @@ httpget(struct server_test *st, void **datap, size_t *sizep, uint16_t *statp,
size_t clen = 0;
void *data = NULL;
char *ctype = NULL;
+ nng_http *conn = st->conn;
const char *ptr;
- if ((rv = httpdo(st->url, st->req, st->res, &data, &clen)) != 0) {
+ if ((rv = httpdo(st, &data, &clen)) != 0) {
goto fail;
}
- *statp = nng_http_res_get_status(st->res);
+ *statp = nng_http_get_status(conn);
if (clen > 0) {
- if ((ptr = nng_http_res_get_header(st->res, "Content-Type")) !=
+ if ((ptr = nng_http_get_header(conn, "Content-Type")) !=
NULL) {
ctype = nng_strdup(ptr);
}
@@ -142,25 +115,22 @@ fail:
}
static void
-httpecho(nng_http_conn *conn, void *arg, nng_aio *aio)
+httpecho(nng_http *conn, void *arg, nng_aio *aio)
{
- nng_http_req *req = nng_http_conn_req(conn);
- nng_http_res *res = nng_http_conn_res(conn);
- int rv;
- void *body;
- size_t len;
+ int rv;
+ void *body;
+ size_t len;
NNI_ARG_UNUSED(arg);
- nng_http_req_get_data(req, &body, &len);
+ nng_http_get_body(conn, &body, &len);
- if (((rv = nng_http_res_copy_data(res, body, len)) != 0) ||
- ((rv = nng_http_res_set_header(
- res, "Content-type", "text/plain")) != 0)) {
+ if (((rv = nng_http_copy_body(conn, body, len)) != 0) ||
+ ((rv = nng_http_set_header(conn, "Content-type", "text/plain")) !=
+ 0)) {
nng_aio_finish(aio, rv);
return;
}
- nng_http_res_set_status(res, NNG_HTTP_STATUS_OK);
- nng_aio_set_output(aio, 0, res);
+ nng_http_set_status(conn, NNG_HTTP_STATUS_OK, NULL);
nng_aio_finish(aio, 0);
}
@@ -188,17 +158,20 @@ server_setup(struct server_test *st, nng_http_handler *h)
NUTS_PASS(nng_aio_result(st->aio));
st->conn = nng_aio_get_output(st->aio, 0);
NUTS_TRUE(st->conn != NULL);
- NUTS_PASS(nng_http_req_alloc(&st->req, st->url));
- NUTS_PASS(nng_http_res_alloc(&st->res));
+ NUTS_PASS(nng_http_set_uri(st->conn, "/", NULL));
}
static void
server_reset(struct server_test *st)
{
- nng_http_req_free(st->req);
- nng_http_res_free(st->res);
- nng_http_req_alloc(&st->req, st->url);
- nng_http_res_alloc(&st->res);
+ if (st->conn) {
+ nng_http_close(st->conn);
+ }
+ nng_http_client_connect(st->cli, st->aio);
+ nng_aio_wait(st->aio);
+ NUTS_PASS(nng_aio_result(st->aio));
+ st->conn = nng_aio_get_output(st->aio, 0);
+ NUTS_PASS(nng_http_set_uri(st->conn, "/", NULL));
}
static void
@@ -211,7 +184,7 @@ server_free(struct server_test *st)
nng_http_client_free(st->cli);
}
if (st->conn != NULL) {
- nng_http_conn_close(st->conn);
+ nng_http_close(st->conn);
}
if (st->s != NULL) {
nng_http_server_release(st->s);
@@ -219,12 +192,6 @@ server_free(struct server_test *st)
if (st->url != NULL) {
nng_url_free(st->url);
}
- if (st->req != NULL) {
- nng_http_req_free(st->req);
- }
- if (st->res != NULL) {
- nng_http_res_free(st->res);
- }
}
static void
@@ -241,26 +208,26 @@ test_server_basic(void)
server_setup(&st, h);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/home.html"));
- nng_http_conn_write_req(st.conn, st.req, st.aio);
+ NUTS_PASS(nng_http_set_uri(st.conn, "/home.html", NULL));
+ nng_http_write_request(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_http_read_response(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- NUTS_TRUE(nng_http_res_get_status(st.res) == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(nng_http_get_status(st.conn) == NNG_HTTP_STATUS_OK);
- ptr = nng_http_res_get_header(st.res, "Content-Length");
+ ptr = nng_http_get_header(st.conn, "Content-Length");
NUTS_TRUE(ptr != NULL);
NUTS_TRUE(atoi(ptr) == (int) strlen(doc1));
iov.iov_len = strlen(doc1);
iov.iov_buf = chunk;
NUTS_PASS(nng_aio_set_iov(st.aio, 1, &iov));
- nng_http_conn_read_all(st.conn, st.aio);
+ nng_http_read_all(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
NUTS_TRUE(nng_aio_count(st.aio) == strlen(doc1));
@@ -276,18 +243,17 @@ test_server_404(void)
server_setup(&st, NULL);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/bogus"));
- nng_http_conn_write_req(st.conn, st.req, st.aio);
+ NUTS_PASS(nng_http_set_uri(st.conn, "/bogus", NULL));
+ nng_http_write_request(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_http_read_response(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- NUTS_TRUE(
- nng_http_res_get_status(st.res) == NNG_HTTP_STATUS_NOT_FOUND);
+ NUTS_TRUE(nng_http_get_status(st.conn) == NNG_HTTP_STATUS_NOT_FOUND);
server_free(&st);
}
@@ -299,18 +265,19 @@ test_server_bad_version(void)
server_setup(&st, NULL);
- NUTS_PASS(nng_http_req_set_version(st.req, "HTTP/0.9"));
- NUTS_PASS(nng_http_req_set_uri(st.req, "/bogus"));
- nng_http_conn_write_req(st.conn, st.req, st.aio);
+ NUTS_PASS(nng_http_set_version(st.conn, "HTTP/0.9"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/bogus", NULL));
+ nng_http_write_request(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_http_read_response(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- NUTS_TRUE(nng_http_res_get_status(st.res) == 505);
+ NUTS_TRUE(nng_http_get_status(st.conn) ==
+ NNG_HTTP_STATUS_HTTP_VERSION_NOT_SUPP);
server_free(&st);
}
@@ -321,18 +288,18 @@ test_server_missing_host(void)
struct server_test st;
server_setup(&st, NULL);
- nng_http_req_del_header(st.req, "Host");
- NUTS_PASS(nng_http_req_set_uri(st.req, "/bogus"));
- nng_http_conn_write_req(st.conn, st.req, st.aio);
+ nng_http_del_header(st.conn, "Host");
+ NUTS_PASS(nng_http_set_uri(st.conn, "/bogus", NULL));
+ nng_http_write_request(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_http_read_response(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- NUTS_TRUE(nng_http_res_get_status(st.res) == 400);
+ NUTS_TRUE(nng_http_get_status(st.conn) == 400);
server_free(&st);
}
@@ -363,21 +330,21 @@ test_server_wrong_method(void)
server_setup(&st, h);
- nng_http_req_set_method(st.req, "POST");
- NUTS_PASS(nng_http_req_set_uri(st.req, "/home.html"));
- nng_http_conn_write_req(st.conn, st.req, st.aio);
+ nng_http_set_method(st.conn, "POST");
+ NUTS_PASS(nng_http_set_uri(st.conn, "/home.html", NULL));
+ nng_http_write_request(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- nng_http_conn_read_res(st.conn, st.res, st.aio);
+ nng_http_read_response(st.conn, st.aio);
nng_aio_wait(st.aio);
NUTS_PASS(nng_aio_result(st.aio));
- NUTS_TRUE(nng_http_res_get_status(st.res) ==
+ NUTS_TRUE(nng_http_get_status(st.conn) ==
NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
- NUTS_MSG("Got result %d: %s", nng_http_res_get_status(st.res),
- nng_http_res_get_reason(st.res));
+ NUTS_MSG("Got result %d: %s", nng_http_get_status(st.conn),
+ nng_http_get_reason(st.conn));
server_free(&st);
}
@@ -398,25 +365,26 @@ test_server_post_handler(void)
server_setup(&st, h);
snprintf(txdata, sizeof(txdata), "1234");
- nng_http_req_set_uri(st.req, "/post");
- nng_http_req_set_data(st.req, txdata, strlen(txdata));
- nng_http_req_set_method(st.req, "POST");
- NUTS_PASS(httpdo(st.url, st.req, st.res, (void **) &rxdata, &size));
- NUTS_TRUE(nng_http_res_get_status(st.res) == NNG_HTTP_STATUS_OK);
+
+ NUTS_PASS(nng_http_set_uri(st.conn, "/post", NULL));
+ nng_http_set_body(st.conn, txdata, strlen(txdata));
+ nng_http_set_method(st.conn, "POST");
+ NUTS_PASS(httpdo(&st, (void **) &rxdata, &size));
+ NUTS_TRUE(nng_http_get_status(st.conn) == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(txdata));
NUTS_TRUE(strncmp(txdata, rxdata, size) == 0);
nng_free(rxdata, size);
server_reset(&st);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/post"));
- nng_http_req_set_method(st.req, "GET");
- NUTS_PASS(nng_http_req_set_data(st.req, txdata, strlen(txdata)));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/post", NULL));
+ nng_http_set_method(st.conn, "GET");
+ nng_http_set_body(st.conn, txdata, strlen(txdata));
- NUTS_PASS(httpdo(st.url, st.req, st.res, &data, &size));
- NUTS_TRUE(nng_http_res_get_status(st.res) ==
+ NUTS_PASS(httpdo(&st, &data, &size));
+ NUTS_TRUE(nng_http_get_status(st.conn) ==
NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
- NUTS_MSG("HTTP status was %u", nng_http_res_get_status(st.res));
+ NUTS_MSG("HTTP status was %u", nng_http_get_status(st.conn));
nng_free(data, size);
server_free(&st);
@@ -437,15 +405,14 @@ test_server_get_redirect(void)
&h, "/here", 303, "http://127.0.0.1/there"));
server_setup(&st, h);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/here"));
- nng_http_req_set_method(st.req, "GET");
+ NUTS_PASS(nng_http_set_uri(st.conn, "/here", NULL));
+ nng_http_set_method(st.conn, "GET");
- NUTS_PASS(httpdo(st.url, st.req, st.res, &data, &size));
- NUTS_TRUE(nng_http_res_get_status(st.res) == 303);
+ NUTS_PASS(httpdo(&st, &data, &size));
+ NUTS_TRUE(nng_http_get_status(st.conn) == 303);
NUTS_MSG("HTTP status got %d, expected %d (url %s)",
- nng_http_res_get_status(st.res), 303, fullurl);
- NUTS_TRUE(
- (dest = nng_http_res_get_header(st.res, "Location")) != NULL);
+ nng_http_get_status(st.conn), 303, fullurl);
+ NUTS_TRUE((dest = nng_http_get_header(st.conn, "Location")) != NULL);
NUTS_MATCH(dest, "http://127.0.0.1/there");
nng_free(data, size);
@@ -468,15 +435,14 @@ test_server_tree_redirect(void)
nng_http_handler_set_tree(h);
server_setup(&st, h);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/here/i/go/again"));
- nng_http_req_set_method(st.req, "GET");
+ NUTS_PASS(nng_http_set_uri(st.conn, "/here/i/go/again", NULL));
+ nng_http_set_method(st.conn, "GET");
- NUTS_PASS(httpdo(st.url, st.req, st.res, &data, &size));
- NUTS_TRUE(nng_http_res_get_status(st.res) == 303);
+ NUTS_PASS(httpdo(&st, &data, &size));
+ NUTS_TRUE(nng_http_get_status(st.conn) == 303);
NUTS_MSG("HTTP status got %d, expected %d (url %s)",
- nng_http_res_get_status(st.res), 303, fullurl);
- NUTS_TRUE(
- (dest = nng_http_res_get_header(st.res, "Location")) != NULL);
+ nng_http_get_status(st.conn), 303, fullurl);
+ NUTS_TRUE((dest = nng_http_get_header(st.conn, "Location")) != NULL);
NUTS_MATCH(dest, "http://127.0.0.1/there/i/go/again");
nng_free(data, size);
@@ -499,12 +465,12 @@ test_server_post_redirect(void)
server_setup(&st, h);
snprintf(txdata, sizeof(txdata), "1234");
- NUTS_PASS(nng_http_req_set_uri(st.req, "/here"));
- nng_http_req_set_data(st.req, txdata, strlen(txdata));
- nng_http_req_set_method(st.req, "POST");
- NUTS_PASS(httpdo(st.url, st.req, st.res, (void **) &data, &size));
- NUTS_TRUE(nng_http_res_get_status(st.res) == 301);
- dest = nng_http_res_get_header(st.res, "Location");
+ NUTS_PASS(nng_http_set_uri(st.conn, "/here", NULL));
+ nng_http_set_body(st.conn, txdata, strlen(txdata));
+ nng_http_set_method(st.conn, "POST");
+ NUTS_PASS(httpdo(&st, (void **) &data, &size));
+ NUTS_TRUE(nng_http_get_status(st.conn) == 301);
+ dest = nng_http_get_header(st.conn, "Location");
NUTS_TRUE(dest != NULL);
NUTS_MATCH(dest, "http://127.0.0.1/there");
nng_free(data, size);
@@ -527,11 +493,11 @@ test_server_post_echo_tree(void)
server_setup(&st, h);
snprintf(txdata, sizeof(txdata), "1234");
- nng_http_req_set_data(st.req, txdata, strlen(txdata));
- nng_http_req_set_method(st.req, "POST");
- NUTS_PASS(nng_http_req_set_uri(st.req, "/some_sub/directory"));
- NUTS_PASS(httpdo(st.url, st.req, st.res, (void **) &rxdata, &size));
- NUTS_TRUE(nng_http_res_get_status(st.res) == NNG_HTTP_STATUS_OK);
+ nng_http_set_body(st.conn, txdata, strlen(txdata));
+ nng_http_set_method(st.conn, "POST");
+ NUTS_PASS(nng_http_set_uri(st.conn, "/some_sub/directory", NULL));
+ NUTS_PASS(httpdo(&st, (void **) &rxdata, &size));
+ NUTS_TRUE(nng_http_get_status(st.conn) == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(txdata));
NUTS_TRUE(strncmp(txdata, rxdata, size) == 0);
nng_free(rxdata, size);
@@ -613,7 +579,7 @@ test_server_multiple_trees(void)
char *ctype;
NUTS_CASE("Directory 1");
- NUTS_PASS(nng_http_req_set_uri(st.req, "/file1.txt"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/file1.txt", NULL));
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(doc1));
@@ -625,7 +591,7 @@ test_server_multiple_trees(void)
server_reset(&st);
NUTS_CASE("Directory 2");
- NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir/file2.txt"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/subdir/file2.txt", NULL));
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(doc2));
@@ -710,7 +676,7 @@ test_serve_directory(void)
NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
server_setup(&st, h);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir1/index.html"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/subdir1/index.html", NULL));
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(doc1));
@@ -740,7 +706,7 @@ test_serve_directory_index(void)
server_setup(&st, h);
NUTS_CASE("Directory 1: index.html");
- NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir1/"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/subdir1/", NULL));
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(doc1));
@@ -752,7 +718,7 @@ test_serve_directory_index(void)
server_reset(&st);
NUTS_CASE("Directory 2: index.htm");
- NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir2/"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/subdir2/", NULL));
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(doc3));
@@ -781,7 +747,7 @@ test_serve_plain_text(void)
NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
server_setup(&st, h);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/file.txt"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/file.txt", NULL));
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(doc2));
@@ -810,12 +776,24 @@ test_serve_file_parameters(void)
NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
server_setup(&st, h);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/file.txt?param=1234"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/file.txt?param=1234", NULL));
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(doc2));
NUTS_TRUE(memcmp(data, doc2, size) == 0);
NUTS_MATCH(ctype, "text/plain");
+ nng_free(data, size);
+ nng_strfree(ctype);
+
+ // again but this time pass parameter as arg
+ nng_http_reset(st.conn);
+ NUTS_PASS(nng_http_set_uri(st.conn, "/file.txt", "param=1234"));
+ NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
+ NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
+ NUTS_TRUE(size == strlen(doc2));
+ NUTS_TRUE(memcmp(data, doc2, size) == 0);
+ NUTS_MATCH(ctype, "text/plain");
+
nng_strfree(ctype);
nng_free(data, size);
@@ -839,7 +817,7 @@ test_serve_missing_index(void)
NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
server_setup(&st, h);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/index.html"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/index.html", NULL));
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_NOT_FOUND);
nng_strfree(ctype);
@@ -865,8 +843,8 @@ test_serve_index_not_post(void)
NUTS_PASS(nng_http_handler_alloc_directory(&h, "/", sd.workdir));
server_setup(&st, h);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/subdir2/index.html"));
- nng_http_req_set_method(st.req, "POST");
+ NUTS_PASS(nng_http_set_uri(st.conn, "/subdir2/index.html", NULL));
+ nng_http_set_method(st.conn, "POST");
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_METHOD_NOT_ALLOWED);
nng_strfree(ctype);
@@ -892,7 +870,7 @@ test_serve_subdir_index(void)
NUTS_PASS(nng_http_handler_alloc_directory(&h, "/docs", sd.workdir));
server_setup(&st, h);
- NUTS_PASS(nng_http_req_set_uri(st.req, "/docs/subdir1/"));
+ NUTS_PASS(nng_http_set_uri(st.conn, "/docs/subdir1/", NULL));
NUTS_PASS(httpget(&st, &data, &size, &stat, &ctype));
NUTS_TRUE(stat == NNG_HTTP_STATUS_OK);
NUTS_TRUE(size == strlen(doc1));