From 169166a0ef4fad56860c40ba2eda23f27b8a4cb1 Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Sun, 12 Jan 2025 09:01:33 -0800 Subject: [PATCH] http: use common canonify at request parse time --- src/core/url.c | 6 +- src/core/url.h | 1 + src/supplemental/http/http_conn.c | 2 +- src/supplemental/http/http_msg.c | 4 +- src/supplemental/http/http_server.c | 107 ++++------------------------ 5 files changed, 21 insertions(+), 99 deletions(-) diff --git a/src/core/url.c b/src/core/url.c index 48c79037d..9ee8c9a98 100644 --- a/src/core/url.c +++ b/src/core/url.c @@ -119,8 +119,8 @@ nni_url_decode(uint8_t *out, const char *in, size_t max_len) return (len); } -static int -url_canonify_uri(char *out) +int +nni_url_canonify_uri(char *out) { size_t src, dst; uint8_t c; @@ -435,7 +435,7 @@ nni_url_parse_inline_inner(nng_url *url, const char *raw) url->u_hostname[i] = (char) tolower(url->u_hostname[i]); } - if ((rv = url_canonify_uri(p)) != 0) { + if ((rv = nni_url_canonify_uri(p)) != 0) { return (rv); } diff --git a/src/core/url.h b/src/core/url.h index d552d453a..c74749c60 100644 --- a/src/core/url.h +++ b/src/core/url.h @@ -35,5 +35,6 @@ extern int nni_url_to_address(nng_sockaddr *, const nng_url *); extern int nni_url_parse_inline(nng_url *, const char *); extern int nni_url_clone_inline(nng_url *, const nng_url *); extern void nni_url_fini(nng_url *); +extern int nni_url_canonify_uri(char *); #endif // CORE_URL_H diff --git a/src/supplemental/http/http_conn.c b/src/supplemental/http/http_conn.c index 65c240f55..e143d9b3f 100644 --- a/src/supplemental/http/http_conn.c +++ b/src/supplemental/http/http_conn.c @@ -1145,7 +1145,7 @@ nni_http_set_content_type(nng_http *conn, const char *ctype) const char * nni_http_get_uri(nng_http *conn) { - return (conn->uri); + return ((conn->uri && conn->uri[0]) ? conn->uri : "/"); } int diff --git a/src/supplemental/http/http_msg.c b/src/supplemental/http/http_msg.c index 8c62fb5f4..3c4ce4915 100644 --- a/src/supplemental/http/http_msg.c +++ b/src/supplemental/http/http_msg.c @@ -16,6 +16,7 @@ #include "core/list.h" #include "core/nng_impl.h" +#include "core/url.h" #include "http_api.h" #include "http_msg.h" #include "nng/http.h" @@ -231,7 +232,8 @@ http_req_parse_line(nng_http *conn, void *line) version++; nni_http_set_method(conn, method); - if (((rv = nni_http_set_uri(conn, uri, NULL)) != 0) || + if (((rv = nni_url_canonify_uri(uri)) != 0) || + ((rv = nni_http_set_uri(conn, uri, NULL)) != 0) || ((rv = nni_http_set_version(conn, version)) != 0)) { return (rv); } diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c index bec0c01a2..096d985c9 100644 --- a/src/supplemental/http/http_server.c +++ b/src/supplemental/http/http_server.c @@ -332,83 +332,6 @@ http_sconn_txdone(void *arg) } } -static char -http_hexval(char c) -{ - if ((c >= '0') && (c <= '9')) { - return (c - '0'); - } - if ((c >= 'a') && (c <= 'f')) { - return ((c - 'a') + 10); - } - if ((c >= 'A') && (c <= 'F')) { - return ((c - 'A') + 10); - } - return (0); -} - -// XXX: REPLACE THIS WITH CODE USING THE URL FRAMEWORK. -static char * -http_uri_canonify(char *path) -{ - char *tmp; - char *dst; - - // Chomp off query string. - if ((tmp = strchr(path, '?')) != NULL) { - *tmp = '\0'; - } - // If the URI was absolute, make it relative. - if ((nni_strncasecmp(path, "http://", strlen("http://")) == 0) || - (nni_strncasecmp(path, "https://", strlen("https://")) == 0)) { - // Skip past the :// - path = strchr(path, ':'); - path += 3; - - // scan for the end of the host, distinguished by a / - // path delimiter. There might not be one, in which - // case the whole thing is the host and we assume the - // path is just /. - if ((path = strchr(path, '/')) == NULL) { - return ("/"); - } - } - - // Now we have to unescape things. Unescaping is a shrinking - // operation (strictly), so this is safe. This is just URL - // decode. Note that paths with an embedded NUL are going to be - // treated as though truncated. Don't be that guy that sends - // %00 in a URL. - // - // XXX: Normalizer needs to leave % encoded stuff in there if - // the characters to which they refer are reserved. See RFC 3986 - // section 6.2.2. - tmp = path; - dst = path; - while (*tmp != '\0') { - char c; - if ((c = *tmp) != '%') { - *dst++ = c; - tmp++; - continue; - } - if (isxdigit(tmp[1]) && isxdigit(tmp[2])) { - c = http_hexval(tmp[1]); - c *= 16; - c += http_hexval(tmp[2]); - *dst++ = c; - tmp += 3; - } else { - // garbage in, garbage out - *dst++ = c; - tmp++; - } - } - *dst = '\0'; - - return ((strlen(path) != 0) ? path : "/"); -} - static void http_sconn_error(http_sconn *sc, uint16_t err) { @@ -509,9 +432,7 @@ http_sconn_rxdone(void *arg) nni_http_handler *head = NULL; const char *val; nni_http_req *req = nni_http_conn_req(sc->conn); - char *uri; - size_t urisz; - char *path; + const char *uri; bool badmeth = false; bool needhost = false; const char *host; @@ -563,6 +484,15 @@ http_sconn_rxdone(void *arg) needhost = true; } + // NB: The URI will already have been canonified by the REQ parser + uri = nni_http_get_uri(sc->conn); + if (uri[0] != '/') { + // We do not support authority form or asterisk form at present + sc->close = true; + http_sconn_error(sc, NNG_HTTP_STATUS_BAD_REQUEST); + return; + } + // If the connection was 1.0, or a connection: close was // requested, then mark this close on our end. if ((val = nni_http_get_header(sc->conn, "Connection")) != NULL) { @@ -588,20 +518,10 @@ http_sconn_rxdone(void *arg) } } - val = nni_http_get_uri(sc->conn); - urisz = strlen(val) + 1; - if ((uri = nni_alloc(urisz)) == NULL) { - http_sconn_close(sc); // out of memory - return; - } - strncpy(uri, val, urisz); - path = http_uri_canonify(uri); - 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); - nni_free(uri, urisz); return; } @@ -614,14 +534,14 @@ http_sconn_rxdone(void *arg) } len = strlen(h->uri); - if (strncmp(path, h->uri, len) != 0) { + if (strncmp(uri, h->uri, len) != 0) { continue; } - switch (path[len]) { + switch (uri[len]) { case '\0': break; case '/': - if ((path[len + 1] != '\0') && (!h->tree)) { + if ((uri[len + 1] != '\0') && (!h->tree)) { // Trailing component and not a directory. continue; } @@ -652,7 +572,6 @@ http_sconn_rxdone(void *arg) if ((h == NULL) && (head != NULL)) { h = head; } - nni_free(uri, urisz); if (h == NULL) { nni_mtx_unlock(&s->mtx); if (badmeth) {