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

Adding WebProxy support for outgoing requests #879

Merged
merged 14 commits into from Mar 31, 2021
Merged

Conversation

vdjuric
Copy link
Contributor

@vdjuric vdjuric commented Mar 27, 2021

Hi,

I added initial WebProxy support for outgoing requests. This is useful for enterprise environments which frequently use internal web proxies.

All tests are successfully passing.

Kind regards,
Vladimir Djurić
https://vladimir.djuric.si/

@ghost
Copy link

ghost commented Mar 27, 2021

CLA assistant check
All CLA requirements met.

/// <summary>
/// Optional web proxy used when communicating with the destination server.
/// </summary>
public System.Net.IWebProxy WebProxy { get; init; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, this would add another non-serializable type to the config model. We've been trying to limit that. Is there a reason we need to directly expose IWebProxy here? Would it be possible to expose the raw values from WebProxyConfigData here and then build the proxy instance in the client factory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, of course. I have done it this way to mimic ProxyHttpClientOptions.ClientCertificate implementation. I will remove IWebProxyConfigLoader and construct web proxy instance in client factory.

In future, we could maybe expose IWebProxy factory so user could have more power when generating web proxy instance. Similar pattern could be applied to ProxyHttpClientOptions.ClientCertificate implementation.

Thank you for your feedback!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,

I just commited newest version. All tests are passing.

Copy link
Member

@MihaZupan MihaZupan Mar 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth exposing all proxy options here?
This could be just a string for the address, where one could use #869 if they need more customization.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have done it this way to mimic ProxyHttpClientOptions.ClientCertificate implementation.

Yeah, ClientCertificate is the other problematic property that we haven't figured out what to do with.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MihaZupan If one will be able to derive from ProxyHttpClientFactory, register new implementation (as singleton) via dependency injection and override ConfigureHandler, then WebProxyOptions can be simplified if needed (by removing BypassOnLocal and UseDefaultCredentials properties).

Although, it may be easier for end-user to configure WebProxy directly from config.

I am neutral here :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Tratcher For certificate loading I would create simple configuration structure with properties:
-X509FindType
-findValue
-validOnly

This would be used for X509Certificate2Collection.Find method: https://docs.microsoft.com/en-us/dotnet/api/system.security.cryptography.x509certificates.x509certificate2collection.find?view=net-5.0

For more advanced scenarios one would have to derive from ProxyHttpClientFactory and override ConfigureHandler.

Copy link
Member

@MihaZupan MihaZupan Mar 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although, it may be easier for end-user to configure WebProxy directly from config.

Right, I'm thinking in terms of how commonly such settings would be changed vs. us mirroring the SocketsHttpHandler surface here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

UseDefaultCredentials seems like it will be a common need. I don't know how frequently BypassOnLocal will be used, but if we already have two options then a third is harmless.

@Tratcher Tratcher self-assigned this Mar 29, 2021
Copy link
Member

@Tratcher Tratcher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I only have a few remaining cosmetic comments.


/// <summary>
/// true to bypass the proxy for local addresses; otherwise, false.
/// If null, default value for <seealso cref="System.Net.WebProxy"/> implementation will be used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which is false?


/// <summary>
/// Controls whether the <seealso cref="System.Net.CredentialCache.DefaultCredentials"/> are sent with requests.
/// If null, default value for <seealso cref="System.Net.WebProxy"/> implementation will be used.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include the default (false?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

I have included internal constants with default values in WebProxyOptions. I am not relying on default values from WebProxy implementation (but currently, they should be same).

Should we leave BypassOnLocal and UseDefaultCredentials properties as nullable bool? or should we change them to non-nullable bool ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We prefer nullables in the config so an extended implementation can choose its own defaults.

}

var options = new WebProxyOptions();
webProxyConfig.Bind(options);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bind is not used for other options, each property is read separately. Does this also need to change for consistency?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional question: since Bind is using reflection, is this acceptable?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. The biggest reason not to use bind is that it messes up the linker (the setters get deleted because it can't tell they're in use). It would be better for consistency to avoid Bind regardless.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! This should be now fixed.

Comment on lines 20 to 21

internal const bool BypassOnLocalDefaultValue = false;
Copy link
Member

@Tratcher Tratcher Mar 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
internal const bool BypassOnLocalDefaultValue = false;

The consumer (ProxyHttpClientFactory) gets to decide what the actual defaults are. The defaults in the doc comments reflect the defaults used by the default ProxyHttpClientFactory. (so many defaults...)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you! Defaults should be now fixed :)

vdjuric and others added 2 commits March 30, 2021 22:33
…actory.cs


Simplification for setting default value

Co-authored-by: Kahbazi <akahbazi@gmail.com>
Copy link
Member

@Tratcher Tratcher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One comment about validation, but otherwise this looks good.

@@ -320,6 +320,8 @@ private ProxyHttpClientOptions CreateProxyHttpClientOptions(IConfigurationSectio
}
}

var webProxy = TryGetWebProxyOptions(section.GetSection(nameof(ProxyHttpClientOptions.WebProxy)));
Copy link
Member

@Tratcher Tratcher Mar 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Style: Why isn't this called inline on line 336 like the others?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With newest commit, I moved web proxy parsing inline to bo more aligned with other parsing (minor cosmetic changes can be still adjusted if needed).

@@ -23,6 +23,16 @@ internal static class ConfigurationReadingExtensions
return configuration[name] is string value ? TimeSpan.Parse(value, CultureInfo.InvariantCulture) : null;
}

internal static Uri ReadUri(this IConfiguration configuration, string name)
{
if (configuration[name] is string value && Uri.TryCreate(value, UriKind.Absolute, out var parsedUri))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've intentionally avoided the Try APIs in the config reader because we want to actively report errors. Use the normal constructor and let it throw if the value is invalid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense. I changed this in newest commit.

@Tratcher Tratcher merged commit 4b71268 into microsoft:main Mar 31, 2021
@Tratcher
Copy link
Member

Thanks

@Tratcher Tratcher added this to the YARP 1.0.0-preview11 milestone Mar 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants