Merge branch 'ms/http-no-more-failonerror'
authorJunio C Hamano <gitster@pobox.com>
Tue, 29 Jan 2019 20:47:55 +0000 (12:47 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 29 Jan 2019 20:47:55 +0000 (12:47 -0800)
Debugging help for http transport.

* ms/http-no-more-failonerror:
test: test GIT_CURL_VERBOSE=1 shows an error
remote-curl: unset CURLOPT_FAILONERROR
remote-curl: define struct for CURLOPT_WRITEFUNCTION
http: enable keep_error for HTTP requests
http: support file handles for HTTP_KEEP_ERROR

1  2 
http.c
remote-curl.c
diff --combined http.c
index 0b6807cef9aa0994be486581ecf18f7829ee4e19,b39921636a8cd1d9a734583c5ea4077ca762ddf2..954bebf6842510ea3fb83334ffe2684ade072f05
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -48,7 -48,6 +48,7 @@@ char curl_errorstr[CURL_ERROR_SIZE]
  
  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_cipherlist;
  static const char *ssl_version;
@@@ -285,9 -284,6 +285,9 @@@ static void process_curl_messages(void
  
  static int http_options(const char *var, const char *value, void *cb)
  {
 +      if (!strcmp("http.version", var)) {
 +              return git_config_string(&curl_http_version, var, value);
 +      }
        if (!strcmp("http.sslverify", var)) {
                curl_ssl_verify = git_config_bool(var, value);
                return 0;
@@@ -793,31 -789,6 +793,31 @@@ static long get_curl_allowed_protocols(
  }
  #endif
  
 +#if LIBCURL_VERSION_NUM >=0x072f00
 +static int get_curl_http_version_opt(const char *version_string, long *opt)
 +{
 +      int i;
 +      static struct {
 +              const char *name;
 +              long opt_token;
 +      } choice[] = {
 +              { "HTTP/1.1", CURL_HTTP_VERSION_1_1 },
 +              { "HTTP/2", CURL_HTTP_VERSION_2 }
 +      };
 +
 +      for (i = 0; i < ARRAY_SIZE(choice); i++) {
 +              if (!strcmp(version_string, choice[i].name)) {
 +                      *opt = choice[i].opt_token;
 +                      return 0;
 +              }
 +      }
 +
 +      warning("unknown value given to http.version: '%s'", version_string);
 +      return -1; /* not found */
 +}
 +
 +#endif
 +
  static CURL *get_curl_handle(void)
  {
        CURL *result = curl_easy_init();
                curl_easy_setopt(result, CURLOPT_SSL_VERIFYHOST, 2);
        }
  
 +#if LIBCURL_VERSION_NUM >= 0x072f00 // 7.47.0
 +    if (curl_http_version) {
 +              long opt;
 +              if (!get_curl_http_version_opt(curl_http_version, &opt)) {
 +                      /* Set request use http version */
 +                      curl_easy_setopt(result, CURLOPT_HTTP_VERSION, opt);
 +              }
 +    }
 +#endif
 +
  #if LIBCURL_VERSION_NUM >= 0x070907
        curl_easy_setopt(result, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
  #endif
@@@ -1876,8 -1837,6 +1876,6 @@@ static int http_request(const char *url
        strbuf_addstr(&buf, "Pragma:");
        if (options && options->no_cache)
                strbuf_addstr(&buf, " no-cache");
-       if (options && options->keep_error)
-               curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
        if (options && options->initial_request &&
            http_follow_config == HTTP_FOLLOW_INITIAL)
                curl_easy_setopt(slot->curl, CURLOPT_FOLLOWLOCATION, 1);
        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_FAILONERROR, 0);
  
        ret = run_one_slot(slot, &results);
  
@@@ -1989,19 -1949,26 +1988,26 @@@ static int http_request_reauth(const ch
                return ret;
  
        /*
-        * If we are using KEEP_ERROR, the previous request may have
-        * put cruft into our output stream; we should clear it out before
-        * making our next request. We only know how to do this for
-        * the strbuf case, but that is enough to satisfy current callers.
+        * The previous request may have put cruft into our output stream; we
+        * should clear it out before making our next request.
         */
-       if (options && options->keep_error) {
-               switch (target) {
-               case HTTP_REQUEST_STRBUF:
-                       strbuf_reset(result);
-                       break;
-               default:
-                       BUG("HTTP_KEEP_ERROR is only supported with strbufs");
+       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");
        }
  
        credential_fill(&http_auth);
@@@ -2353,7 -2320,7 +2359,7 @@@ struct http_object_request *new_http_ob
        hashcpy(freq->sha1, sha1);
        freq->localfile = -1;
  
 -      sha1_file_name(the_repository, &filename, sha1);
 +      loose_object_path(the_repository, &filename, sha1);
        strbuf_addf(&freq->tmpfile, "%s.temp", filename.buf);
  
        strbuf_addf(&prevfile, "%s.prev", filename.buf);
@@@ -2504,7 -2471,7 +2510,7 @@@ int finish_http_object_request(struct h
                unlink_or_warn(freq->tmpfile.buf);
                return -1;
        }
 -      sha1_file_name(the_repository, &filename, freq->sha1);
 +      loose_object_path(the_repository, &filename, freq->sha1);
        freq->rename = finalize_object_file(freq->tmpfile.buf, filename.buf);
        strbuf_release(&filename);
  
diff --combined remote-curl.c
index 90d565c8c5421eee7f929059e8e465ddfbe78dc2,91b39ca0988c154a576f753179c79628d2305435..6ff9c66b90bab5d5e8d45b5baeff99143196b7cd
@@@ -380,7 -380,6 +380,6 @@@ static struct discovery *discover_refs(
        http_options.extra_headers = &extra_headers;
        http_options.initial_request = 1;
        http_options.no_cache = 1;
-       http_options.keep_error = 1;
  
        http_ret = http_get_strbuf(refs_url.buf, &buffer, &http_options);
        switch (http_ret) {
@@@ -546,14 -545,30 +545,30 @@@ static curlioerr rpc_ioctl(CURL *handle
  }
  #endif
  
+ struct rpc_in_data {
+       struct rpc_state *rpc;
+       struct active_request_slot *slot;
+ };
+ /*
+  * A callback for CURLOPT_WRITEFUNCTION. The return value is the bytes consumed
+  * from ptr.
+  */
  static size_t rpc_in(char *ptr, size_t eltsize,
                size_t nmemb, void *buffer_)
  {
        size_t size = eltsize * nmemb;
-       struct rpc_state *rpc = buffer_;
+       struct rpc_in_data *data = buffer_;
+       long response_code;
+       if (curl_easy_getinfo(data->slot->curl, CURLINFO_RESPONSE_CODE,
+                             &response_code) != CURLE_OK)
+               return size;
+       if (response_code >= 300)
+               return size;
        if (size)
-               rpc->any_written = 1;
-       write_or_die(rpc->in, ptr, size);
+               data->rpc->any_written = 1;
+       write_or_die(data->rpc->in, ptr, size);
        return size;
  }
  
@@@ -617,8 -632,7 +632,8 @@@ static int probe_rpc(struct rpc_state *
        return err;
  }
  
 -static curl_off_t xcurl_off_t(size_t len) {
 +static curl_off_t xcurl_off_t(size_t len)
 +{
        uintmax_t size = len;
        if (size > maximum_signed_value_of_type(curl_off_t))
                die("cannot handle pushes this big");
@@@ -634,6 -648,7 +649,7 @@@ static int post_rpc(struct rpc_state *r
        size_t gzip_size = 0;
        int err, large_request = 0;
        int needs_100_continue = 0;
+       struct rpc_in_data rpc_in_data;
  
        /* Try to load the entire request, if we can fit it into the
         * allocated buffer space we can use HTTP/1.0 and avoid the
@@@ -766,7 -781,10 +782,10 @@@ retry
  
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
        curl_easy_setopt(slot->curl, CURLOPT_WRITEFUNCTION, rpc_in);
-       curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
+       rpc_in_data.rpc = rpc;
+       rpc_in_data.slot = slot;
+       curl_easy_setopt(slot->curl, CURLOPT_FILE, &rpc_in_data);
+       curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
  
  
        rpc->any_written = 0;