From: Daniel Stenberg Date: Thu, 1 Jan 2026 16:46:04 +0000 (+0100) Subject: altsvc: accept ma/persist per alternative entry X-Git-Tag: curl-8_18_0~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=03c9215e62523f3ec737e4ce117da70d2ff4ebb4;p=thirdparty%2Fcurl.git altsvc: accept ma/persist per alternative entry The 'ma' and 'persist' keywords should be considered per list entry, not once per header. Expand test 1654 to verify such headers Reported-by: Hunt Darlener Closes #20160 --- diff --git a/lib/altsvc.c b/lib/altsvc.c index 3b99c14590..7006333b07 100644 --- a/lib/altsvc.c +++ b/lib/altsvc.c @@ -459,9 +459,6 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, unsigned short dstport = srcport; /* the same by default */ size_t entries = 0; struct Curl_str alpn; - const char *sp; - time_t maxage = 24 * 3600; /* default is 24 hours */ - bool persist = FALSE; #ifdef CURL_DISABLE_VERBOSE_STRINGS (void)data; #endif @@ -486,44 +483,10 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, curlx_str_trimblanks(&alpn); - /* Handle the optional 'ma' and 'persist' flags once first, as they need to - be known for each alternative service. Unknown flags are skipped. */ - sp = strchr(p, ';'); - if(sp) { - sp++; /* pass the semicolon */ - 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(&sp, &name, 20, '=') || - curlx_str_single(&sp, '=') || - curlx_str_until(&sp, &val, 80, ';')) - 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; - } - if(quoted && curlx_str_single(&sp, '\"')) - break; - if(curlx_str_single(&sp, ';')) - break; - } - } - 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, '\"')) { @@ -562,6 +525,45 @@ CURLcode Curl_altsvc_parse(struct Curl_easy *data, 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 diff --git a/tests/data/test1654 b/tests/data/test1654 index a946e552cc..3d85404aba 100644 --- a/tests/data/test1654 +++ b/tests/data/test1654 @@ -48,6 +48,8 @@ h1 3.example.org 8080 h2 example.com 8080 "20190125 22:34:21" 0 0 h1 3.example.org 8080 h3 yesyes.com 8080 "20190125 22:34:21" 0 0 h2 example.org 80 h2 example.com 443 "20190124 22:36:21" 0 0 h2 example.net 80 h2 example.net 443 "20190124 22:37:21" 0 0 +h2 test.se 443 h2 test2.se 443 "20190124 22:37:21" 0 0 +h2 test.se 443 h2 test3.se 443 "20190124 22:36:21" 0 0 diff --git a/tests/unit/unit1654.c b/tests/unit/unit1654.c index c16a9ebfd6..0c8adcded6 100644 --- a/tests/unit/unit1654.c +++ b/tests/unit/unit1654.c @@ -82,7 +82,7 @@ static CURLcode test_unit1654(const char *arg) fail_unless(Curl_llist_count(&asi->list) == 10, "wrong number of entries"); res = Curl_altsvc_parse(curl, asi, - "h2=\":443\", h3=\":443\"; " + "h2=\":443\"; ma=180, h3=\":443\"; " "persist = \"1\"; ma = 120;\r\n", ALPN_h1, "curl.se", 80); fail_if(res, "Curl_altsvc_parse(6) failed!"); @@ -131,6 +131,12 @@ static CURLcode test_unit1654(const char *arg) ALPN_h2, "8.example.net", 80); fail_if(res, "Curl_altsvc_parse(11) failed!"); + res = 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); + fail_if(res, "Curl_altsvc_parse(12) failed!"); + Curl_altsvc_save(curl, asi, outname); curl_easy_cleanup(curl);