diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/supplemental/http/http.h | 6 | ||||
| -rw-r--r-- | src/supplemental/http/http_api.h | 13 | ||||
| -rw-r--r-- | src/supplemental/http/http_msg.c | 65 | ||||
| -rw-r--r-- | src/supplemental/http/http_public.c | 15 | ||||
| -rw-r--r-- | src/supplemental/http/http_server.c | 179 |
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> </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; } |
