From 26c6a4d89875e6652b47e32a36d0fb00d85fae4e Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Thu, 25 Jun 2026 11:18:27 +0200 Subject: [PATCH] urlapi: do not keep an internal port string Do not allocated an internal port string, just keep and number and a bit if it is present. Format the port string in a local buffer when someone asks for it and copy that. This avoid the format/alloc penalty when parsing URLs and no one ever asking for the port string. (And there will be a method to get the uint16_t value directly in another PR) Closes #22167 --- lib/urlapi-int.h | 4 +-- lib/urlapi.c | 90 +++++++++++++++++++++++++----------------------- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/lib/urlapi-int.h b/lib/urlapi-int.h index 4d8f2c1cb8..4d23dd87b4 100644 --- a/lib/urlapi-int.h +++ b/lib/urlapi-int.h @@ -34,11 +34,11 @@ struct Curl_URL { char *options; /* IMAP only? */ char *host; char *zoneid; /* for numerical IPv6 addresses */ - char *port; char *path; char *query; char *fragment; - unsigned short portnum; /* the numerical version (if 'port' is set) */ + uint16_t portnum; /* the numerical port if present */ + BIT(port_present); /* to support missing port */ BIT(query_present); /* to support blank */ BIT(fragment_present); /* to support blank */ BIT(guessed_scheme); /* when a URL without scheme is parsed */ diff --git a/lib/urlapi.c b/lib/urlapi.c index 05c79dfb0d..77414bd2f8 100644 --- a/lib/urlapi.c +++ b/lib/urlapi.c @@ -73,7 +73,6 @@ static void free_urlhandle(struct Curl_URL *u) curlx_free(u->options); curlx_free(u->host); curlx_free(u->zoneid); - curlx_free(u->port); curlx_free(u->path); curlx_free(u->query); curlx_free(u->fragment); @@ -358,6 +357,8 @@ UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, /* * Find the end of an IPv6 address on the ']' ending bracket. */ + u->portnum = 0; + u->port_present = FALSE; if(hostname[0] == '[') { portptr = strchr(hostname, ']'); if(!portptr) @@ -393,12 +394,8 @@ UNITTEST CURLUcode parse_port(struct Curl_URL *u, struct dynbuf *host, if(curlx_str_number(&portptr, &port, 0xffff) || *portptr) return CURLUE_BAD_PORT_NUMBER; - u->portnum = (unsigned short)port; - /* generate a new port number string to get rid of leading zeroes etc */ - curlx_free(u->port); - u->port = curl_maprintf("%" CURL_FORMAT_CURL_OFF_T, port); - if(!u->port) - return CURLUE_OUT_OF_MEMORY; + u->portnum = (uint16_t)port; + u->port_present = TRUE; } return CURLUE_OK; @@ -1344,12 +1341,12 @@ CURLU *curl_url_dup(const CURLU *in) DUP(u, in, password); DUP(u, in, options); DUP(u, in, host); - DUP(u, in, port); DUP(u, in, path); DUP(u, in, query); DUP(u, in, fragment); DUP(u, in, zoneid); u->portnum = in->portnum; + u->port_present = in->port_present; u->fragment_present = in->fragment_present; u->query_present = in->query_present; } @@ -1484,7 +1481,7 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) else { const char *scheme; char *options = u->options; - char *port = u->port; + char *port = NULL; const struct Curl_scheme *h = NULL; char schemebuf[MAX_SCHEME_LEN + 5]; if(u->scheme) @@ -1494,19 +1491,26 @@ static CURLUcode urlget_url(const CURLU *u, char **part, unsigned int flags) else return CURLUE_NO_SCHEME; + if(u->port_present) { + curl_msnprintf(portbuf, sizeof(portbuf), "%u", u->portnum); + port = portbuf; + } + h = Curl_get_scheme(scheme); if(h) { - if(!port && (flags & CURLU_DEFAULT_PORT)) { + if(!u->port_present && (flags & CURLU_DEFAULT_PORT)) { /* there is no stored port number, but asked to deliver a default one for the scheme */ curl_msnprintf(portbuf, sizeof(portbuf), "%u", h->defport); port = portbuf; } - else if(port && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) + else if(u->port_present && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) { /* there is a stored port number, but asked to inhibit if it matches the default port for the scheme */ port = NULL; + } + if(!(h->flags & PROTOPT_URLOPTIONS)) options = NULL; } @@ -1614,10 +1618,24 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, ifmissing = CURLUE_NO_ZONEID; break; case CURLUPART_PORT: - ptr = u->port; + ptr = NULL; ifmissing = CURLUE_NO_PORT; flags &= ~U_CURLU_URLDECODE; /* never for port */ - if(!ptr && (flags & CURLU_DEFAULT_PORT) && u->scheme) { + if(u->port_present) { + const struct Curl_scheme *h = u->scheme ? + Curl_get_scheme(u->scheme) : NULL; + /* there is a stored port number, but ask to inhibit if + it matches the default one for the scheme */ + if(h && (h->defport == u->portnum) && + (flags & CURLU_NO_DEFAULT_PORT)) { + ptr = NULL; + } + else { + curl_msnprintf(portbuf, sizeof(portbuf), "%u", u->portnum); + ptr = portbuf; + } + } + else if((flags & CURLU_DEFAULT_PORT) && u->scheme) { /* there is no stored port number, but asked to deliver a default one for the scheme */ const struct Curl_scheme *h = Curl_get_scheme(u->scheme); @@ -1626,14 +1644,6 @@ CURLUcode curl_url_get(const CURLU *u, CURLUPart what, ptr = portbuf; } } - else if(ptr && u->scheme) { - /* there is a stored port number, but ask to inhibit if - it matches the default one for the scheme */ - const struct Curl_scheme *h = Curl_get_scheme(u->scheme); - if(h && (h->defport == u->portnum) && - (flags & CURLU_NO_DEFAULT_PORT)) - ptr = NULL; - } break; case CURLUPART_PATH: ptr = u->path; @@ -1700,7 +1710,6 @@ static CURLUcode set_url_scheme(CURLU *u, const char *scheme, static CURLUcode set_url_port(CURLU *u, const char *provided_port) { - char *tmp; curl_off_t port; if(!ISDIGIT(provided_port[0])) /* not a number */ @@ -1708,12 +1717,8 @@ static CURLUcode set_url_port(CURLU *u, const char *provided_port) if(curlx_str_number(&provided_port, &port, 0xffff) || *provided_port) /* weirdly provided number, not good! */ return CURLUE_BAD_PORT_NUMBER; - tmp = curl_maprintf("%" CURL_FORMAT_CURL_OFF_T, port); - if(!tmp) - return CURLUE_OUT_OF_MEMORY; - curlx_free(u->port); - u->port = tmp; - u->portnum = (unsigned short)port; + u->portnum = (uint16_t)port; + u->port_present = TRUE; return CURLUE_OK; } @@ -1796,7 +1801,7 @@ static CURLUcode urlset_clear(CURLU *u, CURLUPart what) break; case CURLUPART_PORT: u->portnum = 0; - curlx_safefree(u->port); + u->port_present = FALSE; break; case CURLUPART_PATH: curlx_safefree(u->path); @@ -2077,26 +2082,25 @@ bool Curl_url_same_origin(CURLU *base, CURLU *href) if(href->host) { if(!curl_strequal(base->host, href->host)) return FALSE; - if(!curl_strequal(base->zoneid ? base->zoneid : "", - href->zoneid ? href->zoneid : "")) - return FALSE; - if(!curl_strequal(base->port, href->port)) { - /* This may still match if only one has an explicit port - * and it is the default for the scheme. */ - if(base->port && href->port) - return FALSE; + if(base->port_present != href->port_present) { + /* one is present, one is not */ s = Curl_get_scheme(base->scheme); if(!s) /* Cannot match default port for unknown scheme */ return FALSE; - - /* The port which is set must be the default one */ - if((base->port && (base->portnum != s->defport)) || - (href->port && (href->portnum != s->defport))) + /* to match, the present one must be the default port */ + if((base->port_present && (base->portnum != s->defport)) || + (href->port_present && (href->portnum != s->defport))) return FALSE; } + else if(base->portnum != href->portnum) /* both present or missing */ + return FALSE; + + if(!curl_strequal(base->zoneid ? base->zoneid : "", + href->zoneid ? href->zoneid : "")) + return FALSE; } - else if(href->port) /* no host in href, then there must be no port */ + else if(href->port_present) /* no host in href, then there must be no port */ return FALSE; return TRUE; }