summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--http.c60
-rw-r--r--http.h8
2 files changed, 68 insertions, 0 deletions
diff --git a/http.c b/http.c
index 1569c56..5199e60 100644
--- a/http.c
+++ b/http.c
@@ -904,11 +904,71 @@ static int http_request(const char *url,
return ret;
}
+/*
+ * Update the "base" url to a more appropriate value, as deduced by
+ * redirects seen when requesting a URL starting with "url".
+ *
+ * The "asked" parameter is a URL that we asked curl to access, and must begin
+ * with "base".
+ *
+ * The "got" parameter is the URL that curl reported to us as where we ended
+ * up.
+ *
+ * Returns 1 if we updated the base url, 0 otherwise.
+ *
+ * Our basic strategy is to compare "base" and "asked" to find the bits
+ * specific to our request. We then strip those bits off of "got" to yield the
+ * new base. So for example, if our base is "http://example.com/foo.git",
+ * and we ask for "http://example.com/foo.git/info/refs", we might end up
+ * with "https://other.example.com/foo.git/info/refs". We would want the
+ * new URL to become "https://other.example.com/foo.git".
+ *
+ * Note that this assumes a sane redirect scheme. It's entirely possible
+ * in the example above to end up at a URL that does not even end in
+ * "info/refs". In such a case we simply punt, as there is not much we can
+ * do (and such a scheme is unlikely to represent a real git repository,
+ * which means we are likely about to abort anyway).
+ */
+static int update_url_from_redirect(struct strbuf *base,
+ const char *asked,
+ const struct strbuf *got)
+{
+ const char *tail;
+ size_t tail_len;
+
+ if (!strcmp(asked, got->buf))
+ return 0;
+
+ if (prefixcmp(asked, base->buf))
+ die("BUG: update_url_from_redirect: %s is not a superset of %s",
+ asked, base->buf);
+
+ tail = asked + base->len;
+ tail_len = strlen(tail);
+
+ if (got->len < tail_len ||
+ strcmp(tail, got->buf + got->len - tail_len))
+ return 0; /* insane redirect scheme */
+
+ strbuf_reset(base);
+ strbuf_add(base, got->buf, got->len - tail_len);
+ return 1;
+}
+
static int http_request_reauth(const char *url,
void *result, int target,
struct http_get_options *options)
{
int ret = http_request(url, result, target, options);
+
+ if (options && options->effective_url && options->base_url) {
+ if (update_url_from_redirect(options->base_url,
+ url, options->effective_url)) {
+ credential_from_url(&http_auth, options->base_url->buf);
+ url = options->effective_url->buf;
+ }
+ }
+
if (ret != HTTP_REAUTH)
return ret;
diff --git a/http.h b/http.h
index 974ede7..12ca5c8 100644
--- a/http.h
+++ b/http.h
@@ -139,6 +139,14 @@ struct http_get_options {
* redirects we followed.
*/
struct strbuf *effective_url;
+
+ /*
+ * If both base_url and effective_url are non-NULL, the base URL will
+ * be munged to reflect any redirections going from the requested url
+ * to effective_url. See the definition of update_url_from_redirect
+ * for details.
+ */
+ struct strbuf *base_url;
};
/* Return values for http_get_*() */