Merge branch 'jk/dumb-http-idx-fetch-fix'
authorJunio C Hamano <gitster@pobox.com>
Tue, 17 Feb 2015 18:15:24 +0000 (10:15 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 17 Feb 2015 18:15:25 +0000 (10:15 -0800)
A broken pack .idx file in the receiving repository prevented the
dumb http transport from fetching a good copy of it from the other
side.

* jk/dumb-http-idx-fetch-fix:
dumb-http: do not pass NULL path to parse_pack_index

1  2 
http.c
t/t5550-http-fetch-dumb.sh
diff --combined http.c
index 4ecf9e8f7b2ac87e96bc5faeaf9392d2b5248465,0031b3a939a5224cb74970e8f1e5a642678bb0eb..2cdf67d8307f3067673324647e681b0874565d55
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -1,4 -1,3 +1,4 @@@
 +#include "git-compat-util.h"
  #include "http.h"
  #include "pack.h"
  #include "sideband.h"
@@@ -62,9 -61,6 +62,9 @@@ static const char *user_agent
  
  static struct credential cert_auth = CREDENTIAL_INIT;
  static int ssl_cert_password_required;
 +#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
 +static unsigned long http_auth_methods = CURLAUTH_ANY;
 +#endif
  
  static struct curl_slist *pragma_header;
  static struct curl_slist *no_pragma_header;
@@@ -117,37 -113,6 +117,37 @@@ size_t fwrite_null(char *ptr, size_t el
        return eltsize * nmemb;
  }
  
 +static void closedown_active_slot(struct active_request_slot *slot)
 +{
 +      active_requests--;
 +      slot->in_use = 0;
 +}
 +
 +static void finish_active_slot(struct active_request_slot *slot)
 +{
 +      closedown_active_slot(slot);
 +      curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
 +
 +      if (slot->finished != NULL)
 +              (*slot->finished) = 1;
 +
 +      /* Store slot results so they can be read after the slot is reused */
 +      if (slot->results != NULL) {
 +              slot->results->curl_result = slot->curl_result;
 +              slot->results->http_code = slot->http_code;
 +#if LIBCURL_VERSION_NUM >= 0x070a08
 +              curl_easy_getinfo(slot->curl, CURLINFO_HTTPAUTH_AVAIL,
 +                                &slot->results->auth_avail);
 +#else
 +              slot->results->auth_avail = 0;
 +#endif
 +      }
 +
 +      /* Run callback if appropriate */
 +      if (slot->callback_func != NULL)
 +              slot->callback_func(slot->callback_data);
 +}
 +
  #ifdef USE_CURL_MULTI
  static void process_curl_messages(void)
  {
@@@ -335,9 -300,6 +335,9 @@@ static CURL *get_curl_handle(void
  {
        CURL *result = curl_easy_init();
  
 +      if (!result)
 +              die("curl_easy_init failed");
 +
        if (!curl_ssl_verify) {
                curl_easy_setopt(result, CURLOPT_SSL_VERIFYPEER, 0);
                curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 0);
@@@ -437,8 -399,7 +437,8 @@@ void http_init(struct remote *remote, c
        git_config(urlmatch_config_entry, &config);
        free(normalized_url);
  
 -      curl_global_init(CURL_GLOBAL_ALL);
 +      if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK)
 +              die("curl_global_init failed");
  
        http_proactive_auth = proactive_auth;
  
        }
  
        curlm = curl_multi_init();
 -      if (curlm == NULL) {
 -              fprintf(stderr, "Error creating curl multi handle.\n");
 -              exit(1);
 -      }
 +      if (!curlm)
 +              die("curl_multi_init failed");
  #endif
  
        if (getenv("GIT_SSL_NO_VERIFY"))
@@@ -614,9 -577,6 +614,9 @@@ struct active_request_slot *get_active_
        curl_easy_setopt(slot->curl, CURLOPT_UPLOAD, 0);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPGET, 1);
        curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 1);
 +#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
 +      curl_easy_setopt(slot->curl, CURLOPT_HTTPAUTH, http_auth_methods);
 +#endif
        if (http_auth.password)
                init_curl_http_auth(slot->curl);
  
@@@ -767,6 -727,12 +767,6 @@@ void run_active_slot(struct active_requ
  #endif
  }
  
 -static void closedown_active_slot(struct active_request_slot *slot)
 -{
 -      active_requests--;
 -      slot->in_use = 0;
 -}
 -
  static void release_active_slot(struct active_request_slot *slot)
  {
        closedown_active_slot(slot);
  #endif
  }
  
 -void finish_active_slot(struct active_request_slot *slot)
 -{
 -      closedown_active_slot(slot);
 -      curl_easy_getinfo(slot->curl, CURLINFO_HTTP_CODE, &slot->http_code);
 -
 -      if (slot->finished != NULL)
 -              (*slot->finished) = 1;
 -
 -      /* Store slot results so they can be read after the slot is reused */
 -      if (slot->results != NULL) {
 -              slot->results->curl_result = slot->curl_result;
 -              slot->results->http_code = slot->http_code;
 -#if LIBCURL_VERSION_NUM >= 0x070a08
 -              curl_easy_getinfo(slot->curl, CURLINFO_HTTPAUTH_AVAIL,
 -                                &slot->results->auth_avail);
 -#else
 -              slot->results->auth_avail = 0;
 -#endif
 -      }
 -
 -      /* Run callback if appropriate */
 -      if (slot->callback_func != NULL)
 -              slot->callback_func(slot->callback_data);
 -}
 -
  void finish_all_active_slots(void)
  {
        struct active_request_slot *slot = active_queue_head;
@@@ -845,7 -836,7 +845,7 @@@ char *get_remote_object_url(const char 
        return strbuf_detach(&buf, NULL);
  }
  
 -int handle_curl_result(struct slot_results *results)
 +static int handle_curl_result(struct slot_results *results)
  {
        /*
         * If we see a failing http code with CURLE_OK, we have turned off
                        credential_reject(&http_auth);
                        return HTTP_NOAUTH;
                } else {
 +#ifdef LIBCURL_CAN_HANDLE_AUTH_ANY
 +                      http_auth_methods &= ~CURLAUTH_GSSNEGOTIATE;
 +#endif
                        return HTTP_REAUTH;
                }
        } else {
@@@ -918,84 -906,6 +918,84 @@@ static CURLcode curlinfo_strbuf(CURL *c
        return ret;
  }
  
 +/*
 + * Check for and extract a content-type parameter. "raw"
 + * should be positioned at the start of the potential
 + * parameter, with any whitespace already removed.
 + *
 + * "name" is the name of the parameter. The value is appended
 + * to "out".
 + */
 +static int extract_param(const char *raw, const char *name,
 +                       struct strbuf *out)
 +{
 +      size_t len = strlen(name);
 +
 +      if (strncasecmp(raw, name, len))
 +              return -1;
 +      raw += len;
 +
 +      if (*raw != '=')
 +              return -1;
 +      raw++;
 +
 +      while (*raw && !isspace(*raw) && *raw != ';')
 +              strbuf_addch(out, *raw++);
 +      return 0;
 +}
 +
 +/*
 + * Extract a normalized version of the content type, with any
 + * spaces suppressed, all letters lowercased, and no trailing ";"
 + * or parameters.
 + *
 + * Note that we will silently remove even invalid whitespace. For
 + * example, "text / plain" is specifically forbidden by RFC 2616,
 + * but "text/plain" is the only reasonable output, and this keeps
 + * our code simple.
 + *
 + * If the "charset" argument is not NULL, store the value of any
 + * charset parameter there.
 + *
 + * Example:
 + *   "TEXT/PLAIN; charset=utf-8" -> "text/plain", "utf-8"
 + *   "text / plain" -> "text/plain"
 + */
 +static void extract_content_type(struct strbuf *raw, struct strbuf *type,
 +                               struct strbuf *charset)
 +{
 +      const char *p;
 +
 +      strbuf_reset(type);
 +      strbuf_grow(type, raw->len);
 +      for (p = raw->buf; *p; p++) {
 +              if (isspace(*p))
 +                      continue;
 +              if (*p == ';') {
 +                      p++;
 +                      break;
 +              }
 +              strbuf_addch(type, tolower(*p));
 +      }
 +
 +      if (!charset)
 +              return;
 +
 +      strbuf_reset(charset);
 +      while (*p) {
 +              while (isspace(*p) || *p == ';')
 +                      p++;
 +              if (!extract_param(p, "charset", charset))
 +                      return;
 +              while (*p && !isspace(*p))
 +                      p++;
 +      }
 +
 +      if (!charset->len && starts_with(type->buf, "text/"))
 +              strbuf_addstr(charset, "ISO-8859-1");
 +}
 +
 +
  /* http_request() targets */
  #define HTTP_REQUEST_STRBUF   0
  #define HTTP_REQUEST_FILE     1
@@@ -1047,13 -957,9 +1047,13 @@@ static int http_request(const char *url
  
        ret = run_one_slot(slot, &results);
  
 -      if (options && options->content_type)
 -              curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE,
 -                              options->content_type);
 +      if (options && options->content_type) {
 +              struct strbuf raw = STRBUF_INIT;
 +              curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE, &raw);
 +              extract_content_type(&raw, options->content_type,
 +                                   options->charset);
 +              strbuf_release(&raw);
 +      }
  
        if (options && options->effective_url)
                curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL,
@@@ -1100,10 -1006,11 +1100,10 @@@ static int update_url_from_redirect(str
        if (!strcmp(asked, got->buf))
                return 0;
  
 -      if (!starts_with(asked, base->buf))
 +      if (!skip_prefix(asked, base->buf, &tail))
                die("BUG: update_url_from_redirect: %s is not a superset of %s",
                    asked, base->buf);
  
 -      tail = asked + base->len;
        tail_len = strlen(tail);
  
        if (got->len < tail_len ||
@@@ -1250,7 -1157,7 +1250,7 @@@ static int fetch_and_setup_pack_index(s
        int ret;
  
        if (has_pack_index(sha1)) {
-               new_pack = parse_pack_index(sha1, NULL);
+               new_pack = parse_pack_index(sha1, sha1_pack_index_name(sha1));
                if (!new_pack)
                        return -1; /* parse_pack_index() already issued error message */
                goto add_pack;
@@@ -1345,7 -1252,7 +1345,7 @@@ int finish_http_pack_request(struct htt
        struct packed_git **lst;
        struct packed_git *p = preq->target;
        char *tmp_idx;
 -      struct child_process ip;
 +      struct child_process ip = CHILD_PROCESS_INIT;
        const char *ip_argv[8];
  
        close_pack_index(p);
        ip_argv[3] = preq->tmpfile;
        ip_argv[4] = NULL;
  
 -      memset(&ip, 0, sizeof(ip));
        ip.argv = ip_argv;
        ip.git_cmd = 1;
        ip.no_stdin = 1;
index ac71418a1b26bc17a1252d6831869b96a8d8c176,c3ba7ed0fea8e43c65d46cfdce985839e8729874..6da942243101c4f5358b4aaf3c62cf6390c258b4
@@@ -165,36 -165,29 +165,54 @@@ test_expect_success 'fetch notices corr
        )
  '
  
+ test_expect_success 'fetch can handle previously-fetched .idx files' '
+       git checkout --orphan branch1 &&
+       echo base >file &&
+       git add file &&
+       git commit -m base &&
+       git --bare init "$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git &&
+       git push "$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git branch1 &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git repack -d &&
+       git checkout -b branch2 branch1 &&
+       echo b2 >>file &&
+       git commit -a -m b2 &&
+       git push "$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git branch2 &&
+       git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH"/repo_packed_branches.git repack -d &&
+       git --bare init clone_packed_branches.git &&
+       git --git-dir=clone_packed_branches.git fetch "$HTTPD_URL"/dumb/repo_packed_branches.git branch1:branch1 &&
+       git --git-dir=clone_packed_branches.git fetch "$HTTPD_URL"/dumb/repo_packed_branches.git branch2:branch2
+ '
  test_expect_success 'did not use upload-pack service' '
        grep '/git-upload-pack' <"$HTTPD_ROOT_PATH"/access.log >act
        : >exp
        test_cmp exp act
  '
  
 +test_expect_success 'git client shows text/plain errors' '
 +      test_must_fail git clone "$HTTPD_URL/error/text" 2>stderr &&
 +      grep "this is the error message" stderr
 +'
 +
 +test_expect_success 'git client does not show html errors' '
 +      test_must_fail git clone "$HTTPD_URL/error/html" 2>stderr &&
 +      ! grep "this is the error message" stderr
 +'
 +
 +test_expect_success 'git client shows text/plain with a charset' '
 +      test_must_fail git clone "$HTTPD_URL/error/charset" 2>stderr &&
 +      grep "this is the error message" stderr
 +'
 +
 +test_expect_success 'http error messages are reencoded' '
 +      test_must_fail git clone "$HTTPD_URL/error/utf16" 2>stderr &&
 +      grep "this is the error message" stderr
 +'
 +
 +test_expect_success 'reencoding is robust to whitespace oddities' '
 +      test_must_fail git clone "$HTTPD_URL/error/odd-spacing" 2>stderr &&
 +      grep "this is the error message" stderr
 +'
 +
  stop_httpd
  test_done