]> git.ipfire.org Git - thirdparty/git.git/blobdiff - http.c
The 19th batch
[thirdparty/git.git] / http.c
diff --git a/http.c b/http.c
index e73b136e5897bd8fce2f874b20f316141ba59c31..6c6cc5c822ad5ae741a9902aad35aa94cef2260d 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1,3 +1,5 @@
+#define USE_THE_REPOSITORY_VARIABLE
+
 #include "git-compat-util.h"
 #include "git-curl-compat.h"
 #include "hex.h"
 #include "git-compat-util.h"
 #include "git-curl-compat.h"
 #include "hex.h"
@@ -38,11 +40,11 @@ char curl_errorstr[CURL_ERROR_SIZE];
 
 static int curl_ssl_verify = -1;
 static int curl_ssl_try;
 
 static int curl_ssl_verify = -1;
 static int curl_ssl_try;
-static const char *curl_http_version = NULL;
-static const char *ssl_cert;
-static const char *ssl_cert_type;
-static const char *ssl_cipherlist;
-static const char *ssl_version;
+static char *curl_http_version;
+static char *ssl_cert;
+static char *ssl_cert_type;
+static char *ssl_cipherlist;
+static char *ssl_version;
 static struct {
        const char *name;
        long ssl_version;
 static struct {
        const char *name;
        long ssl_version;
@@ -59,23 +61,23 @@ static struct {
        { "tlsv1.3", CURL_SSLVERSION_TLSv1_3 },
 #endif
 };
        { "tlsv1.3", CURL_SSLVERSION_TLSv1_3 },
 #endif
 };
-static const char *ssl_key;
-static const char *ssl_key_type;
-static const char *ssl_capath;
-static const char *curl_no_proxy;
+static char *ssl_key;
+static char *ssl_key_type;
+static char *ssl_capath;
+static char *curl_no_proxy;
 #ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
 #ifdef GIT_CURL_HAVE_CURLOPT_PINNEDPUBLICKEY
-static const char *ssl_pinnedkey;
+static char *ssl_pinnedkey;
 #endif
 #endif
-static const char *ssl_cainfo;
+static char *ssl_cainfo;
 static long curl_low_speed_limit = -1;
 static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static long curl_low_speed_limit = -1;
 static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
-static const char *curl_http_proxy;
-static const char *http_proxy_authmethod;
+static char *curl_http_proxy;
+static char *http_proxy_authmethod;
 
 
-static const char *http_proxy_ssl_cert;
-static const char *http_proxy_ssl_key;
-static const char *http_proxy_ssl_ca_info;
+static char *http_proxy_ssl_cert;
+static char *http_proxy_ssl_key;
+static char *http_proxy_ssl_ca_info;
 static struct credential proxy_cert_auth = CREDENTIAL_INIT;
 static int proxy_ssl_cert_password_required;
 
 static struct credential proxy_cert_auth = CREDENTIAL_INIT;
 static int proxy_ssl_cert_password_required;
 
@@ -95,7 +97,7 @@ static struct {
         */
 };
 #ifdef CURLGSSAPI_DELEGATION_FLAG
         */
 };
 #ifdef CURLGSSAPI_DELEGATION_FLAG
-static const char *curl_deleg;
+static char *curl_deleg;
 static struct {
        const char *name;
        long curl_deleg_param;
 static struct {
        const char *name;
        long curl_deleg_param;
@@ -106,13 +108,20 @@ static struct {
 };
 #endif
 
 };
 #endif
 
+enum proactive_auth {
+       PROACTIVE_AUTH_NONE = 0,
+       PROACTIVE_AUTH_IF_CREDENTIALS,
+       PROACTIVE_AUTH_AUTO,
+       PROACTIVE_AUTH_BASIC,
+};
+
 static struct credential proxy_auth = CREDENTIAL_INIT;
 static const char *curl_proxyuserpwd;
 static struct credential proxy_auth = CREDENTIAL_INIT;
 static const char *curl_proxyuserpwd;
-static const char *curl_cookie_file;
+static char *curl_cookie_file;
 static int curl_save_cookies;
 struct credential http_auth = CREDENTIAL_INIT;
 static int curl_save_cookies;
 struct credential http_auth = CREDENTIAL_INIT;
-static int http_proactive_auth;
-static const char *user_agent;
+static enum proactive_auth http_proactive_auth;
+static char *user_agent;
 static int curl_empty_auth = -1;
 
 enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
 static int curl_empty_auth = -1;
 
 enum http_follow_config http_follow_config = HTTP_FOLLOW_INITIAL;
@@ -128,7 +137,6 @@ static unsigned long empty_auth_useless =
        | CURLAUTH_DIGEST;
 
 static struct curl_slist *pragma_header;
        | CURLAUTH_DIGEST;
 
 static struct curl_slist *pragma_header;
-static struct curl_slist *no_pragma_header;
 static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
 
 static struct curl_slist *host_resolutions;
 static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
 
 static struct curl_slist *host_resolutions;
@@ -147,6 +155,12 @@ static int http_schannel_check_revoke = 1;
  */
 static int http_schannel_use_ssl_cainfo;
 
  */
 static int http_schannel_use_ssl_cainfo;
 
+static int always_auth_proactively(void)
+{
+       return http_proactive_auth != PROACTIVE_AUTH_NONE &&
+              http_proactive_auth != PROACTIVE_AUTH_IF_CREDENTIALS;
+}
+
 size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
        size_t size = eltsize * nmemb;
 size_t fread_buffer(char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
 {
        size_t size = eltsize * nmemb;
@@ -299,6 +313,11 @@ size_t fwrite_null(char *ptr UNUSED, size_t eltsize UNUSED, size_t nmemb,
        return nmemb;
 }
 
        return nmemb;
 }
 
+static struct curl_slist *object_request_headers(void)
+{
+       return curl_slist_append(http_copy_default_headers(), "Pragma:");
+}
+
 static void closedown_active_slot(struct active_request_slot *slot)
 {
        active_requests--;
 static void closedown_active_slot(struct active_request_slot *slot)
 {
        active_requests--;
@@ -533,6 +552,20 @@ static int http_options(const char *var, const char *value,
                return 0;
        }
 
                return 0;
        }
 
+       if (!strcmp("http.proactiveauth", var)) {
+               if (!value)
+                       return config_error_nonbool(var);
+               if (!strcmp(value, "auto"))
+                       http_proactive_auth = PROACTIVE_AUTH_AUTO;
+               else if (!strcmp(value, "basic"))
+                       http_proactive_auth = PROACTIVE_AUTH_BASIC;
+               else if (!strcmp(value, "none"))
+                       http_proactive_auth = PROACTIVE_AUTH_NONE;
+               else
+                       warning(_("Unknown value for http.proactiveauth"));
+               return 0;
+       }
+
        /* Fall back on the default ones */
        return git_default_config(var, value, ctx, data);
 }
        /* Fall back on the default ones */
        return git_default_config(var, value, ctx, data);
 }
@@ -557,42 +590,78 @@ static int curl_empty_auth_enabled(void)
        return 0;
 }
 
        return 0;
 }
 
+struct curl_slist *http_append_auth_header(const struct credential *c,
+                                          struct curl_slist *headers)
+{
+       if (c->authtype && c->credential) {
+               struct strbuf auth = STRBUF_INIT;
+               strbuf_addf(&auth, "Authorization: %s %s",
+                           c->authtype, c->credential);
+               headers = curl_slist_append(headers, auth.buf);
+               strbuf_release(&auth);
+       }
+       return headers;
+}
+
 static void init_curl_http_auth(CURL *result)
 {
 static void init_curl_http_auth(CURL *result)
 {
-       if (!http_auth.username || !*http_auth.username) {
-               if (curl_empty_auth_enabled())
+       if ((!http_auth.username || !*http_auth.username) &&
+           (!http_auth.credential || !*http_auth.credential)) {
+               int empty_auth = curl_empty_auth_enabled();
+               if ((empty_auth != -1 && !always_auth_proactively()) || empty_auth == 1) {
                        curl_easy_setopt(result, CURLOPT_USERPWD, ":");
                        curl_easy_setopt(result, CURLOPT_USERPWD, ":");
-               return;
+                       return;
+               } else if (!always_auth_proactively()) {
+                       return;
+               } else if (http_proactive_auth == PROACTIVE_AUTH_BASIC) {
+                       strvec_push(&http_auth.wwwauth_headers, "Basic");
+               }
        }
 
        }
 
-       credential_fill(&http_auth);
+       credential_fill(&http_auth, 1);
 
 
-       curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
-       curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+       if (http_auth.password) {
+               if (always_auth_proactively()) {
+                       /*
+                        * We got a credential without an authtype and we don't
+                        * know what's available.  Since our only two options at
+                        * the moment are auto (which defaults to basic) and
+                        * basic, use basic for now.
+                        */
+                       curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
+               }
+               curl_easy_setopt(result, CURLOPT_USERNAME, http_auth.username);
+               curl_easy_setopt(result, CURLOPT_PASSWORD, http_auth.password);
+       }
 }
 
 /* *var must be free-able */
 }
 
 /* *var must be free-able */
-static void var_override(const char **var, char *value)
+static void var_override(char **var, char *value)
 {
        if (value) {
 {
        if (value) {
-               free((void *)*var);
+               free(*var);
                *var = xstrdup(value);
        }
 }
 
 static void set_proxyauth_name_password(CURL *result)
 {
                *var = xstrdup(value);
        }
 }
 
 static void set_proxyauth_name_password(CURL *result)
 {
+       if (proxy_auth.password) {
                curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
                        proxy_auth.username);
                curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
                        proxy_auth.password);
                curl_easy_setopt(result, CURLOPT_PROXYUSERNAME,
                        proxy_auth.username);
                curl_easy_setopt(result, CURLOPT_PROXYPASSWORD,
                        proxy_auth.password);
+       } else if (proxy_auth.authtype && proxy_auth.credential) {
+               curl_easy_setopt(result, CURLOPT_PROXYHEADER,
+                                http_append_auth_header(&proxy_auth, NULL));
+       }
 }
 
 static void init_curl_proxy_auth(CURL *result)
 {
        if (proxy_auth.username) {
 }
 
 static void init_curl_proxy_auth(CURL *result)
 {
        if (proxy_auth.username) {
-               if (!proxy_auth.password)
-                       credential_fill(&proxy_auth);
+               if (!proxy_auth.password && !proxy_auth.credential)
+                       credential_fill(&proxy_auth, 1);
                set_proxyauth_name_password(result);
        }
 
                set_proxyauth_name_password(result);
        }
 
@@ -626,7 +695,7 @@ static int has_cert_password(void)
                cert_auth.host = xstrdup("");
                cert_auth.username = xstrdup("");
                cert_auth.path = xstrdup(ssl_cert);
                cert_auth.host = xstrdup("");
                cert_auth.username = xstrdup("");
                cert_auth.path = xstrdup(ssl_cert);
-               credential_fill(&cert_auth);
+               credential_fill(&cert_auth, 0);
        }
        return 1;
 }
        }
        return 1;
 }
@@ -641,7 +710,7 @@ static int has_proxy_cert_password(void)
                proxy_cert_auth.host = xstrdup("");
                proxy_cert_auth.username = xstrdup("");
                proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
                proxy_cert_auth.host = xstrdup("");
                proxy_cert_auth.username = xstrdup("");
                proxy_cert_auth.path = xstrdup(http_proxy_ssl_cert);
-               credential_fill(&proxy_cert_auth);
+               credential_fill(&proxy_cert_auth, 0);
        }
        return 1;
 }
        }
        return 1;
 }
@@ -1023,7 +1092,7 @@ static CURL *get_curl_handle(void)
 #endif
        }
 
 #endif
        }
 
-       if (http_proactive_auth)
+       if (http_proactive_auth != PROACTIVE_AUTH_NONE)
                init_curl_http_auth(result);
 
        if (getenv("GIT_SSL_VERSION"))
                init_curl_http_auth(result);
 
        if (getenv("GIT_SSL_VERSION"))
@@ -1158,6 +1227,8 @@ static CURL *get_curl_handle(void)
                 */
                curl_easy_setopt(result, CURLOPT_PROXY, "");
        } else if (curl_http_proxy) {
                 */
                curl_easy_setopt(result, CURLOPT_PROXY, "");
        } else if (curl_http_proxy) {
+               struct strbuf proxy = STRBUF_INIT;
+
                if (starts_with(curl_http_proxy, "socks5h"))
                        curl_easy_setopt(result,
                                CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
                if (starts_with(curl_http_proxy, "socks5h"))
                        curl_easy_setopt(result,
                                CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5_HOSTNAME);
@@ -1196,7 +1267,27 @@ static CURL *get_curl_handle(void)
                if (!proxy_auth.host)
                        die("Invalid proxy URL '%s'", curl_http_proxy);
 
                if (!proxy_auth.host)
                        die("Invalid proxy URL '%s'", curl_http_proxy);
 
-               curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host);
+               strbuf_addstr(&proxy, proxy_auth.host);
+               if (proxy_auth.path) {
+                       curl_version_info_data *ver = curl_version_info(CURLVERSION_NOW);
+
+                       if (ver->version_num < 0x075400)
+                               die("libcurl 7.84 or later is required to support paths in proxy URLs");
+
+                       if (!starts_with(proxy_auth.protocol, "socks"))
+                               die("Invalid proxy URL '%s': only SOCKS proxies support paths",
+                                   curl_http_proxy);
+
+                       if (strcasecmp(proxy_auth.host, "localhost"))
+                               die("Invalid proxy URL '%s': host must be localhost if a path is present",
+                                   curl_http_proxy);
+
+                       strbuf_addch(&proxy, '/');
+                       strbuf_add_percentencode(&proxy, proxy_auth.path, 0);
+               }
+               curl_easy_setopt(result, CURLOPT_PROXY, proxy.buf);
+               strbuf_release(&proxy);
+
                var_override(&curl_no_proxy, getenv("NO_PROXY"));
                var_override(&curl_no_proxy, getenv("no_proxy"));
                curl_easy_setopt(result, CURLOPT_NOPROXY, curl_no_proxy);
                var_override(&curl_no_proxy, getenv("NO_PROXY"));
                var_override(&curl_no_proxy, getenv("no_proxy"));
                curl_easy_setopt(result, CURLOPT_NOPROXY, curl_no_proxy);
@@ -1208,11 +1299,13 @@ static CURL *get_curl_handle(void)
        return result;
 }
 
        return result;
 }
 
-static void set_from_env(const char **var, const char *envname)
+static void set_from_env(char **var, const char *envname)
 {
        const char *val = getenv(envname);
 {
        const char *val = getenv(envname);
-       if (val)
-               *var = val;
+       if (val) {
+               FREE_AND_NULL(*var);
+               *var = xstrdup(val);
+       }
 }
 
 void http_init(struct remote *remote, const char *url, int proactive_auth)
 }
 
 void http_init(struct remote *remote, const char *url, int proactive_auth)
@@ -1265,7 +1358,8 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
        if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
                die("curl_global_init failed");
 
        if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
                die("curl_global_init failed");
 
-       http_proactive_auth = proactive_auth;
+       if (proactive_auth && http_proactive_auth == PROACTIVE_AUTH_NONE)
+               http_proactive_auth = PROACTIVE_AUTH_IF_CREDENTIALS;
 
        if (remote && remote->http_proxy)
                curl_http_proxy = xstrdup(remote->http_proxy);
 
        if (remote && remote->http_proxy)
                curl_http_proxy = xstrdup(remote->http_proxy);
@@ -1275,8 +1369,6 @@ void http_init(struct remote *remote, const char *url, int proactive_auth)
 
        pragma_header = curl_slist_append(http_copy_default_headers(),
                "Pragma: no-cache");
 
        pragma_header = curl_slist_append(http_copy_default_headers(),
                "Pragma: no-cache");
-       no_pragma_header = curl_slist_append(http_copy_default_headers(),
-               "Pragma:");
 
        {
                char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
 
        {
                char *http_max_requests = getenv("GIT_HTTP_MAX_REQUESTS");
@@ -1360,9 +1452,6 @@ void http_cleanup(void)
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
 
        curl_slist_free_all(pragma_header);
        pragma_header = NULL;
 
-       curl_slist_free_all(no_pragma_header);
-       no_pragma_header = NULL;
-
        curl_slist_free_all(host_resolutions);
        host_resolutions = NULL;
 
        curl_slist_free_all(host_resolutions);
        host_resolutions = NULL;
 
@@ -1442,7 +1531,16 @@ struct active_request_slot *get_active_slot(void)
        slot->finished = NULL;
        slot->callback_data = NULL;
        slot->callback_func = NULL;
        slot->finished = NULL;
        slot->callback_data = NULL;
        slot->callback_func = NULL;
+
+       if (curl_cookie_file && !strcmp(curl_cookie_file, "-")) {
+               warning(_("refusing to read cookies from http.cookiefile '-'"));
+               FREE_AND_NULL(curl_cookie_file);
+       }
        curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);
        curl_easy_setopt(slot->curl, CURLOPT_COOKIEFILE, curl_cookie_file);
+       if (curl_save_cookies && (!curl_cookie_file || !curl_cookie_file[0])) {
+               curl_save_cookies = 0;
+               warning(_("ignoring http.savecookies for empty http.cookiefile"));
+       }
        if (curl_save_cookies)
                curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
        if (curl_save_cookies)
                curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
@@ -1452,6 +1550,7 @@ struct active_request_slot *get_active_slot(void)
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, NULL);
+       curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, -1L);
        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
@@ -1469,7 +1568,7 @@ struct active_request_slot *get_active_slot(void)
 
        curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
 
        curl_easy_setopt(slot->curl, CURLOPT_IPRESOLVE, git_curl_ipresolve);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
-       if (http_auth.password || curl_empty_auth_enabled())
+       if (http_auth.password || http_auth.credential || curl_empty_auth_enabled())
                init_curl_http_auth(slot->curl);
 
        return slot;
                init_curl_http_auth(slot->curl);
 
        return slot;
@@ -1758,8 +1857,15 @@ static int handle_curl_result(struct slot_results *results)
        } else if (missing_target(results))
                return HTTP_MISSING_TARGET;
        else if (results->http_code == 401) {
        } else if (missing_target(results))
                return HTTP_MISSING_TARGET;
        else if (results->http_code == 401) {
-               if (http_auth.username && http_auth.password) {
+               if ((http_auth.username && http_auth.password) ||\
+                   (http_auth.authtype && http_auth.credential)) {
+                       if (http_auth.multistage) {
+                               credential_clear_secrets(&http_auth);
+                               return HTTP_REAUTH;
+                       }
                        credential_reject(&http_auth);
                        credential_reject(&http_auth);
+                       if (always_auth_proactively())
+                               http_proactive_auth = PROACTIVE_AUTH_NONE;
                        return HTTP_NOAUTH;
                } else {
                        http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
                        return HTTP_NOAUTH;
                } else {
                        http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
@@ -1946,7 +2052,7 @@ static void write_accept_language(struct strbuf *buf)
 
                /* add '*' */
                REALLOC_ARRAY(language_tags, num_langs + 1);
 
                /* add '*' */
                REALLOC_ARRAY(language_tags, num_langs + 1);
-               language_tags[num_langs++] = "*"; /* it's OK; this won't be freed */
+               language_tags[num_langs++] = xstrdup("*");
 
                /* compute decimal_places */
                for (max_q = 1, decimal_places = 0;
 
                /* compute decimal_places */
                for (max_q = 1, decimal_places = 0;
@@ -1976,8 +2082,7 @@ static void write_accept_language(struct strbuf *buf)
                }
        }
 
                }
        }
 
-       /* free language tags -- last one is a static '*' */
-       for (i = 0; i < num_langs - 1; i++)
+       for (i = 0; i < num_langs; i++)
                free(language_tags[i]);
        free(language_tags);
 }
                free(language_tags[i]);
        free(language_tags);
 }
@@ -2066,11 +2171,15 @@ static int http_request(const char *url,
        /* Add additional headers here */
        if (options && options->extra_headers) {
                const struct string_list_item *item;
        /* Add additional headers here */
        if (options && options->extra_headers) {
                const struct string_list_item *item;
-               for_each_string_list_item(item, options->extra_headers) {
-                       headers = curl_slist_append(headers, item->string);
+               if (options && options->extra_headers) {
+                       for_each_string_list_item(item, options->extra_headers) {
+                               headers = curl_slist_append(headers, item->string);
+                       }
                }
        }
 
                }
        }
 
+       headers = http_append_auth_header(&http_auth, headers);
+
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
        curl_easy_setopt(slot->curl, CURLOPT_URL, url);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
@@ -2152,7 +2261,13 @@ static int http_request_reauth(const char *url,
                               void *result, int target,
                               struct http_get_options *options)
 {
                               void *result, int target,
                               struct http_get_options *options)
 {
-       int ret = http_request(url, result, target, options);
+       int i = 3;
+       int ret;
+
+       if (always_auth_proactively())
+               credential_fill(&http_auth, 1);
+
+       ret = http_request(url, result, target, options);
 
        if (ret != HTTP_OK && ret != HTTP_REAUTH)
                return ret;
 
        if (ret != HTTP_OK && ret != HTTP_REAUTH)
                return ret;
@@ -2165,35 +2280,35 @@ static int http_request_reauth(const char *url,
                }
        }
 
                }
        }
 
-       if (ret != HTTP_REAUTH)
-               return ret;
-
-       /*
-        * The previous request may have put cruft into our output stream; we
-        * should clear it out before making our next request.
-        */
-       switch (target) {
-       case HTTP_REQUEST_STRBUF:
-               strbuf_reset(result);
-               break;
-       case HTTP_REQUEST_FILE:
-               if (fflush(result)) {
-                       error_errno("unable to flush a file");
-                       return HTTP_START_FAILED;
-               }
-               rewind(result);
-               if (ftruncate(fileno(result), 0) < 0) {
-                       error_errno("unable to truncate a file");
-                       return HTTP_START_FAILED;
+       while (ret == HTTP_REAUTH && --i) {
+               /*
+                * The previous request may have put cruft into our output stream; we
+                * should clear it out before making our next request.
+                */
+               switch (target) {
+               case HTTP_REQUEST_STRBUF:
+                       strbuf_reset(result);
+                       break;
+               case HTTP_REQUEST_FILE:
+                       if (fflush(result)) {
+                               error_errno("unable to flush a file");
+                               return HTTP_START_FAILED;
+                       }
+                       rewind(result);
+                       if (ftruncate(fileno(result), 0) < 0) {
+                               error_errno("unable to truncate a file");
+                               return HTTP_START_FAILED;
+                       }
+                       break;
+               default:
+                       BUG("Unknown http_request target");
                }
                }
-               break;
-       default:
-               BUG("Unknown http_request target");
-       }
 
 
-       credential_fill(&http_auth);
+               credential_fill(&http_auth, 1);
 
 
-       return http_request(url, result, target, options);
+               ret = http_request(url, result, target, options);
+       }
+       return ret;
 }
 
 int http_get_strbuf(const char *url,
 }
 
 int http_get_strbuf(const char *url,
@@ -2370,6 +2485,7 @@ void release_http_pack_request(struct http_pack_request *preq)
        }
        preq->slot = NULL;
        strbuf_release(&preq->tmpfile);
        }
        preq->slot = NULL;
        strbuf_release(&preq->tmpfile);
+       curl_slist_free_all(preq->headers);
        free(preq->url);
        free(preq);
 }
        free(preq->url);
        free(preq);
 }
@@ -2454,11 +2570,11 @@ struct http_pack_request *new_direct_http_pack_request(
        }
 
        preq->slot = get_active_slot();
        }
 
        preq->slot = get_active_slot();
+       preq->headers = object_request_headers();
        curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEDATA, preq->packfile);
        curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
        curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
        curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEDATA, preq->packfile);
        curl_easy_setopt(preq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite);
        curl_easy_setopt(preq->slot->curl, CURLOPT_URL, preq->url);
-       curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER,
-               no_pragma_header);
+       curl_easy_setopt(preq->slot->curl, CURLOPT_HTTPHEADER, preq->headers);
 
        /*
         * If there is data present from a previous transfer attempt,
 
        /*
         * If there is data present from a previous transfer attempt,
@@ -2624,13 +2740,14 @@ struct http_object_request *new_http_object_request(const char *base_url,
        }
 
        freq->slot = get_active_slot();
        }
 
        freq->slot = get_active_slot();
+       freq->headers = object_request_headers();
 
        curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEDATA, freq);
        curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);
        curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
        curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
        curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
 
        curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEDATA, freq);
        curl_easy_setopt(freq->slot->curl, CURLOPT_FAILONERROR, 0);
        curl_easy_setopt(freq->slot->curl, CURLOPT_WRITEFUNCTION, fwrite_sha1_file);
        curl_easy_setopt(freq->slot->curl, CURLOPT_ERRORBUFFER, freq->errorstr);
        curl_easy_setopt(freq->slot->curl, CURLOPT_URL, freq->url);
-       curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, no_pragma_header);
+       curl_easy_setopt(freq->slot->curl, CURLOPT_HTTPHEADER, freq->headers);
 
        /*
         * If we have successfully processed data from a previous fetch
 
        /*
         * If we have successfully processed data from a previous fetch
@@ -2718,5 +2835,6 @@ void release_http_object_request(struct http_object_request *freq)
                release_active_slot(freq->slot);
                freq->slot = NULL;
        }
                release_active_slot(freq->slot);
                freq->slot = NULL;
        }
+       curl_slist_free_all(freq->headers);
        strbuf_release(&freq->tmpfile);
 }
        strbuf_release(&freq->tmpfile);
 }