Skip to content

Commit f5637b7

Browse files
committed
Improve HttpEnvironmentProxy uri parsing
1 parent b02019a commit f5637b7

File tree

2 files changed

+20
-22
lines changed

2 files changed

+20
-22
lines changed

src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpEnvironmentProxy.cs

Lines changed: 15 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
5-
using System.Collections.Generic;
6-
using System.Net;
7-
using System.Net.Http;
8-
94
namespace System.Net.Http
105
{
116
internal sealed class HttpEnvironmentProxyCredentials : ICredentials
@@ -115,10 +110,11 @@ private HttpEnvironmentProxy(Uri? httpProxy, Uri? httpsProxy, string? bypassList
115110
}
116111

117112
/// <summary>
118-
/// This function will evaluate given string and it will try to convert
119-
/// it to Uri object. The string could contain URI fragment, IP address and port
120-
/// tuple or just IP address or name. It will return null if parsing fails.
113+
/// Attempt to parse a partial Uri string into a Uri object.
114+
/// The string may contain the scheme, user info, host, and port. The host is the only required part.
115+
/// Example expected inputs: contoso.com, contoso.com:8080, http://contoso.com/, [email protected].
121116
/// </summary>
117+
/// <returns><see langword="null"/> if parsing fails.</returns>
122118
private static Uri? GetUriFromString(string? value)
123119
{
124120
if (string.IsNullOrEmpty(value))
@@ -167,7 +163,7 @@ private HttpEnvironmentProxy(Uri? httpProxy, Uri? httpsProxy, string? bypassList
167163

168164
// Check if there is authentication part with user and possibly password.
169165
// Curl accepts raw text and that may break URI parser.
170-
int separatorIndex = value.LastIndexOf('@');
166+
int separatorIndex = value.IndexOf('@');
171167
if (separatorIndex != -1)
172168
{
173169
// The User and password may or may not be URL encoded.
@@ -187,6 +183,14 @@ private HttpEnvironmentProxy(Uri? httpProxy, Uri? httpsProxy, string? bypassList
187183
}
188184
}
189185

186+
// We expect inputs to not contain the path/query/fragment, but we do handle some simple cases like a trailing slash.
187+
// An arbitrary path can break this parsing logic, but we expect environment variables to be trusted and well formed.
188+
int delimiterIndex = value.IndexOfAny('/', '?', '#');
189+
if (delimiterIndex >= 0)
190+
{
191+
value = value.Substring(0, delimiterIndex);
192+
}
193+
190194
int ipV6AddressEnd = value.IndexOf(']');
191195
separatorIndex = value.LastIndexOf(':');
192196
// No ':' or it is part of IPv6 address.
@@ -197,18 +201,8 @@ private HttpEnvironmentProxy(Uri? httpProxy, Uri? httpsProxy, string? bypassList
197201
else
198202
{
199203
host = value.Substring(0, separatorIndex);
200-
int endIndex = separatorIndex + 1;
201-
// Strip any trailing characters after port number.
202-
while (endIndex < value.Length)
203-
{
204-
if (!char.IsDigit(value[endIndex]))
205-
{
206-
break;
207-
}
208-
endIndex += 1;
209-
}
210204

211-
if (!ushort.TryParse(value.AsSpan(separatorIndex + 1, endIndex - separatorIndex - 1), out port))
205+
if (!ushort.TryParse(value.AsSpan(separatorIndex + 1), out port))
212206
{
213207
return null;
214208
}
@@ -261,8 +255,7 @@ private bool IsMatchInBypassList(Uri input)
261255
{
262256
// This should match either domain it self or any subdomain or host
263257
// .foo.com will match foo.com it self or *.foo.com
264-
if ((s.Length - 1) == input.Host.Length &&
265-
string.Compare(s, 1, input.Host, 0, input.Host.Length, StringComparison.OrdinalIgnoreCase) == 0)
258+
if (s.AsSpan(1).Equals(input.Host, StringComparison.OrdinalIgnoreCase))
266259
{
267260
return true;
268261
}

src/libraries/System.Net.Http/tests/UnitTests/HttpEnvironmentProxyTest.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,13 @@ await RemoteExecutor.Invoke(() =>
137137
[InlineData("[::1]", "[::1]", "80", null, null)]
138138
[InlineData("domain\\foo:[email protected]", "1.1.1.1", "80", "foo", "PLACEHOLDER")]
139139
[InlineData("domain%5Cfoo:[email protected]", "1.1.1.1", "80", "foo", "PLACEHOLDER")]
140+
[InlineData("[email protected]/foo@", "1.2.3.4", "80", "placeholder", "")]
141+
[InlineData("placeholder:@1.2.3.4/foo@:", "1.2.3.4", "80", "placeholder", "")]
140142
[InlineData("HTTP://ABC.COM/", "abc.com", "80", null, null)]
141143
[InlineData("http://10.30.62.64:7890/", "10.30.62.64", "7890", null, null)]
144+
[InlineData("http://1.2.3.4/foo", "1.2.3.4", "80", null, null)]
145+
[InlineData("http://1.2.3.4/foo:", "1.2.3.4", "80", null, null)]
146+
[InlineData("http://1.2.3.4?foo", "1.2.3.4", "80", null, null)]
142147
[InlineData("http://1.2.3.4:8888/foo", "1.2.3.4", "8888", null, null)]
143148
[InlineData("socks4://1.2.3.4:8888/foo", "1.2.3.4", "8888", null, null)]
144149
[InlineData("socks4a://1.2.3.4:8888/foo", "1.2.3.4", "8888", null, null)]

0 commit comments

Comments
 (0)