Skip to content

Commit c93c92f

Browse files
peffjrn
authored andcommitted
http: update base URLs when we see redirects
If a caller asks the http_get_* functions to go to a particular URL and we end up elsewhere due to a redirect, the effective_url field can tell us where we went. It would be nice to remember this redirect and short-cut further requests for two reasons: 1. It's more efficient. Otherwise we spend an extra http round-trip to the server for each subsequent request, just to get redirected. 2. If we end up with an http 401 and are going to ask for credentials, it is to feed them to the redirect target. If the redirect is an http->https upgrade, this means our credentials may be provided on the http leg, just to end up redirected to https. And if the redirect crosses server boundaries, then curl will drop the credentials entirely as it follows the redirect. However, it, it is not enough to simply record the effective URL we saw and use that for subsequent requests. We were originally fed a "base" url like: http://example.com/foo.git and we want to figure out what the new base is, even though the URLs we see may be: original: http://example.com/foo.git/info/refs effective: http://example.com/bar.git/info/refs Subsequent requests will not be for "info/refs", but for other paths relative to the base. We must ask the caller to pass in the original base, and we must pass the redirected base back to the caller (so that it can generate more URLs from it). Furthermore, we need to feed the new base to the credential code, so that requests to credential helpers (or to the user) match the URL we will be requesting. This patch teaches http_request_reauth to do this munging. Since it is the caller who cares about making more URLs, it seems at first glance that callers could simply check effective_url themselves and handle it. However, since we need to update the credential struct before the second re-auth request, we have to do it inside http_request_reauth. Signed-off-by: Jeff King <[email protected]> Signed-off-by: Jonathan Nieder <[email protected]>
1 parent 7886896 commit c93c92f

File tree

2 files changed

+68
-0
lines changed

2 files changed

+68
-0
lines changed

http.c

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -904,11 +904,71 @@ static int http_request(const char *url,
904904
return ret;
905905
}
906906

907+
/*
908+
* Update the "base" url to a more appropriate value, as deduced by
909+
* redirects seen when requesting a URL starting with "url".
910+
*
911+
* The "asked" parameter is a URL that we asked curl to access, and must begin
912+
* with "base".
913+
*
914+
* The "got" parameter is the URL that curl reported to us as where we ended
915+
* up.
916+
*
917+
* Returns 1 if we updated the base url, 0 otherwise.
918+
*
919+
* Our basic strategy is to compare "base" and "asked" to find the bits
920+
* specific to our request. We then strip those bits off of "got" to yield the
921+
* new base. So for example, if our base is "http://example.com/foo.git",
922+
* and we ask for "http://example.com/foo.git/info/refs", we might end up
923+
* with "https://other.example.com/foo.git/info/refs". We would want the
924+
* new URL to become "https://other.example.com/foo.git".
925+
*
926+
* Note that this assumes a sane redirect scheme. It's entirely possible
927+
* in the example above to end up at a URL that does not even end in
928+
* "info/refs". In such a case we simply punt, as there is not much we can
929+
* do (and such a scheme is unlikely to represent a real git repository,
930+
* which means we are likely about to abort anyway).
931+
*/
932+
static int update_url_from_redirect(struct strbuf *base,
933+
const char *asked,
934+
const struct strbuf *got)
935+
{
936+
const char *tail;
937+
size_t tail_len;
938+
939+
if (!strcmp(asked, got->buf))
940+
return 0;
941+
942+
if (prefixcmp(asked, base->buf))
943+
die("BUG: update_url_from_redirect: %s is not a superset of %s",
944+
asked, base->buf);
945+
946+
tail = asked + base->len;
947+
tail_len = strlen(tail);
948+
949+
if (got->len < tail_len ||
950+
strcmp(tail, got->buf + got->len - tail_len))
951+
return 0; /* insane redirect scheme */
952+
953+
strbuf_reset(base);
954+
strbuf_add(base, got->buf, got->len - tail_len);
955+
return 1;
956+
}
957+
907958
static int http_request_reauth(const char *url,
908959
void *result, int target,
909960
struct http_get_options *options)
910961
{
911962
int ret = http_request(url, result, target, options);
963+
964+
if (options && options->effective_url && options->base_url) {
965+
if (update_url_from_redirect(options->base_url,
966+
url, options->effective_url)) {
967+
credential_from_url(&http_auth, options->base_url->buf);
968+
url = options->effective_url->buf;
969+
}
970+
}
971+
912972
if (ret != HTTP_REAUTH)
913973
return ret;
914974

http.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,14 @@ struct http_get_options {
139139
* redirects we followed.
140140
*/
141141
struct strbuf *effective_url;
142+
143+
/*
144+
* If both base_url and effective_url are non-NULL, the base URL will
145+
* be munged to reflect any redirections going from the requested url
146+
* to effective_url. See the definition of update_url_from_redirect
147+
* for details.
148+
*/
149+
struct strbuf *base_url;
142150
};
143151

144152
/* Return values for http_get_*() */

0 commit comments

Comments
 (0)