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);
Today it feels like there is some friction when using a
SocketsConnectionFactorythat needs a non-DNS endpoint. The endpoint passed intoSocketsConnectionFactory.ConnectAsynccomes fromHttpRequestMessage.RequestUri, and that must always be a valid URI and always is turned into aDnsEndPoint.To specify a custom endpoint you need to implement a custom
SocketsConnectionFactoryand override the endpoint. One option is to hardcode the endpoint when the factory is created:This works, but you're limited to a single endpoint for the
SocketsHttpHandler. You can't have differentHttpRequestMessageinstances 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):Now the endpoint can be different per-request. However I'm not sure what the rules are around deciding whether
ConnectAsyncshould be called. Would theRequestUrineed to change if the endpoint specified on HttpRequestMessage.Options changed?Idea: Have
SocketsHttpHandlercheck for a custom endpoint inHttpRequestMessage.Optionsautomatically. If there is a request with an endpoint in the options thenSocketsHttpHandlerwill use it to determine if a connection already exists for that endpoint, and it will pass that endpoint toSocketsConnectionFactory.ConnectAsync.Usage: