Skip to content

[API Proposal]: ValueTask-based Socket.ConnectAsync overloads for Happy Eyeballs #117548

@antonfirsov

Description

@antonfirsov

Background and motivation

One of the original motivations behind #861 / #87932 was to utilize Happy Eyeballs in SocketsHttpHandler, see #26177 (comment), however since then the SocketsHttpHandler code has been updated to use the ValueTask overload. Unless we are willing to duplicate code into SocketsHttpHandler, the SAEA overload is insufficient for utilizing Happy Eyeballs in SocketsHttpHandler. Moreover ValueTask-based overloads would be more useful for the general public.

API Proposal

namespace System.Net.Sockets;

public class Socket
{
    // Existing:
    public ValueTask ConnectAsync(EndPoint remoteEP, CancellationToken cancellationToken);
    public static bool ConnectAsync(SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e)

    // Approved in #861 / #87932
    public static bool ConnectAsync(SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e, ConnectAlgorithm connectAlgorithm);

    // Proposed:
    public ValueTask ConnectAsync(DnsEndPoint remoteEP, ConnectAlgorithm connectAlgorithm, CancellationToken cancellationToken = default);

    // Also consider (needs more implementation work)
    public ValueTask ConnectAsync(IPAddress[] addresses, int port, ConnectAlgorithm connectAlgorithm, CancellationToken cancellationToken)
}

API Usage

Using DnsEndpoint overload in SocketsHttpHandler (simplified)

public async ValueTask<Stream> ConnectToTcpHostAsync(string host, int port, CancellationToken cancellationToken)
{
    try
    {
        Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
        await socket.ConnectAsync(endPoint, ConnectAlgorithm.Parallel, cancellationToken).ConfigureAwait(false);
        return new NetworkStream(socket, ownsSocket: true);
    }
    catch
    {
        socket.Dispose();
        throw;
    }
}

Using IPAddress[] overload in ConnectCallback

using SocketsHttpHandler handler = new SocketsHttpHandler();
handler.ConnectCallback = async (ctx, ct) =>
{
    DnsEndPoint dnsEndPoint = ctx.DnsEndPoint;
    IPAddress[] addresses = await Dns.GetHostAddressesAsync(dnsEndPoint.Host, dnsEndPoint.AddressFamily, ct);

    LogResolvedAdresses(addresses);

    var s = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
    try
    {
        await s.ConnectAsync(addresses, dnsEndPoint.Port, ConnectAlgorithm.Parallel, ct);
        return new NetworkStream(s, ownsSocket: true);
    }
    catch
    {
        s.Dispose();
        throw;
    }
};

Alternative Designs

No response

Risks

I see no high risks from the API itself, however adaption (#26177) should be done with care.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions