Skip to content

Commit

Permalink
http: server callback API simplified
Browse files Browse the repository at this point in the history
This simplified API lets callbacks obtain the response from the
connection objection directly, and does not require the aio to carry
it as a parameter.  Further, the request and response are both
stored inline in the connection, reducing allocations.

This is at present only for the server; the client will get a similar
set of changes.
  • Loading branch information
gdamore committed Jan 6, 2025
1 parent bce6a79 commit f42d0c6
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 276 deletions.
10 changes: 5 additions & 5 deletions docs/man/nng_http_conn_read_req.3http.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= nng_http_conn_read_req(3http)
//
// Copyright 2018 Staysail Systems, Inc. <[email protected]>
// Copyright 2025 Staysail Systems, Inc. <[email protected]>
// Copyright 2018 Capitar IT Group BV <[email protected]>
//
// This document is supplied under the terms of the MIT License, a
Expand All @@ -20,15 +20,15 @@ nng_http_conn_read_req - read HTTP request
#include <nng/nng.h>
#include <nng/supplemental/http/http.h>
void nng_http_conn_read_req(nng_http_conn *conn, nng_http_req *req,
nng_aio *aio);
void nng_http_conn_read_req(nng_http_conn *conn, nng_aio *aio);
----

== DESCRIPTION

The `nng_http_conn_read_req()` function starts an asynchronous read from the
HTTP connection _conn_, reading an HTTP request into the _req_, including all
of the related headers.
HTTP connection _conn_, reading an HTTP request into the request object
associated with _conn_, including all of the related headers.
(The request object can be obtained via `nng_http_conn_req()`.

NOTE: Any HTTP entity/body data associated with the request is *not* read
automatically.
Expand Down
7 changes: 3 additions & 4 deletions docs/man/nng_http_conn_write_res.3http.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= nng_http_conn_write_res(3http)
//
// Copyright 2018 Staysail Systems, Inc. <[email protected]>
// Copyright 2025 Staysail Systems, Inc. <[email protected]>
// Copyright 2018 Capitar IT Group BV <[email protected]>
//
// This document is supplied under the terms of the MIT License, a
Expand All @@ -20,14 +20,13 @@ nng_http_conn_write_res - write HTTP response
#include <nng/nng.h>
#include <nng/supplemental/http/http.h>
void nng_http_conn_write_res(nng_http_conn *conn, nng_http_res *res,
nng_aio *aio);
void nng_http_conn_write_res(nng_http_conn *conn, nng_aio *aio);
----

== DESCRIPTION

The `nng_http_conn_write_res()` function starts an asynchronous write of
the HTTP response _res_ to the connection _conn_.
the HTTP response associated with the connection _conn_.
The entire response is sent,
including headers, and if present, the response body data.
(The response body can be set with
Expand Down
29 changes: 13 additions & 16 deletions docs/man/nng_http_handler_alloc.3http.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= nng_http_handler_alloc(3http)
//
// Copyright 2018 Staysail Systems, Inc. <[email protected]>
// Copyright 2025 Staysail Systems, Inc. <[email protected]>
// Copyright 2018 Capitar IT Group BV <[email protected]>
// Copyright 2020 Dirac Research <[email protected]>
//
Expand All @@ -23,8 +23,10 @@ nng_http_handler_alloc - allocate HTTP server handler
typedef struct nng_http_handler nng_http_handler;
typedef void (*nng_http_hander_func)(nng_http_conn *conn, void *arg, nng_aio *aio);
int nng_http_handler_alloc(nng_http_handler **hp, const char *path,
void (*func)(nng_aio *);
nng_http_handler_func cb);
int nng_http_handler_alloc_directory(nng_http_handler **hp, const char *path,
const char *dirname);
Expand Down Expand Up @@ -72,24 +74,19 @@ rather than just a single element.
The generic (first) form of this creates a handler that uses a user-supplied
function to process HTTP requests.
This function uses the asynchronous I/O framework.
The function takes a pointer to an xref:nng_aio.5.adoc[`nng_aio`] structure.

The _aio_ will be passed with the following input values (retrieved with
xref:nng_aio_get_input.3.adoc[`nng_aio_get_input()`]):
The function receives the connection on _conn_, and the data that it set
previously with `nng_http_handler_set_data` as the second argument. The
final argument is the _aio_, which must be "finished" to complete the operation.

0: `nng_http_req *` __request__:: The client's HTTP request.
1: `nng_http_handler *` __handler__:: Pointer to the handler object.
2: `nng_http_conn *` __conn__:: The underlying HTTP connection.
The function takes a pointer to an xref:nng_aio.5.adoc[`nng_aio`] structure.

The handler should create an `nng_http_res *` response (such as via
xref:nng_http_res_alloc.3http.adoc[`nng_http_res_alloc()`] or
xref:nng_http_res_alloc_error.3http.adoc[`nng_http_res_alloc_error()`]) and store that
in as the first output (index 0) with
xref:nng_aio_set_output.3.adoc[`nng_aio_set_output()`].
The handler should obtain `nng_http_res *` response from the
connection (`nng_http_conn_res`) and update it to reflect the final status.

Alternatively, the handler may send the HTTP response (and any associated
body data) itself using the connection.
In that case the output at index 0 of the _aio_ should be NULL.
The handler may call `nng_http_conn_write_res` to send the response, or
it may simply let the framework do so on its behalf. The server will perform
this step if the callback has not already done so.

Finally, using the xref:nng_aio_finish.3.adoc[`nng_aio_finish()`] function, the
_aio_ should be completed successfully.
Expand Down
30 changes: 13 additions & 17 deletions include/nng/supplemental/http/http.h
Original file line number Diff line number Diff line change
Expand Up @@ -275,15 +275,13 @@ NNG_DECL void nng_http_conn_write_req(
nng_http_conn *, nng_http_req *, nng_aio *);

// nng_http_conn_write_res writes the entire response. It will also write any
// data that has been attached.
NNG_DECL void nng_http_conn_write_res(
nng_http_conn *, nng_http_res *, nng_aio *);
// data that has been attached. It uses the res object in the conn.
NNG_DECL void nng_http_conn_write_res(nng_http_conn *, nng_aio *);

// nng_http_conn_read_req reads an entire request, EXCEPT for any entity
// data. The caller is responsible for processing the headers in the request
// and reading any submitted entity data itself.
NNG_DECL void nng_http_conn_read_req(
nng_http_conn *, nng_http_req *, nng_aio *);
NNG_DECL void nng_http_conn_read_req(nng_http_conn *, nng_aio *);

// nng_http_conn_read_res reads an entire response, EXCEPT for any entity
// data. The caller is responsible for processing the headers in the response
Expand All @@ -310,22 +308,20 @@ typedef struct nng_http_handler nng_http_handler;
// is registered with the server, and that a handler can only be registered
// once per server.
//
// The callback function will receive the following arguments (via
// nng_aio_get_input(): nng_http_request *, nng_http_handler *, and
// nng_http_conn *. The first is a request object, for convenience.
// The second is the handler, from which the callback can obtain any other
// data it has set. The final is the http connection, which can be used
// to hijack the session.
//
// Upon completion, the handler should store an nng_http_res * as the
// first output using nng_aio_set_output. If it does not do so, or supplies
// NULL, then it must send a response itself.
// The callback function should obtain the request (if needed), and
// the response from the connection object using nng_http_conn_req
// and nng_http_conn_res. If the connection is hijacked, then the
// response object will not be used, otherwise the server will send it on
// behalf of the client.
//
// The callback should complete with a result of 0 in most circumstances.
// If it completes with an error, then the connection is terminated, after
// possibly sending a 500 error response to the client.
// possibly sending a 500 error response to the client. The callback signals
// completion by nng_aio_finish. The second argument to this function is the
// handler data that was optionally set by nng_handler_set_data.
typedef void (*nng_http_handler_func)(nng_http_conn *, void *, nng_aio *);
NNG_DECL int nng_http_handler_alloc(
nng_http_handler **, const char *, void (*)(nng_aio *));
nng_http_handler **, const char *, nng_http_handler_func);

// nng_http_handler_free frees the handler. This actually just drops a
// reference count on the handler, as it may be in use by an existing
Expand Down
17 changes: 7 additions & 10 deletions src/supplemental/http/http_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,13 @@ extern int nni_http_conn_getopt(
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_write_res(nni_http_conn *, nni_http_res *, nni_aio *);
extern void nni_http_read_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_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 *);
Expand Down Expand Up @@ -258,15 +259,8 @@ extern int nni_http_hijack(nni_http_conn *);
// Note that methods which modify a handler cannot be called while the handler
// is registered with the server, and that a handler can only be registered
// once per server.
//
// The callback function will receive the following arguments (via
// nng_aio_get_input(): nni_http_request *, nni_http_handler *, and
// nni_http_conn_t *. The first is a request object, for convenience.
// The second is the handler, from which the callback can obtain any other
// data it has set. The final is the http context, from which its possible
// to hijack the session.
extern int nni_http_handler_init(
nni_http_handler **, const char *, void (*)(nni_aio *));
nni_http_handler **, const char *, nng_http_handler_func);

// nni_http_handler_init_file creates a handler with a function to serve
// up a file named in the last argument.
Expand Down Expand Up @@ -395,4 +389,7 @@ extern void nni_http_transact(
// upper layer scheme.
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);

#endif // NNG_SUPPLEMENTAL_HTTP_HTTP_API_H
36 changes: 25 additions & 11 deletions src/supplemental/http/http_conn.c
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ struct nng_http_conn {
size_t rd_bufsz;
bool rd_buffered;

bool res_sent;

enum write_flavor wr_flavor;
};

Expand Down Expand Up @@ -509,10 +511,13 @@ http_wr_submit(nni_http_conn *conn, nni_aio *aio, enum write_flavor flavor)
}

void
nni_http_read_req(nni_http_conn *conn, nni_http_req *req, nni_aio *aio)
nni_http_read_req(nni_http_conn *conn, nni_aio *aio)
{
nni_aio_set_prov_data(aio, req);
nni_aio_set_prov_data(aio, &conn->req);

// clear the sent flag (used for the server)
conn->res_sent = false;
nni_http_req_reset(&conn->req);
nni_mtx_lock(&conn->mtx);
http_rd_submit(conn, aio, HTTP_RD_REQ);
nni_mtx_unlock(&conn->mtx);
Expand Down Expand Up @@ -590,16 +595,18 @@ 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_http_res *res, nni_aio *aio)
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;

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);

conn->res_sent = true;
if ((rv = nni_http_res_get_buf(res, &buf, &bufsz)) != 0) {
nni_aio_finish_error(aio, rv);
return;
Expand Down Expand Up @@ -713,3 +720,10 @@ nni_http_conn_init(nni_http_conn **connp, nng_stream *stream)
}
return (rv);
}

// private to the HTTP framework, used on the server
bool
nni_http_conn_res_sent(nni_http_conn *conn)
{
return (conn->res_sent);
}
46 changes: 30 additions & 16 deletions src/supplemental/http/http_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,8 +263,10 @@ static int
http_entity_alloc_data(nni_http_entity *entity, size_t size)
{
void *newdata;
if ((newdata = nni_zalloc(size)) == NULL) {
return (NNG_ENOMEM);
if (size != 0) {
if ((newdata = nni_zalloc(size)) == NULL) {
return (NNG_ENOMEM);

Check warning on line 268 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L268

Added line #L268 was not covered by tests
}
}
http_entity_set_data(entity, newdata, size);
entity->own = true;
Expand Down Expand Up @@ -1057,25 +1059,37 @@ nni_http_alloc_html_error(char **html, uint16_t code, const char *details)
}

int
nni_http_res_alloc_error(nni_http_res **resp, uint16_t err)
nni_http_res_set_error(nni_http_res *res, uint16_t err)

Check warning on line 1062 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L1062

Added line #L1062 was not covered by tests
{
char *html = NULL;
nni_http_res *res = NULL;
int rv;

if (((rv = nni_http_res_alloc(&res)) != 0) ||
((rv = nni_http_alloc_html_error(&html, err, NULL)) != 0) ||
int rv;
char *html = NULL;

Check warning on line 1065 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L1065

Added line #L1065 was not covered by tests
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);
nni_http_res_free(res);
} else {
nni_strfree(html);
res->code = err;
res->iserr = true;
*resp = res;
return (rv);

Check warning on line 1071 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L1071

Added line #L1071 was not covered by tests
}
nni_strfree(html);
res->code = err;
res->iserr = true;
return (0);
}

Check warning on line 1077 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L1073-L1077

Added lines #L1073 - L1077 were not covered by tests

return (rv);
int
nni_http_res_alloc_error(nni_http_res **resp, uint16_t err)

Check warning on line 1080 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L1080

Added line #L1080 was not covered by tests
{
nni_http_res *res;
int rv;

if ((rv = nni_http_res_alloc(&res)) != 0) {
return (rv);

Check warning on line 1086 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L1086

Added line #L1086 was not covered by tests
}
rv = nni_http_res_set_error(res, err);

Check warning on line 1088 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L1088

Added line #L1088 was not covered by tests
if (rv != 0) {
nni_http_res_free(res);
return (rv);

Check warning on line 1091 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L1090-L1091

Added lines #L1090 - L1091 were not covered by tests
}
*resp = res;
return (0);

Check warning on line 1094 in src/supplemental/http/http_msg.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_msg.c#L1093-L1094

Added lines #L1093 - L1094 were not covered by tests
}
12 changes: 5 additions & 7 deletions src/supplemental/http/http_public.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,25 +482,23 @@ nng_http_conn_write_req(nng_http_conn *conn, nng_http_req *req, nng_aio *aio)
}

void
nng_http_conn_write_res(nng_http_conn *conn, nng_http_res *res, nng_aio *aio)
nng_http_conn_write_res(nng_http_conn *conn, nng_aio *aio)

Check warning on line 485 in src/supplemental/http/http_public.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_public.c#L485

Added line #L485 was not covered by tests
{
#ifdef NNG_SUPP_HTTP
nni_http_write_res(conn, res, aio);
nni_http_write_res(conn, aio);

Check warning on line 488 in src/supplemental/http/http_public.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_public.c#L488

Added line #L488 was not covered by tests
#else
NNI_ARG_UNUSED(conn);
NNI_ARG_UNUSED(res);
nni_aio_finish_error(aio, NNG_ENOTSUP);
#endif
}

void
nng_http_conn_read_req(nng_http_conn *conn, nng_http_req *req, nng_aio *aio)
nng_http_conn_read_req(nng_http_conn *conn, nng_aio *aio)

Check warning on line 496 in src/supplemental/http/http_public.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_public.c#L496

Added line #L496 was not covered by tests
{
#ifdef NNG_SUPP_HTTP
nni_http_read_req(conn, req, aio);
nni_http_read_req(conn, aio);

Check warning on line 499 in src/supplemental/http/http_public.c

View check run for this annotation

Codecov / codecov/patch

src/supplemental/http/http_public.c#L499

Added line #L499 was not covered by tests
#else
NNI_ARG_UNUSED(conn);
NNI_ARG_UNUSED(req);
nni_aio_finish_error(aio, NNG_ENOTSUP);
#endif
}
Expand All @@ -519,7 +517,7 @@ nng_http_conn_read_res(nng_http_conn *conn, nng_http_res *res, nng_aio *aio)

int
nng_http_handler_alloc(
nng_http_handler **hp, const char *uri, void (*cb)(nng_aio *))
nng_http_handler **hp, const char *uri, nng_http_handler_func cb)
{
#ifdef NNG_SUPP_HTTP
return (nni_http_handler_init(hp, uri, cb));
Expand Down
Loading

0 comments on commit f42d0c6

Please sign in to comment.