http: don't always prompt for password

When a username is already specified at the beginning of any HTTP transaction (e.g. "git push https://user@hosting.example.com/project.git" or "git ls-remote https://user@hosting.example.com/project.git"), the code interactively asks for a password before calling into the libcurl library. It is very likely that the reason why user included the username in the URL is because the user knows that it would require authentication to access the resource. Asking for the password upfront would save one roundtrip to get a 401 response, getting the password and then retrying the request. This is a reasonable optimization. HOWEVER. This is done even when $HOME/.netrc might have a corresponding entry to access the site, or the site does not require authentication to access the resource after all. But neither condition can be determined until we call into libcurl library (we do not read and parse $HOME/.netrc ourselves). In these cases, the user is forced to respond to the password prompt, only to give a password that is not used in the HTTP transaction. If the password is in $HOME/.netrc, an empty input would later let the libcurl layer to pick up the password from there, and if the resource does not require authentication, any input would be taken and then discarded without getting used. It is wasteful to ask this unused information to the end user. Reduce the confusion by not trying to optimize for this case and always incur roundtrip penalty. An alternative might be to document this and keep this round-trip optimization as-is. Signed-off-by: Stefan Naewe <stefan.naewe@gmail.com> Helped-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Stefan Naewe committed Nov 4, 2011 at 08:03 UTC 986bbc0842334f0e07731fa37f2a55d2930a5b8c
1 file changed +3 -4
http.c
+3 -4
@@ -279,8 +279,6 @@ static CURL *get_curl_handle(void)
279 curl_easy_setopt(result, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
280 #endif
281
282 - init_curl_http_auth(result);
283 -
282 if (ssl_cert != NULL)
283 curl_easy_setopt(result, CURLOPT_SSLCERT, ssl_cert);
284 if (has_cert_password())
@@ -846,7 +844,7 @@ static int http_request(const char *url, void *result, int target, int options)
844 else if (missing_target(&results))
845 ret = HTTP_MISSING_TARGET;
846 else if (results.http_code == 401) {
849 - if (user_name) {
847 + if (user_name && user_pass) {
848 ret = HTTP_NOAUTH;
849 } else {
850 /*
@@ -855,7 +853,8 @@ static int http_request(const char *url, void *result, int target, int options)
853 * but that is non-portable. Using git_getpass() can at least be stubbed
854 * on other platforms with a different implementation if/when necessary.
855 */
858 - user_name = xstrdup(git_getpass_with_description("Username", description));
856 + if (!user_name)
857 + user_name = xstrdup(git_getpass_with_description("Username", description));
858 init_curl_http_auth(slot->curl);
859 ret = HTTP_REAUTH;
860 }