Merge branch 'jk/http-auth' into maint
authorJunio C Hamano <gitster@pobox.com>
Thu, 16 Mar 2017 20:56:41 +0000 (13:56 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 16 Mar 2017 20:56:41 +0000 (13:56 -0700)
Reduce authentication round-trip over HTTP when the server supports
just a single authentication method.

* jk/http-auth:
http: add an "auto" mode for http.emptyauth
http: restrict auth methods to what the server advertises

1  2 
http.c
diff --combined http.c
index 90a1c0f1131c4a5fcbc50d6cc81c1be9cef3cd71,d9d73a2e607573e88d35c112660293cde92ff1bd..d2d6899b99bdf7c1ba3bae79d3ff53d09e7a1bd6
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -109,7 -109,7 +109,7 @@@ static int curl_save_cookies
  struct credential http_auth = CREDENTIAL_INIT;
  static int http_proactive_auth;
  static const char *user_agent;
- static int curl_empty_auth;
+ static int curl_empty_auth = -1;
  
  enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
  
@@@ -125,6 -125,14 +125,14 @@@ static struct credential cert_auth = CR
  static int ssl_cert_password_required;
  #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
  static unsigned long http_auth_methods = CURLAUTH_ANY;
+ static int http_auth_methods_restricted;
+ /* Modes for which empty_auth cannot actually help us. */
+ static unsigned long empty_auth_useless =
+       CURLAUTH_BASIC
+ #ifdef CURLAUTH_DIGEST_IE
+       | CURLAUTH_DIGEST_IE
+ #endif
+       | CURLAUTH_DIGEST;
  #endif
  
  static struct curl_slist *pragma_header;
@@@ -333,7 -341,10 +341,10 @@@ static int http_options(const char *var
                return git_config_string(&user_agent, var, value);
  
        if (!strcmp("http.emptyauth", var)) {
-               curl_empty_auth = git_config_bool(var, value);
+               if (value && !strcmp("auto", value))
+                       curl_empty_auth = -1;
+               else
+                       curl_empty_auth = git_config_bool(var, value);
                return 0;
        }
  
        return git_default_config(var, value, cb);
  }
  
+ static int curl_empty_auth_enabled(void)
+ {
+       if (curl_empty_auth >= 0)
+               return curl_empty_auth;
+ #ifndef LIBCURL_CAN_HANDLE_AUTH_ANY
+       /*
+        * Our libcurl is too old to do AUTH_ANY in the first place;
+        * just default to turning the feature off.
+        */
+ #else
+       /*
+        * In the automatic case, kick in the empty-auth
+        * hack as long as we would potentially try some
+        * method more exotic than "Basic" or "Digest".
+        *
+        * But only do this when this is our second or
+        * subsequent request, as by then we know what
+        * methods are available.
+        */
+       if (http_auth_methods_restricted &&
+           (http_auth_methods & ~empty_auth_useless))
+               return 1;
+ #endif
+       return 0;
+ }
  static void init_curl_http_auth(CURL *result)
  {
        if (!http_auth.username || !*http_auth.username) {
-               if (curl_empty_auth)
+               if (curl_empty_auth_enabled())
                        curl_easy_setopt(result, CURLOPT_USERPWD, ":");
                return;
        }
@@@ -636,25 -674,11 +674,25 @@@ void setup_curl_trace(CURL *handle
        curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);
  }
  
 +static long get_curl_allowed_protocols(int from_user)
 +{
 +      long allowed_protocols = 0;
 +
 +      if (is_transport_allowed("http", from_user))
 +              allowed_protocols |= CURLPROTO_HTTP;
 +      if (is_transport_allowed("https", from_user))
 +              allowed_protocols |= CURLPROTO_HTTPS;
 +      if (is_transport_allowed("ftp", from_user))
 +              allowed_protocols |= CURLPROTO_FTP;
 +      if (is_transport_allowed("ftps", from_user))
 +              allowed_protocols |= CURLPROTO_FTPS;
 +
 +      return allowed_protocols;
 +}
  
  static CURL *get_curl_handle(void)
  {
        CURL *result = curl_easy_init();
 -      long allowed_protocols = 0;
  
        if (!result)
                die("curl_easy_init failed");
        curl_easy_setopt(result, CURLOPT_POST301, 1);
  #endif
  #if LIBCURL_VERSION_NUM >= 0x071304
 -      if (is_transport_allowed("http"))
 -              allowed_protocols |= CURLPROTO_HTTP;
 -      if (is_transport_allowed("https"))
 -              allowed_protocols |= CURLPROTO_HTTPS;
 -      if (is_transport_allowed("ftp"))
 -              allowed_protocols |= CURLPROTO_FTP;
 -      if (is_transport_allowed("ftps"))
 -              allowed_protocols |= CURLPROTO_FTPS;
 -      curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS, allowed_protocols);
 -      curl_easy_setopt(result, CURLOPT_PROTOCOLS, allowed_protocols);
 +      curl_easy_setopt(result, CURLOPT_REDIR_PROTOCOLS,
 +                       get_curl_allowed_protocols(0));
 +      curl_easy_setopt(result, CURLOPT_PROTOCOLS,
 +                       get_curl_allowed_protocols(-1));
  #else
 -      if (transport_restrict_protocols())
 -              warning("protocol restrictions not applied to curl redirects because\n"
 -                      "your curl version is too old (>= 7.19.4)");
 +      warning("protocol restrictions not applied to curl redirects because\n"
 +              "your curl version is too old (>= 7.19.4)");
  #endif
        if (getenv("GIT_CURL_VERBOSE"))
                curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
@@@ -1079,7 -1110,7 +1117,7 @@@ struct active_request_slot *get_active_
  #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
        curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
  #endif
-       if (http_auth.password || curl_empty_auth)
+       if (http_auth.password || curl_empty_auth_enabled())
                init_curl_http_auth(slot->curl);
  
        return slot;
@@@ -1347,6 -1378,10 +1385,10 @@@ static int handle_curl_result(struct sl
                } else {
  #ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
                        http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
+                       if (results->auth_avail) {
+                               http_auth_methods &= results->auth_avail;
+                               http_auth_methods_restricted = 1;
+                       }
  #endif
                        return HTTP_REAUTH;
                }