Merge branch 'jk/maint-http-half-auth-fetch'
authorJunio C Hamano <gitster@pobox.com>
Tue, 20 Nov 2012 18:30:17 +0000 (10:30 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 20 Nov 2012 18:30:17 +0000 (10:30 -0800)
Fixes fetch from servers that ask for auth only during the actual
packing phase. This is not really a recommended configuration, but it
cleans up the code at the same time.

* jk/maint-http-half-auth-fetch:
remote-curl: retry failed requests for auth even with gzip
remote-curl: hoist gzip buffer size to top of post_rpc

1  2 
remote-curl.c
t/lib-httpd/apache.conf
t/t5551-http-fetch.sh
diff --combined remote-curl.c
index 278fecdd9bd00bf0df0a3d1f35777546794bc92f,fac2befd86b30f2b90e6dfc1a6f851649e980cdd..d8b3600bdd0b96e364775113c63957a2936a3324
@@@ -95,16 -95,15 +95,16 @@@ static struct discovery* discover_refs(
        struct strbuf buffer = STRBUF_INIT;
        struct discovery *last = last_discovery;
        char *refs_url;
 -      int http_ret, is_http = 0, proto_git_candidate = 1;
 +      int http_ret, maybe_smart = 0;
  
        if (last && !strcmp(service, last->service))
                return last;
        free_discovery(last);
  
        strbuf_addf(&buffer, "%sinfo/refs", url);
 -      if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
 -              is_http = 1;
 +      if ((!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) &&
 +           git_env_bool("GIT_SMART_HTTP", 1)) {
 +              maybe_smart = 1;
                if (!strchr(url, '?'))
                        strbuf_addch(&buffer, '?');
                else
        refs_url = strbuf_detach(&buffer, NULL);
  
        http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
 -
 -      /* try again with "plain" url (no ? or & appended) */
 -      if (http_ret != HTTP_OK && http_ret != HTTP_NOAUTH) {
 -              free(refs_url);
 -              strbuf_reset(&buffer);
 -
 -              proto_git_candidate = 0;
 -              strbuf_addf(&buffer, "%sinfo/refs", url);
 -              refs_url = strbuf_detach(&buffer, NULL);
 -
 -              http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
 -      }
 -
        switch (http_ret) {
        case HTTP_OK:
                break;
        last->buf_alloc = strbuf_detach(&buffer, &last->len);
        last->buf = last->buf_alloc;
  
 -      if (is_http && proto_git_candidate
 -              && 5 <= last->len && last->buf[4] == '#') {
 +      if (maybe_smart && 5 <= last->len && last->buf[4] == '#') {
                /* smart HTTP response; validate that the service
                 * pkt-line matches our request.
                 */
@@@ -380,7 -393,7 +380,7 @@@ static int probe_rpc(struct rpc_state *
        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
        curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
        curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
 -      curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
 +      curl_easy_setopt(slot->curl, CURLOPT_ENCODING, NULL);
        curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, "0000");
        curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, 4);
        curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, headers);
@@@ -400,6 -413,7 +400,7 @@@ static int post_rpc(struct rpc_state *r
        struct curl_slist *headers = NULL;
        int use_gzip = rpc->gzip_request;
        char *gzip_body = NULL;
+       size_t gzip_size;
        int err, large_request = 0;
  
        /* Try to load the entire request, if we can fit it into the
@@@ -441,7 -455,7 +442,7 @@@ retry
        curl_easy_setopt(slot->curl, CURLOPT_NOBODY, 0);
        curl_easy_setopt(slot->curl, CURLOPT_POST, 1);
        curl_easy_setopt(slot->curl, CURLOPT_URL, rpc->service_url);
 -      curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "");
 +      curl_easy_setopt(slot->curl, CURLOPT_ENCODING, "gzip");
  
        if (large_request) {
                /* The request body is large and the size cannot be predicted.
                        fflush(stderr);
                }
  
+       } else if (gzip_body) {
+               /*
+                * If we are looping to retry authentication, then the previous
+                * run will have set up the headers and gzip buffer already,
+                * and we just need to send it.
+                */
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, gzip_size);
        } else if (use_gzip && 1024 < rpc->len) {
                /* The client backend isn't giving us compressed data so
                 * we can try to deflate it ourselves, this may save on.
                 * the transfer time.
                 */
-               size_t size;
                git_zstream stream;
                int ret;
  
                memset(&stream, 0, sizeof(stream));
                git_deflate_init_gzip(&stream, Z_BEST_COMPRESSION);
-               size = git_deflate_bound(&stream, rpc->len);
-               gzip_body = xmalloc(size);
+               gzip_size = git_deflate_bound(&stream, rpc->len);
+               gzip_body = xmalloc(gzip_size);
  
                stream.next_in = (unsigned char *)rpc->buf;
                stream.avail_in = rpc->len;
                stream.next_out = (unsigned char *)gzip_body;
-               stream.avail_out = size;
+               stream.avail_out = gzip_size;
  
                ret = git_deflate(&stream, Z_FINISH);
                if (ret != Z_STREAM_END)
                if (ret != Z_OK)
                        die("cannot deflate request; zlib end error %d", ret);
  
-               size = stream.total_out;
+               gzip_size = stream.total_out;
  
                headers = curl_slist_append(headers, "Content-Encoding: gzip");
                curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
-               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, size);
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, gzip_size);
  
                if (options.verbosity > 1) {
                        fprintf(stderr, "POST %s (gzip %lu to %lu bytes)\n",
                                rpc->service_name,
-                               (unsigned long)rpc->len, (unsigned long)size);
+                               (unsigned long)rpc->len, (unsigned long)gzip_size);
                        fflush(stderr);
                }
        } else {
        curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
  
        err = run_slot(slot);
-       if (err == HTTP_REAUTH && !large_request && !use_gzip)
+       if (err == HTTP_REAUTH && !large_request)
                goto retry;
        if (err != HTTP_OK)
                err = -1;
diff --combined t/lib-httpd/apache.conf
index 49d5d877ceeeba3738b0939f14d059c2a75ff3ae,15a3c7155571ec5c9cc3f3d9355787ab92004f35..fe76e84b74472a431eef59a1f4936cfc69367003
@@@ -42,9 -42,6 +42,9 @@@ ErrorLog error.lo
  </IfModule>
  </IfVersion>
  
 +PassEnv GIT_VALGRIND
 +PassEnv GIT_VALGRIND_OPTIONS
 +
  Alias /dumb/ www/
  Alias /auth/dumb/ www/auth/dumb/
  
@@@ -63,7 -60,7 +63,7 @@@
  </LocationMatch>
  ScriptAliasMatch /smart_*[^/]*/(.*) ${GIT_EXEC_PATH}/git-http-backend/$1
  <Directory ${GIT_EXEC_PATH}>
 -      Options None
 +      Options FollowSymlinks
  </Directory>
  <Files ${GIT_EXEC_PATH}/git-http-backend>
        Options ExecCGI
@@@ -99,6 -96,13 +99,13 @@@ SSLEngine O
        Require valid-user
  </LocationMatch>
  
+ <LocationMatch "^/auth-fetch/.*/git-upload-pack$">
+       AuthType Basic
+       AuthName "git-auth"
+       AuthUserFile passwd
+       Require valid-user
+ </LocationMatch>
  <IfDefine DAV>
        LoadModule dav_module modules/mod_dav.so
        LoadModule dav_fs_module modules/mod_dav_fs.so
diff --combined t/t5551-http-fetch.sh
index 5060879d6d93d1f10a1db690bd42c19ee5ec1ae4,5f174da3c7be992c7bc52ee42050775b41af9cc5..c5cd2e348c6fe47af7d94ef6852757c301f8097a
@@@ -32,14 -32,13 +32,14 @@@ setup_askpass_helpe
  cat >exp <<EOF
  > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
  > Accept: */*
 +> Accept-Encoding: gzip
  > Pragma: no-cache
  < HTTP/1.1 200 OK
  < Pragma: no-cache
  < Cache-Control: no-cache, max-age=0, must-revalidate
  < Content-Type: application/x-git-upload-pack-advertisement
  > POST /smart/repo.git/git-upload-pack HTTP/1.1
 -> Accept-Encoding: deflate, gzip
 +> Accept-Encoding: gzip
  > Content-Type: application/x-git-upload-pack-request
  > Accept: application/x-git-upload-pack-result
  > Content-Length: xxx
@@@ -130,24 -129,27 +130,39 @@@ test_expect_success 'clone from auth-on
        test_cmp expect actual
  '
  
+ test_expect_success 'clone from auth-only-for-objects repository' '
+       echo two >expect &&
+       set_askpass user@host &&
+       git clone --bare "$HTTPD_URL/auth-fetch/smart/repo.git" half-auth &&
+       expect_askpass both user@host &&
+       git --git-dir=half-auth log -1 --format=%s >actual &&
+       test_cmp expect actual
+ '
+ test_expect_success 'no-op half-auth fetch does not require a password' '
+       set_askpass wrong &&
+       git --git-dir=half-auth fetch &&
+       expect_askpass none
+ '
 +test_expect_success 'disable dumb http on server' '
 +      git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
 +              config http.getanyfile false
 +'
 +
 +test_expect_success 'GIT_SMART_HTTP can disable smart http' '
 +      (GIT_SMART_HTTP=0 &&
 +       export GIT_SMART_HTTP &&
 +       cd clone &&
 +       test_must_fail git fetch)
 +'
 +
  test -n "$GIT_TEST_LONG" && test_set_prereq EXPENSIVE
  
  test_expect_success EXPENSIVE 'create 50,000 tags in the repo' '
        (
        cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
 -      for i in `seq 50000`
 +      for i in `test_seq 50000`
        do
                echo "commit refs/heads/too-many-refs"
                echo "mark :$i"
        done | git fast-import --export-marks=marks &&
  
        # now assign tags to all the dangling commits we created above
 -      tag=$(perl -e "print \"bla\" x 30") &&
 +      tag=$("$PERL_PATH" -e "print \"bla\" x 30") &&
        sed -e "s/^:\(.\+\) \(.\+\)$/\2 refs\/tags\/$tag-\1/" <marks >>packed-refs
        )
  '