-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
HttpClient specify interface to bind to / make requests from #23267
Comments
BindIPEndPoint delegate was never part of HttpClient. But it was part of HttpWebRequest on .NET Framework. This is not supported on .NET Core. |
Are there any plans to support a multi network card scenario in .NET Core? On a windows machine with multiple NICs how does HttpClient choose which interface to use? |
cc: @stephentoub @karelz |
That sounds like reasonable API request for cc @geoffkizer |
Can I ask why you can't just let the system pick the correct network card? What's your network topology such that the wrong NIC might ever be picked? If the system is confused enough to send regular traffic on the management NIC, having your app pick the management NIC is the least of your worries :-) |
@PeterSmithRedmond It is common (and expected) to be able to control which network interface to bind to at the application level. A machine may have multiple NICs all configured for standard traffic in addition to the management NIC. For example, consider a scenario where there are 3 NICs labeled A - C (+ mgmt card). Lets say I would like to make HTTP requests on interface "C", subscribe to multicast traffic on interface "B", and setup TCP listener on interface "A". This functionality is already available for TcpClient/TcpListener and UdpClient which handles the 2 non-HTTP cases above, making it easy if I decide to rearrange my setup and move my tcp listener to interface B, etc. It would be great if HttpClient could support this type of configuration which is quite common in enterprise scenarios. |
as a follow up to @karelz post, are there any plans to eventually implement this? i am happy to help out, thanks. |
It looks like ConnectHelper is where the necessary code should be to make this happen. In the try-catch after creating the socket (right before the call to public static async ValueTask<Stream> ConnectAsync(string host, int port, EndPoint localEP = null)
{
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
try
{
// **optionally bind to local endpoint**
if (localEP != null)
socket.Bind(localEP);
// TODO dotnet/runtime#23148: cancellation support?
await (IPAddress.TryParse(host, out IPAddress address) ?
socket.ConnectAsync(address, port) :
socket.ConnectAsync(host, port)).ConfigureAwait(false);
}
catch (SocketException se)
{
socket.Dispose();
throw new HttpRequestException(se.Message, se);
}
return new NetworkStream(socket, ownsSocket: true);
} I think the main question is how do we want the localEP argument to bubble up the stack so it can easily be configured on a per client basis? Happy to open a pull request for this if you guys are open to including this in the near future as its a pretty small change (and already supported by Socket). |
When you document this, please remind people that there's a bunch of traffic that might happen that won't go over that interface. For example, DNS lookups and getting certificates and revocation lists won't be compelled to use the IP address. Can you go into more detail on the rationale? you say that people can set up different cards for different traffic -- is this a best practice for data centers? Normally, the best practice is to let the OS decide. What is the benefit of deliberately splitting up the traffic this way? |
What is your use case @narciero aside from desire to control? In many cases you can set up routing table to have desired behavior. It is IP's job to decide packet routing, not HTTP. BTW I think the comparison to Listener does not make much sense to me. In that case you trying to create service which will wait for requests. Responses will be routed according to routing table AFAIK. |
Many APIs, especially with high security requirements like payments, require IP white-listing. More common scenario is Facebook recommending to maintain Server IP Whitelist to protect Graph API calls. Routing cannot solve this issue because the target IPs aren't constant nor completely known in advance. |
I'd like to voice my support for this as well. In our scenario, we have a set of dynamic VMs (one active, a few on standby), and one of the VMs is set (elected) as a current work node with a Global IP bound to it. The Global IP can be re-assigned to another VM if the current is unavailable (a fail-over event). |
Another voice of support: On our IOT use cases, we often need to select the network (e.g. WLAN vs. Broadband) based on specific criteria that may hold per request (such as expected download volume). This is crucial to our ability to connect. The Application is on a mobile device, we often do not even know much about the target network(s) and windows doesn't either. We don't have concrete plans for .net core in this setting but this is a blocker. |
@simonthum that is interesting scenario, thanks for bringing it to our attention. Ask for everyone: Please upvote top comment if it is a feature you need - it will help us with prioritization. |
Personally I'd prefer to see this functionality supported via allowing a callback to either create or configure the Socket, e.g. along the lines of public Func<Socket> CreateSocketFunction { get; set; } or public Action<Socket> ConfigureSocketFunction { get; set; } Then a client could handle this scenario by calling Bind on the Socket, but other configuration would be supported as well, via either what Socket provides or even platform-specific via P/Invokes. |
@stephentoub Yes It is a fall-back, but even then in a |
I think it would need to be something like: public Func<Socket, string, string, int> ConnectFunction { get; set; } The arguments here being scheme, hostname, and port. Or even: public Func<Stream, string, string, int> ConnectFunction { get; set; } Which would allow you to return an arbitrary stream. There are a few details that would need to be worked out: |
Is there any progress with this issue? We're stuck by not being able to explicitly specify an ip address for a query. We're considering using a wrapper for a C++ library as a temporary solution but it doesn't sound super exciting... |
It sounds like a 100% solution would be to factor out the connection setup such that individual steps can be performed by the user selectively. I could open the socket, a stream, decide on reuse (pipelining, http/2), specify a binding address, a proxy, TLS certs etc. but not in every combination. Like a connection management interface with several default implementations to support the desired degree of control. That would be complex, though certainly possible and useful beyond HttpClient. I don't like the (existing) |
Sorry guys for a likely stupid question but I'm a little lost here. On the one hand, this issue says HttpClient doesn't support specifying an explicit ip address but then I see issues like this one (restsharp/RestSharp#1162) that shows seemingly that you can change an ip address |
@siberianguy You can using |
Sure, I was simply highlighting using a Func that returns a Socket or an Action that configures one. We could figure out what arguments to pass in. I could imagine you might want more than just the scheme/host/port in some situations, as well.
That's a whole different ballgame. Just providing a delegate to a configure a socket, or even to create one, leaves the whole connect mechanism up to the SocketsHttpHandler implementation, and as it implemented the connect, it can make assumptions based on it. Elevating that to a delegate that creates the Stream is potentially more powerful, yes, but also is more intrusive, forces our hands on various SocketsHttpHandler implementation details (we may no longer have access to the underlying NetworkStream, we can no longer assume SslStream in cases where we cast to it, etc.), etc., and causes the questions you answer. I've also not seen a use case that would demand this level of control. In contrast, there are known uses for being able to at least configure the socket before the connection attempt is made, and even creating the socket itself (e.g. whether to create it only for IPv4 or IPv6). I'm sure we could come up with use cases for a custom stream, I'm just not yet convinced its worth it. Whatever we do, though, we should probably do it in conjunction with https://github.com/dotnet/corefx/issues/27949, or at least factoring that in, as that could also impact how the socket is created. |
It may also help with cases like dotnet/corefx#31951 when someone may want more aggressive retries. |
@stephentoub Yes, I agree with your points. I was mostly just tossing out some ideas above.
Agreed. To answer my own questions above, I don't think this should affect proxy behavior or SSL. It should just affect how connections are created. Re configure socket vs create socket, it seems to me there are compelling cases for creating the socket, so I lean toward that model. Basically, something like a Func<Socket, DnsEndpoint>.
I basically agree. We should lean toward being conservative here, i.e. Socket. That said, most of the code internally is Stream based anyway because it needs to run over SslStream as well as NetworkStream. But I could go either way on this. |
Is there any hope with this issue? Honestly, it's beyond my understanding how we've got so far with .net core without having a key feature for many enterprise solutions |
Why do you think it impacts "many" enterprise solutions? I see only 8 upvotes on the top issue, also we rarely hear about it from other channels. |
There's a long list of use cases which require choosing an ip address for a request. And it's not only complicated enterprise solutions. Here's our scenario. We're working with crypto exchanges and we have to be very careful with the amount of requests we're making from one ip address, so it's vital to be able to send requests under all the ip addresses attached to a server. We're struggling with the lack of this feature to say the least. May be I'm missing something but in my opinion this feature is something fundamental, so I'm confused why it's considered to be optional. |
@siberianguy almost every bug/feature can be viewed as fundamental/critical in certain scenarios. The key is "How many customers does it impact?" |
@karelz I definitely see where you're coming from but I believe the analysis should be more in-depth than "how many people are influenced by this". I'm pretty sure if we sit together and go through the changes list for 3.0 we will find a bunch of really optional things. Yes, they likely cover wider audiences but they're minor improvements that don't influence those wider audiences' life that much. Here we're talking about a fundamental network-related feature which makes it impossible to implement a significant number of use cases using .net core |
I have another example: |
@tundeanderson same issue here, were you able to find a solution? |
Another vote here. And please backport to full framework HttpClient to ward off reflection based solutions like: https://stackoverflow.com/questions/39689858/how-to-use-httpclient-to-send-a-request-from-a-specific-ip-address-c-sharp |
Unfortunately not |
here's an important security-related scenario: every application that allows user-provided webhooks is exposed to the risk of a hacker trying to access local ressources as in http://localhost/... - and that's harder to prevent that it looks at a first glance: see http://blog.fanout.io/2014/01/27/how-to-safely-invoke-webhooks/ and https://news.ycombinator.com/item?id=7139176 using dedicated NICs that are only routed to the internet would be the easiest way to prevent this. if only I could specify a binding. +1 for the notion that this is just a must-have for any HTTP client. we should not have to argue our case. |
+1 |
Triage: We plan to address this as part of #28721 ... keeping it open due to high popularity of this issue. |
Would you be able to specify which "lower level" primitives you were thinking of? I'm not sure where to start looking for alternatives to EDIT: I ended up using the |
Amazing that in two years, a feature with all these comments and legit requests hasn't been actioned on. Is this even going to be in 5.0 @karelz ? |
@georgiosd also the request is about a common functionality where you expect it have already in by default in any framework / language. |
@bonesoul it's a long thread already with arguments for either side so given that hasn't worked out too well I don't think there's a point in continuing. I get @karelz need to prioritize based on what the majority of users "need" but frankly I think it's bad for business to piss any one group of people off for such a long time, no matter how small the group. I'm also a bit pissed personally because I realized that even though |
Things often are not as simple as they seem. This is part of larger proposal (#28721) which morphed into more general solution in #1793 (which is in active development since November - experiments, architecture review rounds, things to consider and unify with ASP.NET models, compromises on the way). So, yes, it will be part of 5.0 unless something super horrible happens (the API is scheduled for larger API review next week). I am sorry that you've ran into |
By chance is this review going to be recorded like Immo usually does? BTW... looking forward to your ZBB as 5.0 release nears. 😄 |
Yes, it should be part of normal streamed/recorded API review (cc @scalablecory). |
Is this feature scheduled? |
#1793 is planned for .NET 5 and should address this. |
Closing this -- resolved by #1793. |
This has been resolved via the API added here in .NET 5: #41949 |
I have simple task - detect remote IP address to which http client is connected. I don't want to DNS resolve, but need exactly remote IP. I have found solution for .net - https://stackoverflow.com/questions/6655713/how-to-get-ip-address-of-the-server-that-httpwebrequest-connected-to but it doesn't work as it is for dot net, but not core. Answer please, my simple question. |
@scalablecory is there a sample for usage? |
runtime/src/libraries/System.Net.Http/tests/FunctionalTests/SocketsHttpHandlerTest.cs Lines 2394 to 2401 in 7b93881
|
for anyone that may be interested a created a sample repo as an example: https://github.com/bonesoul/dotnet_5_httpclient_rest_bind |
@bonesoul Nice, thanks. BTW you can just create SocketsHttpHandler directly, instead of creating HttpClientHandler and then doing the reflection stuff. The tests do the latter because that's how some of the underlying test infra code is set up. |
On a machine with multiple NICs, how can i specify (if possible) which interface IP to use for making requests?
I am sometimes on a server where the default management interface is not connected to the internet, however there are multiple other NICs that are internet-facing that I would like to be able to use.
With .NET Framework + ServicePointManager I believe you could use BindIPEndPoint.
The text was updated successfully, but these errors were encountered: