From 4bcc532de5b639ace6f96f0a30524a08861843b1 Mon Sep 17 00:00:00 2001
From: Daniel Stenberg <daniel@haxx.se>
Date: Sun, 13 Dec 2015 09:23:36 +0100
Subject: [PATCH 1/2] http: add libcurl option to allow HTTP/2 for HTTPS only

... and stick to 1.1 for HTTP. This is in line with what browsers do and
should have very little risk.
---
 docs/libcurl/opts/CURLOPT_HTTP_VERSION.3 |  9 ++++++++-
 docs/libcurl/symbols-in-versions         |  1 +
 include/curl/curl.h                      |  3 ++-
 lib/http.c                               | 16 +++++++++-------
 lib/url.c                                |  4 ++--
 lib/vtls/gtls.c                          |  4 ++--
 lib/vtls/mbedtls.c                       |  4 ++--
 lib/vtls/nss.c                           |  4 ++--
 lib/vtls/openssl.c                       |  8 ++++----
 lib/vtls/polarssl.c                      |  4 ++--
 10 files changed, 34 insertions(+), 23 deletions(-)

diff --git a/docs/libcurl/opts/CURLOPT_HTTP_VERSION.3 b/docs/libcurl/opts/CURLOPT_HTTP_VERSION.3
index aed7c0e..262365e 100644
--- a/docs/libcurl/opts/CURLOPT_HTTP_VERSION.3
+++ b/docs/libcurl/opts/CURLOPT_HTTP_VERSION.3
@@ -43,12 +43,19 @@ it thinks fit.
 .IP CURL_HTTP_VERSION_1_0
 Enforce HTTP 1.0 requests.
 .IP CURL_HTTP_VERSION_1_1
 Enforce HTTP 1.1 requests.
 .IP CURL_HTTP_VERSION_2_0
-Attempt HTTP 2 requests. libcurl will fall back to HTTP 1.x if HTTP 2 can't be
+Attempt HTTP 2 requests. libcurl will fall back to HTTP 1.1 if HTTP 2 can't be
 negotiated with the server. (Added in 7.33.0)
+
+The alias \fICURL_HTTP_VERSION_2\fI was added in 7.43.0 to better reflect the
+actual protocol name.
+.IP CURL_HTTP_VERSION_2TLS
+Attempt HTTP 2 over TLS (HTTPS) only. libcurl will fall back to HTTP 1.1 if
+HTTP 2 can't be negotiated with the HTTPS server. For clear text HTTP servers,
+libcurl will use 1.1. (Added in 7.47.0)
 .SH DEFAULT
 CURL_HTTP_VERSION_NONE
 .SH PROTOCOLS
 HTTP
 .SH EXAMPLE
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index ef7981f..629d224 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -690,10 +690,11 @@ CURL_HTTPPOST_PTRNAME           7.46.0
 CURL_HTTPPOST_READFILE          7.46.0
 CURL_HTTP_VERSION_1_0           7.9.1
 CURL_HTTP_VERSION_1_1           7.9.1
 CURL_HTTP_VERSION_2             7.43.0
 CURL_HTTP_VERSION_2_0           7.33.0
+CURL_HTTP_VERSION_2TLS          7.47.0
 CURL_HTTP_VERSION_NONE          7.9.1
 CURL_IPRESOLVE_V4               7.10.8
 CURL_IPRESOLVE_V6               7.10.8
 CURL_IPRESOLVE_WHATEVER         7.10.8
 CURL_LOCK_ACCESS_NONE           7.10.3
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 7fd533e..84229bb 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -1720,11 +1720,12 @@ enum {
   CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd
                              like the library to choose the best possible
                              for us! */
   CURL_HTTP_VERSION_1_0,  /* please use HTTP 1.0 in the request */
   CURL_HTTP_VERSION_1_1,  /* please use HTTP 1.1 in the request */
-  CURL_HTTP_VERSION_2_0,  /* please use HTTP 2.0 in the request */
+  CURL_HTTP_VERSION_2_0,  /* please use HTTP 2 in the request */
+  CURL_HTTP_VERSION_2TLS, /* use version 2 for HTTPS, version 1.1 for HTTP */
 
   CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */
 };
 
 /* Convenience definition simple because the name of the version is HTTP/2 and
diff --git a/lib/http.c b/lib/http.c
index 3c9fde5..b77003f 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -1537,15 +1537,17 @@ CURLcode Curl_http_done(struct connectdata *conn,
  * 1.0.
  */
 static bool use_http_1_1plus(const struct SessionHandle *data,
                              const struct connectdata *conn)
 {
-  return ((data->set.httpversion >= CURL_HTTP_VERSION_1_1) ||
-         ((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
-          ((conn->httpversion == 11) ||
-           ((conn->httpversion != 10) &&
-            (data->state.httpversion != 10))))) ? TRUE : FALSE;
+  if((data->state.httpversion == 10) || (conn->httpversion == 10))
+    return FALSE;
+  if((data->set.httpversion == CURL_HTTP_VERSION_1_0) &&
+     (conn->httpversion <= 10))
+    return FALSE;
+  return ((data->set.httpversion == CURL_HTTP_VERSION_NONE) ||
+          (data->set.httpversion >= CURL_HTTP_VERSION_1_1));
 }
 
 /* check and possibly add an Expect: header */
 static CURLcode expect100(struct SessionHandle *data,
                           struct connectdata *conn,
@@ -1783,11 +1785,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
   *done = TRUE;
 
   if(conn->httpversion < 20) { /* unless the connection is re-used and already
                                   http2 */
     switch(conn->negnpn) {
-    case CURL_HTTP_VERSION_2_0:
+    case CURL_HTTP_VERSION_2:
       conn->httpversion = 20; /* we know we're on HTTP/2 now */
       result = Curl_http2_init(conn);
       if(result)
         return result;
 
@@ -2336,11 +2338,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
   if(result)
     return result;
 
   if(!(conn->handler->flags&PROTOPT_SSL) &&
      conn->httpversion != 20 &&
-     (data->set.httpversion == CURL_HTTP_VERSION_2_0)) {
+     (data->set.httpversion == CURL_HTTP_VERSION_2)) {
     /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done
        over SSL */
     result = Curl_http2_request_upgrade(req_buffer, conn);
     if(result)
       return result;
diff --git a/lib/url.c b/lib/url.c
index 13e95f4..5a83207 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -1292,11 +1292,11 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
      * This sets a requested HTTP version to be used. The value is one of
      * the listed enums in curl/curl.h.
      */
     arg = va_arg(param, long);
 #ifndef USE_NGHTTP2
-    if(arg == CURL_HTTP_VERSION_2_0)
+    if(arg >= CURL_HTTP_VERSION_2)
       return CURLE_UNSUPPORTED_PROTOCOL;
 #endif
     data->set.httpversion = arg;
     break;
 
@@ -2863,11 +2863,11 @@ static bool IsPipeliningPossible(const struct SessionHandle *handle,
         handle->set.httpreq == HTTPREQ_HEAD))
       /* didn't ask for HTTP/1.0 and a GET or HEAD */
       return TRUE;
 
     if(Curl_pipeline_wanted(handle->multi, CURLPIPE_MULTIPLEX) &&
-       (handle->set.httpversion == CURL_HTTP_VERSION_2_0))
+       (handle->set.httpversion >= CURL_HTTP_VERSION_2))
       /* allows HTTP/2 */
       return TRUE;
   }
   return FALSE;
 }
diff --git a/lib/vtls/gtls.c b/lib/vtls/gtls.c
index 1c1cc2f..59cd7fb 100644
--- a/lib/vtls/gtls.c
+++ b/lib/vtls/gtls.c
@@ -636,11 +636,11 @@ gtls_connect_step1(struct connectdata *conn,
   if(data->set.ssl_enable_alpn) {
     int cur = 0;
     gnutls_datum_t protocols[2];
 
 #ifdef USE_NGHTTP2
-    if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+    if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
       protocols[cur].data = (unsigned char *)NGHTTP2_PROTO_VERSION_ID;
       protocols[cur].size = NGHTTP2_PROTO_VERSION_ID_LEN;
       cur++;
       infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
     }
@@ -1240,11 +1240,11 @@ gtls_connect_step3(struct connectdata *conn,
 
 #ifdef USE_NGHTTP2
       if(proto.size == NGHTTP2_PROTO_VERSION_ID_LEN &&
          !memcmp(NGHTTP2_PROTO_VERSION_ID, proto.data,
                  NGHTTP2_PROTO_VERSION_ID_LEN)) {
-        conn->negnpn = CURL_HTTP_VERSION_2_0;
+        conn->negnpn = CURL_HTTP_VERSION_2;
       }
       else
 #endif
       if(proto.size == ALPN_HTTP_1_1_LENGTH &&
          !memcmp(ALPN_HTTP_1_1, proto.data, ALPN_HTTP_1_1_LENGTH)) {
diff --git a/lib/vtls/mbedtls.c b/lib/vtls/mbedtls.c
index 0616e68..2fbf9b8 100644
--- a/lib/vtls/mbedtls.c
+++ b/lib/vtls/mbedtls.c
@@ -372,11 +372,11 @@ mbedtls_connect_step1(struct connectdata *conn,
      infof(data, "WARNING: failed to configure "
                  "server name indication (SNI) TLS extension\n");
   }
 
 #ifdef HAS_ALPN
-  if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+  if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
     if(data->set.ssl_enable_alpn) {
       static const char* protocols[] = {
         NGHTTP2_PROTO_VERSION_ID, ALPN_HTTP_1_1, NULL
       };
       mbedtls_ssl_conf_alpn_protocols(&connssl->config, protocols);
@@ -473,11 +473,11 @@ mbedtls_connect_step2(struct connectdata *conn,
     if(next_protocol != NULL) {
       infof(data, "ALPN, server accepted to use %s\n", next_protocol);
 
       if(strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID,
                   NGHTTP2_PROTO_VERSION_ID_LEN)) {
-        conn->negnpn = CURL_HTTP_VERSION_2_0;
+        conn->negnpn = CURL_HTTP_VERSION_2;
       }
       else if(strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) {
         conn->negnpn = CURL_HTTP_VERSION_1_1;
       }
     }
diff --git a/lib/vtls/nss.c b/lib/vtls/nss.c
index c8bd0ce..aacdd6c 100644
--- a/lib/vtls/nss.c
+++ b/lib/vtls/nss.c
@@ -718,11 +718,11 @@ static void HandshakeCallback(PRFileDesc *sock, void *arg)
     }
 
 #ifdef USE_NGHTTP2
     if(buflen == NGHTTP2_PROTO_VERSION_ID_LEN &&
        !memcmp(NGHTTP2_PROTO_VERSION_ID, buf, NGHTTP2_PROTO_VERSION_ID_LEN)) {
-      conn->negnpn = CURL_HTTP_VERSION_2_0;
+      conn->negnpn = CURL_HTTP_VERSION_2;
     }
     else
 #endif
     if(buflen == ALPN_HTTP_1_1_LENGTH &&
        !memcmp(ALPN_HTTP_1_1, buf, ALPN_HTTP_1_1_LENGTH)) {
@@ -1777,11 +1777,11 @@ static CURLcode nss_setup_connect(struct connectdata *conn, int sockindex)
   if(data->set.ssl_enable_npn || data->set.ssl_enable_alpn) {
     int cur = 0;
     unsigned char protocols[128];
 
 #ifdef USE_NGHTTP2
-    if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+    if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
       protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN;
       memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID,
           NGHTTP2_PROTO_VERSION_ID_LEN);
       cur += NGHTTP2_PROTO_VERSION_ID_LEN;
     }
diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c
index fdc27b0..e5b0bb9 100644
--- a/lib/vtls/openssl.c
+++ b/lib/vtls/openssl.c
@@ -1580,16 +1580,16 @@ select_next_proto_cb(SSL *ssl,
   struct connectdata *conn = (struct connectdata*) arg;
 
   (void)ssl;
 
 #ifdef USE_NGHTTP2
-  if(conn->data->set.httpversion == CURL_HTTP_VERSION_2_0 &&
+  if(conn->data->set.httpversion >= CURL_HTTP_VERSION_2 &&
      !select_next_protocol(out, outlen, in, inlen, NGHTTP2_PROTO_VERSION_ID,
                            NGHTTP2_PROTO_VERSION_ID_LEN)) {
     infof(conn->data, "NPN, negotiated HTTP2 (%s)\n",
           NGHTTP2_PROTO_VERSION_ID);
-    conn->negnpn = CURL_HTTP_VERSION_2_0;
+    conn->negnpn = CURL_HTTP_VERSION_2;
     return SSL_TLSEXT_ERR_OK;
   }
 #endif
 
   if(!select_next_protocol(out, outlen, in, inlen, ALPN_HTTP_1_1,
@@ -1857,11 +1857,11 @@ static CURLcode ossl_connect_step1(struct connectdata *conn, int sockindex)
   if(data->set.ssl_enable_alpn) {
     int cur = 0;
     unsigned char protocols[128];
 
 #ifdef USE_NGHTTP2
-    if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+    if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
       protocols[cur++] = NGHTTP2_PROTO_VERSION_ID_LEN;
 
       memcpy(&protocols[cur], NGHTTP2_PROTO_VERSION_ID,
           NGHTTP2_PROTO_VERSION_ID_LEN);
       cur += NGHTTP2_PROTO_VERSION_ID_LEN;
@@ -2183,11 +2183,11 @@ static CURLcode ossl_connect_step2(struct connectdata *conn, int sockindex)
         infof(data, "ALPN, server accepted to use %.*s\n", len, neg_protocol);
 
 #ifdef USE_NGHTTP2
         if(len == NGHTTP2_PROTO_VERSION_ID_LEN &&
            !memcmp(NGHTTP2_PROTO_VERSION_ID, neg_protocol, len)) {
-          conn->negnpn = CURL_HTTP_VERSION_2_0;
+          conn->negnpn = CURL_HTTP_VERSION_2;
         }
         else
 #endif
         if(len == ALPN_HTTP_1_1_LENGTH &&
            !memcmp(ALPN_HTTP_1_1, neg_protocol, ALPN_HTTP_1_1_LENGTH)) {
diff --git a/lib/vtls/polarssl.c b/lib/vtls/polarssl.c
index cf7c344..e7bcf2f 100644
--- a/lib/vtls/polarssl.c
+++ b/lib/vtls/polarssl.c
@@ -357,11 +357,11 @@ polarssl_connect_step1(struct connectdata *conn,
   if(data->set.ssl_enable_alpn) {
     static const char* protocols[3];
     int cur = 0;
 
 #ifdef USE_NGHTTP2
-    if(data->set.httpversion == CURL_HTTP_VERSION_2_0) {
+    if(data->set.httpversion >= CURL_HTTP_VERSION_2) {
       protocols[cur++] = NGHTTP2_PROTO_VERSION_ID;
       infof(data, "ALPN, offering %s\n", NGHTTP2_PROTO_VERSION_ID);
     }
 #endif
 
@@ -461,11 +461,11 @@ polarssl_connect_step2(struct connectdata *conn,
       infof(data, "ALPN, server accepted to use %s\n", next_protocol);
 
 #ifdef USE_NGHTTP2
       if(!strncmp(next_protocol, NGHTTP2_PROTO_VERSION_ID,
                   NGHTTP2_PROTO_VERSION_ID_LEN)) {
-        conn->negnpn = CURL_HTTP_VERSION_2_0;
+        conn->negnpn = CURL_HTTP_VERSION_2;
       }
       else
 #endif
       if(!strncmp(next_protocol, ALPN_HTTP_1_1, ALPN_HTTP_1_1_LENGTH)) {
         conn->negnpn = CURL_HTTP_VERSION_1_1;
-- 
2.6.2

