Skip to content

Commit 22fcece

Browse files
authored
Merge pull request #299 from mjcheetham/noproxy2
Add support for the `NO_PROXY` bypass environment variable
2 parents 10d9d66 + 8e5abe5 commit 22fcece

File tree

7 files changed

+365
-125
lines changed

7 files changed

+365
-125
lines changed

docs/netconfig.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ GCM Core supports other ways of configuring a proxy for convenience and compatib
5555
- `ALL_PROXY`
5656
1. `GCM_HTTP_PROXY` environment variable (_**only** respected by GCM; **deprecated**_)
5757

58+
### Bypassing addresses
59+
60+
In some circumstances you may wish to bypass a configured proxy for specific
61+
addresses. GCM Core supports the cURL environment variable `NO_PROXY` for this
62+
scenariom, as does Git itself.
63+
64+
The `NO_PROXY` environment variable should contain a comma (`,`) or space (` `)
65+
separated list of regular expressions to match hosts that should not be proxied
66+
(should connect directly).
67+
68+
**Example:**
69+
70+
```text
71+
NO_PROXY="contoso.com,www.fabrikam.com"
72+
```
73+
5874
## TLS Verification
5975

6076
If you are using self-signed TLS (SSL) certificates with a self-hosted host provider such as GitHub Enterprise Server or Azure DevOps Server (previously TFS), you may see the following error message when attempting to connect using Git and/or GCM:

src/shared/Microsoft.Git.CredentialManager.Tests/HttpClientFactoryTests.cs

Lines changed: 76 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33
using System;
4+
using System.Collections.Generic;
45
using System.Net;
56
using System.Net.Http;
67
using Moq;
@@ -39,8 +40,8 @@ public void HttpClientFactory_TryCreateProxy_NoProxy_ReturnsFalseOutNull()
3940
{
4041
const string repoPath = "/tmp/repos/foo";
4142
const string repoRemote = "https://remote.example.com/foo.git";
42-
var repoRemoteUri = new Uri(repoRemote);
4343

44+
var repoRemoteUri = new Uri(repoRemote);
4445
var settings = new TestSettings
4546
{
4647
RemoteUri = repoRemoteUri,
@@ -57,59 +58,96 @@ public void HttpClientFactory_TryCreateProxy_NoProxy_ReturnsFalseOutNull()
5758
[Fact]
5859
public void HttpClientFactory_TryCreateProxy_ProxyNoCredentials_ReturnsTrueOutProxyWithUrlDefaultCredentials()
5960
{
61+
const string proxyUrl = "https://proxy.example.com/git";
6062
const string repoPath = "/tmp/repos/foo";
6163
const string repoRemote = "https://remote.example.com/foo.git";
62-
var repoRemoteUri = new Uri(repoRemote);
6364

64-
string proxyConfigString = "https://proxy.example.com/git";
65-
string expectedProxyUrl = proxyConfigString;
65+
var repoRemoteUri = new Uri(repoRemote);
66+
var proxyConfig = new ProxyConfiguration(new Uri(proxyUrl));
6667

6768
var settings = new TestSettings
6869
{
6970
RemoteUri = repoRemoteUri,
7071
RepositoryPath = repoPath,
71-
ProxyConfiguration = new Uri(proxyConfigString)
72+
ProxyConfiguration = proxyConfig
7273
};
7374
var httpFactory = new HttpClientFactory(Mock.Of<ITrace>(), settings, Mock.Of<IStandardStreams>());
7475

7576
bool result = httpFactory.TryCreateProxy(out IWebProxy proxy);
7677

7778
Assert.True(result);
7879
Assert.NotNull(proxy);
79-
var configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
80-
Assert.Equal(expectedProxyUrl, configuredProxyUrl.ToString());
80+
Uri configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
81+
Assert.Equal(proxyUrl, configuredProxyUrl?.ToString());
8182

8283
AssertDefaultCredentials(proxy.Credentials);
8384
}
8485

86+
[Fact]
87+
public void HttpClientFactory_TryCreateProxy_ProxyWithBypass_ReturnsTrueOutProxyWithBypassedHosts()
88+
{
89+
const string proxyUrl = "https://proxy.example.com/git";
90+
const string repoPath = "/tmp/repos/foo";
91+
const string repoRemote = "https://remote.example.com/foo.git";
92+
93+
var bypassList = new List<string> {"https://contoso.com", ".*fabrikam\\.com"};
94+
var repoRemoteUri = new Uri(repoRemote);
95+
var proxyConfig = new ProxyConfiguration(
96+
new Uri(proxyUrl),
97+
userName: null,
98+
password: null,
99+
bypassHosts: bypassList);
100+
101+
var settings = new TestSettings
102+
{
103+
RemoteUri = repoRemoteUri,
104+
RepositoryPath = repoPath,
105+
ProxyConfiguration = proxyConfig
106+
};
107+
var httpFactory = new HttpClientFactory(Mock.Of<ITrace>(), settings, Mock.Of<IStandardStreams>());
108+
109+
bool result = httpFactory.TryCreateProxy(out IWebProxy proxy);
110+
111+
Assert.True(result);
112+
Assert.NotNull(proxy);
113+
Uri configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
114+
Assert.Equal(proxyUrl, configuredProxyUrl?.ToString());
115+
116+
Assert.True(proxy.IsBypassed(new Uri("https://contoso.com")));
117+
Assert.True(proxy.IsBypassed(new Uri("http://fabrikam.com")));
118+
Assert.True(proxy.IsBypassed(new Uri("https://subdomain.fabrikam.com")));
119+
Assert.False(proxy.IsBypassed(repoRemoteUri));
120+
}
121+
85122
[Fact]
86123
public void HttpClientFactory_TryCreateProxy_ProxyWithCredentials_ReturnsTrueOutProxyWithUrlConfiguredCredentials()
87124
{
88-
const string proxyScheme = "https";
89125
const string proxyUser = "john.doe";
90126
const string proxyPass = "letmein";
91-
const string proxyHost = "proxy.example.com/git";
127+
const string proxyUrl = "https://proxy.example.com/git";
92128
const string repoPath = "/tmp/repos/foo";
93129
const string repoRemote = "https://remote.example.com/foo.git";
94130

95-
string proxyConfigString = $"{proxyScheme}://{proxyUser}:{proxyPass}@{proxyHost}";
96-
string expectedProxyUrl = $"{proxyScheme}://{proxyHost}";
97131
var repoRemoteUri = new Uri(repoRemote);
132+
var proxyConfig = new ProxyConfiguration(
133+
new Uri(proxyUrl),
134+
proxyUser,
135+
proxyPass);
98136

99137
var settings = new TestSettings
100138
{
101139
RemoteUri = repoRemoteUri,
102140
RepositoryPath = repoPath,
103-
ProxyConfiguration = new Uri(proxyConfigString)
141+
ProxyConfiguration = proxyConfig
104142
};
105143
var httpFactory = new HttpClientFactory(Mock.Of<ITrace>(), settings, Mock.Of<IStandardStreams>());
106144

107145
bool result = httpFactory.TryCreateProxy(out IWebProxy proxy);
108146

109147
Assert.True(result);
110148
Assert.NotNull(proxy);
111-
var configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
112-
Assert.Equal(expectedProxyUrl, configuredProxyUrl.ToString());
149+
Uri configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
150+
Assert.Equal(proxyUrl, configuredProxyUrl?.ToString());
113151

114152
Assert.NotNull(proxy.Credentials);
115153
Assert.IsType<NetworkCredential>(proxy.Credentials);
@@ -121,30 +159,31 @@ public void HttpClientFactory_TryCreateProxy_ProxyWithCredentials_ReturnsTrueOut
121159
[Fact]
122160
public void HttpClientFactory_TryCreateProxy_ProxyWithNonEmptyUserAndEmptyPass_ReturnsTrueOutProxyWithUrlConfiguredCredentials()
123161
{
124-
const string proxyScheme = "https";
162+
const string proxyUrl = "https://proxy.example.com/git";
125163
const string proxyUser = "john.doe";
126-
const string proxyHost = "proxy.example.com/git";
127164
const string repoPath = "/tmp/repos/foo";
128165
const string repoRemote = "https://remote.example.com/foo.git";
129166

130-
string proxyConfigString = $"{proxyScheme}://{proxyUser}:@{proxyHost}";
131-
string expectedProxyUrl = $"{proxyScheme}://{proxyHost}";
132167
var repoRemoteUri = new Uri(repoRemote);
168+
var proxyConfig = new ProxyConfiguration(
169+
new Uri(proxyUrl),
170+
proxyUser,
171+
password: null);
133172

134173
var settings = new TestSettings
135174
{
136175
RemoteUri = repoRemoteUri,
137176
RepositoryPath = repoPath,
138-
ProxyConfiguration = new Uri(proxyConfigString)
177+
ProxyConfiguration = proxyConfig
139178
};
140179
var httpFactory = new HttpClientFactory(Mock.Of<ITrace>(), settings, Mock.Of<IStandardStreams>());
141180

142181
bool result = httpFactory.TryCreateProxy(out IWebProxy proxy);
143182

144183
Assert.True(result);
145184
Assert.NotNull(proxy);
146-
var configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
147-
Assert.Equal(expectedProxyUrl, configuredProxyUrl.ToString());
185+
Uri configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
186+
Assert.Equal(proxyUrl, configuredProxyUrl?.ToString());
148187

149188
Assert.NotNull(proxy.Credentials);
150189
Assert.IsType<NetworkCredential>(proxy.Credentials);
@@ -156,30 +195,31 @@ public void HttpClientFactory_TryCreateProxy_ProxyWithNonEmptyUserAndEmptyPass_R
156195
[Fact]
157196
public void HttpClientFactory_TryCreateProxy_ProxyWithEmptyUserAndNonEmptyPass_ReturnsTrueOutProxyWithUrlConfiguredCredentials()
158197
{
159-
const string proxyScheme = "https";
198+
const string proxyUrl = "https://proxy.example.com/git";
160199
const string proxyPass = "letmein";
161-
const string proxyHost = "proxy.example.com/git";
162200
const string repoPath = "/tmp/repos/foo";
163201
const string repoRemote = "https://remote.example.com/foo.git";
164202

165-
string proxyConfigString = $"{proxyScheme}://:{proxyPass}@{proxyHost}";
166-
string expectedProxyUrl = $"{proxyScheme}://{proxyHost}";
167203
var repoRemoteUri = new Uri(repoRemote);
204+
var proxyConfig = new ProxyConfiguration(
205+
new Uri(proxyUrl),
206+
userName: null,
207+
password: proxyPass);
168208

169209
var settings = new TestSettings
170210
{
171211
RemoteUri = repoRemoteUri,
172212
RepositoryPath = repoPath,
173-
ProxyConfiguration = new Uri(proxyConfigString)
213+
ProxyConfiguration = proxyConfig
174214
};
175215
var httpFactory = new HttpClientFactory(Mock.Of<ITrace>(), settings, Mock.Of<IStandardStreams>());
176216

177217
bool result = httpFactory.TryCreateProxy(out IWebProxy proxy);
178218

179219
Assert.True(result);
180220
Assert.NotNull(proxy);
181-
var configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
182-
Assert.Equal(expectedProxyUrl, configuredProxyUrl.ToString());
221+
Uri configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
222+
Assert.Equal(proxyUrl, configuredProxyUrl?.ToString());
183223

184224
Assert.NotNull(proxy.Credentials);
185225
Assert.IsType<NetworkCredential>(proxy.Credentials);
@@ -191,27 +231,30 @@ public void HttpClientFactory_TryCreateProxy_ProxyWithEmptyUserAndNonEmptyPass_R
191231
[Fact]
192232
public void HttpClientFactory_TryCreateProxy_ProxyEmptyUserAndEmptyPass_ReturnsTrueOutProxyWithUrlDefaultCredentials()
193233
{
234+
const string proxyUrl = "https://proxy.example.com/git";
194235
const string repoPath = "/tmp/repos/foo";
195236
const string repoRemote = "https://remote.example.com/foo.git";
196237
var repoRemoteUri = new Uri(repoRemote);
197238

198-
string proxyConfigString = "https://:@proxy.example.com/git";
199-
string expectedProxyUrl = "https://proxy.example.com/git";
239+
var proxyConfig = new ProxyConfiguration(
240+
new Uri(proxyUrl),
241+
userName: string.Empty,
242+
password: string.Empty);
200243

201244
var settings = new TestSettings
202245
{
203246
RemoteUri = repoRemoteUri,
204247
RepositoryPath = repoPath,
205-
ProxyConfiguration = new Uri(proxyConfigString)
248+
ProxyConfiguration = proxyConfig
206249
};
207250
var httpFactory = new HttpClientFactory(Mock.Of<ITrace>(), settings, Mock.Of<IStandardStreams>());
208251

209252
bool result = httpFactory.TryCreateProxy(out IWebProxy proxy);
210253

211254
Assert.True(result);
212255
Assert.NotNull(proxy);
213-
var configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
214-
Assert.Equal(expectedProxyUrl, configuredProxyUrl.ToString());
256+
Uri configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
257+
Assert.Equal(proxyUrl, configuredProxyUrl?.ToString());
215258

216259
AssertDefaultCredentials(proxy.Credentials);
217260
}

0 commit comments

Comments
 (0)