aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/http/http_server.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/supplemental/http/http_server.c')
-rw-r--r--src/supplemental/http/http_server.c260
1 files changed, 94 insertions, 166 deletions
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);
}