Code

Merge branch 'rc/maint-curl-helper'
authorJunio C Hamano <gitster@pobox.com>
Sun, 9 May 2010 05:37:24 +0000 (22:37 -0700)
committerJunio C Hamano <gitster@pobox.com>
Sun, 9 May 2010 05:37:24 +0000 (22:37 -0700)
* rc/maint-curl-helper:
  remote-curl: ensure that URLs have a trailing slash
  http: make end_url_with_slash() public
  t5541-http-push: add test for URLs with trailing slash

Conflicts:
remote-curl.c

1  2 
http.c
http.h
remote-curl.c
t/t5541-http-push.sh

diff --combined http.c
index 51253e15a160b8bade37d24308ac3de92e5abe89,163c0e48e471430adea36b9b63a8996de62d71b1..07a03fd79b5475f13d21f2b3a6825df7246188bf
--- 1/http.c
--- 2/http.c
+++ b/http.c
@@@ -204,7 -204,7 +204,7 @@@ static void init_curl_http_auth(CURL *r
        if (user_name) {
                struct strbuf up = STRBUF_INIT;
                if (!user_pass)
 -                      user_pass = xstrdup(getpass("Password: "));
 +                      user_pass = xstrdup(git_getpass("Password: "));
                strbuf_addf(&up, "%s:%s", user_name, user_pass);
                curl_easy_setopt(result, CURLOPT_USERPWD,
                                 strbuf_detach(&up, NULL));
@@@ -219,7 -219,7 +219,7 @@@ static int has_cert_password(void
                return 0;
        /* Only prompt the user once. */
        ssl_cert_password_required = -1;
 -      ssl_cert_password = getpass("Certificate Password: ");
 +      ssl_cert_password = git_getpass("Certificate Password: ");
        if (ssl_cert_password != NULL) {
                ssl_cert_password = xstrdup(ssl_cert_password);
                return 1;
@@@ -720,7 -720,7 +720,7 @@@ static inline int hex(int v
                return 'A' + v - 10;
  }
  
static void end_url_with_slash(struct strbuf *buf, const char *url)
+ void end_url_with_slash(struct strbuf *buf, const char *url)
  {
        strbuf_addstr(buf, url);
        if (buf->len && buf->buf[buf->len - 1] != '/')
@@@ -815,21 -815,7 +815,21 @@@ static int http_request(const char *url
                        ret = HTTP_OK;
                else if (missing_target(&results))
                        ret = HTTP_MISSING_TARGET;
 -              else
 +              else if (results.http_code == 401) {
 +                      if (user_name) {
 +                              ret = HTTP_NOAUTH;
 +                      } else {
 +                              /*
 +                               * git_getpass is needed here because its very likely stdin/stdout are
 +                               * pipes to our parent process.  So we instead need to use /dev/tty,
 +                               * but that is non-portable.  Using git_getpass() can at least be stubbed
 +                               * on other platforms with a different implementation if/when necessary.
 +                               */
 +                              user_name = xstrdup(git_getpass("Username: "));
 +                              init_curl_http_auth(slot->curl);
 +                              ret = HTTP_REAUTH;
 +                      }
 +              } else
                        ret = HTTP_ERROR;
        } else {
                error("Unable to start HTTP request for %s", url);
  
  int http_get_strbuf(const char *url, struct strbuf *result, int options)
  {
 -      return http_request(url, result, HTTP_REQUEST_STRBUF, options);
 +      int http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
 +      if (http_ret == HTTP_REAUTH) {
 +              http_ret = http_request(url, result, HTTP_REQUEST_STRBUF, options);
 +      }
 +      return http_ret;
  }
  
  /*
diff --combined http.h
index 2dd03e88b75e2f4dd7b328e5baf5359adebc945e,c78cacb9c3543caf1f59b9068cac2b053ef088f9..37a6a6a3b0cd0ddbbf395230f04b94fc54191dbe
--- 1/http.h
--- 2/http.h
+++ b/http.h
@@@ -117,6 -117,7 +117,7 @@@ extern void append_remote_object_url(st
                                     int only_two_digit_prefix);
  extern char *get_remote_object_url(const char *url, const char *hex,
                                   int only_two_digit_prefix);
+ extern void end_url_with_slash(struct strbuf *buf, const char *url);
  
  /* Options for http_request_*() */
  #define HTTP_NO_CACHE         1
  #define HTTP_MISSING_TARGET   1
  #define HTTP_ERROR            2
  #define HTTP_START_FAILED     3
 +#define HTTP_REAUTH   4
 +#define HTTP_NOAUTH   5
  
  /*
   * Requests an url and stores the result in a strbuf.
diff --combined remote-curl.c
index 07827562b56e38be543201f80e77ea581f9428f3,1fe59d6db0e85ab938959f08d3bca4a83f748ab5..24fbb9a9b972c1078b3688b2d0683c9704e09ee6
@@@ -9,7 -9,8 +9,7 @@@
  #include "sideband.h"
  
  static struct remote *remote;
- static const char *url;
+ static const char *url; /* always ends with a trailing slash */
 -static struct walker *walker;
  
  struct options {
        int verbosity;
  };
  static struct options options;
  
 -static void init_walker(void)
 -{
 -      if (!walker)
 -              walker = get_http_walker(url, remote);
 -}
 -
  static int set_option(const char *name, const char *value)
  {
        if (!strcmp(name, "verbosity")) {
@@@ -101,7 -108,7 +101,7 @@@ static struct discovery* discover_refs(
                return last;
        free_discovery(last);
  
-       strbuf_addf(&buffer, "%s/info/refs", url);
+       strbuf_addf(&buffer, "%sinfo/refs", url);
        if (!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) {
                is_http = 1;
                if (!strchr(url, '?'))
        }
        refs_url = strbuf_detach(&buffer, NULL);
  
 -      init_walker();
        http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
  
        /* try again with "plain" url (no ? or & appended) */
                strbuf_reset(&buffer);
  
                proto_git_candidate = 0;
-               strbuf_addf(&buffer, "%s/info/refs", url);
+               strbuf_addf(&buffer, "%sinfo/refs", url);
                refs_url = strbuf_detach(&buffer, NULL);
  
                http_ret = http_get_strbuf(refs_url, &buffer, HTTP_NO_CACHE);
        case HTTP_MISSING_TARGET:
                die("%s not found: did you run git update-server-info on the"
                    " server?", refs_url);
 +      case HTTP_NOAUTH:
 +              die("Authentication failed");
        default:
                http_error(refs_url, http_ret);
                die("HTTP request failed");
        return last;
  }
  
 -static int write_discovery(int fd, void *data)
 +static int write_discovery(int in, int out, void *data)
  {
        struct discovery *heads = data;
        int err = 0;
 -      if (write_in_full(fd, heads->buf, heads->len) != heads->len)
 +      if (write_in_full(out, heads->buf, heads->len) != heads->len)
                err = 1;
 -      close(fd);
 +      close(out);
        return err;
  }
  
@@@ -196,7 -202,6 +196,7 @@@ static struct ref *parse_git_refs(struc
        memset(&async, 0, sizeof(async));
        async.proc = write_discovery;
        async.data = heads;
 +      async.out = -1;
  
        if (start_async(&async))
                die("cannot start thread to parse advertised refs");
@@@ -244,8 -249,9 +244,8 @@@ static struct ref *parse_info_refs(stru
                i++;
        }
  
 -      init_walker();
        ref = alloc_ref("HEAD");
 -      if (!walker->fetch_ref(walker, ref) &&
 +      if (!http_fetch_ref(url, ref) &&
            !resolve_remote_symref(ref, refs)) {
                ref->next = refs;
                refs = ref;
@@@ -495,6 -501,7 +495,6 @@@ static int rpc_service(struct rpc_stat
        struct child_process client;
        int err = 0;
  
 -      init_walker();
        memset(&client, 0, sizeof(client));
        client.in = -1;
        client.out = -1;
        rpc->out = client.out;
        strbuf_init(&rpc->result, 0);
  
-       strbuf_addf(&buf, "%s/%s", url, svc);
+       strbuf_addf(&buf, "%s%s", url, svc);
        rpc->service_url = strbuf_detach(&buf, NULL);
  
        strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
  
  static int fetch_dumb(int nr_heads, struct ref **to_fetch)
  {
 +      struct walker *walker;
        char **targets = xmalloc(nr_heads * sizeof(char*));
        int ret, i;
  
        for (i = 0; i < nr_heads; i++)
                targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
  
 -      init_walker();
 +      walker = get_http_walker(url);
        walker->get_all = 1;
        walker->get_tree = 1;
        walker->get_history = 1;
        walker->get_verbosely = options.verbosity >= 3;
        walker->get_recover = 0;
        ret = walker_fetch(walker, nr_heads, targets, NULL, NULL);
 +      walker_free(walker);
  
        for (i = 0; i < nr_heads; i++)
                free(targets[i]);
@@@ -800,13 -805,13 +800,15 @@@ int main(int argc, const char **argv
        remote = remote_get(argv[1]);
  
        if (argc > 2) {
-               url = argv[2];
+               end_url_with_slash(&buf, argv[2]);
        } else {
-               url = remote->url[0];
+               end_url_with_slash(&buf, remote->url[0]);
        }
  
+       url = strbuf_detach(&buf, NULL);
 +      http_init(remote);
 +
        do {
                if (strbuf_getline(&buf, stdin, '\n') == EOF)
                        break;
                }
                strbuf_reset(&buf);
        } while (1);
 +
 +      http_cleanup();
 +
        return 0;
  }
diff --combined t/t5541-http-push.sh
index 795dc2bcdf98e582dd2f05d901b791ab4225ce3b,f0c50d3d774dcbd2449988c65e0813cf25366b89..17e1bdc5a81ce4802f5074ddb9c1996c68fc97be
@@@ -34,8 -34,34 +34,34 @@@ test_expect_success 'setup remote repos
        mv test_repo.git "$HTTPD_DOCUMENT_ROOT_PATH"
  '
  
- test_expect_success 'clone remote repository' '
+ cat >exp <<EOF
+ GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+ POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
+ EOF
+ test_expect_success 'no empty path components' '
+       # In the URL, add a trailing slash, and see if git appends yet another
+       # slash.
        cd "$ROOT_PATH" &&
+       git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
+       sed -e "
+               s/^.* \"//
+               s/\"//
+               s/ [1-9][0-9]*\$//
+               s/^GET /GET  /
+       " >act <"$HTTPD_ROOT_PATH"/access.log &&
+       # Clear the log, so that it does not affect the "used receive-pack
+       # service" test which reads the log too.
+       #
+       # We do this before the actual comparison to ensure the log is cleared.
+       echo > "$HTTPD_ROOT_PATH"/access.log &&
+       test_cmp exp act
+ '
+ test_expect_success 'clone remote repository' '
+       rm -rf test_repo_clone &&
        git clone $HTTPD_URL/smart/test_repo.git test_repo_clone
  '
  
@@@ -68,6 -94,7 +94,7 @@@ test_expect_success 'create and delete 
  '
  
  cat >exp <<EOF
  GET  /smart/test_repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
  POST /smart/test_repo.git/git-upload-pack HTTP/1.1 200
  GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
@@@ -88,8 -115,26 +115,8 @@@ test_expect_success 'used receive-pack 
        test_cmp exp act
  '
  
 -test_expect_success 'non-fast-forward push fails' '
 -      cd "$ROOT_PATH"/test_repo_clone &&
 -      git checkout master &&
 -      echo "changed" > path2 &&
 -      git commit -a -m path2 --amend &&
 -
 -      HEAD=$(git rev-parse --verify HEAD) &&
 -      !(git push -v origin >output 2>&1) &&
 -      (cd "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git &&
 -       test $HEAD != $(git rev-parse --verify HEAD))
 -'
 -
 -test_expect_success 'non-fast-forward push show ref status' '
 -      grep "^ ! \[rejected\][ ]*master -> master (non-fast-forward)$" output
 -'
 -
 -test_expect_success 'non-fast-forward push shows help message' '
 -      grep "To prevent you from losing history, non-fast-forward updates were rejected" \
 -              output
 -'
 +test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
 +      "$ROOT_PATH"/test_repo_clone master
  
  test_expect_success 'push fails for non-fast-forward refs unmatched by remote helper' '
        # create a dissimilarly-named remote ref so that git is unable to match the