Skip to content

Commit 177f0a4

Browse files
committed
Merge branch 'jk/http-auth-redirects'
Handle the case where http transport gets redirected during the authorization request better. * jk/http-auth-redirects: http.c: Spell the null pointer as NULL remote-curl: rewrite base url from info/refs redirects remote-curl: store url as a strbuf remote-curl: make refs_url a strbuf http: update base URLs when we see redirects http: provide effective url to callers http: hoist credential request out of handle_curl_result http: refactor options to http_get_* http_request: factor out curlinfo_strbuf http_get_file: style fixes
2 parents 0d6cf24 + 70900ed commit 177f0a4

File tree

7 files changed

+191
-65
lines changed

7 files changed

+191
-65
lines changed

http-push.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,7 +1542,7 @@ static int remote_exists(const char *path)
15421542

15431543
sprintf(url, "%s%s", repo->url, path);
15441544

1545-
switch (http_get_strbuf(url, NULL, NULL, 0)) {
1545+
switch (http_get_strbuf(url, NULL, NULL)) {
15461546
case HTTP_OK:
15471547
ret = 1;
15481548
break;
@@ -1566,7 +1566,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
15661566
url = xmalloc(strlen(repo->url) + strlen(path) + 1);
15671567
sprintf(url, "%s%s", repo->url, path);
15681568

1569-
if (http_get_strbuf(url, NULL, &buffer, 0) != HTTP_OK)
1569+
if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK)
15701570
die("Couldn't get %s for remote symref\n%s", url,
15711571
curl_errorstr);
15721572
free(url);

http.c

Lines changed: 108 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ static int curl_ftp_no_epsv;
4747
static const char *curl_http_proxy;
4848
static const char *curl_cookie_file;
4949
static int curl_save_cookies;
50-
static struct credential http_auth = CREDENTIAL_INIT;
50+
struct credential http_auth = CREDENTIAL_INIT;
5151
static int http_proactive_auth;
5252
static const char *user_agent;
5353

@@ -861,7 +861,6 @@ int handle_curl_result(struct slot_results *results)
861861
credential_reject(&http_auth);
862862
return HTTP_NOAUTH;
863863
} else {
864-
credential_fill(&http_auth);
865864
return HTTP_REAUTH;
866865
}
867866
} else {
@@ -875,12 +874,25 @@ int handle_curl_result(struct slot_results *results)
875874
}
876875
}
877876

877+
static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
878+
{
879+
char *ptr;
880+
CURLcode ret;
881+
882+
strbuf_reset(buf);
883+
ret = curl_easy_getinfo(curl, info, &ptr);
884+
if (!ret && ptr)
885+
strbuf_addstr(buf, ptr);
886+
return ret;
887+
}
888+
878889
/* http_request() targets */
879890
#define HTTP_REQUEST_STRBUF 0
880891
#define HTTP_REQUEST_FILE 1
881892

882-
static int http_request(const char *url, struct strbuf *type,
883-
void *result, int target, int options)
893+
static int http_request(const char *url,
894+
void *result, int target,
895+
const struct http_get_options *options)
884896
{
885897
struct active_request_slot *slot;
886898
struct slot_results results;
@@ -913,9 +925,9 @@ static int http_request(const char *url, struct strbuf *type,
913925
}
914926

915927
strbuf_addstr(&buf, "Pragma:");
916-
if (options & HTTP_NO_CACHE)
928+
if (options && options->no_cache)
917929
strbuf_addstr(&buf, " no-cache");
918-
if (options & HTTP_KEEP_ERROR)
930+
if (options && options->keep_error)
919931
curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
920932

921933
headers = curl_slist_append(headers, buf.buf);
@@ -933,26 +945,85 @@ static int http_request(const char *url, struct strbuf *type,
933945
ret = HTTP_START_FAILED;
934946
}
935947

936-
if (type) {
937-
char *t;
938-
strbuf_reset(type);
939-
curl_easy_getinfo(slot->curl, CURLINFO_CONTENT_TYPE, &t);
940-
if (t)
941-
strbuf_addstr(type, t);
942-
}
948+
if (options && options->content_type)
949+
curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE,
950+
options->content_type);
951+
952+
if (options && options->effective_url)
953+
curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL,
954+
options->effective_url);
943955

944956
curl_slist_free_all(headers);
945957
strbuf_release(&buf);
946958

947959
return ret;
948960
}
949961

962+
/*
963+
* Update the "base" url to a more appropriate value, as deduced by
964+
* redirects seen when requesting a URL starting with "url".
965+
*
966+
* The "asked" parameter is a URL that we asked curl to access, and must begin
967+
* with "base".
968+
*
969+
* The "got" parameter is the URL that curl reported to us as where we ended
970+
* up.
971+
*
972+
* Returns 1 if we updated the base url, 0 otherwise.
973+
*
974+
* Our basic strategy is to compare "base" and "asked" to find the bits
975+
* specific to our request. We then strip those bits off of "got" to yield the
976+
* new base. So for example, if our base is "http://example.com/foo.git",
977+
* and we ask for "http://example.com/foo.git/info/refs", we might end up
978+
* with "https://other.example.com/foo.git/info/refs". We would want the
979+
* new URL to become "https://other.example.com/foo.git".
980+
*
981+
* Note that this assumes a sane redirect scheme. It's entirely possible
982+
* in the example above to end up at a URL that does not even end in
983+
* "info/refs". In such a case we simply punt, as there is not much we can
984+
* do (and such a scheme is unlikely to represent a real git repository,
985+
* which means we are likely about to abort anyway).
986+
*/
987+
static int update_url_from_redirect(struct strbuf *base,
988+
const char *asked,
989+
const struct strbuf *got)
990+
{
991+
const char *tail;
992+
size_t tail_len;
993+
994+
if (!strcmp(asked, got->buf))
995+
return 0;
996+
997+
if (prefixcmp(asked, base->buf))
998+
die("BUG: update_url_from_redirect: %s is not a superset of %s",
999+
asked, base->buf);
1000+
1001+
tail = asked + base->len;
1002+
tail_len = strlen(tail);
1003+
1004+
if (got->len < tail_len ||
1005+
strcmp(tail, got->buf + got->len - tail_len))
1006+
return 0; /* insane redirect scheme */
1007+
1008+
strbuf_reset(base);
1009+
strbuf_add(base, got->buf, got->len - tail_len);
1010+
return 1;
1011+
}
1012+
9501013
static int http_request_reauth(const char *url,
951-
struct strbuf *type,
9521014
void *result, int target,
953-
int options)
1015+
struct http_get_options *options)
9541016
{
955-
int ret = http_request(url, type, result, target, options);
1017+
int ret = http_request(url, result, target, options);
1018+
1019+
if (options && options->effective_url && options->base_url) {
1020+
if (update_url_from_redirect(options->base_url,
1021+
url, options->effective_url)) {
1022+
credential_from_url(&http_auth, options->base_url->buf);
1023+
url = options->effective_url->buf;
1024+
}
1025+
}
1026+
9561027
if (ret != HTTP_REAUTH)
9571028
return ret;
9581029

@@ -962,7 +1033,7 @@ static int http_request_reauth(const char *url,
9621033
* making our next request. We only know how to do this for
9631034
* the strbuf case, but that is enough to satisfy current callers.
9641035
*/
965-
if (options & HTTP_KEEP_ERROR) {
1036+
if (options && options->keep_error) {
9661037
switch (target) {
9671038
case HTTP_REQUEST_STRBUF:
9681039
strbuf_reset(result);
@@ -971,15 +1042,17 @@ static int http_request_reauth(const char *url,
9711042
die("BUG: HTTP_KEEP_ERROR is only supported with strbufs");
9721043
}
9731044
}
974-
return http_request(url, type, result, target, options);
1045+
1046+
credential_fill(&http_auth);
1047+
1048+
return http_request(url, result, target, options);
9751049
}
9761050

9771051
int http_get_strbuf(const char *url,
978-
struct strbuf *type,
979-
struct strbuf *result, int options)
1052+
struct strbuf *result,
1053+
struct http_get_options *options)
9801054
{
981-
return http_request_reauth(url, type, result,
982-
HTTP_REQUEST_STRBUF, options);
1055+
return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options);
9831056
}
9841057

9851058
/*
@@ -988,24 +1061,25 @@ int http_get_strbuf(const char *url,
9881061
* If a previous interrupted download is detected (i.e. a previous temporary
9891062
* file is still around) the download is resumed.
9901063
*/
991-
static int http_get_file(const char *url, const char *filename, int options)
1064+
static int http_get_file(const char *url, const char *filename,
1065+
struct http_get_options *options)
9921066
{
9931067
int ret;
9941068
struct strbuf tmpfile = STRBUF_INIT;
9951069
FILE *result;
9961070

9971071
strbuf_addf(&tmpfile, "%s.temp", filename);
9981072
result = fopen(tmpfile.buf, "a");
999-
if (! result) {
1073+
if (!result) {
10001074
error("Unable to open local file %s", tmpfile.buf);
10011075
ret = HTTP_ERROR;
10021076
goto cleanup;
10031077
}
10041078

1005-
ret = http_request_reauth(url, NULL, result, HTTP_REQUEST_FILE, options);
1079+
ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options);
10061080
fclose(result);
10071081

1008-
if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
1082+
if (ret == HTTP_OK && move_temp_to_file(tmpfile.buf, filename))
10091083
ret = HTTP_ERROR;
10101084
cleanup:
10111085
strbuf_release(&tmpfile);
@@ -1014,12 +1088,15 @@ static int http_get_file(const char *url, const char *filename, int options)
10141088

10151089
int http_fetch_ref(const char *base, struct ref *ref)
10161090
{
1091+
struct http_get_options options = {0};
10171092
char *url;
10181093
struct strbuf buffer = STRBUF_INIT;
10191094
int ret = -1;
10201095

1096+
options.no_cache = 1;
1097+
10211098
url = quote_ref_url(base, ref->name);
1022-
if (http_get_strbuf(url, NULL, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
1099+
if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) {
10231100
strbuf_rtrim(&buffer);
10241101
if (buffer.len == 40)
10251102
ret = get_sha1_hex(buffer.buf, ref->old_sha1);
@@ -1050,7 +1127,7 @@ static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
10501127
strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
10511128
tmp = strbuf_detach(&buf, NULL);
10521129

1053-
if (http_get_file(url, tmp, 0) != HTTP_OK) {
1130+
if (http_get_file(url, tmp, NULL) != HTTP_OK) {
10541131
error("Unable to get pack index %s", url);
10551132
free(tmp);
10561133
tmp = NULL;
@@ -1103,6 +1180,7 @@ static int fetch_and_setup_pack_index(struct packed_git **packs_head,
11031180

11041181
int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
11051182
{
1183+
struct http_get_options options = {0};
11061184
int ret = 0, i = 0;
11071185
char *url, *data;
11081186
struct strbuf buf = STRBUF_INIT;
@@ -1112,7 +1190,8 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
11121190
strbuf_addstr(&buf, "objects/info/packs");
11131191
url = strbuf_detach(&buf, NULL);
11141192

1115-
ret = http_get_strbuf(url, NULL, &buf, HTTP_NO_CACHE);
1193+
options.no_cache = 1;
1194+
ret = http_get_strbuf(url, &buf, &options);
11161195
if (ret != HTTP_OK)
11171196
goto cleanup;
11181197

http.h

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ extern void http_cleanup(void);
102102
extern int active_requests;
103103
extern int http_is_verbose;
104104
extern size_t http_post_buffer;
105+
extern struct credential http_auth;
105106

106107
extern char curl_errorstr[CURL_ERROR_SIZE];
107108

@@ -125,11 +126,30 @@ extern void append_remote_object_url(struct strbuf *buf, const char *url,
125126
extern char *get_remote_object_url(const char *url, const char *hex,
126127
int only_two_digit_prefix);
127128

128-
/* Options for http_request_*() */
129-
#define HTTP_NO_CACHE 1
130-
#define HTTP_KEEP_ERROR 2
129+
/* Options for http_get_*() */
130+
struct http_get_options {
131+
unsigned no_cache:1,
132+
keep_error:1;
133+
134+
/* If non-NULL, returns the content-type of the response. */
135+
struct strbuf *content_type;
136+
137+
/*
138+
* If non-NULL, returns the URL we ended up at, including any
139+
* redirects we followed.
140+
*/
141+
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;
150+
};
131151

132-
/* Return values for http_request_*() */
152+
/* Return values for http_get_*() */
133153
#define HTTP_OK 0
134154
#define HTTP_MISSING_TARGET 1
135155
#define HTTP_ERROR 2
@@ -142,7 +162,7 @@ extern char *get_remote_object_url(const char *url, const char *hex,
142162
*
143163
* If the result pointer is NULL, a HTTP HEAD request is made instead of GET.
144164
*/
145-
int http_get_strbuf(const char *url, struct strbuf *content_type, struct strbuf *result, int options);
165+
int http_get_strbuf(const char *url, struct strbuf *result, struct http_get_options *options);
146166

147167
extern int http_fetch_ref(const char *base, struct ref *ref);
148168

0 commit comments

Comments
 (0)