Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SNOW-1333163: Add support for default proxy configuration #920

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ The following table lists all valid connection properties:
| TOKEN | Depends | The OAuth token to use for OAuth authentication. Must be used in combination with AUTHENTICATOR=oauth. |
| INSECUREMODE | No | Set to true to disable the certificate revocation list check. Default is false. |
| USEPROXY | No | Set to true if you need to use a proxy server. The default value is false. <br/> <br/> This parameter was introduced in v2.0.4. |
| PROXYHOST | Depends | The hostname of the proxy server. <br/> <br/> If USEPROXY is set to `true`, you must set this parameter. <br/> <br/> This parameter was introduced in v2.0.4. |
| PROXYPORT | Depends | The port number of the proxy server. <br/> <br/> If USEPROXY is set to `true`, you must set this parameter. <br/> <br/> This parameter was introduced in v2.0.4. |
| PROXYHOST | No | The hostname of the proxy server. <br/> <br/> If USEPROXY is set to `true`, you can set this parameter to define the proxy explicitly. If not set, the default proxy will be used (HTTPS_PROXY, WinINet...). <br/> <br/> This parameter was introduced in v2.0.4. |
| PROXYPORT | Depends | The port number of the proxy server. <br/> <br/> If USEPROXY is set to `true` and PROXYHOST is specfied, you must set this parameter. <br/> <br/> This parameter was introduced in v2.0.4. |
| PROXYUSER | No | The username for authenticating to the proxy server. <br/> <br/> This parameter was introduced in v2.0.4. |
| PROXYPASSWORD | Depends | The password for authenticating to the proxy server. <br/> <br/> If USEPROXY is `true` and PROXYUSER is set, you must set this parameter. <br/> <br/> This parameter was introduced in v2.0.4. |
| NONPROXYHOSTS | No | The list of hosts that the driver should connect to directly, bypassing the proxy server. Separate the hostnames with a pipe symbol (\|). You can also use an asterisk (`*`) as a wildcard. <br/> The host target value should fully match with any item from the proxy host list to bypass the proxy server. <br/> <br/> This parameter was introduced in v2.0.4. |
Expand Down
2 changes: 1 addition & 1 deletion Snowflake.Data.Tests/SFBaseTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2012-2021 Snowflake Computing Inc. All rights reserved.
*/

Expand Down
45 changes: 36 additions & 9 deletions Snowflake.Data.Tests/UnitTests/HttpUtilTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2022 Snowflake Computing Inc. All rights reserved.
*/

Expand Down Expand Up @@ -102,48 +102,75 @@ public void TestGetJitter(int seconds)
}

[Test]
public void ShouldCreateHttpClientHandlerWithProxy()
public void ShouldCreateHttpClientHandlerWithExplicitProxy()
{
// given
var config = new HttpClientConfig(
true,
true,
"snowflake.com",
"123",
"testUser",
"proxyPassword",
"localhost",
"localhost",
false,
false,
7
);

// when
var handler = (HttpClientHandler) HttpUtil.Instance.SetupCustomHttpHandler(config);

// then
Assert.IsTrue(handler.UseProxy);
Assert.IsNotNull(handler.Proxy);
}

[Test]
public void ShouldCreateHttpClientHandlerWithoutProxy()
public void ShouldCreateHttpClientHandlerWithImplicitProxy()
{
// given
var config = new HttpClientConfig(
true,
true,
null,
null,
null,
null,
null,
null,
false,
false,
7
);

// when
var handler = (HttpClientHandler) HttpUtil.Instance.SetupCustomHttpHandler(config);

// then
Assert.IsTrue(handler.UseProxy);
Assert.IsNull(handler.Proxy);
}

[Test]
public void ShouldCreateHttpClientHandlerWithoutProxy()
{
// given
var config = new HttpClientConfig(
false,
false,
null,
null,
null,
null,
null,
false,
false,
0
);

// when
var handler = (HttpClientHandler) HttpUtil.Instance.SetupCustomHttpHandler(config);

// then
Assert.IsFalse(handler.UseProxy);
Assert.IsNull(handler.Proxy);
Expand Down
95 changes: 52 additions & 43 deletions Snowflake.Data/Core/HttpUtil.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*
/*
* Copyright (c) 2012-2021 Snowflake Computing Inc. All rights reserved.
*/

Expand All @@ -21,6 +21,7 @@ public class HttpClientConfig
{
public HttpClientConfig(
bool crlCheckEnabled,
bool useProxy,
string proxyHost,
string proxyPort,
string proxyUser,
Expand All @@ -32,6 +33,7 @@ public HttpClientConfig(
bool includeRetryReason = true)
{
CrlCheckEnabled = crlCheckEnabled;
UseProxy = useProxy;
ProxyHost = proxyHost;
ProxyPort = proxyPort;
ProxyUser = proxyUser;
Expand All @@ -45,6 +47,7 @@ public HttpClientConfig(
ConfKey = string.Join(";",
new string[] {
crlCheckEnabled.ToString(),
useProxy.ToString(),
proxyHost,
proxyPort,
proxyUser,
Expand All @@ -57,6 +60,7 @@ public HttpClientConfig(
}

public readonly bool CrlCheckEnabled;
public readonly bool UseProxy;
public readonly string ProxyHost;
public readonly string ProxyPort;
public readonly string ProxyUser;
Expand Down Expand Up @@ -87,7 +91,7 @@ public sealed class HttpUtil

private HttpUtil()
{
// This value is used by AWS SDK and can cause deadlock,
// This value is used by AWS SDK and can cause deadlock,
// so we need to increase the default value of 2
// See: https://github.com/aws/aws-sdk-net/issues/152
ServicePointManager.DefaultConnectionLimit = 50;
Expand Down Expand Up @@ -130,7 +134,7 @@ private HttpClient RegisterNewHttpClientIfNecessary(HttpClientConfig config)

internal HttpMessageHandler SetupCustomHttpHandler(HttpClientConfig config)
{
HttpMessageHandler httpHandler;
HttpClientHandler httpHandler;
try
{
httpHandler = new HttpClientHandler()
Expand All @@ -140,8 +144,7 @@ internal HttpMessageHandler SetupCustomHttpHandler(HttpClientConfig config)
// Enforce tls v1.2
SslProtocols = SslProtocols.Tls12,
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
UseCookies = false, // Disable cookies
UseProxy = false
UseCookies = false // Disable cookies
};
}
// special logic for .NET framework 4.7.1 that
Expand All @@ -151,56 +154,62 @@ internal HttpMessageHandler SetupCustomHttpHandler(HttpClientConfig config)
httpHandler = new HttpClientHandler()
{
AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
UseCookies = false, // Disable cookies
UseProxy = false
UseCookies = false // Disable cookies
};
}

// Add a proxy if necessary
if (null != config.ProxyHost)
if (!config.UseProxy)
{
httpHandler.UseProxy = false;
}
else
{
// Proxy needed
WebProxy proxy = new WebProxy(config.ProxyHost, int.Parse(config.ProxyPort));
httpHandler.UseProxy = true;

// Add credential if provided
if (!String.IsNullOrEmpty(config.ProxyUser))
if (!String.IsNullOrEmpty(config.ProxyHost))
{
ICredentials credentials = new NetworkCredential(config.ProxyUser, config.ProxyPassword);
proxy.Credentials = credentials;
}
// Host explicitly specified, do not use default proxy
WebProxy proxy = new WebProxy(config.ProxyHost, int.Parse(config.ProxyPort));

// Add bypasslist if provided
if (!String.IsNullOrEmpty(config.NoProxyList))
{
string[] bypassList = config.NoProxyList.Split(
new char[] { '|' },
StringSplitOptions.RemoveEmptyEntries);
// Convert simplified syntax to standard regular expression syntax
string entry = null;
for (int i = 0; i < bypassList.Length; i++)
// Add credential if provided
if (!String.IsNullOrEmpty(config.ProxyUser))
{
ICredentials credentials = new NetworkCredential(config.ProxyUser, config.ProxyPassword);
proxy.Credentials = credentials;
}

// Add bypasslist if provided
if (!String.IsNullOrEmpty(config.NoProxyList))
{
// Get the original entry
entry = bypassList[i].Trim();
// . -> [.] because . means any char
entry = entry.Replace(".", "[.]");
// * -> .* because * is a quantifier and need a char or group to apply to
entry = entry.Replace("*", ".*");

entry = entry.StartsWith("^") ? entry : $"^{entry}";

entry = entry.EndsWith("$") ? entry : $"{entry}$";

// Replace with the valid entry syntax
bypassList[i] = entry;
string[] bypassList = config.NoProxyList.Split(
new char[] { '|' },
StringSplitOptions.RemoveEmptyEntries);
// Convert simplified syntax to standard regular expression syntax
string entry = null;
for (int i = 0; i < bypassList.Length; i++)
{
// Get the original entry
entry = bypassList[i].Trim();
// . -> [.] because . means any char
entry = entry.Replace(".", "[.]");
// * -> .* because * is a quantifier and need a char or group to apply to
entry = entry.Replace("*", ".*");

entry = entry.StartsWith("^") ? entry : $"^{entry}";

entry = entry.EndsWith("$") ? entry : $"{entry}$";

// Replace with the valid entry syntax
bypassList[i] = entry;

}
proxy.BypassList = bypassList;
}
proxy.BypassList = bypassList;
}

HttpClientHandler httpHandlerWithProxy = (HttpClientHandler)httpHandler;
httpHandlerWithProxy.UseProxy = true;
httpHandlerWithProxy.Proxy = proxy;
return httpHandlerWithProxy;
httpHandler.Proxy = proxy;
}
}
return httpHandler;
}
Expand Down Expand Up @@ -384,7 +393,7 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
if (httpTimeout.Ticks == 0)
childCts.Cancel();
else
childCts.CancelAfter(httpTimeout);
childCts.CancelAfter(httpTimeout);
}
response = await base.SendAsync(requestMessage, childCts == null ?
cancellationToken : childCts.Token).ConfigureAwait(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ internal HttpClientConfig BuildHttpClientConfig()
{
return new HttpClientConfig(
!insecureMode,
proxyProperties.useProxy,
proxyProperties.proxyHost,
proxyProperties.proxyPort,
proxyProperties.proxyUser,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Snowflake.Data.Core

internal class SFSessionHttpClientProxyProperties
{
internal bool useProxy = false;
internal string proxyHost = null;
internal string proxyPort = null;
internal string nonProxyHosts = null;
Expand All @@ -22,7 +23,10 @@ internal class Extractor : IExtractor
public SFSessionHttpClientProxyProperties ExtractProperties(SFSessionProperties propertiesDictionary)
{
var properties = new SFSessionHttpClientProxyProperties();
if (Boolean.Parse(propertiesDictionary[SFSessionProperty.USEPROXY]))

properties.useProxy = Boolean.Parse(propertiesDictionary[SFSessionProperty.USEPROXY]);

if (properties.useProxy)
{
// Let's try to get the associated RestRequester
propertiesDictionary.TryGetValue(SFSessionProperty.PROXYHOST, out properties.proxyHost);
Expand Down
Loading
Loading