aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/http
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2018-10-07 13:04:00 -0700
committerGarrett D'Amore <garrett@damore.org>2018-10-07 13:14:13 -0700
commit617bb5112834eee40d7eaf00bfc7e98e0ae1ff01 (patch)
tree686a5566b64d1cb79b495e00f5c106145f58b74b /src/supplemental/http
parent6c334f30cccaa9ddae81ee0865621b6695fb7e3a (diff)
downloadnng-617bb5112834eee40d7eaf00bfc7e98e0ae1ff01.tar.gz
nng-617bb5112834eee40d7eaf00bfc7e98e0ae1ff01.tar.bz2
nng-617bb5112834eee40d7eaf00bfc7e98e0ae1ff01.zip
fixes #745 HTTP server redirect handler
Diffstat (limited to 'src/supplemental/http')
-rw-r--r--src/supplemental/http/http.h6
-rw-r--r--src/supplemental/http/http_api.h13
-rw-r--r--src/supplemental/http/http_msg.c65
-rw-r--r--src/supplemental/http/http_public.c15
-rw-r--r--src/supplemental/http/http_server.c179
5 files changed, 215 insertions, 63 deletions
diff --git a/src/supplemental/http/http.h b/src/supplemental/http/http.h
index d9edd5c8..d0854981 100644
--- a/src/supplemental/http/http.h
+++ b/src/supplemental/http/http.h
@@ -328,6 +328,12 @@ NNG_DECL int nng_http_handler_alloc_file(
NNG_DECL int nng_http_handler_alloc_static(
nng_http_handler **, const char *, const void *, size_t, const char *);
+// nng_http_handler_alloc_redirect creates an HTTP redirect handler.
+// The status is given, along with the new URL. If the status is 0,
+// then 301 will be used instead.
+NNG_DECL int nng_http_handler_alloc_redirect(
+ nng_http_handler **, const char *, uint16_t, const char *);
+
// nng_http_handler_alloc_file creates a "directory" based handler, that
// serves up static content from the given directory tree. Directories
// that contain an index.html or index.htm file use that file for the
diff --git a/src/supplemental/http/http_api.h b/src/supplemental/http/http_api.h
index a30399d2..14e842be 100644
--- a/src/supplemental/http/http_api.h
+++ b/src/supplemental/http/http_api.h
@@ -121,6 +121,13 @@ extern int nni_http_res_set_reason(nni_http_res *, const char *);
// 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 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 *);
@@ -214,7 +221,7 @@ extern int nni_http_hijack(nni_http_conn *);
//
// The callback function will receive the following arguments (via
// nng_aio_get_input(): nni_http_request *, nni_http_handler *, and
-// nni_http_context_t *. The first is a request object, for convenience.
+// 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.
@@ -244,6 +251,10 @@ extern int nni_http_handler_init_directory(
extern int nni_http_handler_init_static(
nni_http_handler **, const char *, const void *, size_t, const char *);
+// nni_http_handler_init_redirect creates a handler that redirects the request.
+extern int nni_http_handler_init_redirect(
+ nni_http_handler **, const char *, uint16_t, const char *);
+
// nni_http_handler_fini destroys a handler. This should only be done before
// the handler is added, or after it is deleted. The server automatically
// calls this for any handlers still registered with it if it is destroyed.
diff --git a/src/supplemental/http/http_msg.c b/src/supplemental/http/http_msg.c
index 3b78a2a9..da60c746 100644
--- a/src/supplemental/http/http_msg.c
+++ b/src/supplemental/http/http_msg.c
@@ -128,15 +128,19 @@ nni_http_res_reset(nni_http_res *res)
void
nni_http_req_free(nni_http_req *req)
{
- nni_http_req_reset(req);
- NNI_FREE_STRUCT(req);
+ if (req != NULL) {
+ nni_http_req_reset(req);
+ NNI_FREE_STRUCT(req);
+ }
}
void
nni_http_res_free(nni_http_res *res)
{
- nni_http_res_reset(res);
- NNI_FREE_STRUCT(res);
+ if (res != NULL) {
+ nni_http_res_reset(res);
+ NNI_FREE_STRUCT(res);
+ }
}
static int
@@ -1012,36 +1016,49 @@ nni_http_res_set_reason(nni_http_res *res, const char *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_alloc_error(nni_http_res **resp, uint16_t err)
{
- char html[512];
+ char * html = NULL;
+ nni_http_res *res = NULL;
int rv;
- nni_http_res *res;
- if ((rv = nni_http_res_alloc(&res)) != 0) {
- return (rv);
- }
-
- // very simple builtin error page
- (void) snprintf(html, sizeof(html),
- "<head><title>%d %s</title></head>"
- "<body><p/><h1 align=\"center\">"
- "<span style=\"font-size: 36px; border-radius: 5px; "
- "background-color: black; color: white; padding: 7px; "
- "font-family: Arial, sans serif;\">%d</span></h1>"
- "<p align=\"center\">"
- "<span style=\"font-size: 24px; font-family: Arial, sans serif;\">"
- "%s</span></p></body>",
- err, nni_http_reason(err), err, nni_http_reason(err));
-
- res->code = err;
- if (((rv = nni_http_res_set_header(
+ if (((rv = nni_http_res_alloc(&res)) != 0) ||
+ ((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);
}
diff --git a/src/supplemental/http/http_public.c b/src/supplemental/http/http_public.c
index b86521b0..50ef03fa 100644
--- a/src/supplemental/http/http_public.c
+++ b/src/supplemental/http/http_public.c
@@ -559,6 +559,21 @@ nng_http_handler_alloc_directory(
}
int
+nng_http_handler_alloc_redirect(
+ nng_http_handler **hp, const char *uri, uint16_t status, const char *where)
+{
+#ifdef NNG_SUPP_HTTP
+ return (nni_http_handler_init_redirect(hp, uri, status, where));
+#else
+ NNI_ARG_UNUSED(hp);
+ NNI_ARG_UNUSED(uri);
+ NNI_ARG_UNUSED(status);
+ NNI_ARG_UNUSED(where);
+ return (NNG_ENOTSUP);
+#endif
+}
+
+int
nng_http_handler_alloc_static(nng_http_handler **hp, const char *uri,
const void *data, size_t size, const char *ctype)
{
diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c
index 1a7f2c25..44c338f4 100644
--- a/src/supplemental/http/http_server.c
+++ b/src/supplemental/http/http_server.c
@@ -229,12 +229,8 @@ http_sconn_reap(void *arg)
if (sc->conn != NULL) {
nni_http_conn_fini(sc->conn);
}
- if (sc->req != NULL) {
- nni_http_req_free(sc->req);
- }
- if (sc->res != NULL) {
- nni_http_res_free(sc->res);
- }
+ nni_http_req_free(sc->req);
+ nni_http_res_free(sc->res);
nni_aio_fini(sc->rxaio);
nni_aio_fini(sc->txaio);
nni_aio_fini(sc->txdataio);
@@ -294,10 +290,8 @@ http_sconn_txdatdone(void *arg)
return;
}
- if (sc->res != NULL) {
- nni_http_res_free(sc->res);
- sc->res = NULL;
- }
+ nni_http_res_free(sc->res);
+ sc->res = NULL;
if (sc->close) {
http_sconn_close(sc);
@@ -325,10 +319,8 @@ http_sconn_txdone(void *arg)
return;
}
- if (sc->res != NULL) {
- nni_http_res_free(sc->res);
- sc->res = NULL;
- }
+ nni_http_res_free(sc->res);
+ sc->res = NULL;
sc->handler = NULL;
nni_http_req_reset(sc->req);
nni_http_read_req(sc->conn, sc->req, sc->rxaio);
@@ -1089,10 +1081,10 @@ nni_http_server_res_error(nni_http_server *s, nni_http_res *res)
{
http_error *epage;
char * body = NULL;
+ char * html = NULL;
size_t len;
uint16_t code = nni_http_res_get_status(res);
int rv;
- char html[512];
nni_mtx_lock(&s->errors_mtx);
NNI_LIST_FOREACH (&s->errors, epage) {
@@ -1102,23 +1094,16 @@ nni_http_server_res_error(nni_http_server *s, nni_http_res *res)
break;
}
}
+ nni_mtx_unlock(&s->errors_mtx);
+
if (body == NULL) {
- const char *reason = nni_http_reason(code);
- // very simple builtin error page
- (void) snprintf(html, sizeof(html),
- "<head><title>%d %s</title></head>"
- "<body><p/><h1 align=\"center\">"
- "<span style=\"font-size: 36px; border-radius: 5px; "
- "background-color: black; color: white; padding: 7px; "
- "font-family: Arial, sans serif;\">%d</span></h1>"
- "<p align=\"center\">"
- "<span style=\"font-size: 24px; font-family: Arial, sans "
- "serif;\">"
- "%s</span></p></body>",
- code, reason, code, reason);
+ 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) &&
@@ -1126,8 +1111,8 @@ nni_http_server_res_error(nni_http_server *s, nni_http_res *res)
res, "Content-Type", "text/html; charset=UTF-8")) == 0)) {
nni_http_res_set_status(res, code);
}
+ nni_strfree(html);
- nni_mtx_unlock(&s->errors_mtx);
return (rv);
}
@@ -1335,9 +1320,7 @@ http_handle_file(nni_aio *aio)
((rv = nni_http_res_set_header(res, "Content-Type", ctype)) !=
0) ||
((rv = nni_http_res_copy_data(res, data, size)) != 0)) {
- if (res != NULL) {
- nni_http_res_free(res);
- }
+ nni_http_res_free(res);
nni_free(data, size);
nni_aio_finish_error(aio, rv);
return;
@@ -1526,9 +1509,7 @@ http_handle_dir(nni_aio *aio)
((rv = nni_http_res_set_header(res, "Content-Type", ctype)) !=
0) ||
((rv = nni_http_res_copy_data(res, data, size)) != 0)) {
- if (res != NULL) {
- nni_http_res_free(res);
- }
+ nni_http_res_free(res);
nni_free(data, size);
nni_aio_finish_error(aio, rv);
return;
@@ -1573,6 +1554,130 @@ nni_http_handler_init_directory(
return (0);
}
+typedef struct http_redirect {
+ uint16_t code;
+ char * where;
+} http_redirect;
+
+static void
+http_handle_redirect(nni_aio *aio)
+{
+ nni_http_res * r = NULL;
+ char * html = NULL;
+ char * msg = NULL;
+ char * loc = NULL;
+ http_redirect * hr;
+ nni_http_handler *h;
+ int rv;
+ nni_http_req * req;
+ const char * base;
+ const char * uri;
+
+ req = nni_aio_get_input(aio, 0);
+ h = nni_aio_get_input(aio, 1);
+ base = nni_http_handler_get_uri(h); // base uri
+ uri = nni_http_req_get_uri(req);
+
+ hr = nni_http_handler_get_data(h);
+
+ // If we are doing a full tree, then include the entire suffix.
+ if (strncmp(uri, base, strlen(base)) == 0) {
+ rv = nni_asprintf(&loc, "%s%s", hr->where, uri + strlen(base));
+ if (rv != 0) {
+ nni_aio_finish_error(aio, rv);
+ return;
+ }
+ } else {
+ 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_res_alloc(&r)) != 0) ||
+ ((rv = nni_http_alloc_html_error(&html, hr->code, msg)) != 0) ||
+ ((rv = nni_http_res_set_status(r, hr->code)) != 0) ||
+ ((rv = nni_http_res_set_header(r, "Connection", "close")) != 0) ||
+ ((rv = nni_http_res_set_header(
+ r, "Content-Type", "text/html; charset=UTF-8")) != 0) ||
+ ((rv = nni_http_res_set_header(r, "Location", loc)) != 0) ||
+ ((rv = nni_http_res_copy_data(r, html, strlen(html))) != 0)) {
+ if (loc != hr->where) {
+ nni_strfree(loc);
+ }
+ nni_strfree(msg);
+ nni_strfree(html);
+ nni_http_res_free(r);
+ nni_aio_finish_error(aio, rv);
+ return;
+ }
+ if (loc != hr->where) {
+ nni_strfree(loc);
+ }
+ nni_strfree(msg);
+ nni_strfree(html);
+ nni_aio_set_output(aio, 0, r);
+ nni_aio_finish(aio, 0, 0);
+}
+
+static void
+http_redirect_free(void *arg)
+{
+ http_redirect *hr;
+
+ if ((hr = arg) != NULL) {
+ nni_strfree(hr->where);
+ NNI_FREE_STRUCT(hr);
+ }
+}
+
+int
+nni_http_handler_init_redirect(nni_http_handler **hpp, const char *uri,
+ uint16_t status, const char *where)
+{
+ nni_http_handler *h;
+ int rv;
+ http_redirect * hr;
+
+ if ((hr = NNI_ALLOC_STRUCT(hr)) == NULL) {
+ return (NNG_ENOMEM);
+ }
+ if ((hr->where = nni_strdup(where)) == NULL) {
+ NNI_FREE_STRUCT(hr);
+ return (NNG_ENOMEM);
+ }
+ if (status == 0) {
+ status = NNG_HTTP_STATUS_STATUS_MOVED_PERMANENTLY;
+ }
+ hr->code = status;
+
+ if ((rv = nni_http_handler_init(&h, uri, http_handle_redirect)) != 0) {
+ http_redirect_free(hr);
+ return (rv);
+ }
+
+ if (((rv = nni_http_handler_set_method(h, NULL)) != 0) ||
+ ((rv = nni_http_handler_set_data(h, hr, http_redirect_free)) !=
+ 0)) {
+ http_redirect_free(hr);
+ nni_http_handler_fini(h);
+ return (rv);
+ }
+
+ // We don't need to collect the body at all, because the handler
+ // just discards the content and closes the connection.
+ nni_http_handler_collect_body(h, false, 0);
+
+ *hpp = h;
+ return (0);
+}
+
typedef struct http_static {
void * data;
size_t size;
@@ -1599,9 +1704,7 @@ http_handle_static(nni_aio *aio)
((rv = nni_http_res_set_header(r, "Content-Type", ctype)) != 0) ||
((rv = nni_http_res_set_status(r, NNG_HTTP_STATUS_OK)) != 0) ||
((rv = nni_http_res_set_data(r, hs->data, hs->size)) != 0)) {
- if (r != NULL) {
- nni_http_res_free(r);
- }
+ nni_http_res_free(r);
nni_aio_finish_error(aio, rv);
return;
}