Skip to content

Commit

Permalink
Add support for no_proxy environment variable for .NET Framework targ…
Browse files Browse the repository at this point in the history
…et. (#3200)

The .NET Core targets rely on the .NET runtime to process the no_proxy environment variable.
  • Loading branch information
normj committed Feb 27, 2024
1 parent bc8e142 commit ac08855
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 18 deletions.
@@ -0,0 +1,9 @@
{
"core": {
"changeLogMessages": [
"Add support for no_proxy environment variable for .NET Framework targets."
],
"type": "patch",
"updateMinimum": true
}
}
Expand Up @@ -16,6 +16,7 @@
using Amazon.Runtime.Internal.Transform;
using Amazon.Runtime.Internal.Util;
using Amazon.Util;
using Amazon.Util.Internal;
using System;
using System.Collections.Generic;
using System.Globalization;
Expand Down Expand Up @@ -474,13 +475,16 @@ public virtual void ConfigureRequest(IRequestContext requestContext)
requestContext.Metrics.AddProperty(Metric.ProxyPort, requestContext.ClientConfig.ProxyPort);
_request.Proxy = proxy;
}
else if (_request.RequestUri.Scheme == Uri.UriSchemeHttp)
else if(!NoProxyFilter.Instance.Match(_request.RequestUri))
{
_request.Proxy = requestContext.ClientConfig.GetHttpProxy();
}
else if (_request.RequestUri.Scheme == Uri.UriSchemeHttps)
{
_request.Proxy = requestContext.ClientConfig.GetHttpsProxy();
if (_request.RequestUri.Scheme == Uri.UriSchemeHttp)
{
_request.Proxy = requestContext.ClientConfig.GetHttpProxy();
}
else if (_request.RequestUri.Scheme == Uri.UriSchemeHttps)
{
_request.Proxy = requestContext.ClientConfig.GetHttpsProxy();
}
}

// Set service point properties.
Expand Down
87 changes: 87 additions & 0 deletions sdk/src/Core/Amazon.Util/Internal/_bcl/NoProxyFilter.cs
@@ -0,0 +1,87 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using Amazon.Runtime.Internal.Util;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Amazon.Util.Internal
{
/// <summary>
/// This type is used to check if an out going request should not use the https_proxy or http_proxy environment variable if the
/// host matches a pattern in the no_proxy environment variable.
///
/// The SDK does not do a full implementation of the no_proxy evaluation. It handles host name match with wildcard support. A full
/// implementation would also support DNS IP resolution and CIDR pattern matching.
/// </summary>
public class NoProxyFilter
{
internal const string NO_PROXY_ENV_NAME = "no_proxy";

public static readonly NoProxyFilter Instance = new NoProxyFilter(new EnvironmentVariableRetriever());

private IList<Regex> _proxyFilterRegex = new List<Regex>();

public NoProxyFilter(IEnvironmentVariableRetriever environmentVariableRetriever)
{
var filters = environmentVariableRetriever.GetEnvironmentVariable(NO_PROXY_ENV_NAME)?.Split(',');
if (filters == null)
{
return;
}

foreach (var filter in filters)
{
if (string.IsNullOrEmpty(filter))
{
continue;
}

try
{
var regExPattern = string.Concat("^", Regex.Escape(filter.Trim()).Replace("\\*", ".*"), "$");
var regEx = new Regex(regExPattern, RegexOptions.Compiled);
_proxyFilterRegex.Add(regEx);
}
catch(ArgumentException e)
{
// We don't have any control if users have put invalid information in the no_proxy and the SDK's implementation is not a full
// implementation of no_proxy, we will skip any segements in the no_proxy env var that we fail to parse. Since no_proxy has existed
// before the SDK added this support we could introduce a breaking change if we start throwing fatal exceptions at this point.
Logger.GetLogger(typeof(NoProxyFilter)).Error(e, "Failed parse segment \"{0}\" in no_proxy environment variable.", filter);
}
}
}

/// <summary>
/// Evaluates if the host in the Uri should be exluded from proxy if it matches a pattern in the no_proxy environment variable.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
public bool Match(Uri uri)
{
var host = uri.Host;
foreach(var filter in _proxyFilterRegex)
{
if(filter.IsMatch(host))
{
return true;
}
}

return false;
}
}
}
41 changes: 41 additions & 0 deletions sdk/src/Core/Amazon.Util/Internal/_netstandard/NoProxyFilter.cs
@@ -0,0 +1,41 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using System;


namespace Amazon.Util.Internal
{
/// <summary>
/// This is a stub implementation because in .NET Core we rely on the .NET runtime to evaluate the no_proxy environment variable.
/// </summary>
public class NoProxyFilter
{
public static readonly NoProxyFilter Instance = new NoProxyFilter();

public NoProxyFilter()
{
}

/// <summary>
/// This is a stub method because in .NET Core we rely on the .NET runtime to evaluate the no_proxy environment variable.
/// </summary>
/// <param name="uri"></param>
/// <returns></returns>
public bool Match(Uri uri)
{
return false;
}
}
}
Expand Up @@ -30,6 +30,7 @@
using ThirdParty.Json.LitJson;
using Amazon.Runtime;
using System.Threading.Tasks;
using Amazon.Util.Internal;

#pragma warning disable 1591

Expand Down Expand Up @@ -103,7 +104,7 @@ private static async Task LoadDefinitionsFromWebAsync(AmazonEC2Config ec2Config)
foreach (var location in DownloadLocations)
{
var useProxy = webProxy;
if (useProxy == null)
if (useProxy == null && !NoProxyFilter.Instance.Match(new Uri(location)))
{
if (location.StartsWith(httpPrefix))
{
Expand Down
3 changes: 2 additions & 1 deletion sdk/src/Services/EC2/Custom/Util/_bcl/ImageUtilities.bcl.cs
Expand Up @@ -29,6 +29,7 @@

using ThirdParty.Json.LitJson;
using Amazon.Runtime;
using Amazon.Util.Internal;

#pragma warning disable 1591

Expand Down Expand Up @@ -103,7 +104,7 @@ private static void LoadDefinitionsFromWeb(AmazonEC2Config ec2Config)
foreach (var location in DownloadLocations)
{
var useProxy = webProxy;
if (useProxy == null)
if (useProxy == null && !NoProxyFilter.Instance.Match(new Uri(location)))
{
if (location.StartsWith(httpPrefix))
{
Expand Down
2 changes: 1 addition & 1 deletion sdk/src/Services/S3/Custom/AmazonS3Client.Extensions.cs
Expand Up @@ -482,7 +482,7 @@ internal void ConfigureProxy(HttpWebRequest httpRequest)
{
httpRequest.Proxy.Credentials = Config.ProxyCredentials;
}
if (httpRequest.Proxy == null)
if (httpRequest.Proxy == null && !NoProxyFilter.Instance.Match(httpRequest.RequestUri))
{
if (httpRequest.RequestUri.Scheme == Uri.UriSchemeHttp)
{
Expand Down
18 changes: 10 additions & 8 deletions sdk/src/Services/S3/Custom/Util/AmazonS3HttpUtil.cs
Expand Up @@ -15,8 +15,7 @@
using System;
using System.Net;
using Amazon.Runtime;
using Amazon.Util;
using Amazon.Runtime.Internal.Util;
using Amazon.Util.Internal;

#if AWS_ASYNC_API
using System.Threading.Tasks;
Expand Down Expand Up @@ -117,13 +116,16 @@ private static void SetProxyIfAvailableAndConfigured(IClientConfig config, HttpW
{
httpWebRequest.Proxy = proxy;
}
else if (httpWebRequest.RequestUri.Scheme == Uri.UriSchemeHttp)
else if (!NoProxyFilter.Instance.Match(httpWebRequest.RequestUri))
{
httpWebRequest.Proxy = config.GetHttpProxy();
}
else if (httpWebRequest.RequestUri.Scheme == Uri.UriSchemeHttps)
{
httpWebRequest.Proxy = config.GetHttpsProxy();
if (httpWebRequest.RequestUri.Scheme == Uri.UriSchemeHttp)
{
httpWebRequest.Proxy = config.GetHttpProxy();
}
else if (httpWebRequest.RequestUri.Scheme == Uri.UriSchemeHttps)
{
httpWebRequest.Proxy = config.GetHttpsProxy();
}
}
}
}
Expand Down
Expand Up @@ -21,6 +21,8 @@
using Amazon.Runtime.SharedInterfaces;
using Amazon.SecurityToken.Model;
using Amazon.SecurityToken.SAML;
using Amazon.Util.Internal;

#if AWS_ASYNC_API
using System.Threading.Tasks;
#endif
Expand All @@ -47,7 +49,7 @@ public partial class AmazonSecurityTokenServiceClient : AmazonServiceClient, IAm
try
{
var proxy = Config.GetWebProxy();
if (proxy == null)
if (proxy == null && !NoProxyFilter.Instance.Match(new Uri(endpoint)))
{
if (endpoint.StartsWith(httpPrefix))
{
Expand Down
64 changes: 64 additions & 0 deletions sdk/test/UnitTests/Custom/NoProxyFilterTest.cs
@@ -0,0 +1,64 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using Amazon.Runtime.Internal;
using Amazon.Util.Internal;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;

namespace AWSSDK.UnitTests
{
[TestClass]
public class NoProxyFilterTest
{
[TestMethod]
[DataRow("dynamodb.*.amazonaws.com", "dynamodb.us-west-2.amazonaws.com", true)]
[DataRow("dynamodb.*.amazonaws.com", "ec2.us-west-2.amazonaws.com", false)]
[DataRow("169.254.169.254", "169.254.169.254", true)]
[DataRow("169.254.169.*", "169.254.169.254", true)]
[DataRow("www.foo.com", "www.foo.com", true)]
[DataRow(" www.foo.com ", "www.foo.com", true)]
[DataRow("*.foo.com", "www.foo.com", true)]
[DataRow("*foo.com", "foo.com", true)]
[DataRow("www.foo.com", "www.bar.com", false)]
[DataRow("*foo.com", "www.bar.com", false)]
[DataRow("*foo.com", "bar.com", false)]
[DataRow("", "bar.com", false)]
[DataRow(null, "bar.com", false)]
[DataRow("127.0.0.1,localhost,ssm.us-west-2.amazonaws.com", "ssm.us-west-2.amazonaws.com", true)]
[DataRow("127.0.0.1,localhost,ssm.us-west-2.amazonaws.com", "autoscaling.us-west-2.amazonaws.com", false)]
public void Match(string noProxyValue, string testUri, bool skip)
{
var filter = new NoProxyFilter(new EnvironmentVariableStub(noProxyValue));
Assert.AreEqual(skip, filter.Match(new Uri("https://" + testUri + "/")));
}

class EnvironmentVariableStub : IEnvironmentVariableRetriever
{
private readonly string _noProxyValue;
public EnvironmentVariableStub(string noProxyValue)
{
_noProxyValue = noProxyValue;
}

public string GetEnvironmentVariable(string key)
{
if (!string.Equals(key, NoProxyFilter.NO_PROXY_ENV_NAME, StringComparison.OrdinalIgnoreCase))
return null;

return _noProxyValue;
}
}
}
}

0 comments on commit ac08855

Please sign in to comment.