http: add custom hostname to IP address resolutions

Libcurl has a CURLOPT_RESOLVE easy option that allows
the result of hostname resolution in the following
format to be passed:

	[+]HOST:PORT:ADDRESS[,ADDRESS]

This way, redirects and everything operating against the
HOST+PORT will use the provided ADDRESS(s).

The following format is also allowed to stop using
hostname resolutions that have already been passed:

	-HOST:PORT

See https://curl.se/libcurl/c/CURLOPT_RESOLVE.html for
more details.

Let's add a corresponding "http.curloptResolve" config
option that takes advantage of CURLOPT_RESOLVE.

Each value configured for the "http.curloptResolve" key
is passed "as is" to libcurl through CURLOPT_RESOLVE, so
it should be in one of the above 2 formats. This keeps
the implementation simple and makes us consistent with
libcurl's CURLOPT_RESOLVE, and with curl's corresponding
`--resolve` command line option.

The implementation uses CURLOPT_RESOLVE only in
get_active_slot() which is called by all the HTTP
request sending functions.

Signed-off-by: Christian Couder <chriscool@tuxfamily.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Christian Couder 2022-05-16 10:38:51 +02:00 committed by Junio C Hamano
parent 6cd33dceed
commit 511cfd3bff
3 changed files with 41 additions and 0 deletions

View File

@ -98,6 +98,22 @@ http.version::
- HTTP/2 - HTTP/2
- HTTP/1.1 - HTTP/1.1
http.curloptResolve::
Hostname resolution information that will be used first by
libcurl when sending HTTP requests. This information should
be in one of the following formats:
- [+]HOST:PORT:ADDRESS[,ADDRESS]
- -HOST:PORT
+
The first format redirects all requests to the given `HOST:PORT`
to the provided `ADDRESS`(s). The second format clears all
previous config values for that `HOST:PORT` combination. To
allow easy overriding of all the settings inherited from the
system config, an empty value will reset all resolution
information to the empty list.
http.sslVersion:: http.sslVersion::
The SSL version to use when negotiating an SSL connection, if you The SSL version to use when negotiating an SSL connection, if you
want to force the default. The available and default version want to force the default. The available and default version

18
http.c
View File

@ -128,6 +128,8 @@ static struct curl_slist *pragma_header;
static struct curl_slist *no_pragma_header; static struct curl_slist *no_pragma_header;
static struct string_list extra_http_headers = STRING_LIST_INIT_DUP; static struct string_list extra_http_headers = STRING_LIST_INIT_DUP;
static struct curl_slist *host_resolutions;
static struct active_request_slot *active_queue_head; static struct active_request_slot *active_queue_head;
static char *cached_accept_language; static char *cached_accept_language;
@ -393,6 +395,18 @@ static int http_options(const char *var, const char *value, void *cb)
return 0; return 0;
} }
if (!strcmp("http.curloptresolve", var)) {
if (!value) {
return config_error_nonbool(var);
} else if (!*value) {
curl_slist_free_all(host_resolutions);
host_resolutions = NULL;
} else {
host_resolutions = curl_slist_append(host_resolutions, value);
}
return 0;
}
if (!strcmp("http.followredirects", var)) { if (!strcmp("http.followredirects", var)) {
if (value && !strcmp(value, "initial")) if (value && !strcmp(value, "initial"))
http_follow_config = HTTP_FOLLOW_INITIAL; http_follow_config = HTTP_FOLLOW_INITIAL;
@ -1131,6 +1145,9 @@ void http_cleanup(void)
curl_slist_free_all(no_pragma_header); curl_slist_free_all(no_pragma_header);
no_pragma_header = NULL; no_pragma_header = NULL;
curl_slist_free_all(host_resolutions);
host_resolutions = NULL;
if (curl_http_proxy) { if (curl_http_proxy) {
free((void *)curl_http_proxy); free((void *)curl_http_proxy);
curl_http_proxy = NULL; curl_http_proxy = NULL;
@ -1211,6 +1228,7 @@ struct active_request_slot *get_active_slot(void)
if (curl_save_cookies) if (curl_save_cookies)
curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file); curl_easy_setopt(slot->curl, CURLOPT_COOKIEJAR, curl_cookie_file);
curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header); curl_easy_setopt(slot->curl, CURLOPT_HTTPHEADER, pragma_header);
curl_easy_setopt(slot->curl, CURLOPT_RESOLVE, host_resolutions);
curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr); curl_easy_setopt(slot->curl, CURLOPT_ERRORBUFFER, curl_errorstr);
curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(slot->curl, CURLOPT_CUSTOMREQUEST, NULL);
curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL); curl_easy_setopt(slot->curl, CURLOPT_READFUNCTION, NULL);

View File

@ -567,4 +567,11 @@ test_expect_success 'client falls back from v2 to v0 to match server' '
grep symref=HEAD:refs/heads/ trace grep symref=HEAD:refs/heads/ trace
' '
test_expect_success 'passing hostname resolution information works' '
BOGUS_HOST=gitbogusexamplehost.invalid &&
BOGUS_HTTPD_URL=$HTTPD_PROTO://$BOGUS_HOST:$LIB_HTTPD_PORT &&
test_must_fail git ls-remote "$BOGUS_HTTPD_URL/smart/repo.git" >/dev/null &&
git -c "http.curloptResolve=$BOGUS_HOST:$LIB_HTTPD_PORT:127.0.0.1" ls-remote "$BOGUS_HTTPD_URL/smart/repo.git" >/dev/null
'
test_done test_done