]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
urlapi: do not keep an internal port string
authorStefan Eissing <stefan@eissing.org>
Thu, 25 Jun 2026 09:18:27 +0000 (11:18 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 25 Jun 2026 12:07:40 +0000 (14:07 +0200)
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
lib/urlapi.c

index 4d8f2c1cb8dff328f6ecf66ae28bdbba8ef7eeea..4d23dd87b44cdc8d51834437381c0bfa9d7076e6 100644 (file)
@@ -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 */
index 05c79dfb0df4eaa89340d2fad55d11c5e6d54d72..77414bd2f8009c2d11bb6ce98c6ff7d515e8cbf8 100644 (file)
@@ -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;
 }