aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2025-01-06 15:20:09 -0800
committerGarrett D'Amore <garrett@damore.org>2025-01-09 23:22:56 -0800
commit73f50e2679525e7df8734c875a3c12732565f953 (patch)
tree23bd167dfcd95305b58a29c142b51879011f63b2 /src
parenta381af4f5ca79576a4a9b461529a0f22fcf1e088 (diff)
downloadnng-2.0.0-alpha.3.tar.gz
nng-2.0.0-alpha.3.tar.bz2
nng-2.0.0-alpha.3.zip
http: The big HTTP API refactoring of January 2025.v2.0.0-alpha.3http-client-trans
This represents a major change in the HTTP code base, consisting of a complete revamp of the HTTP API. The changes here are too numerous to mention, but the end result should be a vastly simpler API for both server and client applications. Many needless allocations were removed by providing fixed buffers for various parameters and headers when possible. A few bugs were fixed. Most especially we have fixed some bugs around very large URIs and headers, and we have also addressed conformance bugs to more closely conform to RFCs 9110 and 9112. As part of this work, the APIs for WebSockets changed slightly as well. In particular the properties available for accessing headers have changed. There is still documentation conversion work to do, and additional functionality (such as proper support for chunked transfers), but this is a big step in the right direction.
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/sp/transport/ws/ws_test.c11
-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
-rw-r--r--src/supplemental/websocket/websocket.c348
-rw-r--r--src/supplemental/websocket/websocket_test.c16
13 files changed, 1373 insertions, 1786 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index be3d3605..3cf64f94 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -11,6 +11,7 @@
nng_sources(nng.c nng_legacy.c)
nng_headers(nng/args.h)
nng_headers(nng/nng.h)
+nng_headers(nng/http.h)
target_include_directories(nng PRIVATE ${PROJECT_SOURCE_DIR}/src)
target_include_directories(nng_testing PRIVATE ${PROJECT_SOURCE_DIR}/src)
diff --git a/src/sp/transport/ws/ws_test.c b/src/sp/transport/ws/ws_test.c
index 5d6215e6..98856d4f 100644
--- a/src/sp/transport/ws/ws_test.c
+++ b/src/sp/transport/ws/ws_test.c
@@ -231,17 +231,6 @@ check_props_v4(nng_msg *msg)
NUTS_PASS(nng_pipe_get_bool(p, NNG_OPT_TCP_NODELAY, &b));
NUTS_TRUE(b); // default
-
- // Request Header
- char *buf = NULL;
- NUTS_PASS(nng_pipe_get_string(p, NNG_OPT_WS_REQUEST_HEADERS, &buf));
- NUTS_TRUE(strstr(buf, "Sec-WebSocket-Key") != NULL);
- nng_strfree(buf);
-
- // Response Header
- NUTS_PASS(nng_pipe_get_string(p, NNG_OPT_WS_RESPONSE_HEADERS, &buf));
- NUTS_TRUE(strstr(buf, "Sec-WebSocket-Accept") != NULL);
- nng_strfree(buf);
}
void
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));
diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c
index 70323b0c..86edf123 100644
--- a/src/supplemental/websocket/websocket.c
+++ b/src/supplemental/websocket/websocket.c
@@ -14,6 +14,7 @@
#include <string.h>
#include "core/nng_impl.h"
+#include "nng/http.h"
#include "supplemental/http/http_api.h"
#include "base64.h"
@@ -21,7 +22,7 @@
#include "websocket.h"
// This should be removed or handled differently in the future.
-typedef int (*nni_ws_listen_hook)(void *, nng_http_req *, nng_http_res *);
+typedef int (*nni_ws_listen_hook)(void *, nng_http *);
// We have chosen to be a bit more stringent in the size of the frames that
// we send, while we more generously allow larger incoming frames. These
@@ -55,6 +56,7 @@ struct nni_ws {
bool inmsg;
bool send_text;
bool recv_text;
+ bool recv_res;
nni_mtx mtx;
nni_list sendq;
nni_list recvq;
@@ -68,16 +70,21 @@ struct nni_ws {
nni_aio httpaio;
nni_aio connaio; // connect aio
nni_aio *useraio; // user aio, during HTTP negotiation
- nni_http_conn *http;
- nni_http_req *req;
- nni_http_res *res;
- char *reqhdrs;
- char *reshdrs;
+ nng_http *http;
size_t maxframe;
size_t fragsize;
size_t recvmax; // largest message size
nni_ws_listener *listener;
nni_ws_dialer *dialer;
+ char keybuf[29]; // key on client, accept on server
+ struct {
+ nni_http_header connection;
+ nni_http_header upgrade;
+ nni_http_header wsaccept;
+ nni_http_header wskey;
+ nni_http_header wsproto;
+ nni_http_header wsversion;
+ } hdrs;
};
struct nni_ws_listener {
@@ -235,64 +242,6 @@ ws_set_header(nni_list *l, const char *n, const char *v)
return (ws_set_header_ext(l, n, v, true));
}
-static int
-ws_set_headers(nni_list *l, const char *str)
-{
- char *dupstr;
- size_t duplen;
- char *n;
- char *v;
- char *nl;
- int rv;
-
- if ((dupstr = nni_strdup(str)) == NULL) {
- return (NNG_ENOMEM);
- }
- duplen = strlen(dupstr) + 1; // so we can free it later
-
- n = dupstr;
- for (;;) {
- if ((v = strchr(n, ':')) == NULL) {
- // Note that this also means that if
- // a bare word is present, we ignore it.
- break;
- }
- *v = '\0';
- v++;
- while (*v == ' ') {
- // Skip leading whitespace. Not strictly
- // necessary, but still a good idea.
- v++;
- }
- nl = v;
- // Find the end of the line -- should be CRLF, but can
- // also be unterminated or just LF if user
- while ((*nl != '\0') && (*nl != '\r') && (*nl != '\n')) {
- nl++;
- }
- while ((*nl == '\r') || (*nl == '\n')) {
- *nl = '\0';
- nl++;
- }
-
- // Note that this can lead to a partial failure. As this
- // is most likely ENOMEM, don't worry too much about it.
- // This method does *not* eliminate duplicates.
- if ((rv = ws_set_header_ext(l, n, v, false)) != 0) {
- goto done;
- }
-
- // Advance to the next name.
- n = nl;
- }
-
- rv = 0;
-
-done:
- nni_free(dupstr, duplen);
- return (rv);
-}
-
// This looks, case independently for a word in a list, which is either
// space or comma separated.
static bool
@@ -1244,8 +1193,6 @@ ws_fini(void *arg)
nni_http_conn_fini(ws->http);
}
- nni_strfree(ws->reqhdrs);
- nni_strfree(ws->reshdrs);
nni_aio_fini(&ws->rxaio);
nni_aio_fini(&ws->txaio);
nni_aio_fini(&ws->closeaio);
@@ -1325,14 +1272,14 @@ ws_http_cb_dialer(nni_ws *ws, nni_aio *aio)
// If we have no response structure, then this was completion
// of sending the request. Prepare an empty response, and read it.
- if (ws->res == NULL) {
- ws->res = nni_http_conn_res(ws->http);
- nni_http_read_res(ws->http, ws->res, &ws->httpaio);
+ if (!ws->recv_res) {
+ ws->recv_res = true;
+ nng_http_read_response(ws->http, &ws->httpaio);
nni_mtx_unlock(&d->mtx);
return;
}
- status = nni_http_res_get_status(ws->res);
+ status = nni_http_get_status(ws->http);
switch (status) {
case NNG_HTTP_STATUS_SWITCHING:
break;
@@ -1342,6 +1289,7 @@ ws_http_cb_dialer(nni_ws *ws, nni_aio *aio)
goto err;
case NNG_HTTP_STATUS_NOT_FOUND:
case NNG_HTTP_STATUS_METHOD_NOT_ALLOWED:
+ case NNG_HTTP_STATUS_NOT_IMPLEMENTED:
rv = NNG_ECONNREFUSED; // Treat these as refusals.
goto err;
case NNG_HTTP_STATUS_BAD_REQUEST:
@@ -1351,34 +1299,31 @@ ws_http_cb_dialer(nni_ws *ws, nni_aio *aio)
goto err;
}
- // Check that the server gave us back the right key.
- rv = ws_make_accept(
- nni_http_req_get_header(ws->req, "Sec-WebSocket-Key"), wskey);
+ rv = ws_make_accept(ws->keybuf, wskey);
if (rv != 0) {
goto err;
}
-#define GETH(h) nni_http_res_get_header(ws->res, h)
-
- if (((ptr = GETH("Sec-WebSocket-Accept")) == NULL) ||
+ if (((ptr = nng_http_get_header(ws->http, "Sec-WebSocket-Accept")) ==
+ NULL) ||
(strcmp(ptr, wskey) != 0) ||
- ((ptr = GETH("Connection")) == NULL) ||
+ ((ptr = nng_http_get_header(ws->http, "Connection")) == NULL) ||
(!ws_contains_word(ptr, "upgrade")) ||
- ((ptr = GETH("Upgrade")) == NULL) ||
+ ((ptr = nng_http_get_header(ws->http, "Upgrade")) == NULL) ||
(strcmp(ptr, "websocket") != 0)) {
ws_close_error(ws, WS_CLOSE_PROTOCOL_ERR);
rv = NNG_EPROTO;
goto err;
}
if (d->proto != NULL) {
- if (((ptr = GETH("Sec-WebSocket-Protocol")) == NULL) ||
+ if (((ptr = nng_http_get_header(
+ ws->http, "Sec-WebSocket-Protocol")) == NULL) ||
(!ws_contains_word(d->proto, ptr))) {
ws_close_error(ws, WS_CLOSE_PROTOCOL_ERR);
rv = NNG_EPROTO;
goto err;
}
}
-#undef GETH
// At this point, we are in business!
nni_list_remove(&d->wspend, ws);
@@ -1506,19 +1451,16 @@ ws_listener_free(void *arg)
}
static void
-ws_handler(nng_http_conn *conn, void *arg, nng_aio *aio)
+ws_handler(nng_http *conn, void *arg, nng_aio *aio)
{
nni_ws_listener *l = arg;
- ;
- nni_http_req *req = nng_http_conn_req(conn);
- nni_http_res *res = nng_http_conn_res(conn);
- nni_ws *ws;
- const char *ptr;
- const char *proto;
- uint16_t status;
- int rv;
- char key[29];
- ws_header *hdr;
+ nni_ws *ws;
+ const char *ptr;
+ const char *proto;
+ uint16_t status;
+ int rv;
+ char key[29];
+ ws_header *hdr;
nni_mtx_lock(&l->mtx);
if (l->closed) {
@@ -1527,39 +1469,39 @@ ws_handler(nng_http_conn *conn, void *arg, nng_aio *aio)
}
// Now check the headers, etc.
- if (strcmp(nni_http_req_get_version(req), "HTTP/1.1") != 0) {
+ if (strcmp(nng_http_get_version(conn), "HTTP/1.1") != 0) {
status = NNG_HTTP_STATUS_HTTP_VERSION_NOT_SUPP;
goto err;
}
- if (strcmp(nni_http_req_get_method(req), "GET") != 0) {
+ if (strcmp(nng_http_get_method(conn), "GET") != 0) {
// HEAD request. We can't really deal with it.
status = NNG_HTTP_STATUS_BAD_REQUEST;
goto err;
}
-#define GETH(h) nni_http_req_get_header(req, h)
-#define SETH(h, v) nni_http_res_set_header(res, h, v)
-
- if ((((ptr = GETH("Content-Length")) != NULL) && (atoi(ptr) > 0)) ||
- (((ptr = GETH("Transfer-Encoding")) != NULL) &&
+ if ((((ptr = nng_http_get_header(conn, "Content-Length")) != NULL) &&
+ (atoi(ptr) > 0)) ||
+ (((ptr = nng_http_get_header(conn, "Transfer-Encoding")) !=
+ NULL) &&
(nni_strcasestr(ptr, "chunked") != NULL))) {
- status = NNG_HTTP_STATUS_PAYLOAD_TOO_LARGE;
+ status = NNG_HTTP_STATUS_CONTENT_TOO_LARGE;
goto err;
}
// These headers have to be present.
- if (((ptr = GETH("Upgrade")) == NULL) ||
+ if (((ptr = nng_http_get_header(conn, "Upgrade")) == NULL) ||
(!ws_contains_word(ptr, "websocket")) ||
- ((ptr = GETH("Connection")) == NULL) ||
+ ((ptr = nng_http_get_header(conn, "Connection")) == NULL) ||
(!ws_contains_word(ptr, "upgrade")) ||
- ((ptr = GETH("Sec-WebSocket-Version")) == NULL) ||
+ ((ptr = nng_http_get_header(conn, "Sec-WebSocket-Version")) ==
+ NULL) ||
(strcmp(ptr, "13") != 0)) {
status = NNG_HTTP_STATUS_BAD_REQUEST;
goto err;
}
- if (((ptr = GETH("Sec-WebSocket-Key")) == NULL) ||
+ if (((ptr = nng_http_get_header(conn, "Sec-WebSocket-Key")) == NULL) ||
(ws_make_accept(ptr, key) != 0)) {
status = NNG_HTTP_STATUS_BAD_REQUEST;
goto err;
@@ -1569,7 +1511,7 @@ ws_handler(nng_http_conn *conn, void *arg, nng_aio *aio)
// we need to try to match it to what the handler says we
// support. (If no suitable option is found in the handler, we
// fail the request.)
- proto = GETH("Sec-WebSocket-Protocol");
+ proto = nng_http_get_header(conn, "Sec-WebSocket-Protocol");
if (proto == NULL) {
if (l->proto != NULL) {
status = NNG_HTTP_STATUS_BAD_REQUEST;
@@ -1581,23 +1523,13 @@ ws_handler(nng_http_conn *conn, void *arg, nng_aio *aio)
goto err;
}
- nni_http_res_set_status(res, NNG_HTTP_STATUS_SWITCHING);
-
- if ((SETH("Connection", "Upgrade") != 0) ||
- (SETH("Upgrade", "websocket") != 0) ||
- (SETH("Sec-WebSocket-Accept", key) != 0)) {
- status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
- goto err;
- }
- if ((proto != NULL) && (SETH("Sec-WebSocket-Protocol", proto) != 0)) {
- status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
- goto err;
- }
+ nng_http_set_status(conn, NNG_HTTP_STATUS_SWITCHING, NULL);
// Set any user supplied headers. This is better than using a hook
- // for most things, because it is loads easier.
+ // for most things, because it is loads easier. Note that websocket
+ // headers we care about will be overridden below!
NNI_LIST_FOREACH (&l->headers, hdr) {
- if (SETH(hdr->name, hdr->value) != 0) {
+ if (nng_http_set_header(conn, hdr->name, hdr->value) != 0) {
status = NNG_HTTP_STATUS_INTERNAL_SERVER_ERROR;
goto err;
}
@@ -1608,33 +1540,25 @@ ws_handler(nng_http_conn *conn, void *arg, nng_aio *aio)
// need to, because it's much more complex. But if you want to set
// up an HTTP Authorization handler this might be the only choice.
if (l->hookfn != NULL) {
- rv = l->hookfn(l->hookarg, req, res);
+ rv = l->hookfn(l->hookarg, conn);
if (rv != 0) {
nni_aio_finish_error(aio, rv);
nni_mtx_unlock(&l->mtx);
return;
}
- if (nni_http_res_get_status(res) !=
- NNG_HTTP_STATUS_SWITCHING) {
+ if (nng_http_get_status(conn) != NNG_HTTP_STATUS_SWITCHING) {
// The hook has decided to give back a
// different reply and we are not upgrading
// anymore. For example the Origin might not
// be permitted, or another level of
- // authentication may be required. (Note that
- // the hook can also give back various other
- // headers, but it would be bad for it to alter
- // the websocket mandated headers.)
- nni_aio_set_output(aio, 0, res);
+ // authentication may be required.
nni_aio_finish(aio, 0, 0);
nni_mtx_unlock(&l->mtx);
return;
}
}
-#undef GETH
-#undef SETH
-
// We are good to go, provided we can get the websocket struct,
// and send the reply.
if ((rv = ws_init(&ws)) != 0) {
@@ -1642,8 +1566,6 @@ ws_handler(nng_http_conn *conn, void *arg, nng_aio *aio)
goto err;
}
ws->http = conn;
- ws->req = req;
- ws->res = res;
ws->server = true;
ws->maxframe = l->maxframe;
ws->fragsize = l->fragsize;
@@ -1652,9 +1574,23 @@ ws_handler(nng_http_conn *conn, void *arg, nng_aio *aio)
ws->recv_text = l->recv_text;
ws->send_text = l->send_text;
ws->listener = l;
+ memcpy(ws->keybuf, key, sizeof(ws->keybuf));
+
+ nni_http_set_static_header(
+ conn, &ws->hdrs.connection, "Connection", "Upgrade");
+ nni_http_set_static_header(
+ conn, &ws->hdrs.upgrade, "Upgrade", "websocket");
+ nni_http_set_static_header(
+ conn, &ws->hdrs.wsaccept, "Sec-WebSocket-Accept", ws->keybuf);
+ if (proto != NULL) {
+ // NB: we still have the request protocol in the header, so
+ // that should be fine.
+ nni_http_set_static_header(
+ conn, &ws->hdrs.wsproto, "Sec-WebSocket-Protocol", proto);
+ }
nni_list_append(&l->reply, ws);
- nni_http_write_res(conn, &ws->httpaio);
+ nng_http_write_response(conn, &ws->httpaio);
(void) nni_http_hijack(conn);
nni_aio_set_output(aio, 0, NULL);
nni_aio_finish(aio, 0, 0);
@@ -1662,10 +1598,9 @@ ws_handler(nng_http_conn *conn, void *arg, nng_aio *aio)
return;
err:
- 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);
} else {
- nni_aio_set_output(aio, 0, res);
nni_aio_finish(aio, 0, 0);
}
nni_mtx_unlock(&l->mtx);
@@ -1862,20 +1797,6 @@ ws_listener_get_recvmax(void *arg, void *buf, size_t *szp, nni_type t)
}
static int
-ws_listener_set_res_headers(void *arg, const void *buf, size_t sz, nni_type t)
-{
- nni_ws_listener *l = arg;
- int rv;
-
- if ((rv = ws_check_string(buf, sz, t)) == 0) {
- nni_mtx_lock(&l->mtx);
- rv = ws_set_headers(&l->headers, buf);
- nni_mtx_unlock(&l->mtx);
- }
- return (rv);
-}
-
-static int
ws_listener_set_proto(void *arg, const void *buf, size_t sz, nni_type t)
{
nni_ws_listener *l = arg;
@@ -1996,11 +1917,6 @@ static const nni_option ws_listener_options[] = {
.o_get = ws_listener_get_recvmax,
},
{
- .o_name = NNG_OPT_WS_RESPONSE_HEADERS,
- .o_set = ws_listener_set_res_headers,
- // XXX: Get not implemented yet; likely of marginal value.
- },
- {
.o_name = NNG_OPT_WS_PROTOCOL,
.o_set = ws_listener_set_proto,
.o_get = ws_listener_get_proto,
@@ -2025,7 +1941,7 @@ ws_listener_set_header(nni_ws_listener *l, const char *name, const void *buf,
size_t sz, nni_type t)
{
int rv;
- name += strlen(NNG_OPT_WS_RESPONSE_HEADER);
+ name += strlen(NNG_OPT_WS_HEADER);
if ((rv = ws_check_string(buf, sz, t)) == 0) {
nni_mtx_lock(&l->mtx);
rv = ws_set_header(&l->headers, name, buf);
@@ -2047,7 +1963,7 @@ ws_listener_set(
}
if (rv == NNG_ENOTSUP) {
- if (startswith(name, NNG_OPT_WS_RESPONSE_HEADER)) {
+ if (startswith(name, NNG_OPT_WS_HEADER)) {
rv = ws_listener_set_header(l, name, buf, sz, t);
}
}
@@ -2147,10 +2063,8 @@ ws_conn_cb(void *arg)
nni_ws_dialer *d;
nni_ws *ws;
nni_aio *uaio;
- nni_http_req *req = NULL;
int rv;
uint8_t raw[16];
- char wskey[25];
ws_header *hdr;
ws = arg;
@@ -2192,35 +2106,36 @@ ws_conn_cb(void *arg)
for (int i = 0; i < 16; i++) {
raw[i] = (uint8_t) nni_random();
}
- nni_base64_encode(raw, 16, wskey, 24);
- wskey[24] = '\0';
-
- req = nni_http_conn_req(ws->http);
+ nni_base64_encode(raw, 16, ws->keybuf, 24);
+ ws->keybuf[24] = '\0';
-#define SETH(h, v) nni_http_req_set_header(req, h, v)
- if ((rv != 0) || ((rv = nni_http_req_set_url(req, d->url)) != 0) ||
- ((rv = SETH("Upgrade", "websocket")) != 0) ||
- ((rv = SETH("Connection", "Upgrade")) != 0) ||
- ((rv = SETH("Sec-WebSocket-Key", wskey)) != 0) ||
- ((rv = SETH("Sec-WebSocket-Version", "13")) != 0)) {
+ if ((rv = nni_http_set_uri(
+ ws->http, d->url->u_path, d->url->u_query)) != 0) {
goto err;
}
- if ((d->proto != NULL) &&
- ((rv = SETH("Sec-WebSocket-Protocol", d->proto)) != 0)) {
- goto err;
+ nni_http_set_static_header(
+ ws->http, &ws->hdrs.connection, "Connection", "Upgrade");
+ nni_http_set_static_header(
+ ws->http, &ws->hdrs.upgrade, "Upgrade", "websocket");
+ nni_http_set_static_header(
+ ws->http, &ws->hdrs.wskey, "Sec-WebSocket-Key", ws->keybuf);
+ nni_http_set_static_header(
+ ws->http, &ws->hdrs.wsversion, "Sec-WebSocket-Version", "13");
+
+ if (d->proto != NULL) {
+ nni_http_set_static_header(ws->http, &ws->hdrs.wsproto,
+ "Sec-WebSocket-Protocol", d->proto);
}
NNI_LIST_FOREACH (&d->headers, hdr) {
- if ((rv = SETH(hdr->name, hdr->value)) != 0) {
+ if ((rv = nni_http_set_header(
+ ws->http, hdr->name, hdr->value)) != 0) {
goto err;
}
}
-#undef SETH
-
- ws->req = req;
- nni_http_write_req(ws->http, req, &ws->httpaio);
+ nni_http_write_req(ws->http, &ws->httpaio);
nni_mtx_unlock(&ws->mtx);
return;
@@ -2455,20 +2370,6 @@ ws_dialer_get_recvmax(void *arg, void *buf, size_t *szp, nni_type t)
}
static int
-ws_dialer_set_req_headers(void *arg, const void *buf, size_t sz, nni_type t)
-{
- nni_ws_dialer *d = arg;
- int rv;
-
- if ((rv = ws_check_string(buf, sz, t)) == 0) {
- nni_mtx_lock(&d->mtx);
- rv = ws_set_headers(&d->headers, buf);
- nni_mtx_unlock(&d->mtx);
- }
- return (rv);
-}
-
-static int
ws_dialer_set_proto(void *arg, const void *buf, size_t sz, nni_type t)
{
nni_ws_dialer *d = arg;
@@ -2544,11 +2445,6 @@ static const nni_option ws_dialer_options[] = {
.o_get = ws_dialer_get_recvmax,
},
{
- .o_name = NNG_OPT_WS_REQUEST_HEADERS,
- .o_set = ws_dialer_set_req_headers,
- // XXX: Get not implemented yet; likely of marginal value.
- },
- {
.o_name = NNG_OPT_WS_PROTOCOL,
.o_set = ws_dialer_set_proto,
.o_get = ws_dialer_get_proto,
@@ -2574,7 +2470,7 @@ ws_dialer_set_header(
nni_ws_dialer *d, const char *name, const void *buf, size_t sz, nni_type t)
{
int rv;
- name += strlen(NNG_OPT_WS_REQUEST_HEADER);
+ name += strlen(NNG_OPT_WS_HEADER);
if ((rv = ws_check_string(buf, sz, t)) == 0) {
nni_mtx_lock(&d->mtx);
rv = ws_set_header(&d->headers, name, buf);
@@ -2596,7 +2492,7 @@ ws_dialer_set(
}
if (rv == NNG_ENOTSUP) {
- if (startswith(name, NNG_OPT_WS_REQUEST_HEADER)) {
+ if (startswith(name, NNG_OPT_WS_HEADER)) {
rv = ws_dialer_set_header(d, name, buf, sz, t);
}
}
@@ -2773,34 +2669,10 @@ ws_str_recv(void *arg, nng_aio *aio)
}
static int
-ws_get_request_headers(void *arg, void *buf, size_t *szp, nni_type t)
-{
- nni_ws *ws = arg;
- nni_mtx_lock(&ws->mtx);
- if (ws->reqhdrs == NULL) {
- ws->reqhdrs = nni_http_req_headers(ws->req);
- }
- nni_mtx_unlock(&ws->mtx);
- return (nni_copyout_str(ws->reqhdrs, buf, szp, t));
-}
-
-static int
-ws_get_response_headers(void *arg, void *buf, size_t *szp, nni_type t)
-{
- nni_ws *ws = arg;
- nni_mtx_lock(&ws->mtx);
- if (ws->reshdrs == NULL) {
- ws->reshdrs = nni_http_res_headers(ws->res);
- }
- nni_mtx_unlock(&ws->mtx);
- return (nni_copyout_str(ws->reshdrs, buf, szp, t));
-}
-
-static int
ws_get_request_uri(void *arg, void *buf, size_t *szp, nni_type t)
{
nni_ws *ws = arg;
- return (nni_copyout_str(nni_http_req_get_uri(ws->req), buf, szp, t));
+ return (nni_copyout_str(nni_http_get_uri(ws->http), buf, szp, t));
}
static int
@@ -2829,14 +2701,6 @@ ws_get_send_text(void *arg, void *buf, size_t *szp, nni_type t)
static const nni_option ws_options[] = {
{
- .o_name = NNG_OPT_WS_REQUEST_HEADERS,
- .o_get = ws_get_request_headers,
- },
- {
- .o_name = NNG_OPT_WS_RESPONSE_HEADERS,
- .o_get = ws_get_response_headers,
- },
- {
.o_name = NNG_OPT_WS_REQUEST_URI,
.o_get = ws_get_request_uri,
},
@@ -2854,25 +2718,11 @@ static const nni_option ws_options[] = {
};
static int
-ws_get_req_header(
- nni_ws *ws, const char *nm, void *buf, size_t *szp, nni_type t)
-{
- const char *s;
- nm += strlen(NNG_OPT_WS_REQUEST_HEADER);
- s = nni_http_req_get_header(ws->req, nm);
- if (s == NULL) {
- return (NNG_ENOENT);
- }
- return (nni_copyout_str(s, buf, szp, t));
-}
-
-static int
-ws_get_res_header(
- nni_ws *ws, const char *nm, void *buf, size_t *szp, nni_type t)
+ws_get_header(nni_ws *ws, const char *nm, void *buf, size_t *szp, nni_type t)
{
const char *s;
- nm += strlen(NNG_OPT_WS_RESPONSE_HEADER);
- s = nni_http_res_get_header(ws->res, nm);
+ nm += strlen(NNG_OPT_WS_HEADER);
+ s = nni_http_get_header(ws->http, nm);
if (s == NULL) {
return (NNG_ENOENT);
}
@@ -2897,10 +2747,8 @@ ws_str_get(void *arg, const char *nm, void *buf, size_t *szp, nni_type t)
}
// Check for generic headers...
if (rv == NNG_ENOTSUP) {
- if (startswith(nm, NNG_OPT_WS_REQUEST_HEADER)) {
- rv = ws_get_req_header(ws, nm, buf, szp, t);
- } else if (startswith(nm, NNG_OPT_WS_RESPONSE_HEADER)) {
- rv = ws_get_res_header(ws, nm, buf, szp, t);
+ if (startswith(nm, NNG_OPT_WS_HEADER)) {
+ rv = ws_get_header(ws, nm, buf, szp, t);
}
}
return (rv);
diff --git a/src/supplemental/websocket/websocket_test.c b/src/supplemental/websocket/websocket_test.c
index 30ebb3f5..98bcf658 100644
--- a/src/supplemental/websocket/websocket_test.c
+++ b/src/supplemental/websocket/websocket_test.c
@@ -163,10 +163,10 @@ test_websocket_conn_props(void)
NUTS_PASS(nng_stream_dialer_alloc(&d, uri));
NUTS_PASS(nng_stream_dialer_set_string(
- d, NNG_OPT_WS_REQUEST_HEADER "NNG-Req", "True"));
+ d, NNG_OPT_WS_HEADER "NNG-Req", "True"));
NUTS_PASS(nng_stream_listener_set_string(
- l, NNG_OPT_WS_RESPONSE_HEADER "NNG-Rep", "True"));
+ l, NNG_OPT_WS_HEADER "NNG-Rep", "True"));
nng_stream_dialer_dial(d, daio);
nng_stream_listener_accept(l, laio);
@@ -203,20 +203,20 @@ test_websocket_conn_props(void)
nng_stream_get_size(c1, NNG_OPT_TCP_NODELAY, &sz), NNG_EBADTYPE);
NUTS_FAIL(nng_stream_get_string(
- c1, NNG_OPT_WS_REQUEST_HEADER "No-Such-Header", &str),
+ c1, NNG_OPT_WS_HEADER "No-Such-Header", &str),
NNG_ENOENT);
- NUTS_PASS(nng_stream_get_string(
- c1, NNG_OPT_WS_REQUEST_HEADER "NNG-Req", &str));
+ NUTS_PASS(
+ nng_stream_get_string(c1, NNG_OPT_WS_HEADER "NNG-Req", &str));
NUTS_MATCH(str, "True");
nng_strfree(str);
- NUTS_PASS(nng_stream_get_string(
- c2, NNG_OPT_WS_RESPONSE_HEADER "NNG-Rep", &str));
+ NUTS_PASS(
+ nng_stream_get_string(c2, NNG_OPT_WS_HEADER "NNG-Rep", &str));
NUTS_MATCH(str, "True");
nng_strfree(str);
NUTS_PASS(nng_stream_get_string(
- c1, NNG_OPT_WS_REQUEST_HEADER "Sec-WebSocket-Version", &str));
+ c1, NNG_OPT_WS_HEADER "Sec-WebSocket-Version", &str));
NUTS_TRUE(str != NULL);
NUTS_TRUE(strcmp(str, "13") == 0);
nng_strfree(str);