Merge branch 'jk/http-walker-limit-redirect'
authorJunio C Hamano <gitster@pobox.com>
Mon, 19 Dec 2016 22:45:32 +0000 (14:45 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 19 Dec 2016 22:45:32 +0000 (14:45 -0800)
Update the error messages from the dumb-http client when it fails
to obtain loose objects; we used to give sensible error message
only upon 404 but we now forbid unexpected redirects that needs to
be reported with something sensible.

* jk/http-walker-limit-redirect:
http-walker: complain about non-404 loose object errors

1  2 
http.c
diff --combined http.c
index c4e14c0af1d85edfe1f70092eaeb7b18ebf9c736,5cd3ffd67f40e7e377bf688211105c1b966d4d60..051fe6e5ab77a5dc6e53dce7011d0fc445f15ab0
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -11,7 -11,6 +11,7 @@@
  #include "gettext.h"
  #include "transport.h"
  
 +static struct trace_key trace_curl = TRACE_KEY_INIT(CURL);
  #if LIBCURL_VERSION_NUM >= 0x070a08
  long int git_curl_ipresolve = CURL_IPRESOLVE_WHATEVER;
  #else
@@@ -90,18 -89,6 +90,18 @@@ static struct 
         * here, too
         */
  };
 +#if LIBCURL_VERSION_NUM >= 0x071600
 +static const char *curl_deleg;
 +static struct {
 +      const char *name;
 +      long curl_deleg_param;
 +} curl_deleg_levels[] = {
 +      { "none", CURLGSSAPI_DELEGATION_NONE },
 +      { "policy", CURLGSSAPI_DELEGATION_POLICY_FLAG },
 +      { "always", CURLGSSAPI_DELEGATION_FLAG },
 +};
 +#endif
 +
  static struct credential proxy_auth = CREDENTIAL_INIT;
  static const char *curl_proxyuserpwd;
  static const char *curl_cookie_file;
@@@ -215,13 -202,6 +215,13 @@@ static void finish_active_slot(struct a
                slot->callback_func(slot->callback_data);
  }
  
 +static void xmulti_remove_handle(struct active_request_slot *slot)
 +{
 +#ifdef USE_CURL_MULTI
 +      curl_multi_remove_handle(curlm, slot->curl);
 +#endif
 +}
 +
  #ifdef USE_CURL_MULTI
  static void process_curl_messages(void)
  {
                               slot->curl != curl_message->easy_handle)
                                slot = slot->next;
                        if (slot != NULL) {
 -                              curl_multi_remove_handle(curlm, slot->curl);
 +                              xmulti_remove_handle(slot);
                                slot->curl_result = curl_result;
                                finish_active_slot(slot);
                        } else {
@@@ -337,15 -317,6 +337,15 @@@ static int http_options(const char *var
                return 0;
        }
  
 +      if (!strcmp("http.delegation", var)) {
 +#if LIBCURL_VERSION_NUM >= 0x071600
 +              return git_config_string(&curl_deleg, var, value);
 +#else
 +              warning(_("Delegation control is not supported with cURL < 7.22.0"));
 +              return 0;
 +#endif
 +      }
 +
        if (!strcmp("http.pinnedpubkey", var)) {
  #if LIBCURL_VERSION_NUM >= 0x072c00
                return git_config_pathname(&ssl_pinnedkey, var, value);
  
  static void init_curl_http_auth(CURL *result)
  {
 -      if (!http_auth.username) {
 +      if (!http_auth.username || !*http_auth.username) {
                if (curl_empty_auth)
                        curl_easy_setopt(result, CURLOPT_USERPWD, ":");
                return;
@@@ -518,125 -489,6 +518,125 @@@ static void set_curl_keepalive(CURL *c
  }
  #endif
  
 +static void redact_sensitive_header(struct strbuf *header)
 +{
 +      const char *sensitive_header;
 +
 +      if (skip_prefix(header->buf, "Authorization:", &sensitive_header) ||
 +          skip_prefix(header->buf, "Proxy-Authorization:", &sensitive_header)) {
 +              /* The first token is the type, which is OK to log */
 +              while (isspace(*sensitive_header))
 +                      sensitive_header++;
 +              while (*sensitive_header && !isspace(*sensitive_header))
 +                      sensitive_header++;
 +              /* Everything else is opaque and possibly sensitive */
 +              strbuf_setlen(header,  sensitive_header - header->buf);
 +              strbuf_addstr(header, " <redacted>");
 +      }
 +}
 +
 +static void curl_dump_header(const char *text, unsigned char *ptr, size_t size, int hide_sensitive_header)
 +{
 +      struct strbuf out = STRBUF_INIT;
 +      struct strbuf **headers, **header;
 +
 +      strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n",
 +              text, (long)size, (long)size);
 +      trace_strbuf(&trace_curl, &out);
 +      strbuf_reset(&out);
 +      strbuf_add(&out, ptr, size);
 +      headers = strbuf_split_max(&out, '\n', 0);
 +
 +      for (header = headers; *header; header++) {
 +              if (hide_sensitive_header)
 +                      redact_sensitive_header(*header);
 +              strbuf_insert((*header), 0, text, strlen(text));
 +              strbuf_insert((*header), strlen(text), ": ", 2);
 +              strbuf_rtrim((*header));
 +              strbuf_addch((*header), '\n');
 +              trace_strbuf(&trace_curl, (*header));
 +      }
 +      strbuf_list_free(headers);
 +      strbuf_release(&out);
 +}
 +
 +static void curl_dump_data(const char *text, unsigned char *ptr, size_t size)
 +{
 +      size_t i;
 +      struct strbuf out = STRBUF_INIT;
 +      unsigned int width = 60;
 +
 +      strbuf_addf(&out, "%s, %10.10ld bytes (0x%8.8lx)\n",
 +              text, (long)size, (long)size);
 +      trace_strbuf(&trace_curl, &out);
 +
 +      for (i = 0; i < size; i += width) {
 +              size_t w;
 +
 +              strbuf_reset(&out);
 +              strbuf_addf(&out, "%s: ", text);
 +              for (w = 0; (w < width) && (i + w < size); w++) {
 +                      unsigned char ch = ptr[i + w];
 +
 +                      strbuf_addch(&out,
 +                                     (ch >= 0x20) && (ch < 0x80)
 +                                     ? ch : '.');
 +              }
 +              strbuf_addch(&out, '\n');
 +              trace_strbuf(&trace_curl, &out);
 +      }
 +      strbuf_release(&out);
 +}
 +
 +static int curl_trace(CURL *handle, curl_infotype type, char *data, size_t size, void *userp)
 +{
 +      const char *text;
 +      enum { NO_FILTER = 0, DO_FILTER = 1 };
 +
 +      switch (type) {
 +      case CURLINFO_TEXT:
 +              trace_printf_key(&trace_curl, "== Info: %s", data);
 +      default:                /* we ignore unknown types by default */
 +              return 0;
 +
 +      case CURLINFO_HEADER_OUT:
 +              text = "=> Send header";
 +              curl_dump_header(text, (unsigned char *)data, size, DO_FILTER);
 +              break;
 +      case CURLINFO_DATA_OUT:
 +              text = "=> Send data";
 +              curl_dump_data(text, (unsigned char *)data, size);
 +              break;
 +      case CURLINFO_SSL_DATA_OUT:
 +              text = "=> Send SSL data";
 +              curl_dump_data(text, (unsigned char *)data, size);
 +              break;
 +      case CURLINFO_HEADER_IN:
 +              text = "<= Recv header";
 +              curl_dump_header(text, (unsigned char *)data, size, NO_FILTER);
 +              break;
 +      case CURLINFO_DATA_IN:
 +              text = "<= Recv data";
 +              curl_dump_data(text, (unsigned char *)data, size);
 +              break;
 +      case CURLINFO_SSL_DATA_IN:
 +              text = "<= Recv SSL data";
 +              curl_dump_data(text, (unsigned char *)data, size);
 +              break;
 +      }
 +      return 0;
 +}
 +
 +void setup_curl_trace(CURL *handle)
 +{
 +      if (!trace_want(&trace_curl))
 +              return;
 +      curl_easy_setopt(handle, CURLOPT_VERBOSE, 1L);
 +      curl_easy_setopt(handle, CURLOPT_DEBUGFUNCTION, curl_trace);
 +      curl_easy_setopt(handle, CURLOPT_DEBUGDATA, NULL);
 +}
 +
 +
  static CURL *get_curl_handle(void)
  {
        CURL *result = curl_easy_init();
        curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
  #endif
  
 +#if LIBCURL_VERSION_NUM >= 0x071600
 +      if (curl_deleg) {
 +              int i;
 +              for (i = 0; i < ARRAY_SIZE(curl_deleg_levels); i++) {
 +                      if (!strcmp(curl_deleg, curl_deleg_levels[i].name)) {
 +                              curl_easy_setopt(result, CURLOPT_GSSAPI_DELEGATION,
 +                                              curl_deleg_levels[i].curl_deleg_param);
 +                              break;
 +                      }
 +              }
 +              if (i == ARRAY_SIZE(curl_deleg_levels))
 +                      warning("Unknown delegation method '%s': using default",
 +                              curl_deleg);
 +      }
 +#endif
 +
        if (http_proactive_auth)
                init_curl_http_auth(result);
  
                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, 1);
 +              curl_easy_setopt(result, CURLOPT_VERBOSE, 1L);
 +      setup_curl_trace(result);
  
        curl_easy_setopt(result, CURLOPT_USERAGENT,
                user_agent ? user_agent : git_user_agent());
         * precedence here, as in CURL.
         */
        if (!curl_http_proxy) {
 -              if (!strcmp(http_auth.protocol, "https")) {
 +              if (http_auth.protocol && !strcmp(http_auth.protocol, "https")) {
                        var_override(&curl_http_proxy, getenv("HTTPS_PROXY"));
                        var_override(&curl_http_proxy, getenv("https_proxy"));
                } else {
@@@ -937,7 -773,9 +937,7 @@@ void http_cleanup(void
        while (slot != NULL) {
                struct active_request_slot *next = slot->next;
                if (slot->curl != NULL) {
 -#ifdef USE_CURL_MULTI
 -                      curl_multi_remove_handle(curlm, slot->curl);
 -#endif
 +                      xmulti_remove_handle(slot);
                        curl_easy_cleanup(slot->curl);
                }
                free(slot);
@@@ -1086,8 -924,6 +1086,8 @@@ int start_active_slot(struct active_req
  
        if (curlm_result != CURLM_OK &&
            curlm_result != CURLM_CALL_MULTI_PERFORM) {
 +              warning("curl_multi_add_handle failed: %s",
 +                      curl_multi_strerror(curlm_result));
                active_requests--;
                slot->in_use = 0;
                return 0;
@@@ -1227,13 -1063,13 +1227,13 @@@ void run_active_slot(struct active_requ
  static void release_active_slot(struct active_request_slot *slot)
  {
        closedown_active_slot(slot);
 -      if (slot->curl && curl_session_count > min_curl_sessions) {
 -#ifdef USE_CURL_MULTI
 -              curl_multi_remove_handle(curlm, slot->curl);
 -#endif
 -              curl_easy_cleanup(slot->curl);
 -              slot->curl = NULL;
 -              curl_session_count--;
 +      if (slot->curl) {
 +              xmulti_remove_handle(slot);
 +              if (curl_session_count > min_curl_sessions) {
 +                      curl_easy_cleanup(slot->curl);
 +                      slot->curl = NULL;
 +                      curl_session_count--;
 +              }
        }
  #ifdef USE_CURL_MULTI
        fill_active_slots();
@@@ -2058,7 -1894,7 +2058,7 @@@ static size_t fwrite_sha1_file(char *pt
                if (c != CURLE_OK)
                        die("BUG: curl_easy_getinfo for HTTP code failed: %s",
                                curl_easy_strerror(c));
-               if (slot->http_code >= 400)
+               if (slot->http_code >= 300)
                        return size;
        }