-
Notifications
You must be signed in to change notification settings - Fork 5.2k
Description
Today it feels like there is some friction when using a SocketsConnectionFactory
that needs a non-DNS endpoint. The endpoint passed into SocketsConnectionFactory.ConnectAsync
comes from HttpRequestMessage.RequestUri
, and that must always be a valid URI and always is turned into a DnsEndPoint
.
To specify a custom endpoint you need to implement a custom SocketsConnectionFactory
and override the endpoint. One option is to hardcode the endpoint when the factory is created:
public class UnixDomainSocketConnectionFactory : SocketsConnectionFactory
{
private readonly UnixDomainSocketEndPoint _endPoint;
public UnixDomainSocketConnectionFactory(UnixDomainSocketEndPoint endPoint) : base(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)
{
_endPoint = endPoint;
}
public override ValueTask<Connection> ConnectAsync(EndPoint? endPoint, IConnectionProperties? options = null, CancellationToken cancellationToken = default)
{
return base.ConnectAsync(_endPoint, options, cancellationToken);
}
}
This works, but you're limited to a single endpoint for the SocketsHttpHandler
. You can't have different HttpRequestMessage
instances be sent to different UDS endpoints.
An alternative could be to put the endpoint in HttpRequestMessage.Option
. Something like this (I haven't tried it):
public class UnixDomainSocketConnectionFactory2 : SocketsConnectionFactory
{
public UnixDomainSocketConnectionFactory2() : base(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified)
{
}
public override ValueTask<Connection> ConnectAsync(EndPoint? endPoint, IConnectionProperties? options = null, CancellationToken cancellationToken = default)
{
if (options.TryGet(typeof(HttpRequestMessage), out var request))
{
request.Options.TryGetValue(new HttpRequestOptionsKey<EndPoint>(nameof(EndPoint)), out endPoint);
}
return base.ConnectAsync(endPoint, options, cancellationToken);
}
}
Now the endpoint can be different per-request. However I'm not sure what the rules are around deciding whether ConnectAsync
should be called. Would the RequestUri
need to change if the endpoint specified on HttpRequestMessage.Options changed?
Idea: Have SocketsHttpHandler
check for a custom endpoint in HttpRequestMessage.Options
automatically. If there is a request with an endpoint in the options then SocketsHttpHandler
will use it to determine if a connection already exists for that endpoint, and it will pass that endpoint to SocketsConnectionFactory.ConnectAsync
.
Usage:
var socketsHttpHandler = new SocketsHttpHandler
{
ConnectionFactory = new SocketsConnectionFactory(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);
};
var httpClient = new HttpClient(socketsHttpHandler);
// Create request and specify UDS endpoint in its options.
// SocketsHttpHandler knows about this option name and will use it to figure out if a connection exists
// and will pass this endpoint to ConnectAsync.
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost");
var endPointKey = new HttpRequestOptionsKey<EndPoint>(nameof(EndPoint));
request.Options.Set(endPointKey, new UnixDomainSocketEndPoint(SocketPath));
var response = await httpClient.SendAsync(request);