]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
alpn: query filter
authorStefan Eissing <stefan@eissing.org>
Thu, 17 Jul 2025 09:53:31 +0000 (11:53 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Mon, 28 Jul 2025 12:04:31 +0000 (14:04 +0200)
Add a connection filter query to obtained the negotiated ALPN
protocol to check in setup/protocols how the connection needs
to behave.

Remove the members `alpn` and `proxy_alpn` from `connectdata`.

Closes #17947

16 files changed:
lib/cf-h2-proxy.c
lib/cf-https-connect.c
lib/cfilters.c
lib/cfilters.h
lib/http.c
lib/http_proxy.c
lib/socks.c
lib/url.c
lib/urldata.h
lib/vquic/curl_ngtcp2.c
lib/vquic/curl_osslq.c
lib/vquic/curl_quiche.c
lib/vtls/schannel.c
lib/vtls/vtls.c
tests/http/test_10_proxy.py
tests/http/test_13_proxy_auth.py

index 7cfdbf988d821d8b0ce8d394f6f249842f1ba513..162881b17eacdfcd17e0d219833808d6898809f0 100644 (file)
@@ -1534,6 +1534,12 @@ static CURLcode cf_h2_proxy_query(struct Curl_cfilter *cf,
     }
     break;
   }
+  case CF_QUERY_ALPN_NEGOTIATED: {
+    const char **palpn = pres2;
+    DEBUGASSERT(palpn);
+    *palpn = NULL;
+    return CURLE_OK;
+  }
   default:
     break;
   }
index bbf1ffaa78d578a6f627354d0c47781897a20c23..f36d227b117a4d6113779ed6a4da681ea5965030 100644 (file)
@@ -226,24 +226,22 @@ static CURLcode baller_connected(struct Curl_cfilter *cf,
   cf->next = winner->cf;
   winner->cf = NULL;
 
-  switch(cf->conn->alpn) {
-  case CURL_HTTP_VERSION_3:
-    break;
-  case CURL_HTTP_VERSION_2:
 #ifdef USE_NGHTTP2
+  {
     /* Using nghttp2, we add the filter "below" us, so when the conn
      * closes, we tear it down for a fresh reconnect */
-    result = Curl_http2_switch_at(cf, data);
-    if(result) {
-      ctx->state = CF_HC_FAILURE;
-      ctx->result = result;
-      return result;
+    const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
+    if(alpn && !strcmp("h2", alpn)) {
+      result = Curl_http2_switch_at(cf, data);
+      if(result) {
+        ctx->state = CF_HC_FAILURE;
+        ctx->result = result;
+        return result;
+      }
     }
-#endif
-    break;
-  default:
-    break;
   }
+#endif
+
   ctx->state = CF_HC_SUCCESS;
   cf->connected = TRUE;
   return result;
index 6326f79f786cbbadbf53fddcb77823374a85ef7a..069d6a24a3b42322530bc744cba99a2dfd3b6e59 100644 (file)
@@ -564,7 +564,7 @@ bool Curl_conn_is_ip_connected(struct Curl_easy *data, int sockindex)
   return FALSE;
 }
 
-bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf)
+static bool cf_is_ssl(struct Curl_cfilter *cf)
 {
   for(; cf; cf = cf->next) {
     if(cf->cft->flags & CF_TYPE_SSL)
@@ -577,7 +577,7 @@ bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf)
 
 bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex)
 {
-  return conn ? Curl_conn_cf_is_ssl(conn->cfilter[sockindex]) : FALSE;
+  return conn ? cf_is_ssl(conn->cfilter[sockindex]) : FALSE;
 }
 
 bool Curl_conn_get_ssl_info(struct Curl_easy *data,
@@ -613,6 +613,13 @@ unsigned char Curl_conn_get_transport(struct Curl_easy *data,
   return Curl_conn_cf_get_transport(cf, data);
 }
 
+const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
+                                          struct connectdata *conn)
+{
+  struct Curl_cfilter *cf = conn->cfilter[FIRSTSOCKET];
+  return Curl_conn_cf_get_alpn_negotiated(cf, data);
+}
+
 unsigned char Curl_conn_http_version(struct Curl_easy *data,
                                      struct connectdata *conn)
 {
@@ -810,6 +817,17 @@ unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf,
   return (unsigned char)(data->conn ? data->conn->transport_wanted : 0);
 }
 
+const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf,
+                                             struct Curl_easy *data)
+{
+  const char *alpn = NULL;
+  CURL_TRC_CF(data, cf, "query ALPN");
+  if(cf && !cf->cft->query(cf, data, CF_QUERY_ALPN_NEGOTIATED, NULL,
+                           CURL_UNCONST(&alpn)))
+    return alpn;
+  return NULL;
+}
+
 static const struct Curl_sockaddr_ex *
 cf_get_remote_addr(struct Curl_cfilter *cf, struct Curl_easy *data)
 {
index 39d906d673b120f05ade63aca2de125a614819ec..d468e5ad85836ad579cca4100cfc7ac616a8e86e 100644 (file)
@@ -158,6 +158,10 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
  * - CF_QUERY_SSL_CTX_INFO: same as CF_QUERY_SSL_INFO, but give the SSL_CTX
  *                      when available, or the same internal pointer
  *                      when the TLS stack does not differentiate.
+ * - CF_QUERY_ALPN_NEGOTIATED: The ALPN selected by the server as
+                        null-terminated string or NULL if none
+                        selected/handshake not done. Implemented by filter
+                        types CF_TYPE_SSL or CF_TYPE_IP_CONNECT.
  */
 /*      query                             res1       res2     */
 #define CF_QUERY_MAX_CONCURRENT     1  /* number     -        */
@@ -176,6 +180,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
 #define CF_QUERY_SSL_INFO          12  /* -    struct curl_tlssessioninfo * */
 #define CF_QUERY_SSL_CTX_INFO      13  /* -    struct curl_tlssessioninfo * */
 #define CF_QUERY_TRANSPORT         14  /* TRNSPRT_*  - * */
+#define CF_QUERY_ALPN_NEGOTIATED   15  /* -          const char * */
 
 /**
  * Query the cfilter for properties. Filters ignorant of a query will
@@ -331,12 +336,6 @@ CURLcode Curl_conn_cf_cntrl(struct Curl_cfilter *cf,
                             bool ignore_result,
                             int event, int arg1, void *arg2);
 
-/**
- * Determine if the connection filter chain is using SSL to the remote host
- * (or will be once connected).
- */
-bool Curl_conn_cf_is_ssl(struct Curl_cfilter *cf);
-
 /**
  * Get the socket used by the filter chain starting at `cf`.
  * Returns CURL_SOCKET_BAD if not available.
@@ -354,6 +353,9 @@ bool Curl_conn_cf_needs_flush(struct Curl_cfilter *cf,
 unsigned char Curl_conn_cf_get_transport(struct Curl_cfilter *cf,
                                          struct Curl_easy *data);
 
+const char *Curl_conn_cf_get_alpn_negotiated(struct Curl_cfilter *cf,
+                                             struct Curl_easy *data);
+
 #define CURL_CF_SSL_DEFAULT  -1
 #define CURL_CF_SSL_DISABLE  0
 #define CURL_CF_SSL_ENABLE   1
@@ -418,6 +420,10 @@ unsigned char Curl_conn_http_version(struct Curl_easy *data,
 unsigned char Curl_conn_get_transport(struct Curl_easy *data,
                                       struct connectdata *conn);
 
+/* Get the negotiated ALPN protocol or NULL if none in play */
+const char *Curl_conn_get_alpn_negotiated(struct Curl_easy *data,
+                                          struct connectdata *conn);
+
 /**
  * Close the filter chain at `sockindex` for connection `data->conn`.
   * Filters remain in place and may be connected again afterwards.
index 94b3d2cc714926c696293cce2924f22ed1a04063..c3f9d4e9b0943502a53745b4e4d35a2c8717cb7f 100644 (file)
@@ -2673,17 +2673,17 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   char *altused = NULL;
   const char *p_accept;      /* Accept: string */
   unsigned char httpversion;
+  const char *alpn;
 
   /* Always consider the DO phase done after this function call, even if there
      may be parts of the request that are not yet sent, since we can deal with
      the rest of the request in the PERFORM phase. */
   *done = TRUE;
-
-  switch(conn->alpn) {
-  case CURL_HTTP_VERSION_3:
+  alpn = Curl_conn_get_alpn_negotiated(data, conn);
+  if(alpn && !strcmp("h3", alpn)) {
     DEBUGASSERT(Curl_conn_http_version(data, conn) == 30);
-    break;
-  case CURL_HTTP_VERSION_2:
+  }
+  else if(alpn && !strcmp("h2", alpn)) {
 #ifndef CURL_DISABLE_PROXY
     if((Curl_conn_http_version(data, conn) != 20) &&
        conn->bits.proxy && !conn->bits.tunnel_proxy
@@ -2695,11 +2695,8 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     else
 #endif
       DEBUGASSERT(Curl_conn_http_version(data, conn) == 20);
-    break;
-  case CURL_HTTP_VERSION_1_1:
-    /* continue with HTTP/1.x when explicitly requested */
-    break;
-  default:
+  }
+  else {
     /* Check if user wants to use HTTP/2 with clear TCP */
     if(Curl_http2_may_switch(data)) {
       DEBUGF(infof(data, "HTTP/2 over clean TCP"));
@@ -2707,7 +2704,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
       if(result)
         goto fail;
     }
-    break;
   }
 
   /* Add collecting of headers written to client. For a new connection,
index 6f435a86745dfa1bb5e5fb61ab696b6d21187c8d..df51484eb9770f9a0240ce79261c14087e6ec7db 100644 (file)
@@ -325,36 +325,42 @@ connect_sub:
   if(!ctx->cf_protocol) {
     struct Curl_cfilter *cf_protocol = NULL;
     int httpversion = 0;
-    int alpn = Curl_conn_cf_is_ssl(cf->next) ?
-      cf->conn->proxy_alpn : CURL_HTTP_VERSION_1_1;
-
-    /* First time call after the subchain connected */
-    switch(alpn) {
-    case CURL_HTTP_VERSION_NONE:
-    case CURL_HTTP_VERSION_1_0:
-    case CURL_HTTP_VERSION_1_1:
+    const char *alpn = Curl_conn_cf_get_alpn_negotiated(cf->next, data);
+
+    if(alpn)
+      infof(data, "CONNECT: '%s' negotiated", alpn);
+    else
+      infof(data, "CONNECT: no ALPN negotiated");
+
+    if(alpn && !strcmp(alpn, "http/1.0")) {
+      CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.0");
+      result = Curl_cf_h1_proxy_insert_after(cf, data);
+      if(result)
+        goto out;
+      cf_protocol = cf->next;
+      httpversion = 10;
+    }
+    else if(!alpn || !strcmp(alpn, "http/1.1")) {
       CURL_TRC_CF(data, cf, "installing subfilter for HTTP/1.1");
-      infof(data, "CONNECT tunnel: HTTP/1.%d negotiated",
-            (alpn == CURL_HTTP_VERSION_1_0) ? 0 : 1);
       result = Curl_cf_h1_proxy_insert_after(cf, data);
       if(result)
         goto out;
       cf_protocol = cf->next;
-      httpversion = (alpn == CURL_HTTP_VERSION_1_0) ? 10 : 11;
-      break;
+      /* Assume that without an ALPN, we are talking to an ancient one */
+      httpversion = 11;
+    }
 #ifdef USE_NGHTTP2
-    case CURL_HTTP_VERSION_2:
+    else if(!strcmp(alpn, "h2")) {
       CURL_TRC_CF(data, cf, "installing subfilter for HTTP/2");
-      infof(data, "CONNECT tunnel: HTTP/2 negotiated");
       result = Curl_cf_h2_proxy_insert_after(cf, data);
       if(result)
         goto out;
       cf_protocol = cf->next;
       httpversion = 20;
-      break;
+    }
 #endif
-    default:
-      infof(data, "CONNECT tunnel: unsupported ALPN(%d) negotiated", alpn);
+    else {
+      failf(data, "CONNECT: negotiated ALPN '%s' not supported", alpn);
       result = CURLE_COULDNT_CONNECT;
       goto out;
     }
@@ -391,6 +397,12 @@ CURLcode Curl_cf_http_proxy_query(struct Curl_cfilter *cf,
     *pres1 = (int)cf->conn->http_proxy.port;
     *((const char **)pres2) = cf->conn->http_proxy.host.name;
     return CURLE_OK;
+  case CF_QUERY_ALPN_NEGOTIATED: {
+    const char **palpn = pres2;
+    DEBUGASSERT(palpn);
+    *palpn = NULL;
+    return CURLE_OK;
+  }
   default:
     break;
   }
index 5fc6cebcb5e123500df81e6581bdc4af08a6a26c..66c3a601a855bac5de8c0598e8073ac37d0e9b00 100644 (file)
@@ -1195,15 +1195,22 @@ static CURLcode socks_cf_query(struct Curl_cfilter *cf,
 {
   struct socks_state *sx = cf->ctx;
 
-  if(sx) {
-    switch(query) {
-    case CF_QUERY_HOST_PORT:
+  switch(query) {
+  case CF_QUERY_HOST_PORT:
+    if(sx) {
       *pres1 = sx->remote_port;
       *((const char **)pres2) = sx->hostname;
       return CURLE_OK;
-    default:
-      break;
     }
+    break;
+  case CF_QUERY_ALPN_NEGOTIATED: {
+    const char **palpn = pres2;
+    DEBUGASSERT(palpn);
+    *palpn = NULL;
+    return CURLE_OK;
+  }
+  default:
+    break;
   }
   return cf->next ?
     cf->next->cft->query(cf->next, data, query, pres1, pres2) :
index f25a58eb7fccadd2b4f8ab58479012b75f0ad20e..204f380137bf961387b5db52833f37c380a3195a 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -1400,17 +1400,15 @@ void Curl_verboseconnect(struct Curl_easy *data,
           conn->primary.remote_port);
 #ifndef CURL_DISABLE_HTTP
     if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
-      switch(conn->alpn) {
-      case CURL_HTTP_VERSION_3:
-        infof(data, "using HTTP/3");
-        break;
-      case CURL_HTTP_VERSION_2:
-        infof(data, "using HTTP/2");
-        break;
-      default:
+      const char *alpn = Curl_conn_get_alpn_negotiated(data, conn);
+      if(!alpn || !strcmp("http/1.1", alpn) || !strcmp("http/1.0", alpn))
         infof(data, "using HTTP/1.x");
-        break;
-      }
+      else if(!strcmp("h2", alpn))
+        infof(data, "using HTTP/2");
+      else if(!strcmp("h3", alpn))
+        infof(data, "using HTTP/3");
+      else
+        infof(data, "using ALPN protocol '%s'", alpn);
     }
 #endif
 }
index 1f3312be0f1e35f68ee18f807236de2f361372d7..fffc38a614b8703f5df844c61cc00700cb352872 100644 (file)
@@ -780,11 +780,6 @@ struct connectdata {
   unsigned short localport;
   unsigned short secondary_port; /* secondary socket remote port to connect to
                                     (ftp) */
-  unsigned char alpn; /* APLN TLS negotiated protocol, a CURL_HTTP_VERSION*
-                         value */
-#ifndef CURL_DISABLE_PROXY
-  unsigned char proxy_alpn; /* APLN of proxy tunnel, CURL_HTTP_VERSION* */
-#endif
   unsigned char transport_wanted; /* one of the TRNSPRT_* defines. Not
    necessarily the transport the connection ends using due to Alt-Svc
    and happy eyeballing. Use `Curl_conn_get_transport() for actual value
index 052f280afc150dcf3ce4b4f7890493e3df316331..5ec11c41713a9108527c2e86a4a7241ee05e6540 100644 (file)
@@ -2544,7 +2544,6 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
     if(result)
       goto out;
     if(cf->connected) {
-      cf->conn->alpn = CURL_HTTP_VERSION_3;
       *done = TRUE;
       goto out;
     }
@@ -2566,7 +2565,6 @@ static CURLcode cf_ngtcp2_connect(struct Curl_cfilter *cf,
     if(!result) {
       CURL_TRC_CF(data, cf, "peer verified");
       cf->connected = TRUE;
-      cf->conn->alpn = CURL_HTTP_VERSION_3;
       *done = TRUE;
       connkeep(cf->conn, "HTTP/3 default");
     }
@@ -2665,6 +2663,12 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
       return CURLE_OK;
     break;
   }
+  case CF_QUERY_ALPN_NEGOTIATED: {
+    const char **palpn = pres2;
+    DEBUGASSERT(palpn);
+    *palpn = cf->connected ? "h3" : NULL;
+    return CURLE_OK;
+  }
   default:
     break;
   }
index 3371fd3848f931ecab89d4975e46d232b745ee18..29a912000cb986570b193159da48690cc8383f8e 100644 (file)
@@ -1802,7 +1802,6 @@ static CURLcode cf_osslq_connect(struct Curl_cfilter *cf,
     if(!result) {
       CURL_TRC_CF(data, cf, "peer verified");
       cf->connected = TRUE;
-      cf->conn->alpn = CURL_HTTP_VERSION_3;
       *done = TRUE;
       connkeep(cf->conn, "HTTP/3 default");
     }
@@ -2357,6 +2356,12 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
       return CURLE_OK;
     break;
   }
+  case CF_QUERY_ALPN_NEGOTIATED: {
+    const char **palpn = pres2;
+    DEBUGASSERT(palpn);
+    *palpn = cf->connected ? "h3" : NULL;
+    return CURLE_OK;
+  }
   default:
     break;
   }
index 8a4243469bff56a228c19e15ee2eac25f66ca5f9..aafd474babfc3747a185c5ffacbafb5ffe3c4c9c 100644 (file)
@@ -1416,7 +1416,6 @@ static CURLcode cf_quiche_connect(struct Curl_cfilter *cf,
         goto out;
       }
       cf->connected = TRUE;
-      cf->conn->alpn = CURL_HTTP_VERSION_3;
       *done = TRUE;
       connkeep(cf->conn, "HTTP/3 default");
     }
@@ -1556,6 +1555,12 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
       return CURLE_OK;
     break;
   }
+  case CF_QUERY_ALPN_NEGOTIATED: {
+    const char **palpn = pres2;
+    DEBUGASSERT(palpn);
+    *palpn = cf->connected ? "h3" : NULL;
+    return CURLE_OK;
+  }
   default:
     break;
   }
index fe997ee200a313170681f0af308b5e525118ea87..fb8d6eb0b29d377affe816640ab4722e9357661f 100644 (file)
@@ -1591,19 +1591,18 @@ schannel_connect_step3(struct Curl_cfilter *cf, struct Curl_easy *data)
 
     if(alpn_result.ProtoNegoStatus ==
        SecApplicationProtocolNegotiationStatus_Success) {
-      unsigned char prev_alpn = cf->conn->alpn;
-
+      if(backend->recv_renegotiating &&
+         connssl->negotiated.alpn &&
+         strncmp(connssl->negotiated.alpn,
+                 (const char *)alpn_result.ProtocolId,
+                 alpn_result.ProtocolIdSize)) {
+        /* Renegotiation selected a different protocol now, we cannot
+         * deal with this */
+        failf(data, "schannel: server selected an ALPN protocol too late");
+        return CURLE_SSL_CONNECT_ERROR;
+      }
       Curl_alpn_set_negotiated(cf, data, connssl, alpn_result.ProtocolId,
                                alpn_result.ProtocolIdSize);
-      if(backend->recv_renegotiating) {
-        if(prev_alpn != cf->conn->alpn &&
-           prev_alpn != CURL_HTTP_VERSION_NONE) {
-          /* Renegotiation selected a different protocol now, we cannot
-           * deal with this */
-          failf(data, "schannel: server selected an ALPN protocol too late");
-          return CURLE_SSL_CONNECT_ERROR;
-        }
-      }
     }
     else {
       if(!backend->recv_renegotiating)
index 104afd1f84cf7e74e374eed0bd836c62c7653180..0ff79ea70b038010e874c8e242fbb1ceb6c78884 100644 (file)
@@ -1571,15 +1571,24 @@ static CURLcode ssl_cf_query(struct Curl_cfilter *cf,
     return CURLE_OK;
   }
   case CF_QUERY_SSL_INFO:
-  case CF_QUERY_SSL_CTX_INFO: {
-    struct curl_tlssessioninfo *info = pres2;
-    struct cf_call_data save;
-    CF_DATA_SAVE(save, cf, data);
-    info->backend = Curl_ssl_backend();
-    info->internals = connssl->ssl_impl->get_internals(
-      cf->ctx, (query == CF_QUERY_SSL_INFO) ?
-      CURLINFO_TLS_SSL_PTR : CURLINFO_TLS_SESSION);
-    CF_DATA_RESTORE(cf, save);
+  case CF_QUERY_SSL_CTX_INFO:
+    if(!Curl_ssl_cf_is_proxy(cf)) {
+      struct curl_tlssessioninfo *info = pres2;
+      struct cf_call_data save;
+      CF_DATA_SAVE(save, cf, data);
+      info->backend = Curl_ssl_backend();
+      info->internals = connssl->ssl_impl->get_internals(
+        cf->ctx, (query == CF_QUERY_SSL_INFO) ?
+        CURLINFO_TLS_SSL_PTR : CURLINFO_TLS_SESSION);
+      CF_DATA_RESTORE(cf, save);
+      return CURLE_OK;
+    }
+    break;
+  case CF_QUERY_ALPN_NEGOTIATED: {
+    const char **palpn = pres2;
+    DEBUGASSERT(palpn);
+    *palpn = connssl->negotiated.alpn;
+    CURL_TRC_CF(data, cf, "query ALPN: returning '%s'", *palpn);
     return CURLE_OK;
   }
   default:
@@ -1636,7 +1645,7 @@ struct Curl_cftype Curl_cft_ssl_proxy = {
   Curl_cf_def_cntrl,
   cf_ssl_is_alive,
   Curl_cf_def_conn_keep_alive,
-  Curl_cf_def_query,
+  ssl_cf_query,
 };
 
 #endif /* !CURL_DISABLE_PROXY */
@@ -1708,9 +1717,11 @@ static CURLcode cf_ssl_proxy_create(struct Curl_cfilter **pcf,
   struct Curl_cfilter *cf = NULL;
   struct ssl_connect_data *ctx;
   CURLcode result;
-  bool use_alpn = conn->bits.tls_enable_alpn;
+  /* ALPN is default, but if user explicitly disables it, obey */
+  bool use_alpn = data->set.ssl_enable_alpn;
   http_majors allowed = CURL_HTTP_V1x;
 
+  (void)conn;
 #ifdef USE_HTTP2
   if(conn->http_proxy.proxytype == CURLPROXY_HTTPS2) {
     use_alpn = TRUE;
@@ -1939,14 +1950,7 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
                                   size_t proto_len)
 {
   CURLcode result = CURLE_OK;
-  unsigned char *palpn =
-#ifndef CURL_DISABLE_PROXY
-    (cf->conn->bits.tunnel_proxy && Curl_ssl_cf_is_proxy(cf)) ?
-    &cf->conn->proxy_alpn : &cf->conn->alpn
-#else
-    &cf->conn->alpn
-#endif
-    ;
+  (void)cf;
 
   if(connssl->negotiated.alpn) {
     /* When we ask for a specific ALPN protocol, we need the confirmation
@@ -1988,38 +1992,12 @@ CURLcode Curl_alpn_set_negotiated(struct Curl_cfilter *cf,
   }
 
   if(proto && proto_len) {
-    if(proto_len == ALPN_HTTP_1_1_LENGTH &&
-       !memcmp(ALPN_HTTP_1_1, proto, ALPN_HTTP_1_1_LENGTH)) {
-      *palpn = CURL_HTTP_VERSION_1_1;
-    }
-#ifdef USE_HTTP2
-    else if(proto_len == ALPN_H2_LENGTH &&
-            !memcmp(ALPN_H2, proto, ALPN_H2_LENGTH)) {
-      *palpn = CURL_HTTP_VERSION_2;
-    }
-#endif
-#ifdef USE_HTTP3
-    else if(proto_len == ALPN_H3_LENGTH &&
-            !memcmp(ALPN_H3, proto, ALPN_H3_LENGTH)) {
-      *palpn = CURL_HTTP_VERSION_3;
-    }
-#endif
-    else {
-      *palpn = CURL_HTTP_VERSION_NONE;
-      failf(data, "unsupported ALPN protocol: '%.*s'", (int)proto_len, proto);
-      /* Previous code just ignored it and some vtls backends even ignore the
-       * return code of this function. */
-      /* return CURLE_NOT_BUILT_IN; */
-      goto out;
-    }
-
     if(connssl->state == ssl_connection_deferred)
       infof(data, VTLS_INFOF_ALPN_DEFERRED, (int)proto_len, proto);
     else
       infof(data, VTLS_INFOF_ALPN_ACCEPTED, (int)proto_len, proto);
   }
   else {
-    *palpn = CURL_HTTP_VERSION_NONE;
     if(connssl->state == ssl_connection_deferred)
       infof(data, VTLS_INFOF_NO_ALPN_DEFERRED);
     else
index 1138f945d14baafec9ca1ed28963b2845ac5282b..ac70ec3eb25a563ba8955bb0df542b67b26f2f15 100644 (file)
@@ -54,7 +54,7 @@ class TestProxy:
 
     def get_tunnel_proto_used(self, r: ExecResult):
         for line in r.trace_lines:
-            m = re.match(r'.* CONNECT tunnel: (\S+) negotiated$', line)
+            m = re.match(r'.* CONNECT: \'(\S+)\' negotiated$', line)
             if m:
                 return m.group(1)
         assert False, f'tunnel protocol not found in:\n{"".join(r.trace_lines)}'
@@ -161,8 +161,7 @@ class TestProxy:
                                extra_args=xargs)
         r.check_response(count=1, http_status=200,
                          protocol='HTTP/2' if proto == 'h2' else 'HTTP/1.1')
-        assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r) == tunnel
         srcfile = os.path.join(httpd.docs_dir, 'data.json')
         dfile = curl.download_file(0)
         assert filecmp.cmp(srcfile, dfile, shallow=False)
@@ -193,8 +192,7 @@ class TestProxy:
                                extra_args=xargs)
         r.check_response(count=count, http_status=200,
                          protocol='HTTP/2' if proto == 'h2' else 'HTTP/1.1')
-        assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r) == tunnel
         srcfile = os.path.join(httpd.docs_dir, fname)
         for i in range(count):
             dfile = curl.download_file(i)
@@ -226,8 +224,7 @@ class TestProxy:
         xargs = curl.get_proxy_args(tunnel=True, proto=tunnel)
         r = curl.http_upload(urls=[url], data=f'@{srcfile}', alpn_proto=proto,
                              extra_args=xargs)
-        assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r) == tunnel
         r.check_response(count=count, http_status=200)
         indata = open(srcfile).readlines()
         for i in range(count):
@@ -249,8 +246,7 @@ class TestProxy:
         r = curl.http_download(urls=[url1, url2], alpn_proto='http/1.1', with_stats=True,
                                extra_args=xargs)
         r.check_response(count=2, http_status=200)
-        assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r) == tunnel
         if tunnel == 'h2':
             # TODO: we would like to reuse the first connection for the
             # second URL, but this is currently not possible
@@ -276,8 +272,7 @@ class TestProxy:
         r1 = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
                                 extra_args=proxy_args)
         r1.check_response(count=1, http_status=200)
-        assert self.get_tunnel_proto_used(r1) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r1) == tunnel
         # get the args, duplicate separated with '--next'
         x2_args = r1.args[1:]
         x2_args.append('--next')
@@ -302,8 +297,7 @@ class TestProxy:
         r1 = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
                                 extra_args=proxy_args)
         r1.check_response(count=1, http_status=200)
-        assert self.get_tunnel_proto_used(r1) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r1) == tunnel
         # get the args, duplicate separated with '--next'
         x2_args = r1.args[1:]
         x2_args.append('--next')
@@ -329,8 +323,7 @@ class TestProxy:
         r1 = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
                                 extra_args=proxy_args)
         r1.check_response(count=1, http_status=200)
-        assert self.get_tunnel_proto_used(r1) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r1) == tunnel
         # get the args, duplicate separated with '--next'
         x2_args = r1.args[1:]
         x2_args.append('--next')
@@ -356,8 +349,7 @@ class TestProxy:
         r1 = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
                                 extra_args=proxy_args)
         r1.check_response(count=1, http_status=200)
-        assert self.get_tunnel_proto_used(r1) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r1) == tunnel
         # get the args, duplicate separated with '--next'
         x2_args = r1.args[1:]
         x2_args.append('--next')
index 57575953cca7d2856a331a3fcef388b79d472180..fe45ae40ec0d828303c8d95971a35e1559c3a6b2 100644 (file)
@@ -45,7 +45,7 @@ class TestProxyAuth:
 
     def get_tunnel_proto_used(self, r: ExecResult):
         for line in r.trace_lines:
-            m = re.match(r'.* CONNECT tunnel: (\S+) negotiated$', line)
+            m = re.match(r'.* CONNECT: \'(\S+)\' negotiated$', line)
             if m:
                 return m.group(1)
         assert False, f'tunnel protocol not found in:\n{"".join(r.trace_lines)}'
@@ -133,8 +133,7 @@ class TestProxyAuth:
                                extra_args=xargs)
         # expect "COULD_NOT_CONNECT"
         r.check_response(exitcode=56, http_status=None)
-        assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r) == tunnel
 
     @pytest.mark.skipif(condition=not Env.have_nghttpx(), reason="no nghttpx available")
     @pytest.mark.skipif(condition=not Env.curl_has_feature('HTTPS-proxy'),
@@ -154,8 +153,7 @@ class TestProxyAuth:
                                extra_args=xargs)
         r.check_response(count=1, http_status=200,
                          protocol='HTTP/2' if proto == 'h2' else 'HTTP/1.1')
-        assert self.get_tunnel_proto_used(r) == 'HTTP/2' \
-            if tunnel == 'h2' else 'HTTP/1.1'
+        assert self.get_tunnel_proto_used(r) == tunnel
 
     @pytest.mark.skipif(condition=not Env.curl_has_feature('SPNEGO'),
                         reason='curl lacks SPNEGO support')