aboutsummaryrefslogtreecommitdiff
path: root/src/supplemental/http/http_server.c
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/http_server.c
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/http_server.c')
-rw-r--r--src/supplemental/http/http_server.c179
1 files changed, 141 insertions, 38 deletions
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;
}