]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
lib: clarify 'conn->httpversion'
authorStefan Eissing <stefan@eissing.org>
Wed, 22 Jan 2025 13:45:30 +0000 (14:45 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 24 Jan 2025 09:59:02 +0000 (10:59 +0100)
The variable `conn->httpversion` was used for several purposes and it
was unclear at which time the value represents what.

- rename `conn->httpversion` to `conn->httpversion_seen`
  This makes clear that the variable only records the last
  HTTP version seen on the connection - if any. And that it
  no longer is an indication of what version to use.
- Change Alt-Svc handling to no longer modify `conn->httpversion`
  but set `data->state.httpwant` for influencing the HTTP version
  to use on a transfer.
- Add `data->req.httpversion_sent` to have a record of what
  HTTP version was sent in a request
- Add connection filter type CF_TYPE_HTTP
- Add filter query `CF_QUERY_HTTP_VERSION` to ask what HTTP
  filter version is in place
- Lookup filters HTTP version instead of using `conn->httpversion`

Test test_12_05 now switches to HTTP/1.1 correctly and the
expectations have been fixed.

Removed the connection fitler "is_httpN()" checks and using
the version query instead.

Closes #16073

21 files changed:
lib/cfilters.c
lib/cfilters.h
lib/http.c
lib/http.h
lib/http2.c
lib/http2.h
lib/http_proxy.c
lib/request.c
lib/request.h
lib/rtsp.c
lib/transfer.c
lib/url.c
lib/urldata.h
lib/vquic/curl_msh3.c
lib/vquic/curl_ngtcp2.c
lib/vquic/curl_osslq.c
lib/vquic/curl_quiche.c
lib/vquic/vquic.c
lib/vquic/vquic.h
tests/data/test471
tests/http/test_12_reuse.py

index 3aecaff543765fe0598054082eb7c87d6495b01e..8b0535615108db98b09813f0b8d458fd8db7efd9 100644 (file)
@@ -494,13 +494,35 @@ bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex)
   for(; cf; cf = cf->next) {
     if(cf->cft->flags & CF_TYPE_MULTIPLEX)
       return TRUE;
-    if(cf->cft->flags & CF_TYPE_IP_CONNECT
-       || cf->cft->flags & CF_TYPE_SSL)
+    if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL))
       return FALSE;
   }
   return FALSE;
 }
 
+unsigned char Curl_conn_http_version(struct Curl_easy *data)
+{
+  struct Curl_cfilter *cf;
+  CURLcode result = CURLE_UNKNOWN_OPTION;
+  unsigned char v = 0;
+
+  cf = data->conn ? data->conn->cfilter[FIRSTSOCKET] : NULL;
+  for(; cf; cf = cf->next) {
+    if(cf->cft->flags & CF_TYPE_HTTP) {
+      int value = 0;
+      result = cf->cft->query(cf, data, CF_QUERY_HTTP_VERSION, &value, NULL);
+      if(!result && ((value < 0) || (value > 255)))
+        result = CURLE_FAILED_INIT;
+      else
+        v = (unsigned char)value;
+      break;
+    }
+    if(cf->cft->flags & (CF_TYPE_IP_CONNECT|CF_TYPE_SSL))
+      break;
+  }
+  return result ? 0 : v;
+}
+
 bool Curl_conn_data_pending(struct Curl_easy *data, int sockindex)
 {
   struct Curl_cfilter *cf;
index f6d8221a1c42102f31ae75bb4ce67eec0e49582e..2d5599a90abf82463f5379b9f981e97a90639b8f 100644 (file)
@@ -176,6 +176,7 @@ typedef CURLcode Curl_cft_cntrl(struct Curl_cfilter *cf,
 #define CF_QUERY_STREAM_ERROR       6  /* error code - */
 #define CF_QUERY_NEED_FLUSH         7  /* TRUE/FALSE - */
 #define CF_QUERY_IP_INFO            8  /* TRUE/FALSE struct ip_quadruple */
+#define CF_QUERY_HTTP_VERSION       9  /* number (10/11/20/30)   -  */
 
 /**
  * Query the cfilter for properties. Filters ignorant of a query will
@@ -195,11 +196,13 @@ typedef CURLcode Curl_cft_query(struct Curl_cfilter *cf,
  * CF_TYPE_SSL:        provide SSL/TLS
  * CF_TYPE_MULTIPLEX:  provides multiplexing of easy handles
  * CF_TYPE_PROXY       provides proxying
+ * CF_TYPE_HTTP        implement a version of the HTTP protocol
  */
 #define CF_TYPE_IP_CONNECT  (1 << 0)
 #define CF_TYPE_SSL         (1 << 1)
 #define CF_TYPE_MULTIPLEX   (1 << 2)
 #define CF_TYPE_PROXY       (1 << 3)
+#define CF_TYPE_HTTP        (1 << 4)
 
 /* A connection filter type, e.g. specific implementation. */
 struct Curl_cftype {
@@ -392,6 +395,12 @@ bool Curl_conn_is_ssl(struct connectdata *conn, int sockindex);
  */
 bool Curl_conn_is_multiplex(struct connectdata *conn, int sockindex);
 
+/**
+ * Return the HTTP version used on the FIRSTSOCKET connection filters
+ * or 0 if unknown. Value otherwise is 09, 10, 11, etc.
+ */
+unsigned char Curl_conn_http_version(struct Curl_easy *data);
+
 /**
  * Close the filter chain at `sockindex` for connection `data->conn`.
   * Filters remain in place and may be connected again afterwards.
index f3bacd290a8243c75c2d820994f9042a36364f6b..cef73a578173abb9484f38f45986abdd6f3ec1f9 100644 (file)
@@ -108,9 +108,10 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn);
 static CURLcode http_range(struct Curl_easy *data,
                            Curl_HttpReq httpreq);
 static CURLcode http_req_complete(struct Curl_easy *data,
-                                  struct dynbuf *r, Curl_HttpReq httpreq);
+                                  struct dynbuf *r, int httpversion,
+                                  Curl_HttpReq httpreq);
 static CURLcode http_req_set_reader(struct Curl_easy *data,
-                                    Curl_HttpReq httpreq,
+                                    Curl_HttpReq httpreq, int httpversion,
                                     const char **tep);
 static CURLcode http_size(struct Curl_easy *data);
 static CURLcode http_statusline(struct Curl_easy *data,
@@ -121,8 +122,6 @@ static CURLcode http_useragent(struct Curl_easy *data);
 #ifdef HAVE_LIBZ
 static CURLcode http_transferencode(struct Curl_easy *data);
 #endif
-static bool use_http_1_1plus(const struct Curl_easy *data,
-                             const struct connectdata *conn);
 
 
 /*
@@ -533,7 +532,7 @@ CURLcode Curl_http_auth_act(struct Curl_easy *data)
     else
       data->info.httpauthpicked = data->state.authhost.picked;
     if(data->state.authhost.picked == CURLAUTH_NTLM &&
-       conn->httpversion > 11) {
+       (data->req.httpversion_sent > 11)) {
       infof(data, "Forcing HTTP/1.1 for NTLM");
       connclose(conn, "Force HTTP/1.1 connection");
       data->state.httpwant = CURL_HTTP_VERSION_1_1;
@@ -1217,45 +1216,55 @@ CURLcode Curl_http_done(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-/*
- * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons
- * to avoid it include:
- *
- * - if the user specifically requested HTTP 1.0
- * - if the server we are connected to only supports 1.0
- * - if any server previously contacted to handle this request only supports
- * 1.0.
- */
-static bool use_http_1_1plus(const struct Curl_easy *data,
-                             const struct connectdata *conn)
+/* Determine if we may use HTTP 1.1 for this request. */
+static bool http_may_use_1_1(const struct Curl_easy *data)
 {
-  if((data->state.httpversion == 10) || (conn->httpversion == 10))
+  const struct connectdata *conn = data->conn;
+  /* We have seen a previous response for *this* transfer with 1.0,
+   * on another connection or the same one. */
+  if(data->state.httpversion == 10)
+    return FALSE;
+  /* We have seen a previous response on *this* connection with 1.0. */
+  if(conn->httpversion_seen == 10)
     return FALSE;
+  /* We want 1.0 and have seen no previous response on *this* connection
+     with a higher version (maybe no response at all yet). */
   if((data->state.httpwant == CURL_HTTP_VERSION_1_0) &&
-     (conn->httpversion <= 10))
+     (conn->httpversion_seen <= 10))
     return FALSE;
+  /* We want something newer than 1.0 or have no preferences. */
   return (data->state.httpwant == CURL_HTTP_VERSION_NONE) ||
          (data->state.httpwant >= CURL_HTTP_VERSION_1_1);
 }
 
-static const char *get_http_string(const struct Curl_easy *data,
-                                   const struct connectdata *conn)
+static unsigned char http_request_version(struct Curl_easy *data)
+{
+  unsigned char httpversion = Curl_conn_http_version(data);
+  if(!httpversion) {
+    /* No specific HTTP connection filter installed. */
+    httpversion = http_may_use_1_1(data) ? 11 : 10;
+  }
+  return httpversion;
+}
+
+static const char *get_http_string(int httpversion)
 {
-  if(Curl_conn_is_http3(data, conn, FIRSTSOCKET))
-    return "3";
-  if(Curl_conn_is_http2(data, conn, FIRSTSOCKET))
-    return "2";
-  if(use_http_1_1plus(data, conn))
-    return "1.1";
-
-  return "1.0";
+  switch(httpversion) {
+    case 30:
+      return "3";
+    case 20:
+      return "2";
+    case 11:
+      return "1.1";
+    default:
+      return "1.0";
+  }
 }
 
 CURLcode Curl_add_custom_headers(struct Curl_easy *data,
-                                 bool is_connect,
+                                 bool is_connect, int httpversion,
                                  struct dynbuf *req)
 {
-  struct connectdata *conn = data->conn;
   char *ptr;
   struct curl_slist *h[2];
   struct curl_slist *headers;
@@ -1268,7 +1277,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
   if(is_connect)
     proxy = HEADER_CONNECT;
   else
-    proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy ?
+    proxy = data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy ?
       HEADER_PROXY : HEADER_SERVER;
 
   switch(proxy) {
@@ -1368,7 +1377,7 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data,
                      Connection: */
                   checkprefix("Connection:", compare))
             ;
-          else if((conn->httpversion >= 20) &&
+          else if((httpversion >= 20) &&
                   checkprefix("Transfer-Encoding:", compare))
             /* HTTP/2 does not support chunked requests */
             ;
@@ -1900,7 +1909,7 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq)
 }
 
 static CURLcode http_req_set_reader(struct Curl_easy *data,
-                                    Curl_HttpReq httpreq,
+                                    Curl_HttpReq httpreq, int httpversion,
                                     const char **tep)
 {
   CURLcode result = CURLE_OK;
@@ -1920,12 +1929,10 @@ static CURLcode http_req_set_reader(struct Curl_easy *data,
     data->req.upload_chunky =
       Curl_compareheader(ptr,
                          STRCONST("Transfer-Encoding:"), STRCONST("chunked"));
-    if(data->req.upload_chunky &&
-       use_http_1_1plus(data, data->conn) &&
-       (data->conn->httpversion >= 20)) {
-       infof(data, "suppressing chunked transfer encoding on connection "
-             "using HTTP version 2 or higher");
-       data->req.upload_chunky = FALSE;
+    if(data->req.upload_chunky && (httpversion >= 20)) {
+      infof(data, "suppressing chunked transfer encoding on connection "
+            "using HTTP version 2 or higher");
+      data->req.upload_chunky = FALSE;
     }
   }
   else {
@@ -1933,10 +1940,10 @@ static CURLcode http_req_set_reader(struct Curl_easy *data,
 
     if(req_clen < 0) {
       /* indeterminate request content length */
-      if(use_http_1_1plus(data, data->conn)) {
+      if(httpversion > 10) {
         /* On HTTP/1.1, enable chunked, on HTTP/2 and later we do not
          * need it */
-        data->req.upload_chunky = (data->conn->httpversion < 20);
+        data->req.upload_chunky = (httpversion < 20);
       }
       else {
         failf(data, "Chunky upload is not supported by HTTP 1.0");
@@ -1955,7 +1962,7 @@ static CURLcode http_req_set_reader(struct Curl_easy *data,
 }
 
 static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
-                          bool *announced_exp100)
+                          int httpversion, bool *announced_exp100)
 {
   CURLcode result;
   char *ptr;
@@ -1974,9 +1981,7 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
     *announced_exp100 =
       Curl_compareheader(ptr, STRCONST("Expect:"), STRCONST("100-continue"));
   }
-  else if(!data->state.disableexpect &&
-          use_http_1_1plus(data, data->conn) &&
-          (data->conn->httpversion < 20)) {
+  else if(!data->state.disableexpect && (httpversion == 11)) {
     /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
        Expect: 100-continue to the headers which actually speeds up post
        operations (as there is one packet coming back from the web server) */
@@ -1992,7 +1997,8 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r,
 }
 
 static CURLcode http_req_complete(struct Curl_easy *data,
-                                  struct dynbuf *r, Curl_HttpReq httpreq)
+                                  struct dynbuf *r, int httpversion,
+                                  Curl_HttpReq httpreq)
 {
   CURLcode result = CURLE_OK;
   curl_off_t req_clen;
@@ -2052,7 +2058,7 @@ static CURLcode http_req_complete(struct Curl_easy *data,
           goto out;
       }
     }
-    result = addexpect(data, r, &announced_exp100);
+    result = addexpect(data, r, httpversion, &announced_exp100);
     if(result)
       goto out;
     break;
@@ -2326,6 +2332,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   struct dynbuf req;
   char *altused = NULL;
   const char *p_accept;      /* Accept: string */
+  unsigned char httpversion;
 
   /* 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
@@ -2334,29 +2341,29 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
 
   switch(conn->alpn) {
   case CURL_HTTP_VERSION_3:
-    DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET));
+    DEBUGASSERT(Curl_conn_http_version(data) == 30);
     break;
   case CURL_HTTP_VERSION_2:
 #ifndef CURL_DISABLE_PROXY
-    if(!Curl_conn_is_http2(data, conn, FIRSTSOCKET) &&
+    if((Curl_conn_http_version(data) != 20) &&
        conn->bits.proxy && !conn->bits.tunnel_proxy
       ) {
-      result = Curl_http2_switch(data, conn, FIRSTSOCKET);
+      result = Curl_http2_switch(data);
       if(result)
         goto fail;
     }
     else
 #endif
-      DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
+      DEBUGASSERT(Curl_conn_http_version(data) == 20);
     break;
   case CURL_HTTP_VERSION_1_1:
     /* continue with HTTP/1.x when explicitly requested */
     break;
   default:
     /* Check if user wants to use HTTP/2 with clear TCP */
-    if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) {
+    if(Curl_http2_may_switch(data)) {
       DEBUGF(infof(data, "HTTP/2 over clean TCP"));
-      result = Curl_http2_switch(data, conn, FIRSTSOCKET);
+      result = Curl_http2_switch(data);
       if(result)
         goto fail;
     }
@@ -2420,7 +2427,10 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     goto fail;
 #endif
 
-  result = http_req_set_reader(data, httpreq, &te);
+  httpversion = http_request_version(data);
+  httpstring = get_http_string(httpversion);
+
+  result = http_req_set_reader(data, httpreq, httpversion, &te);
   if(result)
     goto fail;
 
@@ -2431,8 +2441,6 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(result)
     goto fail;
 
-  httpstring = get_http_string(data, conn);
-
   /* initialize a dynamic send-buffer */
   Curl_dyn_init(&req, DYN_HTTP_REQUEST);
 
@@ -2526,8 +2534,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
     goto fail;
   }
 
-  if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) &&
-     conn->httpversion < 20 &&
+  if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) &&
      (data->state.httpwant == CURL_HTTP_VERSION_2)) {
     /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done
        over SSL */
@@ -2546,19 +2553,19 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done)
   if(!result)
     result = Curl_add_timecondition(data, &req);
   if(!result)
-    result = Curl_add_custom_headers(data, FALSE, &req);
+    result = Curl_add_custom_headers(data, FALSE, httpversion, &req);
 
   if(!result) {
     /* req_send takes ownership of the 'req' memory on success */
-    result = http_req_complete(data, &req, httpreq);
+    result = http_req_complete(data, &req, httpversion, httpreq);
     if(!result)
-      result = Curl_req_send(data, &req);
+      result = Curl_req_send(data, &req, httpversion);
   }
   Curl_dyn_free(&req);
   if(result)
     goto fail;
 
-  if((conn->httpversion >= 20) && data->req.upload_chunky)
+  if((httpversion >= 20) && data->req.upload_chunky)
     /* upload_chunky was set above to set up the request in a chunky fashion,
        but is disabled here again to avoid that the chunked encoded version is
        actually used when sending the request body over h2 */
@@ -2680,8 +2687,8 @@ static CURLcode http_header(struct Curl_easy *data,
         )) ? HD_VAL(hd, hdlen, "Alt-Svc:") : NULL;
     if(v) {
       /* the ALPN of the current request */
-      enum alpnid id = (conn->httpversion == 30) ? ALPN_h3 :
-                         (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
+      enum alpnid id = (k->httpversion == 30) ? ALPN_h3 :
+                         (k->httpversion == 20) ? ALPN_h2 : ALPN_h1;
       return Curl_altsvc_parse(data, data->asi, v, id, conn->host.name,
                                curlx_uitous((unsigned int)conn->remote_port));
     }
@@ -2753,7 +2760,7 @@ static CURLcode http_header(struct Curl_easy *data,
       streamclose(conn, "Connection: close used");
       return CURLE_OK;
     }
-    if((conn->httpversion == 10) &&
+    if((k->httpversion == 10) &&
        HD_IS_AND_SAYS(hd, hdlen, "Connection:", "keep-alive")) {
       /*
        * An HTTP/1.0 reply with the 'Connection: keep-alive' line
@@ -2843,7 +2850,7 @@ static CURLcode http_header(struct Curl_easy *data,
 #ifndef CURL_DISABLE_PROXY
     v = HD_VAL(hd, hdlen, "Proxy-Connection:");
     if(v) {
-      if((conn->httpversion == 10) && conn->bits.httpproxy &&
+      if((k->httpversion == 10) && conn->bits.httpproxy &&
          HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "keep-alive")) {
         /*
          * When an HTTP/1.0 reply comes when using a proxy, the
@@ -2854,7 +2861,7 @@ static CURLcode http_header(struct Curl_easy *data,
         connkeep(conn, "Proxy-Connection keep-alive"); /* do not close */
         infof(data, "HTTP/1.0 proxy connection set to keep alive");
       }
-      else if((conn->httpversion == 11) && conn->bits.httpproxy &&
+      else if((k->httpversion == 11) && conn->bits.httpproxy &&
               HD_IS_AND_SAYS(hd, hdlen, "Proxy-Connection:", "close")) {
         /*
          * We get an HTTP/1.1 response from a proxy and it says it will
@@ -3043,11 +3050,11 @@ static CURLcode http_statusline(struct Curl_easy *data,
   case 30:
 #endif
     /* no major version switch mid-connection */
-    if(conn->httpversion &&
-       (k->httpversion/10 != conn->httpversion/10)) {
+    if(k->httpversion_sent &&
+       (k->httpversion/10 != k->httpversion_sent/10)) {
       failf(data, "Version mismatch (from HTTP/%u to HTTP/%u)",
-            conn->httpversion/10, k->httpversion/10);
-      return CURLE_UNSUPPORTED_PROTOCOL;
+            k->httpversion_sent/10, k->httpversion/10);
+      return CURLE_WEIRD_SERVER_REPLY;
     }
     break;
   default:
@@ -3058,7 +3065,7 @@ static CURLcode http_statusline(struct Curl_easy *data,
 
   data->info.httpcode = k->httpcode;
   data->info.httpversion = k->httpversion;
-  conn->httpversion = (unsigned char)k->httpversion;
+  conn->httpversion_seen = (unsigned char)k->httpversion;
 
   if(!data->state.httpversion || data->state.httpversion > k->httpversion)
     /* store the lowest server version we encounter */
@@ -3238,7 +3245,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
 
   if(k->upgr101 == UPGR101_RECEIVED) {
     /* supposedly upgraded to http2 now */
-    if(conn->httpversion != 20)
+    if(data->req.httpversion != 20)
       infof(data, "Lying server, not serving HTTP/2");
   }
 
@@ -3274,8 +3281,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
       break;
     case 101:
       /* Switching Protocols only allowed from HTTP/1.1 */
-
-      if(conn->httpversion != 11) {
+      if(k->httpversion_sent != 11) {
         /* invalid for other HTTP versions */
         failf(data, "unexpected 101 response code");
         result = CURLE_WEIRD_SERVER_REPLY;
@@ -3289,6 +3295,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
         /* We expect more response from HTTP/2 later */
         k->header = TRUE;
         k->headerline = 0; /* restart the header line counter */
+        k->httpversion_sent = 20; /* It's a HTTP/2 request now */
         /* Any remaining `buf` bytes are already HTTP/2 and passed to
          * be processed. */
         result = Curl_http2_upgrade(data, conn, FIRSTSOCKET, buf, blen);
@@ -3337,7 +3344,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
   }
 
   if((k->size == -1) && !k->chunk && !conn->bits.close &&
-     (conn->httpversion == 11) &&
+     (k->httpversion == 11) &&
      !(conn->handler->protocol & CURLPROTO_RTSP) &&
      data->state.httpreq != HTTPREQ_HEAD) {
     /* On HTTP 1.1, when connection is not to get closed, but no
@@ -3486,9 +3493,7 @@ static CURLcode http_on_response(struct Curl_easy *data,
      like to call http2_handle_stream_close to properly close a
      stream. In order to do this, we keep reading until we
      close the stream. */
-  if(0 == k->maxdownload
-     && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
-     && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
+  if((0 == k->maxdownload) && (k->httpversion_sent < 20))
     k->download_done = TRUE;
 
   /* final response without error, prepare to receive the body */
@@ -3573,7 +3578,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
           p++;
           if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) {
             if(ISBLANK(p[2])) {
-              k->httpversion = 10 + (p[1] - '0');
+              k->httpversion = (unsigned char)(10 + (p[1] - '0'));
               p += 3;
               if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
                 k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
@@ -3593,7 +3598,7 @@ static CURLcode http_rw_hd(struct Curl_easy *data,
         case '3':
           if(!ISBLANK(p[1]))
             break;
-          k->httpversion = (*p - '0') * 10;
+          k->httpversion = (unsigned char)((*p - '0') * 10);
           p += 2;
           if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
             k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
@@ -3723,10 +3728,11 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
                            Curl_dyn_len(&data->state.headerb));
 
         if(st == STATUS_BAD) {
-          /* this is not the beginning of a protocol first header line */
+          /* this is not the beginning of a protocol first header line.
+           * Cannot be 0.9 if version was detected or connection was reused. */
           k->header = FALSE;
           streamclose(conn, "bad HTTP: No end-of-message indicator");
-          if(conn->httpversion >= 10) {
+          if((k->httpversion >= 10) || conn->bits.reuse) {
             failf(data, "Invalid status line");
             return CURLE_WEIRD_SERVER_REPLY;
           }
@@ -3761,8 +3767,9 @@ static CURLcode http_parse_headers(struct Curl_easy *data,
                                        Curl_dyn_len(&data->state.headerb));
       if(st == STATUS_BAD) {
         streamclose(conn, "bad HTTP: No end-of-message indicator");
-        /* this is not the beginning of a protocol first header line */
-        if(conn->httpversion >= 10) {
+        /* this is not the beginning of a protocol first header line.
+         * Cannot be 0.9 if version was detected or connection was reused. */
+        if((k->httpversion >= 10) || conn->bits.reuse) {
           failf(data, "Invalid status line");
           return CURLE_WEIRD_SERVER_REPLY;
         }
index 691c98e9c527a3133720ae5cabd6793bf0a71ea3..d12813a6ff4e7365977ee27dfe5e6767e714229c 100644 (file)
@@ -76,7 +76,7 @@ char *Curl_checkProxyheaders(struct Curl_easy *data,
 
 CURLcode Curl_add_timecondition(struct Curl_easy *data, struct dynbuf *req);
 CURLcode Curl_add_custom_headers(struct Curl_easy *data, bool is_connect,
-                                 struct dynbuf *req);
+                                 int httpversion, struct dynbuf *req);
 CURLcode Curl_dynhds_add_custom(struct Curl_easy *data, bool is_connect,
                                 struct dynhds *hds);
 
index 840e579d72ad3c8671458b30854c61659cb604ff..7797197819f4b1a7087575e38b1e7587b4d39fc5 100644 (file)
@@ -2723,6 +2723,9 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
     }
     break;
   }
+  case CF_QUERY_HTTP_VERSION:
+    *pres1 = 20;
+    return CURLE_OK;
   default:
     break;
   }
@@ -2733,7 +2736,7 @@ static CURLcode cf_h2_query(struct Curl_cfilter *cf,
 
 struct Curl_cftype Curl_cft_nghttp2 = {
   "HTTP/2",
-  CF_TYPE_MULTIPLEX,
+  CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
   CURL_LOG_LVL_NONE,
   cf_h2_destroy,
   cf_h2_connect,
@@ -2807,35 +2810,12 @@ out:
   return result;
 }
 
-static bool cf_is_http2(struct Curl_cfilter *cf,
-                        const struct Curl_easy *data)
-{
-  (void)data;
-  for(; cf; cf = cf->next) {
-    if(cf->cft == &Curl_cft_nghttp2)
-      return TRUE;
-    if(cf->cft->flags & CF_TYPE_IP_CONNECT)
-      return FALSE;
-  }
-  return FALSE;
-}
-
-bool Curl_conn_is_http2(const struct Curl_easy *data,
-                        const struct connectdata *conn,
-                        int sockindex)
-{
-  return conn ? cf_is_http2(conn->cfilter[sockindex], data) : FALSE;
-}
-
-bool Curl_http2_may_switch(struct Curl_easy *data,
-                           struct connectdata *conn,
-                           int sockindex)
+bool Curl_http2_may_switch(struct Curl_easy *data)
 {
-  (void)sockindex;
-  if(!Curl_conn_is_http2(data, conn, sockindex) &&
+  if(Curl_conn_http_version(data) < 20 &&
      data->state.httpwant == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) {
 #ifndef CURL_DISABLE_PROXY
-    if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
+    if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) {
       /* We do not support HTTP/2 proxies yet. Also it is debatable
          whether or not this setting should apply to HTTP/2 proxies. */
       infof(data, "Ignoring HTTP/2 prior knowledge due to proxy");
@@ -2847,21 +2827,19 @@ bool Curl_http2_may_switch(struct Curl_easy *data,
   return FALSE;
 }
 
-CURLcode Curl_http2_switch(struct Curl_easy *data,
-                           struct connectdata *conn, int sockindex)
+CURLcode Curl_http2_switch(struct Curl_easy *data)
 {
   struct Curl_cfilter *cf;
   CURLcode result;
 
-  DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
+  DEBUGASSERT(Curl_conn_http_version(data) < 20);
 
-  result = http2_cfilter_add(&cf, data, conn, sockindex, FALSE);
+  result = http2_cfilter_add(&cf, data, data->conn, FIRSTSOCKET, FALSE);
   if(result)
     return result;
   CURL_TRC_CF(data, cf, "switching connection to HTTP/2");
 
-  conn->httpversion = 20; /* we know we are on HTTP/2 now */
-  conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
+  data->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   Curl_multi_connchanged(data->multi);
 
   if(cf->next) {
@@ -2876,14 +2854,13 @@ CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data)
   struct Curl_cfilter *cf_h2;
   CURLcode result;
 
-  DEBUGASSERT(!cf_is_http2(cf, data));
+  DEBUGASSERT(Curl_conn_http_version(data) < 20);
 
   result = http2_cfilter_insert_after(cf, data, FALSE);
   if(result)
     return result;
 
   cf_h2 = cf->next;
-  cf->conn->httpversion = 20; /* we know we are on HTTP/2 now */
   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   Curl_multi_connchanged(data->multi);
 
@@ -2902,7 +2879,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
   struct cf_h2_ctx *ctx;
   CURLcode result;
 
-  DEBUGASSERT(!Curl_conn_is_http2(data, conn, sockindex));
+  DEBUGASSERT(Curl_conn_http_version(data) <  20);
   DEBUGASSERT(data->req.upgr101 == UPGR101_RECEIVED);
 
   result = http2_cfilter_add(&cf, data, conn, sockindex, TRUE);
@@ -2935,7 +2912,6 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
           " after upgrade: len=%zu", nread);
   }
 
-  conn->httpversion = 20; /* we know we are on HTTP/2 now */
   conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
   Curl_multi_connchanged(data->multi);
 
@@ -2950,7 +2926,7 @@ CURLcode Curl_http2_upgrade(struct Curl_easy *data,
    CURLE_HTTP2_STREAM error! */
 bool Curl_h2_http_1_1_error(struct Curl_easy *data)
 {
-  if(Curl_conn_is_http2(data, data->conn, FIRSTSOCKET)) {
+  if(Curl_conn_http_version(data) == 20) {
     int err = Curl_conn_get_stream_error(data, data->conn, FIRSTSOCKET);
     return err == NGHTTP2_HTTP_1_1_REQUIRED;
   }
index dbb1784661e7448debeb5158bd822efcf73b97f6..93cc2d44f2592c62a99bb2b3e94a30f0641b0dc8 100644 (file)
@@ -44,15 +44,9 @@ CURLcode Curl_http2_request_upgrade(struct dynbuf *req,
 /* returns true if the HTTP/2 stream error was HTTP_1_1_REQUIRED */
 bool Curl_h2_http_1_1_error(struct Curl_easy *data);
 
-bool Curl_conn_is_http2(const struct Curl_easy *data,
-                        const struct connectdata *conn,
-                        int sockindex);
-bool Curl_http2_may_switch(struct Curl_easy *data,
-                           struct connectdata *conn,
-                           int sockindex);
+bool Curl_http2_may_switch(struct Curl_easy *data);
 
-CURLcode Curl_http2_switch(struct Curl_easy *data,
-                           struct connectdata *conn, int sockindex);
+CURLcode Curl_http2_switch(struct Curl_easy *data);
 
 CURLcode Curl_http2_switch_at(struct Curl_cfilter *cf, struct Curl_easy *data);
 
@@ -69,12 +63,10 @@ extern struct Curl_cftype Curl_cft_nghttp2;
 
 #else /* USE_NGHTTP2 */
 
-#define Curl_cf_is_http2(a,b) FALSE
-#define Curl_conn_is_http2(a,b,c) FALSE
-#define Curl_http2_may_switch(a,b,c) FALSE
+#define Curl_http2_may_switch(a) FALSE
 
 #define Curl_http2_request_upgrade(x,y) CURLE_UNSUPPORTED_PROTOCOL
-#define Curl_http2_switch(a,b,c)        CURLE_UNSUPPORTED_PROTOCOL
+#define Curl_http2_switch(a)            CURLE_UNSUPPORTED_PROTOCOL
 #define Curl_http2_upgrade(a,b,c,d,e)   CURLE_UNSUPPORTED_PROTOCOL
 #define Curl_h2_http_1_1_error(x) 0
 #endif
index 154dc31963ed2dd7eec0cd5d639b4c9f896e8c2e..3a2e9f2b05f484180d8fed52731bd4d8a256a555 100644 (file)
@@ -56,7 +56,7 @@ static bool hd_name_eq(const char *n1, size_t n1len,
 }
 
 static CURLcode dynhds_add_custom(struct Curl_easy *data,
-                                  bool is_connect,
+                                  bool is_connect, int httpversion,
                                   struct dynhds *hds)
 {
   struct connectdata *conn = data->conn;
@@ -169,9 +169,9 @@ static CURLcode dynhds_add_custom(struct Curl_easy *data,
                  Connection: */
               hd_name_eq(name, namelen, STRCONST("Connection:")))
         ;
-      else if((conn->httpversion >= 20) &&
+      else if((httpversion >= 20) &&
               hd_name_eq(name, namelen, STRCONST("Transfer-Encoding:")))
-        /* HTTP/2 does not support chunked requests */
+        /* HTTP/2 and HTTP/3 do not support chunked requests */
         ;
       else if((hd_name_eq(name, namelen, STRCONST("Authorization:")) ||
                hd_name_eq(name, namelen, STRCONST("Cookie:"))) &&
@@ -221,11 +221,18 @@ CURLcode Curl_http_proxy_get_destination(struct Curl_cfilter *cf,
   return CURLE_OK;
 }
 
+struct cf_proxy_ctx {
+  /* the protocol specific sub-filter we install during connect */
+  struct Curl_cfilter *cf_protocol;
+  int httpversion; /* HTTP version used to CONNECT */
+};
+
 CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
                                         struct Curl_cfilter *cf,
                                         struct Curl_easy *data,
                                         int http_version_major)
 {
+  struct cf_proxy_ctx *ctx = cf->ctx;
   const char *hostname = NULL;
   char *authority = NULL;
   int port;
@@ -286,7 +293,7 @@ CURLcode Curl_http_proxy_create_CONNECT(struct httpreq **preq,
       goto out;
   }
 
-  result = dynhds_add_custom(data, TRUE, &req->headers);
+  result = dynhds_add_custom(data, TRUE, ctx->httpversion, &req->headers);
 
 out:
   if(result && req) {
@@ -298,12 +305,6 @@ out:
   return result;
 }
 
-
-struct cf_proxy_ctx {
-  /* the protocol specific sub-filter we install during connect */
-  struct Curl_cfilter *cf_protocol;
-};
-
 static CURLcode http_proxy_cf_connect(struct Curl_cfilter *cf,
                                       struct Curl_easy *data,
                                       bool blocking, bool *done)
@@ -325,6 +326,7 @@ connect_sub:
   *done = FALSE;
   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;
 
@@ -340,6 +342,7 @@ connect_sub:
       if(result)
         goto out;
       cf_protocol = cf->next;
+      httpversion = (alpn == CURL_HTTP_VERSION_1_0) ? 10 : 11;
       break;
 #ifdef USE_NGHTTP2
     case CURL_HTTP_VERSION_2:
@@ -349,6 +352,7 @@ connect_sub:
       if(result)
         goto out;
       cf_protocol = cf->next;
+      httpversion = 20;
       break;
 #endif
     default:
@@ -358,6 +362,7 @@ connect_sub:
     }
 
     ctx->cf_protocol = cf_protocol;
+    ctx->httpversion = httpversion;
     /* after we installed the filter "below" us, we call connect
      * on out sub-chain again.
      */
index b1298a0387c7881e8e96a16bd8c92669d4de8421..d5f04e9f1d6f692189d55fc358a457975c43ea59 100644 (file)
@@ -66,7 +66,8 @@ CURLcode Curl_req_soft_reset(struct SingleRequest *req,
   req->headerbytecount = 0;
   req->allheadercount =  0;
   req->deductheadercount = 0;
-
+  req->httpversion_sent = 0;
+  req->httpversion = 0;
   result = Curl_client_start(data);
   if(result)
     return result;
@@ -373,7 +374,8 @@ static CURLcode req_send_buffer_add(struct Curl_easy *data,
   return CURLE_OK;
 }
 
-CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
+CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req,
+                       unsigned char httpversion)
 {
   CURLcode result;
   const char *buf;
@@ -382,6 +384,7 @@ CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *req)
   if(!data || !data->conn)
     return CURLE_FAILED_INIT;
 
+  data->req.httpversion_sent = httpversion;
   buf = Curl_dyn_ptr(req);
   blen = Curl_dyn_len(req);
   if(!Curl_creader_total_length(data)) {
index b760c2634da8d9ea9f4a6b837daefcedca41bf86..4c77be962f3e06ee19dd0eeb5e86a90f34857497 100644 (file)
@@ -81,10 +81,11 @@ struct SingleRequest {
                                    first one */
   curl_off_t offset;            /* possible resume offset read from the
                                    Content-Range: header */
-  int httpversion;              /* Version in response (09, 10, 11, etc.) */
   int httpcode;                 /* error code from the 'HTTP/1.? XXX' or
                                    'RTSP/1.? XXX' line */
   int keepon;
+  unsigned char httpversion_sent; /* Version in request (09, 10, 11, etc.) */
+  unsigned char httpversion;    /* Version in response (09, 10, 11, etc.) */
   enum upgrade101 upgr101;      /* 101 upgrade state */
 
   /* Client Writer stack, handles transfer- and content-encodings, protocol
@@ -199,9 +200,11 @@ void Curl_req_hard_reset(struct SingleRequest *req, struct Curl_easy *data);
  * bytes are really send.
  * @param data      the transfer making the request
  * @param buf       the complete header bytes, no body
+ * @param httpversion version used in request (09, 10, 11, etc.)
  * @return CURLE_OK (on blocking with *pnwritten == 0) or error.
  */
-CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf);
+CURLcode Curl_req_send(struct Curl_easy *data, struct dynbuf *buf,
+                       unsigned char httpversion);
 
 /**
  * TRUE iff the request has sent all request headers and data.
index f6f4a33f1ce5010ecb4e690fe49c1a9628863b42..e26586cace851d6446255fdaba076652892c14de 100644 (file)
@@ -230,6 +230,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   Curl_RtspReq rtspreq = data->set.rtspreq;
   struct RTSP *rtsp = data->req.p.rtsp;
   struct dynbuf req_buffer;
+  unsigned char httpversion = 11; /* RTSP is close to HTTP/1.1, sort of... */
 
   const char *p_request = NULL;
   const char *p_session_id = NULL;
@@ -499,7 +500,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
       goto out;
   }
 
-  result = Curl_add_custom_headers(data, FALSE, &req_buffer);
+  result = Curl_add_custom_headers(data, FALSE, httpversion, &req_buffer);
   if(result)
     goto out;
 
@@ -585,7 +586,7 @@ static CURLcode rtsp_do(struct Curl_easy *data, bool *done)
   Curl_xfer_setup1(data, CURL_XFER_SENDRECV, -1, TRUE);
 
   /* issue the request */
-  result = Curl_req_send(data, &req_buffer);
+  result = Curl_req_send(data, &req_buffer, httpversion);
   if(result) {
     failf(data, "Failed sending RTSP request");
     goto out;
index 4e0454fe8364451311d75b953d506d17d015f79e..83e894750fdb0a6ba77a461f9c6cc17139724c7e 100644 (file)
@@ -782,7 +782,7 @@ static void xfer_setup(
   DEBUGASSERT((writesockindex <= 1) && (writesockindex >= -1));
   DEBUGASSERT(!shutdown || (sockindex == -1) || (writesockindex == -1));
 
-  if(conn->bits.multiplex || conn->httpversion >= 20 || want_send) {
+  if(Curl_conn_is_multiplex(conn, FIRSTSOCKET) || want_send) {
     /* when multiplexing, the read/write sockets need to be the same! */
     conn->sockfd = sockindex == -1 ?
       ((writesockindex == -1 ? CURL_SOCKET_BAD : conn->sock[writesockindex])) :
index 1461674fa74d9c283e50e694fc1cab63cfd21993..d6747c0e74c38c87331fd641e4f529c47c8a3c8d 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -1002,7 +1002,7 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
   if(match->may_multiplex &&
      (data->state.httpwant == CURL_HTTP_VERSION_2_0) &&
      (needle->handler->protocol & CURLPROTO_HTTP) &&
-     !conn->httpversion) {
+     !conn->httpversion_seen) {
     if(data->set.pipewait) {
       infof(data, "Server upgrade does not support multiplex yet, wait");
       match->found = NULL;
@@ -1036,17 +1036,18 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
    * so we take any existing connection. */
   if((needle->handler->protocol & PROTO_FAMILY_HTTP) &&
      (data->state.httpwant != CURL_HTTP_VERSION_2TLS)) {
-    if((conn->httpversion >= 20) &&
+    unsigned char httpversion = Curl_conn_http_version(data);
+    if((httpversion >= 20) &&
        (data->state.httpwant < CURL_HTTP_VERSION_2_0)) {
       DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
              " with httpversion=%d, we want a version less than h2",
-             conn->connection_id, conn->httpversion));
+             conn->connection_id, httpversion));
     }
-    if((conn->httpversion >= 30) &&
+    if((httpversion >= 30) &&
        (data->state.httpwant < CURL_HTTP_VERSION_3)) {
       DEBUGF(infof(data, "nor reusing conn #%" CURL_FORMAT_CURL_OFF_T
              " with httpversion=%d, we want a version less than h3",
-             conn->connection_id, conn->httpversion));
+             conn->connection_id, httpversion));
       return FALSE;
     }
   }
@@ -3135,14 +3136,14 @@ static CURLcode parse_connect_to_slist(struct Curl_easy *data,
         /* protocol version switch */
         switch(as->dst.alpnid) {
         case ALPN_h1:
-          conn->httpversion = 11;
+          data->state.httpwant = CURL_HTTP_VERSION_1_1;
           break;
         case ALPN_h2:
-          conn->httpversion = 20;
+          data->state.httpwant = CURL_HTTP_VERSION_2_0;
           break;
         case ALPN_h3:
           conn->transport = TRNSPRT_QUIC;
-          conn->httpversion = 30;
+          data->state.httpwant = CURL_HTTP_VERSION_3;
           break;
         default: /* should not be possible */
           break;
index 224167f7f04b76cf89cae096b48371e1bf1001f5..b18594d1377d0697c4129e090646d1e8d77c4691 100644 (file)
@@ -950,7 +950,9 @@ struct connectdata {
 #endif
   unsigned char transport; /* one of the TRNSPRT_* defines */
   unsigned char ip_version; /* copied from the Curl_easy at creation time */
-  unsigned char httpversion; /* the HTTP version*10 reported by the server */
+  /* HTTP version last responded with by the server.
+   * 0 at start, then one of 09, 10, 11, etc. */
+  unsigned char httpversion_seen;
   unsigned char connect_only;
   unsigned char gssapi_delegation; /* inherited from set.gssapi_delegation */
 };
index dc43766a81954f5cb621a71e8cb9fe26127cb191..9e7cfbea4fce4bf41791c5000a95d63d7cca81d6 100644 (file)
@@ -918,7 +918,6 @@ static CURLcode cf_msh3_connect(struct Curl_cfilter *cf,
     if(ctx->handshake_succeeded) {
       CURL_TRC_CF(data, cf, "handshake succeeded");
       cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
-      cf->conn->httpversion = 30;
       cf->connected = TRUE;
       cf->conn->alpn = CURL_HTTP_VERSION_3;
       *done = TRUE;
@@ -1025,6 +1024,9 @@ static CURLcode cf_msh3_query(struct Curl_cfilter *cf,
       *when = ctx->handshake_at;
     return CURLE_OK;
   }
+  case CF_QUERY_HTTP_VERSION:
+    *pres1 = 30;
+    return CURLE_OK;
   default:
     break;
   }
@@ -1047,7 +1049,7 @@ static bool cf_msh3_conn_is_alive(struct Curl_cfilter *cf,
 
 struct Curl_cftype Curl_cft_http3 = {
   "HTTP/3",
-  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
   0,
   cf_msh3_destroy,
   cf_msh3_connect,
index 2f0a5c0ec58c112a8b357c553e5698ade013a70b..afe3b9d6b42f077463c1d0ee181e2cfdcf323321 100644 (file)
@@ -470,7 +470,6 @@ static int cf_ngtcp2_handshake_completed(ngtcp2_conn *tconn, void *user_data)
   ctx->handshake_at = Curl_now();
   ctx->tls_handshake_complete = TRUE;
   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
-  cf->conn->httpversion = 30;
 
   ctx->tls_vrfy_result = Curl_vquic_tls_verify_peer(&ctx->tls, cf,
                                                     data, &ctx->peer);
@@ -2594,6 +2593,9 @@ static CURLcode cf_ngtcp2_query(struct Curl_cfilter *cf,
       *when = ctx->handshake_at;
     return CURLE_OK;
   }
+  case CF_QUERY_HTTP_VERSION:
+    *pres1 = 30;
+    return CURLE_OK;
   default:
     break;
   }
@@ -2656,7 +2658,7 @@ out:
 
 struct Curl_cftype Curl_cft_http3 = {
   "HTTP/3",
-  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
   0,
   cf_ngtcp2_destroy,
   cf_ngtcp2_connect,
index bd9d21f09f744bd298437d22af77ab3ac5d640ad..1b78497f165aa111d04ae965a259ce495715f9dc 100644 (file)
@@ -570,7 +570,6 @@ static CURLcode cf_osslq_verify_peer(struct Curl_cfilter *cf,
   struct cf_osslq_ctx *ctx = cf->ctx;
 
   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
-  cf->conn->httpversion = 30;
 
   return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
 }
@@ -2359,6 +2358,9 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
       *when = ctx->handshake_at;
     return CURLE_OK;
   }
+  case CF_QUERY_HTTP_VERSION:
+    *pres1 = 30;
+    return CURLE_OK;
   default:
     break;
   }
@@ -2369,7 +2371,7 @@ static CURLcode cf_osslq_query(struct Curl_cfilter *cf,
 
 struct Curl_cftype Curl_cft_http3 = {
   "HTTP/3",
-  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
   0,
   cf_osslq_destroy,
   cf_osslq_connect,
index 59be1c9ec3276189aba38f1917f7c71d0c136a01..ec0dccecfd9ec9ff61da0cd8674f92132dc69e8c 100644 (file)
@@ -1372,7 +1372,6 @@ static CURLcode cf_quiche_verify_peer(struct Curl_cfilter *cf,
   struct cf_quiche_ctx *ctx = cf->ctx;
 
   cf->conn->bits.multiplex = TRUE; /* at least potentially multiplexed */
-  cf->conn->httpversion = 30;
 
   return Curl_vquic_tls_verify_peer(&ctx->tls, cf, data, &ctx->peer);
 }
@@ -1566,6 +1565,9 @@ static CURLcode cf_quiche_query(struct Curl_cfilter *cf,
       *when = ctx->handshake_at;
     return CURLE_OK;
   }
+  case CF_QUERY_HTTP_VERSION:
+    *pres1 = 30;
+    return CURLE_OK;
   default:
     break;
   }
@@ -1613,7 +1615,7 @@ static bool cf_quiche_conn_is_alive(struct Curl_cfilter *cf,
 
 struct Curl_cftype Curl_cft_http3 = {
   "HTTP/3",
-  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX,
+  CF_TYPE_IP_CONNECT | CF_TYPE_SSL | CF_TYPE_MULTIPLEX | CF_TYPE_HTTP,
   0,
   cf_quiche_destroy,
   cf_quiche_connect,
index c0d6e2f3eb4c6d99ca726666176ce6efdc7477fc..9ac351861267271c5183b93b1d041a6ad1dbfddf 100644 (file)
@@ -689,24 +689,6 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
 #endif
 }
 
-bool Curl_conn_is_http3(const struct Curl_easy *data,
-                        const struct connectdata *conn,
-                        int sockindex)
-{
-#if defined(USE_NGTCP2) && defined(USE_NGHTTP3)
-  return Curl_conn_is_ngtcp2(data, conn, sockindex);
-#elif defined(USE_OPENSSL_QUIC) && defined(USE_NGHTTP3)
-  return Curl_conn_is_osslq(data, conn, sockindex);
-#elif defined(USE_QUICHE)
-  return Curl_conn_is_quiche(data, conn, sockindex);
-#elif defined(USE_MSH3)
-  return Curl_conn_is_msh3(data, conn, sockindex);
-#else
-  return (conn->handler->protocol & PROTO_FAMILY_HTTP) &&
-         (conn->httpversion == 30);
-#endif
-}
-
 CURLcode Curl_conn_may_http3(struct Curl_easy *data,
                              const struct connectdata *conn)
 {
index c1ca1df6aa0c1a6376c8404d2798a9a9321728cc..1cd3e258f42be25a48ef61cfb8e25f34c52f4a9d 100644 (file)
@@ -46,16 +46,8 @@ CURLcode Curl_cf_quic_create(struct Curl_cfilter **pcf,
                              const struct Curl_addrinfo *ai,
                              int transport);
 
-bool Curl_conn_is_http3(const struct Curl_easy *data,
-                        const struct connectdata *conn,
-                        int sockindex);
-
 extern struct Curl_cftype Curl_cft_http3;
 
-#else /* USE_HTTP3 */
-
-#define Curl_conn_is_http3(a,b,c)   FALSE
-
 #endif /* !USE_HTTP3 */
 
 CURLcode Curl_conn_may_http3(struct Curl_easy *data,
index 69ecb5e210fe75ba50cfdae2e06d6118e8e90d45..4a08fed278d7f8d8799d02c821a1491873f1c692 100644 (file)
@@ -68,7 +68,7 @@ Accept: */*
 </protocol>
 # curl: (1) Version mismatch (from HTTP/1 to HTTP/2)
 <errorcode>
-1
+8
 </errorcode>
 </verify>
 </testcase>
index 6e6e1912c3487d6afa17fd6822a575b84b781fea..18d88596b1c50d2295e49ac485a1002120c96e95 100644 (file)
@@ -98,6 +98,7 @@ class TestReuse:
         for s in r.stats:
             assert s['http_version'] == '3', f'{s}'
 
+    @pytest.mark.skipif(condition=not Env.have_h3(), reason="h3 not supported")
     def test_12_04_alt_svc_h3h2(self, env: Env, httpd, nghttpx):
         httpd.clear_extra_configs()
         httpd.reload()
@@ -115,11 +116,12 @@ class TestReuse:
             '--alt-svc', f'{asfile}',
         ])
         r.check_response(count=count, http_status=200)
-        # We expect the connection to be reused
+        # We expect the connection to be reused and use HTTP/2
         assert r.total_connects == 1
         for s in r.stats:
             assert s['http_version'] == '2', f'{s}'
 
+    @pytest.mark.skipif(condition=not Env.have_h3(), reason="h3 not supported")
     def test_12_05_alt_svc_h3h1(self, env: Env, httpd, nghttpx):
         httpd.clear_extra_configs()
         httpd.reload()
@@ -137,9 +139,7 @@ class TestReuse:
             '--alt-svc', f'{asfile}',
         ])
         r.check_response(count=count, http_status=200)
-        # We expect the connection to be reused
+        # We expect the connection to be reused and use HTTP/1.1
         assert r.total_connects == 1
-        # When using http/1.1 from alt-svc, we ALPN-negotiate 'h2,http/1.1' anyway
-        # which means our server gives us h2
         for s in r.stats:
-            assert s['http_version'] == '2', f'{s}'
+            assert s['http_version'] == '1.1', f'{s}'