-
Notifications
You must be signed in to change notification settings - Fork 10.4k
Add support for customising the creation of Kestrel listen sockets #32827
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 support for customising the creation of Kestrel listen sockets #32827
Conversation
As mentioned in #32794 it is currently not possible to configure the underlying listen and accept sockets used by Kestrel. In certain circumstances it is desirable to be able to configure socket options - a concrete case that I recently came across is setting the `SO_RECV_ANYIF` socket option on macOS so that Kestrel can listen on the `awdl0` interface. This change adds the API suggested by #32794 with some tweaks, notably splitting the configuration of the _listen_ socket from the _accept_ socket. On some platforms (*nix at least, not sure about Windows) the accept socket does not appear to inherit the socket options configured on the listen socket. So, I've added: - `Action<EndPoint, Socket>? ConfigureListenSocket { get; set; }` which allows the listen socket to be configured. - `Action<EndPoint, Socket>? ConfigureAcceptSocket { get; set; }` which allows accept sockets to be configured. There's also some tests using IPv4, IPv6 and unix domain sockets. I have no idea how to use other kinds of `EndPoint` (e.g. `FileHandleEndPoint`) with Kestrel so have left those out of the tests. Happy to add them to get the additional coverage - just need some pointers on how to use.
Thank you for submitting this for API review. This will be reviewed by @dotnet/aspnet-api-review at the next meeting of the ASP.NET Core API Review group. Please ensure you take a look at the API review process documentation and ensure that:
|
src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs
Outdated
Show resolved
Hide resolved
I wonder if it wouldn't be better to change ConfigureListenSocket from |
Create seems like a different scenario from augmentation, and requires more from the implementer. |
@@ -111,14 +113,19 @@ internal void Bind() | |||
{ | |||
listenSocket.DualMode = true; | |||
} | |||
|
|||
ConfigureSocket(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we go the Action<EndPoint, Socket>
route, wouldn't it be better to call it after binding but before listening? This would make the FileHandleEndPoint and IPEndPoint cases more similar.
If we go the Func<EndPoint, Socket>
route, I'd expect the returned Socket to already be bound.
src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs
Outdated
Show resolved
Hide resolved
Not really given |
Are you saying that this method would exist when moving to a
|
…ns.cs Co-authored-by: Stephen Halter <halter73@gmail.com>
New proposed API discussed in triage: public class SocketTransportOptions
{
+ public Func<EndPoint, Socket> CreateListenSocket { get; set; }
+ public static Socket CreateDefaultListenSocket(EndPoint);
} We don't think |
@halter73 how do you "call base" for the listen socket. |
Is CreateDefaultListenSocket supposed to be static? |
Yea it should be the stateless default. |
Yeah. It should be static. I think it would basically be the current implementation SocketConnectionListen.Bind(), but instead of calling We might have to bypass the CreateListenSocket for FileHandleEndPoints since I can't think of a great way to manage the |
If we make it non-static, we could do some trickery where the SocketConnectionListener adds an internal reference to itself to SocketTransportOptions before calling the CreateDefaultListenSocket callback allowing CreateDefaultListenSocket to set We could also make CreateListenSocket a |
Does the That said, is the handle really owned in this case? The user is passing an opaque handle created elsewhere - is it intentional that it is marked as "owned" by the socket transport? Shouldn't they be responsible for cleanup? Reason I ask - not having that field allows |
This removes `ConfigureAcceptedSocket` and changes `ConfigureListenSocket` to be a factory for the socket. A static method that creates the default socket is defined in `SocketTransportOptions.CreateDefaultListenSocket` - this effectively lifts the code that created a socket for an `EndPoint` in `SocketConnectionListener.Bind` to `SocketTransportOptions`. If `SocketTransportOptions.CreateListenSocket` is set then it is used in preference of `SocketTransportOptions.CreateDefaultListenSocket` and it is expected that the function creates the right type of socket for the passed `EndPoint`. Implementors can call `SocketTransportOptions.CreateDefaultListenSocket` themselves and manipulate the returned socket instance as they see fit. Note that during implementation I removed the `_socketHandle` field from `SocketConnectionListener` - this was only set so that `Dispose` could be called when the listener is disposed. Under the hood `Socket` already disposes a handle passed to it during finalization, but only if the `ownsHandle` parameter is `true` . In this case the `SafeSocketHandle` _is_ instantiated with this parameter so the the underlying handle will be closed when the `_listenSocket` field is disposed - that is currently the case when the listener is disposed.
I've updated to use the triaged API (using a static
|
src/Servers/Kestrel/Transport.Sockets/src/SocketTransportOptions.cs
Outdated
Show resolved
Hide resolved
public class SocketTransportOptions
{
+ public Func<EndPoint, Func<EndPoint, Socket>, Socket> CreateListenSocket { get; set; }
} Edit: never mind |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I now see that the SafeSocketHandle is disposed by Socket all along because we were specifying ownsHandle: true
. This means the previous manual tracking of the SafeSocketHandle was redundant and there's no behavioral change. Or at least that's what it seems like. I'll step through the code to confirm before merging.
@halter73 Static method is new as well for API review? |
Yes. This is still |
API review: public class SocketTransportOptions
{
+ public static Socket CreateDefaultBoundListenSocket(EndPoint);
+ public Func<EndPoint, Socket> CreateBoundListenSocket { get; set; }
} |
@BrennanConroy currently the implementation of |
…tenSocket` Moves the call to `Socket.Bind` from the `SocketConnectionListener` to `SocketTransportOptions` and adds xmldoc detailing behaviour. Also added additional comments and another test to validate the behaviour of `CreateDefaultBoundListenSocket` using different kinds of endpoints.
I've updated the code to match the API review. Let me know if you want any additional tweaks @BrennanConroy |
@BrennanConroy @halter73 is this good? |
Thanks @deanward81! |
Did we measure the perf impact of that change? Asking for a friend :/ |
@sebastienros We didn't measure the perf of this change, but I'd be shocked if this had a perf impact. This moves the creation of the listen socket (not the accepted socket) to a Func, but keeps the logic the same by default. Even if there was a perf hit from calling a Func, it's only called once per address at app startup. |
#34769 would be more likely to affect |
Fixes #32794. As mentioned in the issue it is currently not possible to configure the underlying listen socket used by Kestrel. In certain circumstances it is desirable to be able to configure socket options when the socket is created - a concrete case that I recently came across is setting the
SO_RECV_ANYIF
socket option on macOS so that Kestrel can listen on theawdl0
interface (more details on that specific case here).This change adds the API suggested by #32794 by adding a function that allows the caller to manage the creation of the listening socket -
Func<EndPoint, Socket> CreateListenSocket { get; set; }
allows the creation of the listen socket to be configured.There's also some tests using IPv4, IPv6, unix domain sockets and file handles.