Skip to content

Commit e5a3277

Browse files
sathieuethomson
authored andcommitted
Add NO_PROXY env support
Item 2 of 3 from libgit2#4164 Signed-off-by: Mathieu Parent <math.parent@gmail.com>
1 parent d56b407 commit e5a3277

File tree

6 files changed

+200
-9
lines changed

6 files changed

+200
-9
lines changed

src/remote.c

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -849,11 +849,70 @@ 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(git_remote *remote, bool use_ssl, char **proxy_url)
852+
int git_remote__get_http_proxy_bypass(git_net_url *url, git_buf *no_proxy_env, bool *bypass)
853+
{
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;
859+
860+
error = git_buf_printf(&hostport, "%s:%s", url->host, url->port);
861+
if (error < 0)
862+
return error;
863+
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);
898+
899+
goto end;
900+
901+
found:
902+
*bypass = true;
903+
904+
end:
905+
git_buf_dispose(&hostport);
906+
return 0;
907+
}
908+
909+
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, git_net_url *url, char **proxy_url)
853910
{
854911
git_config *cfg;
855912
git_config_entry *ce = NULL;
856-
git_buf val = GIT_BUF_INIT;
913+
git_buf proxy_env = GIT_BUF_INIT;
914+
git_buf no_proxy_env = GIT_BUF_INIT;
915+
bool bypass = false;
857916
int error;
858917

859918
GIT_ASSERT_ARG(remote);
@@ -898,11 +957,11 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur
898957
}
899958

900959
/* http_proxy / https_proxy environment variables */
901-
error = git__getenv(&val, use_ssl ? "https_proxy" : "http_proxy");
960+
error = git__getenv(&proxy_env, use_ssl ? "https_proxy" : "http_proxy");
902961

903962
/* try uppercase environment variables */
904963
if (error == GIT_ENOTFOUND)
905-
error = git__getenv(&val, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
964+
error = git__getenv(&proxy_env, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
906965

907966
if (error < 0) {
908967
if (error == GIT_ENOTFOUND) {
@@ -913,13 +972,35 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur
913972
return error;
914973
}
915974

916-
*proxy_url = git_buf_detach(&val);
975+
/* no_proxy/NO_PROXY environment variables */
976+
error = git__getenv(&no_proxy_env, "no_proxy");
977+
if (error == GIT_ENOTFOUND)
978+
error = git__getenv(&no_proxy_env, "NO_PROXY");
979+
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+
}
917995

918996
found:
919997
GIT_ERROR_CHECK_ALLOC(*proxy_url);
998+
999+
cleanup:
1000+
git_buf_dispose(&no_proxy_env);
9201001
git_config_entry_free(ce);
9211002

922-
return 0;
1003+
return error;
9231004
}
9241005

9251006
/* DWIM `refspecs` based on `refs` and append the output to `out` */

src/remote.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "common.h"
1111

12+
#include "net.h"
1213
#include "git2/remote.h"
1314
#include "git2/transport.h"
1415
#include "git2/sys/transport.h"
@@ -46,7 +47,8 @@ typedef struct git_remote_connection_opts {
4647
int git_remote__connect(git_remote *remote, git_direction direction, const git_remote_callbacks *callbacks, const git_remote_connection_opts *conn);
4748

4849
int git_remote__urlfordirection(git_buf *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
49-
int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url);
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);
5052

5153
git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname);
5254
git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname);

src/transports/http.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ static int lookup_proxy(
306306
remote = transport->owner->owner;
307307
use_ssl = !strcmp(transport->server.url.scheme, "https");
308308

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

311311
if (error || !config)
312312
goto done;

src/transports/winhttp.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ 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;
376377
char *proxy_url = NULL;
377378
wchar_t ct[MAX_CONTENT_TYPE_LEN];
378379
LPCWSTR types[] = { L"*/*", NULL };
@@ -429,7 +430,8 @@ static int winhttp_stream_connect(winhttp_stream *s)
429430
proxy_opts = &t->owner->proxy;
430431
if (proxy_opts->type == GIT_PROXY_AUTO) {
431432
/* Set proxy if necessary */
432-
if (git_remote__get_http_proxy(t->owner->owner, (strcmp(t->server.url.scheme, "https") == 0), &proxy_url) < 0)
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)
433435
goto on_error;
434436
}
435437
else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {

tests/online/clone.c

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ static char *_remote_expectcontinue = NULL;
3636
static int _orig_proxies_need_reset = 0;
3737
static char *_orig_http_proxy = NULL;
3838
static char *_orig_https_proxy = NULL;
39+
static char *_orig_no_proxy = NULL;
3940

4041
static int ssl_cert(git_cert *cert, int valid, const char *host, void *payload)
4142
{
@@ -110,9 +111,11 @@ void test_online_clone__cleanup(void)
110111
if (_orig_proxies_need_reset) {
111112
cl_setenv("HTTP_PROXY", _orig_http_proxy);
112113
cl_setenv("HTTPS_PROXY", _orig_https_proxy);
114+
cl_setenv("NO_PROXY", _orig_no_proxy);
113115

114116
git__free(_orig_http_proxy);
115117
git__free(_orig_https_proxy);
118+
git__free(_orig_no_proxy);
116119
}
117120

118121
git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, NULL, NULL);
@@ -854,6 +857,7 @@ void test_online_clone__proxy_credentials_in_environment(void)
854857

855858
_orig_http_proxy = cl_getenv("HTTP_PROXY");
856859
_orig_https_proxy = cl_getenv("HTTPS_PROXY");
860+
_orig_no_proxy = cl_getenv("NO_PROXY");
857861
_orig_proxies_need_reset = 1;
858862

859863
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
@@ -865,6 +869,7 @@ void test_online_clone__proxy_credentials_in_environment(void)
865869

866870
cl_setenv("HTTP_PROXY", url.ptr);
867871
cl_setenv("HTTPS_PROXY", url.ptr);
872+
cl_setenv("NO_PROXY", NULL);
868873

869874
cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
870875

@@ -893,6 +898,67 @@ void test_online_clone__proxy_credentials_in_url_https(void)
893898
git_buf_dispose(&url);
894899
}
895900

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+
896962
void test_online_clone__proxy_auto_not_detected(void)
897963
{
898964
g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;

tests/remote/no_proxy.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#include "clar_libgit2.h"
2+
#include "remote.h"
3+
4+
/* Suite data */
5+
struct no_proxy_test_entry {
6+
char url[128];
7+
char no_proxy[128];
8+
bool bypass;
9+
};
10+
11+
static struct no_proxy_test_entry no_proxy_test_entries[] = {
12+
{"https://example.com/", "", false},
13+
{"https://example.com/", "example.org", false},
14+
{"https://example.com/", "*", true},
15+
{"https://example.com/", "example.com,example.org", true},
16+
{"https://example.com/", ".example.com,example.org", false},
17+
{"https://foo.example.com/", ".example.com,example.org", true},
18+
{"https://example.com/", "foo.example.com,example.org", false},
19+
20+
};
21+
22+
void test_remote_no_proxy__entries(void)
23+
{
24+
unsigned int i;
25+
git_net_url url = GIT_NET_URL_INIT;
26+
git_buf no_proxy = GIT_BUF_INIT;
27+
bool bypass = false;
28+
29+
for (i = 0; i < ARRAY_SIZE(no_proxy_test_entries); ++i) {
30+
cl_git_pass(git_net_url_parse(&url, no_proxy_test_entries[i].url));
31+
cl_git_pass(git_buf_sets(&no_proxy, no_proxy_test_entries[i].no_proxy));
32+
cl_git_pass(git_remote__get_http_proxy_bypass(&url, &no_proxy, &bypass));
33+
34+
cl_assert_(bypass == no_proxy_test_entries[i].bypass, no_proxy_test_entries[i].no_proxy);
35+
36+
git_net_url_dispose(&url);
37+
git_buf_dispose(&no_proxy);
38+
}
39+
40+
}

0 commit comments

Comments
 (0)