Skip to content

Commit e5becd0

Browse files
committed
Merge branch 'jk/http-auth-redirects' into maint
We did not handle cases where http transport gets redirected during the authorization request (e.g. from http:// to https://). * 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 486b65a + 70900ed commit e5becd0

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
@@ -1543,7 +1543,7 @@ static int remote_exists(const char *path)
15431543

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

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

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

http.c

Lines changed: 108 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ static long curl_low_speed_time = -1;
4545
static int curl_ftp_no_epsv;
4646
static const char *curl_http_proxy;
4747
static const char *curl_cookie_file;
48-
static struct credential http_auth = CREDENTIAL_INIT;
48+
struct credential http_auth = CREDENTIAL_INIT;
4949
static int http_proactive_auth;
5050
static const char *user_agent;
5151

@@ -806,7 +806,6 @@ int handle_curl_result(struct slot_results *results)
806806
credential_reject(&http_auth);
807807
return HTTP_NOAUTH;
808808
} else {
809-
credential_fill(&http_auth);
810809
return HTTP_REAUTH;
811810
}
812811
} else {
@@ -820,12 +819,25 @@ int handle_curl_result(struct slot_results *results)
820819
}
821820
}
822821

822+
static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
823+
{
824+
char *ptr;
825+
CURLcode ret;
826+
827+
strbuf_reset(buf);
828+
ret = curl_easy_getinfo(curl, info, &ptr);
829+
if (!ret && ptr)
830+
strbuf_addstr(buf, ptr);
831+
return ret;
832+
}
833+
823834
/* http_request() targets */
824835
#define HTTP_REQUEST_STRBUF 0
825836
#define HTTP_REQUEST_FILE 1
826837

827-
static int http_request(const char *url, struct strbuf *type,
828-
void *result, int target, int options)
838+
static int http_request(const char *url,
839+
void *result, int target,
840+
const struct http_get_options *options)
829841
{
830842
struct active_request_slot *slot;
831843
struct slot_results results;
@@ -858,9 +870,9 @@ static int http_request(const char *url, struct strbuf *type,
858870
}
859871

860872
strbuf_addstr(&buf, "Pragma:");
861-
if (options & HTTP_NO_CACHE)
873+
if (options && options->no_cache)
862874
strbuf_addstr(&buf, " no-cache");
863-
if (options & HTTP_KEEP_ERROR)
875+
if (options && options->keep_error)
864876
curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
865877

866878
headers = curl_slist_append(headers, buf.buf);
@@ -878,26 +890,85 @@ static int http_request(const char *url, struct strbuf *type,
878890
ret = HTTP_START_FAILED;
879891
}
880892

881-
if (type) {
882-
char *t;
883-
strbuf_reset(type);
884-
curl_easy_getinfo(slot->curl, CURLINFO_CONTENT_TYPE, &t);
885-
if (t)
886-
strbuf_addstr(type, t);
887-
}
893+
if (options && options->content_type)
894+
curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE,
895+
options->content_type);
896+
897+
if (options && options->effective_url)
898+
curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL,
899+
options->effective_url);
888900

889901
curl_slist_free_all(headers);
890902
strbuf_release(&buf);
891903

892904
return ret;
893905
}
894906

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+
895958
static int http_request_reauth(const char *url,
896-
struct strbuf *type,
897959
void *result, int target,
898-
int options)
960+
struct http_get_options *options)
899961
{
900-
int ret = http_request(url, type, result, target, options);
962+
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+
901972
if (ret != HTTP_REAUTH)
902973
return ret;
903974

@@ -907,7 +978,7 @@ static int http_request_reauth(const char *url,
907978
* making our next request. We only know how to do this for
908979
* the strbuf case, but that is enough to satisfy current callers.
909980
*/
910-
if (options & HTTP_KEEP_ERROR) {
981+
if (options && options->keep_error) {
911982
switch (target) {
912983
case HTTP_REQUEST_STRBUF:
913984
strbuf_reset(result);
@@ -916,15 +987,17 @@ static int http_request_reauth(const char *url,
916987
die("BUG: HTTP_KEEP_ERROR is only supported with strbufs");
917988
}
918989
}
919-
return http_request(url, type, result, target, options);
990+
991+
credential_fill(&http_auth);
992+
993+
return http_request(url, result, target, options);
920994
}
921995

922996
int http_get_strbuf(const char *url,
923-
struct strbuf *type,
924-
struct strbuf *result, int options)
997+
struct strbuf *result,
998+
struct http_get_options *options)
925999
{
926-
return http_request_reauth(url, type, result,
927-
HTTP_REQUEST_STRBUF, options);
1000+
return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options);
9281001
}
9291002

9301003
/*
@@ -933,24 +1006,25 @@ int http_get_strbuf(const char *url,
9331006
* If a previous interrupted download is detected (i.e. a previous temporary
9341007
* file is still around) the download is resumed.
9351008
*/
936-
static int http_get_file(const char *url, const char *filename, int options)
1009+
static int http_get_file(const char *url, const char *filename,
1010+
struct http_get_options *options)
9371011
{
9381012
int ret;
9391013
struct strbuf tmpfile = STRBUF_INIT;
9401014
FILE *result;
9411015

9421016
strbuf_addf(&tmpfile, "%s.temp", filename);
9431017
result = fopen(tmpfile.buf, "a");
944-
if (! result) {
1018+
if (!result) {
9451019
error("Unable to open local file %s", tmpfile.buf);
9461020
ret = HTTP_ERROR;
9471021
goto cleanup;
9481022
}
9491023

950-
ret = http_request_reauth(url, NULL, result, HTTP_REQUEST_FILE, options);
1024+
ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options);
9511025
fclose(result);
9521026

953-
if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
1027+
if (ret == HTTP_OK && move_temp_to_file(tmpfile.buf, filename))
9541028
ret = HTTP_ERROR;
9551029
cleanup:
9561030
strbuf_release(&tmpfile);
@@ -959,12 +1033,15 @@ static int http_get_file(const char *url, const char *filename, int options)
9591033

9601034
int http_fetch_ref(const char *base, struct ref *ref)
9611035
{
1036+
struct http_get_options options = {0};
9621037
char *url;
9631038
struct strbuf buffer = STRBUF_INIT;
9641039
int ret = -1;
9651040

1041+
options.no_cache = 1;
1042+
9661043
url = quote_ref_url(base, ref->name);
967-
if (http_get_strbuf(url, NULL, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
1044+
if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) {
9681045
strbuf_rtrim(&buffer);
9691046
if (buffer.len == 40)
9701047
ret = get_sha1_hex(buffer.buf, ref->old_sha1);
@@ -995,7 +1072,7 @@ static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
9951072
strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
9961073
tmp = strbuf_detach(&buf, NULL);
9971074

998-
if (http_get_file(url, tmp, 0) != HTTP_OK) {
1075+
if (http_get_file(url, tmp, NULL) != HTTP_OK) {
9991076
error("Unable to get pack index %s", url);
10001077
free(tmp);
10011078
tmp = NULL;
@@ -1048,6 +1125,7 @@ static int fetch_and_setup_pack_index(struct packed_git **packs_head,
10481125

10491126
int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
10501127
{
1128+
struct http_get_options options = {0};
10511129
int ret = 0, i = 0;
10521130
char *url, *data;
10531131
struct strbuf buf = STRBUF_INIT;
@@ -1057,7 +1135,8 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
10571135
strbuf_addstr(&buf, "objects/info/packs");
10581136
url = strbuf_detach(&buf, NULL);
10591137

1060-
ret = http_get_strbuf(url, NULL, &buf, HTTP_NO_CACHE);
1138+
options.no_cache = 1;
1139+
ret = http_get_strbuf(url, &buf, &options);
10611140
if (ret != HTTP_OK)
10621141
goto cleanup;
10631142

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)