diff options
Diffstat (limited to 'src/supplemental/http/http_server.c')
| -rw-r--r-- | src/supplemental/http/http_server.c | 260 |
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); } |
