]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
altsvc: use Curl_peer in processing
authorStefan Eissing <stefan@eissing.org>
Fri, 26 Jun 2026 08:50:14 +0000 (10:50 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 26 Jun 2026 12:12:53 +0000 (14:12 +0200)
Use `data->state.origin` when processing alt-avc information.

Decomplexify parsing function.

Closes #22184

lib/altsvc.c
lib/altsvc.h
lib/connect.c
lib/connect.h
lib/http.c
lib/url.c
tests/unit/unit1654.c

index 80913d9a1d8b5a070a3cbd56981cb3959768de5f..6b8b2617f9f3623babe6ddb1979ed94072b1c5cb 100644 (file)
@@ -78,6 +78,12 @@ const char *Curl_alpnid2str(enum alpnid id)
   }
 }
 
+static enum alpnid Curl_str2alpnid(const struct Curl_str *cstr)
+{
+  return Curl_alpn2alpnid((const unsigned char *)curlx_str(cstr),
+                          curlx_strlen(cstr));
+}
+
 #define altsvc_free(x) curlx_free(x)
 
 static struct altsvc *altsvc_createid(const char *srchost,
@@ -447,23 +453,115 @@ static bool hostcompare(const char *host, const char *check)
 
 /* altsvc_flush() removes all alternatives for this source origin from the
    list */
-static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
-                         const char *srchost, unsigned short srcport)
+static void altsvc_flush(struct altsvcinfo *asi,
+                         struct Curl_peer *origin,
+                         enum alpnid origin_alpnid)
 {
   struct Curl_llist_node *e;
   struct Curl_llist_node *n;
   for(e = Curl_llist_head(&asi->list); e; e = n) {
     struct altsvc *as = Curl_node_elem(e);
     n = Curl_node_next(e);
-    if((srcalpnid == as->src.alpnid) &&
-       (srcport == as->src.port) &&
-       hostcompare(srchost, as->src.host)) {
+    if((origin_alpnid == as->src.alpnid) &&
+       (origin->port == as->src.port) &&
+       hostcompare(origin->hostname, as->src.host)) {
       Curl_node_remove(e);
       altsvc_free(as);
     }
   }
 }
 
+static void altsvc_parse_params(const char **pp,
+                                time_t *pmaxage,
+                                bool *ppersist)
+{
+  curlx_str_passblanks(pp);
+  if(curlx_str_single(pp, ';'))
+    return;
+
+  for(;;) {
+    struct Curl_str name;
+    struct Curl_str val;
+    const char *vp;
+    curl_off_t num;
+    bool quoted;
+
+    /* allow some extra whitespaces around name and value */
+    if(curlx_str_until(pp, &name, 20, '=') ||
+       curlx_str_single(pp, '=') ||
+       curlx_str_cspn(pp, &val, ",;"))
+      break;  /* skip further parameter parsing */
+
+    curlx_str_trimblanks(&name);
+    curlx_str_trimblanks(&val);
+    /* the value might be quoted */
+    vp = curlx_str(&val);
+    quoted = (*vp == '\"');
+    if(quoted)
+      vp++;
+    /* we process 2 number value parameters: 'ma' and 'persist' */
+    if(curlx_str_number(&vp, &num, TIME_T_MAX))
+      break; /* not a number, skip further parameter parsing */
+
+    if(curlx_str_casecompare(&name, "ma"))
+      *pmaxage = (time_t)num;
+    else if(curlx_str_casecompare(&name, "persist") && (num == 1))
+      *ppersist = TRUE;
+
+    *pp = vp; /* point to the byte ending the value */
+    curlx_str_passblanks(pp);
+    if(quoted && curlx_str_single(pp, '\"'))
+      break; /* was quoted but not ended in quote, skip */
+    curlx_str_passblanks(pp);
+    if(curlx_str_single(pp, ';'))
+      break; /* no further parameters */
+  }
+}
+
+static bool altsvc_parse_dest(const char **pp,
+                              struct Curl_easy *data,
+                              struct Curl_peer *origin,
+                              struct Curl_str *dsthost,
+                              uint16_t *pdstport)
+{
+  curl_off_t port = 0;
+
+  if(curlx_str_single(pp, '\"'))
+    return FALSE;
+
+  /* quoted string, with hostname or just :port ? */
+  if(curlx_str_single(pp, ':')) { /* is hostname:port ? */
+    if(curlx_str_single(pp, '[')) { /* DNS hostname/ipv4 */
+      if(curlx_str_until(pp, dsthost, MAX_ALTSVC_HOSTLEN, ':')) {
+        infof(data, "Bad alt-svc hostname, ignoring.");
+        return FALSE;
+      }
+    }
+    else { /* IPv6 hostname */
+      if(curlx_str_until(pp, dsthost, MAX_IPADR_LEN, ']') ||
+         curlx_str_single(pp, ']')) {
+        infof(data, "Bad alt-svc IPv6 hostname, ignoring.");
+        return FALSE;
+      }
+    }
+    if(curlx_str_single(pp, ':'))
+      return FALSE; /* not followed by ':' */
+  }
+  else  /* is only :port, hostname is effectively origin */
+    curlx_str_assign(dsthost, origin->hostname,
+                     strlen(origin->hostname));
+
+  if(curlx_str_number(pp, &port, 0xffff)) {
+    infof(data, "Unknown alt-svc port number, ignoring.");
+    return FALSE;
+  }
+
+  *pdstport = (uint16_t)port;
+  if(curlx_str_single(pp, '\"'))
+    return FALSE; /* quoted string not ending here as expected */
+  return TRUE;
+}
+
 /*
  * Curl_altsvc_parse() takes an incoming alt-svc response header and stores
  * the data correctly in the cache.
@@ -477,161 +575,91 @@ static void altsvc_flush(struct altsvcinfo *asi, enum alpnid srcalpnid,
  */
 CURLcode Curl_altsvc_parse(struct Curl_easy *data,
                            struct altsvcinfo *asi, const char *value,
-                           enum alpnid srcalpnid, const char *srchost,
-                           unsigned short srcport)
+                           struct Curl_peer *origin,
+                           enum alpnid origin_alpnid)
 {
-  const char *p = value;
   struct altsvc *as;
-  unsigned short dstport = srcport; /* the same by default */
   size_t entries = 0;
   struct Curl_str alpn;
+  const char *p;
 
   DEBUGASSERT(asi);
+  DEBUGASSERT(origin);
+  /* RFC 7838, The "Alt-Svc" header field value is basically
+   * Alt-Svc: (clear|alpn="(host)?:port"\s*(;\s*parameter=value)*)
+   * This can be repeated, comma-separated.
+   *
+   * We parse "best effort", ignoring values we do not recognize.
+   */
 
-  /* initial check for "clear" */
+  /* Try to catch a standalone "clear" */
+  p = value;
   if(!curlx_str_cspn(&p, &alpn, ";\n\r")) {
     curlx_str_trimblanks(&alpn);
     /* "clear" is a magic keyword */
     if(curlx_str_casecompare(&alpn, "clear")) {
       /* Flush cached alternatives for this source origin */
-      altsvc_flush(asi, srcalpnid, srchost, srcport);
+      altsvc_flush(asi, origin, origin_alpnid);
       return CURLE_OK;
     }
   }
 
-  p = value;
+  /* Not a standalone "clear", parse from start for alpn entries */
+  for(p = value; *p;) {
+    time_t maxage = 24 * 3600; /* default is 24 hours */
+    bool persist = FALSE;
+    enum alpnid dstalpnid;
+    struct Curl_str dsthost;
+    uint16_t dstport;
 
-  if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
-    return CURLE_OK; /* strange line */
-
-  curlx_str_trimblanks(&alpn);
-
-  do {
-    if(!curlx_str_single(&p, '=')) {
-      time_t maxage = 24 * 3600; /* default is 24 hours */
-      bool persist = FALSE;
-      /* [protocol]="[host][:port], [protocol]="[host][:port]" */
-      enum alpnid dstalpnid = Curl_str2alpnid(&alpn);
-      if(!curlx_str_single(&p, '\"')) {
-        struct Curl_str dsthost;
-        curl_off_t port = 0;
-        if(curlx_str_single(&p, ':')) {
-          /* hostname starts here */
-          if(curlx_str_single(&p, '[')) {
-            if(curlx_str_until(&p, &dsthost, MAX_ALTSVC_HOSTLEN, ':')) {
-              infof(data, "Bad alt-svc hostname, ignoring.");
-              break;
-            }
-          }
-          else {
-            /* IPv6 hostname */
-            if(curlx_str_until(&p, &dsthost, MAX_IPADR_LEN, ']') ||
-               curlx_str_single(&p, ']')) {
-              infof(data, "Bad alt-svc IPv6 hostname, ignoring.");
-              break;
-            }
-          }
-          if(curlx_str_single(&p, ':'))
-            break;
-        }
-        else
-          /* no destination name, use source host */
-          curlx_str_assign(&dsthost, srchost, strlen(srchost));
+    if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
+      break; /* not another entry, leave */
+    curlx_str_trimblanks(&alpn);
+    dstalpnid = Curl_str2alpnid(&alpn);
 
-        if(curlx_str_number(&p, &port, 0xffff)) {
-          infof(data, "Unknown alt-svc port number, ignoring.");
-          break;
-        }
+    if(curlx_str_single(&p, '='))
+      break;
 
-        dstport = (unsigned short)port;
-
-        if(curlx_str_single(&p, '\"'))
-          break;
-
-        /* Handle the optional 'ma' and 'persist' flags. Unknown flags are
-           skipped. */
-        curlx_str_passblanks(&p);
-        if(!curlx_str_single(&p, ';')) {
-          for(;;) {
-            struct Curl_str name;
-            struct Curl_str val;
-            const char *vp;
-            curl_off_t num;
-            bool quoted;
-            /* allow some extra whitespaces around name and value */
-            if(curlx_str_until(&p, &name, 20, '=') ||
-               curlx_str_single(&p, '=') ||
-               curlx_str_cspn(&p, &val, ",;"))
-              break;
-            curlx_str_trimblanks(&name);
-            curlx_str_trimblanks(&val);
-            /* the value might be quoted */
-            vp = curlx_str(&val);
-            quoted = (*vp == '\"');
-            if(quoted)
-              vp++;
-            if(!curlx_str_number(&vp, &num, TIME_T_MAX)) {
-              if(curlx_str_casecompare(&name, "ma"))
-                maxage = (time_t)num;
-              else if(curlx_str_casecompare(&name, "persist") && (num == 1))
-                persist = TRUE;
-            }
-            else
-              break;
-            p = vp; /* point to the byte ending the value */
-            curlx_str_passblanks(&p);
-            if(quoted && curlx_str_single(&p, '\"'))
-              break;
-            curlx_str_passblanks(&p);
-            if(curlx_str_single(&p, ';'))
-              break;
-          }
-        }
-        if(dstalpnid) {
-          if(!entries++)
-            /* Flush cached alternatives for this source origin, if any - when
-               this is the first entry of the line. */
-            altsvc_flush(asi, srcalpnid, srchost, srcport);
-
-          as = altsvc_createid(srchost, strlen(srchost),
-                               curlx_str(&dsthost),
-                               curlx_strlen(&dsthost),
-                               srcalpnid, dstalpnid,
-                               srcport, dstport);
-          if(as) {
-            time_t secs = time(NULL);
-            /* The expires time also needs to take the Age: value (if any)
-               into account. [See RFC 7838 section 3.1] */
-            if(maxage > (TIME_T_MAX - secs))
-              as->expires = TIME_T_MAX;
-            else
-              as->expires = maxage + secs;
-            as->persist = persist;
-            altsvc_append(asi, as);
-            infof(data, "Added alt-svc: %.*s:%d over %s",
-                  (int)curlx_strlen(&dsthost), curlx_str(&dsthost),
-                  dstport, Curl_alpnid2str(dstalpnid));
-          }
-          else
-            return CURLE_OUT_OF_MEMORY;
-        }
-      }
-      else
-        break;
+    /* Parse altsvc hostname:port */
+    if(!altsvc_parse_dest(&p, data, origin, &dsthost, &dstport))
+      break;
 
-      /* after the double quote there can be a comma if there is another
-         string or a semicolon if no more */
-      if(curlx_str_single(&p, ','))
-        break;
+    /* Parse optional parameters */
+    altsvc_parse_params(&p, &maxage, &persist);
 
-      /* comma means another alternative is present */
-      if(curlx_str_until(&p, &alpn, MAX_ALTSVC_LINE, '='))
-        break;
-      curlx_str_trimblanks(&alpn);
+    if(dstalpnid) { /* this is a known ALPN id, e.g. not ALPN_none */
+      if(!entries++)
+        /* Flush cached alternatives for this source origin, if any - when
+           this is the first entry of the line. */
+        altsvc_flush(asi, origin, origin_alpnid);
+
+      as = altsvc_createid(origin->hostname, strlen(origin->hostname),
+                           curlx_str(&dsthost),
+                           curlx_strlen(&dsthost),
+                           origin_alpnid, dstalpnid,
+                           origin->port, dstport);
+      if(as) {
+        time_t secs = time(NULL);
+        /* The expires time also needs to take the Age: value (if any)
+           into account. [See RFC 7838 section 3.1] */
+        if(maxage > (TIME_T_MAX - secs))
+          as->expires = TIME_T_MAX;
+        else
+          as->expires = maxage + secs;
+        as->persist = persist;
+        altsvc_append(asi, as);
+        infof(data, "Added alt-svc: %.*s:%u over %s",
+              (int)curlx_strlen(&dsthost), curlx_str(&dsthost),
+              dstport, Curl_alpnid2str(dstalpnid));
+      }
+      else
+        return CURLE_OUT_OF_MEMORY;
     }
-    else
+
+    /* When this is followed by a comma, we expect another entry */
+    if(curlx_str_single(&p, ','))
       break;
-  } while(1);
+  }
 
   return CURLE_OK;
 }
@@ -640,38 +668,42 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data,
  * Return TRUE on a match
  */
 bool Curl_altsvc_lookup(struct altsvcinfo *asi,
-                        enum alpnid srcalpnid, const char *srchost,
-                        int srcport,
+                        struct Curl_peer *origin,
+                        enum alpnid origin_alpnid,
                         struct altsvc **dstentry,
                         const int versions, /* one or more bits */
                         bool *psame_destination)
 {
-  struct Curl_llist_node *e;
-  struct Curl_llist_node *n;
-  time_t now = time(NULL);
   DEBUGASSERT(asi);
-  DEBUGASSERT(srchost);
+  DEBUGASSERT(origin);
   DEBUGASSERT(dstentry);
-
   *psame_destination = FALSE;
-  for(e = Curl_llist_head(&asi->list); e; e = n) {
-    struct altsvc *as = Curl_node_elem(e);
-    n = Curl_node_next(e);
-    if(as->expires < now) {
-      /* an expired entry, remove */
-      Curl_node_remove(e);
-      altsvc_free(as);
-      continue;
-    }
-    if((as->src.alpnid == srcalpnid) &&
-       hostcompare(srchost, as->src.host) &&
-       (as->src.port == srcport) &&
-       (versions & (int)as->dst.alpnid)) {
-      /* match */
-      *dstentry = as;
-      *psame_destination = (srcport == as->dst.port) &&
-                           hostcompare(srchost, as->dst.host);
-      return TRUE;
+
+  if(Curl_llist_count(&asi->list)) {
+    struct Curl_llist_node *e;
+    struct Curl_llist_node *n;
+    time_t now = time(NULL);
+
+    for(e = Curl_llist_head(&asi->list); e; e = n) {
+      struct altsvc *as = Curl_node_elem(e);
+      n = Curl_node_next(e);
+      if(as->expires < now) {
+        /* an expired entry, remove */
+        Curl_node_remove(e);
+        altsvc_free(as);
+        continue;
+      }
+      if((origin_alpnid == as->src.alpnid) &&
+         (versions & (int)as->dst.alpnid) &&
+         (origin->port == as->src.port) &&
+         hostcompare(origin->hostname, as->src.host)) {
+        /* match */
+        *dstentry = as;
+        /* alt-svc on the same host+port or another one? */
+        *psame_destination = (origin->port == as->dst.port) &&
+                             hostcompare(origin->hostname, as->dst.host);
+        return TRUE;
+      }
     }
   }
   return FALSE;
index dc1740bce13ebbda76f69090552f8c4d3f6086b4..cf9e21808ecf9deddc3c8a2e3a80b24a1aac0436 100644 (file)
@@ -28,6 +28,8 @@
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC)
 #include "llist.h"
 
+struct Curl_peer;
+
 /* the maximum number of alt-svc entries kept in a single cache */
 #define MAX_ALTSVC_ENTRIES 5000
 
@@ -60,11 +62,11 @@ CURLcode Curl_altsvc_ctrl(struct Curl_easy *data, const long ctrl);
 void Curl_altsvc_cleanup(struct altsvcinfo **asi);
 CURLcode Curl_altsvc_parse(struct Curl_easy *data,
                            struct altsvcinfo *asi, const char *value,
-                           enum alpnid srcalpnid, const char *srchost,
-                           unsigned short srcport);
+                           struct Curl_peer *origin,
+                           enum alpnid origin_alpnid);
 bool Curl_altsvc_lookup(struct altsvcinfo *asi,
-                        enum alpnid srcalpnid, const char *srchost,
-                        int srcport,
+                        struct Curl_peer *origin,
+                        enum alpnid origin_alpnid,
                         struct altsvc **dstentry,
                         const int versions, /* CURLALTSVC_H* bits */
                         bool *psame_destination);
index a2b86c74587dec32cf2be7a568202ce0543414c0..6d6ddd2e91a14d37870cbfb9d84840dda7a3385e 100644 (file)
@@ -57,12 +57,6 @@ enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len)
   return ALPN_none; /* unknown, probably rubbish input */
 }
 
-enum alpnid Curl_str2alpnid(const struct Curl_str *cstr)
-{
-  return Curl_alpn2alpnid((const unsigned char *)curlx_str(cstr),
-                          curlx_strlen(cstr));
-}
-
 #endif
 
 /*
index 95bde1eb4cf6364285518f9ce645ca7a660764e4..a5b7042d6e59c4fca0beae21d202f6de3d20c9bd 100644 (file)
@@ -32,7 +32,6 @@ struct Curl_peer;
 struct Curl_str;
 
 enum alpnid Curl_alpn2alpnid(const unsigned char *name, size_t len);
-enum alpnid Curl_str2alpnid(const struct Curl_str *cstr);
 
 /* generic function that returns how much time there is left to run, according
    to the timeouts set */
index 83d0e011528ef2729d9d507327a5d740dcf39c12..a0aee51c78809e36436af29ddba8b65c45c8faac 100644 (file)
@@ -3208,9 +3208,7 @@ static CURLcode http_header_a(struct Curl_easy *data,
     struct SingleRequest *k = &data->req;
     enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
       (k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
-    return Curl_altsvc_parse(
-      data, data->asi, v, id, data->state.origin->hostname,
-      curlx_uitous((unsigned int)data->state.origin->port));
+    return Curl_altsvc_parse(data, data->asi, v, data->state.origin, id);
   }
 #else
   (void)data;
index 1567d5bf3c50cc3f26706028096a7fdd1a7465a0..ea635fd1e43bd7523e2371a016c6be3cc73399af 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -1901,7 +1901,7 @@ static CURLcode url_set_conn_peer(struct Curl_easy *data,
                                   struct connectdata *conn)
 {
   CURLcode result = CURLE_OK;
-  const struct Curl_peer *origin = conn->origin;
+  struct Curl_peer *origin = conn->origin;
   struct Curl_peer *via_peer = NULL;
   struct curl_slist *conn_to_entry = data->set.connect_to;
 
@@ -1953,8 +1953,7 @@ static CURLcode url_set_conn_peer(struct Curl_easy *data,
     if(!hit && (neg->wanted & CURL_HTTP_V3x)) {
       srcalpnid = ALPN_h3;
       hit = Curl_altsvc_lookup(data->asi,
-                               ALPN_h3, origin->hostname,
-                               origin->port, /* from */
+                               origin, ALPN_h3, /* from */
                                &as /* to */,
                                allowed_alpns, &same_dest);
     }
@@ -1964,8 +1963,7 @@ static CURLcode url_set_conn_peer(struct Curl_easy *data,
        !neg->h2_prior_knowledge) {
       srcalpnid = ALPN_h2;
       hit = Curl_altsvc_lookup(data->asi,
-                               ALPN_h2, origin->hostname,
-                               origin->port, /* from */
+                               origin, ALPN_h2, /* from */
                                &as /* to */,
                                allowed_alpns, &same_dest);
     }
@@ -1974,8 +1972,7 @@ static CURLcode url_set_conn_peer(struct Curl_easy *data,
        !neg->only_10) {
       srcalpnid = ALPN_h1;
       hit = Curl_altsvc_lookup(data->asi,
-                               ALPN_h1, origin->hostname,
-                               origin->port, /* from */
+                               origin, ALPN_h1, /* from */
                                &as /* to */,
                                allowed_alpns, &same_dest);
     }
index ef42153ba1d89484e403d2b9c31846839060b0a5..3f851d1bc752191961f959b56993d4bfe91b9fdb 100644 (file)
@@ -31,9 +31,12 @@ static CURLcode test_unit1654(const char *arg)
 
 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_ALTSVC)
   char outname[256];
-  CURL *curl;
+  CURL *curl = NULL;
   CURLcode result;
   struct altsvcinfo *asi = Curl_altsvc_init();
+  struct Curl_peer *origin = NULL;
+  const struct Curl_scheme *scheme = &Curl_scheme_https;
+
   abort_if(!asi, "Curl_altsvc_i");
   result = Curl_altsvc_load(asi, arg);
   fail_if(result, "Curl_altsvc_load");
@@ -44,101 +47,125 @@ static CURLcode test_unit1654(const char *arg)
   fail_if(!curl, "curl_easy_init");
   if(!curl)
     goto fail;
+
   fail_unless(Curl_llist_count(&asi->list) == 4, "wrong number of entries");
   curl_msnprintf(outname, sizeof(outname), "%s-out", arg);
 
+  if(Curl_peer_create(curl, scheme, "example.org", 8080, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi, "h2=\"example.com:8080\"\r\n",
-                             ALPN_h1, "example.org", 8080);
+                             origin, ALPN_h1);
   fail_if(result, "Curl_altsvc_parse() failed!");
   fail_unless(Curl_llist_count(&asi->list) == 5, "wrong number of entries");
 
+  if(Curl_peer_create(curl, scheme, "2.example.org", 8080, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi, "h3=\":8080\"\r\n",
-                             ALPN_h1, "2.example.org", 8080);
+                             origin, ALPN_h1);
   fail_if(result, "Curl_altsvc_parse(2) failed!");
   fail_unless(Curl_llist_count(&asi->list) == 6, "wrong number of entries");
 
+  if(Curl_peer_create(curl, scheme, "3.example.org", 8080, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi,
                              "h2=\"example.com:8080\", "
                              "h3=\"yesyes.com:8080\"\r\n",
-                             ALPN_h1, "3.example.org", 8080);
+                             origin, ALPN_h1);
   fail_if(result, "Curl_altsvc_parse(3) failed!");
   /* that one should make two entries */
   fail_unless(Curl_llist_count(&asi->list) == 8, "wrong number of entries");
 
+  if(Curl_peer_create(curl, scheme, "example.org", 80, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi,
                              "h2=\"example.com:443\"; ma = 120;\r\n",
-                             ALPN_h2, "example.org", 80);
+                             origin, ALPN_h2);
   fail_if(result, "Curl_altsvc_parse(4) failed!");
   fail_unless(Curl_llist_count(&asi->list) == 9, "wrong number of entries");
 
   /* quoted 'ma' value */
+  if(Curl_peer_create(curl, scheme, "example.net", 80, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi,
                              "h2=\"example.net:443\"; ma=\"180\";\r\n",
-                             ALPN_h2, "example.net", 80);
+                             origin, ALPN_h2);
   fail_if(result, "Curl_altsvc_parse(5) failed!");
   fail_unless(Curl_llist_count(&asi->list) == 10, "wrong number of entries");
 
+  if(Curl_peer_create(curl, scheme, "curl.se", 80, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi,
                              "h2=\":443\"; ma=180, h3=\":443\"; "
                              "persist = \"1\"; ma = 120;\r\n",
-                             ALPN_h1, "curl.se", 80);
+                             origin, ALPN_h1);
   fail_if(result, "Curl_altsvc_parse(6) failed!");
   fail_unless(Curl_llist_count(&asi->list) == 12, "wrong number of entries");
 
   /* clear that one again and decrease the counter */
   result = Curl_altsvc_parse(curl, asi, "clear;\r\n",
-                             ALPN_h1, "curl.se", 80);
+                             origin, ALPN_h1);
   fail_if(result, "Curl_altsvc_parse(7) failed!");
   fail_unless(Curl_llist_count(&asi->list) == 10, "wrong number of entries");
 
   result = Curl_altsvc_parse(curl, asi,
                              "h2=\":443\", h3=\":443\"; "
                              "persist = \"1\"; ma = 120;\r\n",
-                             ALPN_h1, "curl.se", 80);
+                             origin, ALPN_h1);
   fail_if(result, "Curl_altsvc_parse(6) failed!");
   fail_unless(Curl_llist_count(&asi->list) == 12, "wrong number of entries");
 
   /* clear - without semicolon */
   result = Curl_altsvc_parse(curl, asi, "clear\r\n",
-                             ALPN_h1, "curl.se", 80);
+                             origin, ALPN_h1);
   fail_if(result, "Curl_altsvc_parse(7) failed!");
   fail_unless(Curl_llist_count(&asi->list) == 10, "wrong number of entries");
 
   /* only a non-existing alpn */
+  if(Curl_peer_create(curl, scheme, "5.example.net", 80, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi,
                              "h6=\"example.net:443\"; ma=\"180\";\r\n",
-                             ALPN_h2, "5.example.net", 80);
+                             origin, ALPN_h2);
   fail_if(result, "Curl_altsvc_parse(8) failed!");
 
   /* missing quote in alpn host */
+  if(Curl_peer_create(curl, scheme, "6.example.net", 80, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi,
                              "h2=\"example.net:443,; ma=\"180\";\r\n",
-                             ALPN_h2, "6.example.net", 80);
+                             origin, ALPN_h2);
   fail_if(result, "Curl_altsvc_parse(9) failed!");
 
   /* missing port in hostname */
+  if(Curl_peer_create(curl, scheme, "7.example.net", 80, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi,
                              "h2=\"example.net\"; ma=\"180\";\r\n",
-                             ALPN_h2, "7.example.net", 80);
+                             origin, ALPN_h2);
   fail_if(result, "Curl_altsvc_parse(10) failed!");
 
   /* illegal port in hostname */
+  if(Curl_peer_create(curl, scheme, "8.example.net", 80, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi,
                              "h2=\"example.net:70000\"; ma=\"180\";\r\n",
-                             ALPN_h2, "8.example.net", 80);
+                             origin, ALPN_h2);
   fail_if(result, "Curl_altsvc_parse(11) failed!");
 
+  if(Curl_peer_create(curl, scheme, "test.se", 443, &origin))
+    goto fail;
   result = Curl_altsvc_parse(curl, asi,
                              "h2=\"test2.se:443\"; ma=\"180 \" ; unknown=2, "
                              "h2=\"test3.se:443\"; ma = 120;\r\n",
-                             ALPN_h2, "test.se", 443);
+                             origin, ALPN_h2);
   fail_if(result, "Curl_altsvc_parse(12) failed!");
 
   Curl_altsvc_save(curl, asi, outname);
 
-  curl_easy_cleanup(curl);
 fail:
+  curl_easy_cleanup(curl);
   Curl_altsvc_cleanup(&asi);
+  Curl_peer_unlink(&origin);
 #endif
 
   UNITTEST_END(curl_global_cleanup())