Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 22e88da

Browse files
authored
Improve parsing of WinINet default proxy strings in SocketsHttpHandler (#28671)
This is a partial fix for #28603.
1 parent 0a61daf commit 22e88da

File tree

2 files changed

+132
-19
lines changed

2 files changed

+132
-19
lines changed

src/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpSystemProxy.cs

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,16 @@ namespace System.Net.Http
1313
{
1414
internal sealed class HttpSystemProxy : IWebProxy, IDisposable
1515
{
16-
private readonly Uri _proxyUri; // URI of the system proxy if set
16+
private readonly Uri _insecureProxyUri; // URI of the http system proxy if set
17+
private readonly Uri _secureProxyUri; // URI of the https system proxy if set
1718
private readonly List<Regex> _bypass; // list of domains not to proxy
1819
private readonly bool _bypassLocal = false; // we should bypass domain considered local
1920
private readonly List<IPAddress> _localIp;
2021
private ICredentials _credentials;
2122
private readonly WinInetProxyHelper _proxyHelper;
2223
private SafeWinHttpHandle _sessionHandle;
2324
private bool _disposed;
25+
private static readonly char[] s_proxyDelimiters = {';', ' ', '\n', '\r', '\t'};
2426

2527
public static bool TryCreate(out IWebProxy proxy)
2628
{
@@ -62,7 +64,7 @@ private HttpSystemProxy(WinInetProxyHelper proxyHelper, SafeWinHttpHandle sessio
6264

6365
if (proxyHelper.ManualSettingsOnly)
6466
{
65-
_proxyUri = GetUriFromString(proxyHelper.Proxy);
67+
ParseProxyConfig(proxyHelper.Proxy, out _insecureProxyUri, out _secureProxyUri);
6668

6769
if (!string.IsNullOrWhiteSpace(proxyHelper.ProxyBypass))
6870
{
@@ -178,7 +180,7 @@ public void Dispose()
178180

179181
/// <summary>
180182
/// This function will evaluate given string and it will try to convert
181-
/// it to Uri object. The string could contain URI fragment, IP address and port
183+
/// it to a Uri object. The string could contain URI fragment, IP address and port
182184
/// tuple or just IP address or name. It will return null if parsing fails.
183185
/// </summary>
184186
private static Uri GetUriFromString(string value)
@@ -202,6 +204,64 @@ private static Uri GetUriFromString(string value)
202204
return null;
203205
}
204206

207+
/// <summary>
208+
/// This function is used to parse WinINet Proxy strings. The strings are a semicolon
209+
/// or whitespace separated list, with each entry in the following format:
210+
/// ([<scheme>=][<scheme>"://"]<server>[":"<port>])
211+
/// </summary>
212+
private static void ParseProxyConfig(string value, out Uri insecureProxy, out Uri secureProxy )
213+
{
214+
secureProxy = null;
215+
insecureProxy = null;
216+
if (string.IsNullOrEmpty(value))
217+
{
218+
return;
219+
}
220+
221+
int idx = value.IndexOf("http://");
222+
if (idx >= 0)
223+
{
224+
int proxyLength = GetProxySubstringLength(value, idx);
225+
Uri.TryCreate(value.Substring(idx, proxyLength) , UriKind.Absolute, out insecureProxy);
226+
}
227+
228+
if (insecureProxy == null)
229+
{
230+
idx = value.IndexOf("http=");
231+
if (idx >= 0)
232+
{
233+
idx += 5; // Skip "http=" so we can replace it with "http://"
234+
int proxyLength = GetProxySubstringLength(value, idx);
235+
Uri.TryCreate("http://" + value.Substring(idx, proxyLength) , UriKind.Absolute, out insecureProxy);
236+
}
237+
}
238+
239+
idx = value.IndexOf("https://");
240+
if (idx >= 0)
241+
{
242+
idx += 8; // Skip "https://" so we can replace it with "http://"
243+
int proxyLength = GetProxySubstringLength(value, idx);
244+
Uri.TryCreate("http://" + value.Substring(idx, proxyLength) , UriKind.Absolute, out secureProxy);
245+
}
246+
247+
if (secureProxy == null)
248+
{
249+
idx = value.IndexOf("https=");
250+
if (idx >= 0)
251+
{
252+
idx += 6; // Skip "https=" so we can replace it with "http://"
253+
int proxyLength = GetProxySubstringLength(value, idx);
254+
Uri.TryCreate("http://" + value.Substring(idx, proxyLength) , UriKind.Absolute, out secureProxy);
255+
}
256+
}
257+
}
258+
259+
private static int GetProxySubstringLength(String proxyString, int idx)
260+
{
261+
int endOfProxy = proxyString.IndexOfAny(s_proxyDelimiters, idx);
262+
return (endOfProxy == -1) ? proxyString.Length - idx : endOfProxy - idx;
263+
}
264+
205265
/// <summary>
206266
/// Gets the proxy URI. (IWebProxy interface)
207267
/// </summary>
@@ -261,7 +321,7 @@ public Uri GetProxy(Uri uri)
261321
}
262322

263323
// We did not find match on bypass list.
264-
return _proxyUri;
324+
return (uri.Scheme == UriScheme.Https || uri.Scheme == UriScheme.Wss) ? _secureProxyUri : _insecureProxyUri;
265325
}
266326

267327
// For anything else ask WinHTTP.

src/System.Net.Http/tests/UnitTests/HttpSystemProxyTest.cs

Lines changed: 68 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,29 +15,53 @@ public class HttpSystemProxyTest : RemoteExecutorTestBase
1515
{
1616
private readonly ITestOutputHelper _output;
1717
private const string FakeProxyString = "http://proxy.contoso.com";
18-
private readonly Uri fakeProxyUri = new Uri("http://proxy.contoso.com");
19-
private readonly Uri fooHttp = new Uri("http://foo.com");
20-
private readonly Uri fooHttps = new Uri("https://foo.com");
18+
private const string insecureProxyUri = "http://proxy.insecure.com";
19+
private const string secureProxyUri = "http://proxy.secure.com";
20+
private const string fooHttp = "http://foo.com";
21+
private const string fooHttps = "https://foo.com";
22+
private const string fooWs = "ws://foo.com";
23+
private const string fooWss = "wss://foo.com";
2124

2225
public HttpSystemProxyTest(ITestOutputHelper output)
2326
{
2427
_output = output;
2528
}
2629

27-
[Fact]
28-
public void HttpProxy_SystemProxy_Loaded()
30+
[Theory]
31+
[InlineData("http://proxy.insecure.com", true, false)]
32+
[InlineData("http=proxy.insecure.com", true, false)]
33+
[InlineData("http://proxy.insecure.com http://proxy.wrong.com", true, false)]
34+
[InlineData("https=proxy.secure.com http=proxy.insecure.com", true, true)]
35+
[InlineData("https://proxy.secure.com\nhttp://proxy.insecure.com", true, true)]
36+
[InlineData("https=proxy.secure.com\nhttp=proxy.insecure.com", true, true)]
37+
[InlineData("https://proxy.secure.com;http://proxy.insecure.com", true, true)]
38+
[InlineData("https=proxy.secure.com;http=proxy.insecure.com", true, true)]
39+
[InlineData(";http=proxy.insecure.com;;", true, false)]
40+
[InlineData(" http=proxy.insecure.com ", true, false)]
41+
[InlineData("http=proxy.insecure.com;http=proxy.wrong.com", true, false)]
42+
[InlineData("http=http://proxy.insecure.com", true, false)]
43+
[InlineData("https=https://proxy.secure.com", false, true)]
44+
public void HttpProxy_SystemProxy_Loaded(string rawProxyString, bool hasInsecureProxy, bool hasSecureProxy)
2945
{
30-
IWebProxy p;
46+
RemoteInvoke((proxyString, insecureProxy, secureProxy) =>
47+
{
48+
IWebProxy p;
3149

32-
FakeRegistry.Reset();
33-
Assert.False(HttpSystemProxy.TryCreate(out p));
50+
FakeRegistry.Reset();
51+
Assert.False(HttpSystemProxy.TryCreate(out p));
3452

35-
FakeRegistry.WinInetProxySettings.Proxy = FakeProxyString;
53+
FakeRegistry.WinInetProxySettings.Proxy = proxyString;
54+
WinInetProxyHelper proxyHelper = new WinInetProxyHelper();
3655

37-
Assert.True(HttpSystemProxy.TryCreate(out p));
38-
Assert.NotNull(p);
39-
Assert.Equal(fakeProxyUri, p.GetProxy(fooHttp));
40-
Assert.Equal(fakeProxyUri, p.GetProxy(fooHttps));
56+
Assert.True(HttpSystemProxy.TryCreate(out p));
57+
Assert.NotNull(p);
58+
59+
Assert.Equal(Boolean.Parse(insecureProxy) ? new Uri(insecureProxyUri) : null, p.GetProxy(new Uri(fooHttp)));
60+
Assert.Equal(Boolean.Parse(secureProxy) ? new Uri(secureProxyUri) : null, p.GetProxy(new Uri(fooHttps)));
61+
Assert.Equal(Boolean.Parse(insecureProxy) ? new Uri(insecureProxyUri) : null, p.GetProxy(new Uri(fooWs)));
62+
Assert.Equal(Boolean.Parse(secureProxy) ? new Uri(secureProxyUri) : null, p.GetProxy(new Uri(fooWss)));
63+
return SuccessExitCode;
64+
}, rawProxyString, hasInsecureProxy.ToString(), hasSecureProxy.ToString()).Dispose();
4165
}
4266

4367
[Theory]
@@ -66,7 +90,7 @@ public void HttpProxy_Local_Bypassed(string name, bool shouldBypass)
6690
IWebProxy p;
6791

6892
FakeRegistry.Reset();
69-
FakeRegistry.WinInetProxySettings.Proxy = FakeProxyString;
93+
FakeRegistry.WinInetProxySettings.Proxy = insecureProxyUri;
7094
FakeRegistry.WinInetProxySettings.ProxyBypass = "23.23.86.44;*.foo.com;<local>;BAR.COM; ; 162*;[2002::11];[*:f8b0:4005:80a::200e]; http://www.xn--mnchhausen-9db.at;http://*.xn--bb-bjab.eu;http://xn--bb-bjab.eu;";
7195

7296
Assert.True(HttpSystemProxy.TryCreate(out p));
@@ -93,7 +117,7 @@ public void HttpProxy_Local_Parsing(string bypass, int count)
93117
IWebProxy p;
94118

95119
FakeRegistry.Reset();
96-
FakeRegistry.WinInetProxySettings.Proxy = FakeProxyString;
120+
FakeRegistry.WinInetProxySettings.Proxy = insecureProxyUri;
97121
FakeRegistry.WinInetProxySettings.ProxyBypass = bypassValue;
98122

99123
Assert.True(HttpSystemProxy.TryCreate(out p));
@@ -112,7 +136,36 @@ public void HttpProxy_Local_Parsing(string bypass, int count)
112136
}
113137
return SuccessExitCode;
114138
}, bypass, count.ToString()).Dispose();
139+
}
115140

141+
[Theory]
142+
[InlineData("http://")]
143+
[InlineData("http=")]
144+
[InlineData("http://;")]
145+
[InlineData("http=;")]
146+
[InlineData(" ; ")]
147+
[InlineData("proxy.contoso.com")]
148+
public void HttpProxy_InvalidSystemProxy_Null(string rawProxyString)
149+
{
150+
RemoteInvoke((proxyString) =>
151+
{
152+
IWebProxy p;
153+
154+
FakeRegistry.Reset();
155+
Assert.False(HttpSystemProxy.TryCreate(out p));
156+
157+
FakeRegistry.WinInetProxySettings.Proxy = proxyString;
158+
WinInetProxyHelper proxyHelper = new WinInetProxyHelper();
159+
160+
Assert.True(HttpSystemProxy.TryCreate(out p));
161+
Assert.NotNull(p);
162+
163+
Assert.Equal(null, p.GetProxy(new Uri(fooHttp)));
164+
Assert.Equal(null, p.GetProxy(new Uri(fooHttps)));
165+
Assert.Equal(null, p.GetProxy(new Uri(fooWs)));
166+
Assert.Equal(null, p.GetProxy(new Uri(fooWss)));
167+
return SuccessExitCode;
168+
}, rawProxyString).Dispose();
116169
}
117170
}
118171
}

0 commit comments

Comments
 (0)