-
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
Add ConnectCallback on SocketsHttpHandler (aka custom Dialers) #28721
Comments
ping @karelz, @stephentoub |
Our current API guidance says to not use tuples in public API. (Either make a named struct, or make a custom delegate type to use an out parameter) I didn't personally find the |
@bartonjs The more I think about it, the more I think introducing a custom delegate makes sense for parameter naming. Introducing a struct for the result of the Dialer, with a constructor taking only a Stream would certainly make things more obvious (about optionality of the Socket return value). Wdyt ? |
A few thoughts:
Finally, I expect very few developers will use this property. It's a reasonable extensibility point to add, but I would prioritize keeping the API as clutter-free as possible (e.g. not adding a lot of helper types) rather than trying to make the API super easy to use. |
@stephentoub thanks, updated the ProposedAPI with your suggestions. |
FYI, "dialer" is a term used in Go. I don't love it either, just providing context. |
@simonferquel, regarding https://twitter.com/sferquel/status/1097893536412979200, yes, this would only be for .NET Core. |
@stephentoub hmm, that is bad news for Docker Desktop (which runs on the full .Net Framework, and won't easily move to .net core). |
Well, not so easy at all after all, devil is in the detail. |
ping @stephentoub @karelz, I see it is in milestone future, do you have a status for it (like should I pursue efforts on this)? |
@simonferquel, we're still interested, this just hasn't been at the top of our list. I doubt it'll make it for 3.0, but I'll defer to @karelz on that. |
Thanks for the heads-up, no problem for skipping 3.0. docker desktop is far from ready to be ported to .net core anyway :) |
The issues linked above by Geoff are on my top list for post-3.0. I have been thinking about them just recently. |
If we were to pass in a Func<string, int, HttpRequestMessage, Threading.CancellationToken, Threading.Tasks.ValueTask<IO.Stream>> This is because there is some complex proxy logic that users would need to otherwise worry about, and we'd need to expose additional APIs to enable them to worry about it. |
I'd like this a similar API for the same reasons. Ideally, I'd be able to replace the "socket" with a connection from this API https://github.com/davidfowl/BedrockTransports#client-api. |
I think this will be useful for the .NET gRPC client. One of our goals in 5.0 is providing gRPC over named pipes and unix sockets. WCF has the ability to use named pipes for IPC, and this feature will let us address a gap in the gRPC vs WCF comparison. |
We actually do exactly that at docker: in our golang base we have extensive use of grpc over arbitrary streams (docker build with buildkit for example upgrade an http connection to the build endpoint to raw r/w stream and establish a grpc connection on it, dockerd talks to containers using grpc over Unix sockets as well). |
Yea, I'd like it too as it will help us with benchmarking if we can easily remove I/O from the equation. I prefer the Func<Uri, CancellationToken, ValueTask<Stream>> Can we reach consensus? @dotnet/ncl any thoughts? |
I suggest enumerating all known use cases and validating that this design is sufficient to support them. Are there cases where it's important to know whether we're targeting a proxy? What would the recommended approach be if various settings from the handler are needed; access the handler itself by closure? Will this design force allocations that wouldn't otherwise be necessary, e.g. to construct the Uri? Are there scenarios where a dev would need to pass in additional info into the handler, such as via headers in the request message? Etc. |
A customer has indicated via support channels a want for DNS caching. This feature would allow them to solve that. |
Triage: We should do it. Next step we need to verify the API proposal above. |
Having just evaluated adopting gRPC in a large application, I can tell you that we aren't likely to adopt this library unless it added support for operating over a .NET Stream (and/or IDuplexPipe).
Add support for these things specifically instead of general .NET Stream support means it's more of a hassle to test and higher overhead for scenarios where everything is in-proc. In particular, I'd like the option to skip the 'dialer' altogether. Let me just pass a .NET Stream to two parties and they can slap the gRPC protocol on top of it and talk to each other. |
This design needs to account for actual HTTP/1 and HTTP/2 scenarios where actual networking sockets are involved. If you already have a connected socket then this callback would just return the stream directly. |
The dialer concept is not mutually exclusive with named pipes and unix domain sockets.
The ability to pass in a stream directly to e.g. |
Prototyping this now for benchmarking of #1590 |
Summary of the use cases we've collected so far:
|
Something like @davidfowl's Bedrock might be related work. |
So I actually have an implementation request on how I think this should work. I don't want |
@jhudsoncedaron agreed. One goal this aligns with is that it should be very easy to use the default functionality but just add a small shim. |
I think connection pooling must be taken into account here too. In my case I need to send HTTP messages via a custom transport to several different clients, but they are all using the same host name. If the custom stream gets pooled in this scenario, requests would get sent to the wrong connection. |
|
To do this we'd need to add a pool partition key. I'm not sure if that feature is needed strongly enough to warrant the added API/complexity -- I might suggest handling this via a pseudo-hostname that contains such a key that the custom dialer can can then strip off. |
Incidentally that's my plan. |
I might be missing something, but I don't see how this would be possible, if the host name in the request message must be the same but we want to use different transport streams. |
@createivbox It looks something like this. https://en.wikipedia.org/wiki/Schwartzian_transform |
Thank you very much, but in my case I need to send the original host value. Replacing the host name for the connection pool only is not possible with the current proposal, because we cannot change it back when it gets transmitted. A simple additional value in the connection pool key that may be added through HttpRequest properties would solve this, but I understand this is a very rare case. |
I am really looking forward to this feature, is it coming any time soon? |
Any progress on this? |
Yes, see #1793 (some details about planned API review in #23267 (comment)) |
Closing this -- resolved by #1793. |
This has been resolved via the API added here in .NET 5: #41949 |
Summary
The current
SocketHttpHandler
implementation cleanly separates the HTTP grammar to the actual TCP connection handling. However, whileKestrel
on the server side allows to expose a service over non-TCP protocols such as Unix Sockets (and probably named pipes as well on Windows), theHttpClient
does not provide such functionality.As the
SocketHttpHandler
main logic only depends on Socket and Stream (and actually has a fallback mechanism to be able to work with no Socket, and just a Stream), this proposal is about adding custom Dialers to the SocketHttpHandler, allowing a user to use whatever transport he wants to connect to the server.Also, there is no way to reuse all the Http grammar goodness implemented in System.Net.Http in a custom
MessageHandler
in a foreign assembly, because all this is internal. One potential alternative to this proposal would then be to refactor all this internal code in pieces consumable from a user.Proposed API
This Proposal adds two properties to
SocketHttpHandler
:Usage
Open Questions
We could only use a single Dialer property, but it would not be obvious to the user that it is actually ok to not return a Socket.-> Only one property now. If required, we can introduce anIWithSocket
interface that the returned stream might implement to provide raw socket access to theSocketsHttpHandler
C# 8 constructs such asSingle stream return value (optionally implementing IWithSocket) fixes the questionpublic Func<string, int, Threading.CancellationToken, Threading.Tasks.ValueTask<(Sockets.Socket?, IO.Stream)>>
could help, but I am not sure how this kind of constructs is exposed to other users.Another suggestion, is to introduce a delegate type for this Dialer callback with explicit documentation about the optional nature of the Socket return value.Ruled out by single return value + HttpRequestMessage in the callback parameterYet another one would be to have a single Dialer callback property, and add an extension methodNo extension method should be introducedWithStreamDialer
onSocketsHttpHandler
that would wrap a "Stream only" callback.Reference
WIP implementation: dotnet/corefx@master...simonferquel:custom-dialers
Prototype of previous related work done for Docker Desktop: https://github.com/simonferquel/HttpOverStream/tree/master/src/HttpOverStream (illustrate the difficulty of implementing a custom MessageHandler without access to internal code from System.Net.Http)
The text was updated successfully, but these errors were encountered: