Prompt for a username when an HTTP request 401s
authorScott Chacon <schacon@gmail.com>
Thu, 1 Apr 2010 22:14:35 +0000 (15:14 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 2 Apr 2010 06:24:59 +0000 (23:24 -0700)
When an HTTP request returns a 401, Git will currently fail with a
confusing message saying that it got a 401, which is not very
descriptive.

Currently if a user wants to use Git over HTTP, they have to use one
URL with the username in the URL (e.g. "http://user@host.com/repo.git")
for write access and another without the username for unauthenticated
read access (unless they want to be prompted for the password each
time). However, since the HTTP servers will return a 401 if an action
requires authentication, we can prompt for username and password if we
see this, allowing us to use a single URL for both purposes.

This patch changes http_request to prompt for the username and password,
then return HTTP_REAUTH so http_get_strbuf can try again. If it gets
a 401 even when a user/pass is supplied, http_request will now return
HTTP_NOAUTH which remote_curl can then use to display a more
intelligent error message that is less confusing.

Signed-off-by: Scott Chacon <schacon@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
http.c
http.h
remote-curl.c
diff --git a/http.c b/http.c
index 4814217c6401faa1fd8f13f0288758f57b5e3755..51253e15a160b8bade37d24308ac3de92e5abe89 100644 (file)
--- a/http.c
+++ b/http.c
@@ -815,7 +815,21 @@ static int http_request(const char *url, void *result, int target, int options)
                        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);
@@ -831,7 +845,11 @@ static int http_request(const char *url, void *result, int target, int options)
 
 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 --git a/http.h b/http.h
index 5c9441c10ce708be426afe7424d63dcbb68a49e2..2dd03e88b75e2f4dd7b328e5baf5359adebc945e 100644 (file)
--- a/http.h
+++ b/http.h
@@ -126,6 +126,8 @@ extern char *get_remote_object_url(const char *url, const char *hex,
 #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.
index b76bfcb3d3cdbbee2e3279a6696c7d6b526176d7..07827562b56e38be543201f80e77ea581f9428f3 100644 (file)
@@ -132,6 +132,8 @@ static struct discovery* discover_refs(const char *service)
        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");