Skip to content

Commit 3c0f14c

Browse files
committed
remote: refactor proxy detection
Update the proxy detection for a remote. 1. Honor `http.<url>.proxy` syntax for a remote's direct URL and parent URLs. 2. Honor an empty configuration URL to override a proxy configuration. Add tests to ensure that configuration specificity is honored.
1 parent f89dc91 commit 3c0f14c

File tree

7 files changed

+238
-221
lines changed

7 files changed

+238
-221
lines changed

src/remote.c

Lines changed: 94 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -849,112 +849,91 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote
849849
return remote->transport->ls(out, size, remote->transport);
850850
}
851851

852-
int git_remote__get_http_proxy_bypass(git_net_url *url, git_buf *no_proxy_env, bool *bypass)
852+
static int lookup_config(char **out, git_config *cfg, const char *name)
853853
{
854-
int error = 0;
855-
char *p_start = no_proxy_env->ptr;
856-
size_t p_length = 0;
857-
char c;
858-
git_buf hostport = GIT_BUF_INIT;
854+
git_config_entry *ce = NULL;
855+
int error;
859856

860-
error = git_buf_printf(&hostport, "%s:%s", url->host, url->port);
861-
if (error < 0)
857+
if ((error = git_config__lookup_entry(&ce, cfg, name, false)) < 0)
862858
return error;
863859

864-
*bypass = false;
865-
866-
do {
867-
c = *(p_start + p_length);
868-
if ((c == ',') || (c == 0)) {
869-
if ((p_length == 1) && (*p_start == '*')) {
870-
// wildcard match (*)
871-
goto found;
872-
} else if ((p_length == strlen(url->host)) && !memcmp(p_start, url->host, p_length)) {
873-
// exact host match
874-
goto found;
875-
} else if ((p_length == strlen(hostport.ptr)) && !memcmp(p_start, hostport.ptr, p_length)) {
876-
// exact host:port match
877-
goto found;
878-
} else {
879-
if ((p_length >= 2) && (*p_start == '*') && (*(p_start + 1) == '.')) {
880-
// *.foo == .foo
881-
p_start++;
882-
p_length--;
883-
}
884-
if ((*p_start == '.') && (strlen(url->host) > p_length) && !memcmp(p_start, url->host + strlen(url->host) - p_length, p_length)) {
885-
// host suffix match (.example.org)
886-
goto found;
887-
} else if ((*p_start == '.') && (strlen(hostport.ptr) > p_length) && !memcmp(p_start, hostport.ptr + strlen(hostport.ptr) - p_length, p_length)) {
888-
// host:port suffix match (.example.org:443)
889-
goto found;
890-
}
891-
}
892-
p_start += p_length + 1;
893-
p_length = 0;
894-
} else {
895-
p_length++;
896-
}
897-
} while(c != 0);
860+
if (ce && ce->value) {
861+
*out = git__strdup(ce->value);
862+
GIT_ERROR_CHECK_ALLOC(*out);
863+
} else {
864+
error = GIT_ENOTFOUND;
865+
}
866+
867+
git_config_entry_free(ce);
868+
return error;
869+
}
898870

899-
goto end;
871+
static void url_config_trim(git_net_url *url)
872+
{
873+
size_t len = strlen(url->path);
900874

901-
found:
902-
*bypass = true;
875+
if (url->path[len - 1] == '/') {
876+
len--;
877+
} else {
878+
while (len && url->path[len - 1] != '/')
879+
len--;
880+
}
903881

904-
end:
905-
git_buf_dispose(&hostport);
906-
return 0;
882+
url->path[len] = '\0';
907883
}
908884

909-
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, git_net_url *url, char **proxy_url)
885+
static int http_proxy_config(char **out, git_remote *remote, git_net_url *url)
910886
{
911887
git_config *cfg;
912-
git_config_entry *ce = NULL;
913-
git_buf proxy_env = GIT_BUF_INIT;
914-
git_buf no_proxy_env = GIT_BUF_INIT;
915-
bool bypass = false;
888+
git_buf buf = GIT_BUF_INIT;
889+
git_net_url lookup_url = GIT_NET_URL_INIT;
916890
int error;
917891

918-
GIT_ASSERT_ARG(remote);
892+
if ((error = git_net_url_dup(&lookup_url, url)) < 0 ||
893+
(error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
894+
goto done;
919895

920-
if (!proxy_url || !remote->repo)
921-
return -1;
896+
/* remote.<name>.proxy config setting */
897+
if (remote->name && remote->name[0]) {
898+
git_buf_clear(&buf);
922899

923-
*proxy_url = NULL;
900+
if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0 ||
901+
(error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
902+
goto done;
903+
}
924904

925-
if ((error = git_repository_config__weakptr(&cfg, remote->repo)) < 0)
926-
return error;
905+
while (true) {
906+
git_buf_clear(&buf);
927907

928-
/* Go through the possible sources for proxy configuration, from most specific
929-
* to least specific. */
908+
if ((error = git_buf_puts(&buf, "http.")) < 0 ||
909+
(error = git_net_url_fmt(&buf, &lookup_url)) < 0 ||
910+
(error = git_buf_puts(&buf, ".proxy")) < 0 ||
911+
(error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
912+
goto done;
930913

931-
/* remote.<name>.proxy config setting */
932-
if (remote->name && remote->name[0]) {
933-
git_buf buf = GIT_BUF_INIT;
914+
if (! lookup_url.path[0])
915+
break;
934916

935-
if ((error = git_buf_printf(&buf, "remote.%s.proxy", remote->name)) < 0)
936-
return error;
917+
url_config_trim(&lookup_url);
918+
}
937919

938-
error = git_config__lookup_entry(&ce, cfg, git_buf_cstr(&buf), false);
939-
git_buf_dispose(&buf);
920+
git_buf_clear(&buf);
940921

941-
if (error < 0)
942-
return error;
922+
error = lookup_config(out, cfg, "http.proxy");
943923

944-
if (ce && ce->value) {
945-
*proxy_url = git__strdup(ce->value);
946-
goto found;
947-
}
948-
}
924+
done:
925+
git_buf_dispose(&buf);
926+
git_net_url_dispose(&lookup_url);
927+
return error;
928+
}
949929

950-
/* http.proxy config setting */
951-
if ((error = git_config__lookup_entry(&ce, cfg, "http.proxy", false)) < 0)
952-
return error;
930+
static int http_proxy_env(char **out, git_remote *remote, git_net_url *url)
931+
{
932+
git_buf proxy_env = GIT_BUF_INIT, no_proxy_env = GIT_BUF_INIT;
933+
bool use_ssl = (strcmp(url->scheme, "https") == 0);
934+
int error;
953935

954-
if (ce && ce->value) {
955-
*proxy_url = git__strdup(ce->value);
956-
goto found;
957-
}
936+
GIT_UNUSED(remote);
958937

959938
/* http_proxy / https_proxy environment variables */
960939
error = git__getenv(&proxy_env, use_ssl ? "https_proxy" : "http_proxy");
@@ -963,46 +942,51 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, git_net_url *ur
963942
if (error == GIT_ENOTFOUND)
964943
error = git__getenv(&proxy_env, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
965944

966-
if (error < 0) {
967-
if (error == GIT_ENOTFOUND) {
968-
git_error_clear();
969-
error = 0;
970-
}
971-
972-
return error;
973-
}
945+
if (error)
946+
goto done;
974947

975948
/* no_proxy/NO_PROXY environment variables */
976949
error = git__getenv(&no_proxy_env, "no_proxy");
950+
977951
if (error == GIT_ENOTFOUND)
978952
error = git__getenv(&no_proxy_env, "NO_PROXY");
979953

980-
if (error == GIT_ENOTFOUND) {
981-
git_error_clear();
982-
error = 0;
983-
} else if (error < 0) {
984-
goto cleanup;
985-
} else {
986-
error = git_remote__get_http_proxy_bypass(url, &no_proxy_env, &bypass);
987-
}
988-
989-
if (bypass) {
990-
git_buf_dispose(&proxy_env);
991-
goto cleanup;
992-
} else {
993-
*proxy_url = git_buf_detach(&proxy_env);
994-
}
954+
if (error && error != GIT_ENOTFOUND)
955+
goto done;
995956

996-
found:
997-
GIT_ERROR_CHECK_ALLOC(*proxy_url);
957+
if (!git_net_url_matches_pattern_list(url, no_proxy_env.ptr))
958+
*out = git_buf_detach(&proxy_env);
959+
else
960+
error = GIT_ENOTFOUND;
998961

999-
cleanup:
962+
done:
963+
git_buf_dispose(&proxy_env);
1000964
git_buf_dispose(&no_proxy_env);
1001-
git_config_entry_free(ce);
1002-
1003965
return error;
1004966
}
1005967

968+
int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url)
969+
{
970+
int error;
971+
972+
GIT_ASSERT_ARG(out);
973+
GIT_ASSERT_ARG(remote);
974+
GIT_ASSERT_ARG(remote->repo);
975+
976+
*out = NULL;
977+
978+
/*
979+
* Go through the possible sources for proxy configuration,
980+
* Examine the various git config options first, then
981+
* consult environment variables.
982+
*/
983+
if ((error = http_proxy_config(out, remote, url)) != GIT_ENOTFOUND ||
984+
(error = http_proxy_env(out, remote, url)) != GIT_ENOTFOUND)
985+
return error;
986+
987+
return 0;
988+
}
989+
1006990
/* DWIM `refspecs` based on `refs` and append the output to `out` */
1007991
static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
1008992
{

src/remote.h

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@
99

1010
#include "common.h"
1111

12-
#include "net.h"
1312
#include "git2/remote.h"
1413
#include "git2/transport.h"
1514
#include "git2/sys/transport.h"
1615

1716
#include "refspec.h"
1817
#include "vector.h"
18+
#include "net.h"
1919

2020
#define GIT_REMOTE_ORIGIN "origin"
2121

@@ -47,8 +47,7 @@ typedef struct git_remote_connection_opts {
4747
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn);
4848

4949
int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
50-
int git_remote__get_http_proxy_bypass(git_net_url *url, git_buf *no_proxy_env, bool *bypass);
51-
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, git_net_url *url, char **proxy_url);
50+
int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url);
5251

5352
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname);
5453
git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname);

src/transports/http.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,6 @@ static int lookup_proxy(
290290
{
291291
const char *proxy;
292292
git_remote *remote;
293-
bool use_ssl;
294293
char *config = NULL;
295294
int error = 0;
296295

@@ -304,9 +303,8 @@ static int lookup_proxy(
304303

305304
case GIT_PROXY_AUTO:
306305
remote = transport->owner->owner;
307-
use_ssl = !strcmp(transport->server.url.scheme, "https");
308306

309-
error = git_remote__get_http_proxy(remote, use_ssl, &transport->server.url, &config);
307+
error = git_remote__http_proxy(&config, remote, &transport->server.url);
310308

311309
if (error || !config)
312310
goto done;

src/transports/winhttp.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,6 @@ static int winhttp_stream_connect(winhttp_stream *s)
373373
{
374374
winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
375375
git_buf buf = GIT_BUF_INIT;
376-
bool use_ssl;
377376
char *proxy_url = NULL;
378377
wchar_t ct[MAX_CONTENT_TYPE_LEN];
379378
LPCWSTR types[] = { L"*/*", NULL };
@@ -430,8 +429,7 @@ static int winhttp_stream_connect(winhttp_stream *s)
430429
proxy_opts = &t->owner->proxy;
431430
if (proxy_opts->type == GIT_PROXY_AUTO) {
432431
/* Set proxy if necessary */
433-
use_ssl = strcmp(t->server.url.scheme, "https") == 0;
434-
if (git_remote__get_http_proxy(t->owner->owner, use_ssl, &t->server.url, &proxy_url) < 0)
432+
if (git_remote__http_proxy(&proxy_url, t->owner->owner, &t->server.url) < 0)
435433
goto on_error;
436434
}
437435
else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {
@@ -744,7 +742,7 @@ static void CALLBACK winhttp_status(
744742
git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
745743

746744
break;
747-
745+
748746
case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
749747
((winhttp_stream *) ctx)->status_sending_request_reached = 1;
750748

tests/online/clone.c

Lines changed: 0 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -898,67 +898,6 @@ void test_online_clone__proxy_credentials_in_url_https(void)
898898
git_buf_dispose(&url);
899899
}
900900

901-
struct no_proxy_test_entry {
902-
char no_proxy[128];
903-
bool bypass;
904-
};
905-
906-
static struct no_proxy_test_entry no_proxy_test_entries[] = {
907-
{"*", true},
908-
{"github.com", true},
909-
{"github.com:443", true},
910-
{"github.com:80", false},
911-
{".github.com", false},
912-
{"*.github.com", false},
913-
{".com", true},
914-
{"*.com", true},
915-
{".com:443", true},
916-
{"*.com:443", true},
917-
{".com:80", false},
918-
{"*.com:80", false},
919-
{"", false}
920-
};
921-
922-
void test_online_clone__no_proxy_in_environment(void)
923-
{
924-
int error = 0;
925-
unsigned int i;
926-
git_buf proxy_url = GIT_BUF_INIT;
927-
928-
_orig_http_proxy = cl_getenv("HTTP_PROXY");
929-
_orig_https_proxy = cl_getenv("HTTPS_PROXY");
930-
_orig_no_proxy = cl_getenv("NO_PROXY");
931-
_orig_proxies_need_reset = 1;
932-
933-
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
934-
g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
935-
936-
cl_git_pass(git_buf_printf(&proxy_url, "http://does-not-exists.example.org:1234/"));
937-
938-
cl_setenv("HTTP_PROXY", proxy_url.ptr);
939-
cl_setenv("HTTPS_PROXY", proxy_url.ptr);
940-
941-
942-
for (i = 0; i < ARRAY_SIZE(no_proxy_test_entries); ++i) {
943-
cl_setenv("NO_PROXY", no_proxy_test_entries[i].no_proxy);
944-
error = git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options);
945-
946-
if (no_proxy_test_entries[i].bypass) {
947-
cl_assert_(error == 0, no_proxy_test_entries[i].no_proxy);
948-
} else {
949-
cl_assert_(error == -1, no_proxy_test_entries[i].no_proxy);
950-
}
951-
952-
if (g_repo) {
953-
git_repository_free(g_repo);
954-
g_repo = NULL;
955-
}
956-
cl_fixture_cleanup("./foo");
957-
}
958-
959-
git_buf_dispose(&proxy_url);
960-
}
961-
962901
void test_online_clone__proxy_auto_not_detected(void)
963902
{
964903
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;

0 commit comments

Comments
 (0)