Skip to content

Client/server networking abstractions #10308

Closed
@ReubenBond

Description

@ReubenBond

Epic #10869

The goal of this issue is to specify abstractions which the .NET community can use to build robust, correct, high-performance client & server applications. This is a follow-up to #4772 (Project Bedrock) and is largely a summary of a discussion with @davidfowl, @anurse, @halter73, @shirhatti, & @jkotalik.

Initiating & accepting connections

Clients initiate connections to an endpoint using IConnectionFactory. Servers bind to an endpoint using IConnectionListenerFactory and then accept incoming connections using the IConnectionListener returned at binding time.

public abstract class ConnectionContext : IAsyncDisposable
{
  // unchanged except for IAsyncDisposable base
}

public interface IConnectionListenerFactory
{
    ValueTask<IConnectionListener> BindAsync(System.Net.EndPoint endpoint, CancellationToken cancellationToken = default);
}

public interface IConnectionListener : IAsyncDisposable
{
    EndPoint EndPoint { get; }
    ValueTask<ConnectionContext> AcceptAsync(CancellationToken cancellationToken = default);
    ValueTask UnbindAsync(CancellationToken cancellationToken = default);
}

public interface IConnectionFactory
{
    ValueTask<ConnectionContext> ConnectAsync(System.Net.EndPoint endpoint, CancellationToken cancellationToken = default);
}

A framework might want to support multiple different transports, such as file + socket. In that case, it can use multiple IConnectionListenerFactory implementations.

The shared abstractions should not dictate how these factories are are specified or configured: frameworks should continue to follow a framework-specific builder pattern. Eg, KestrelServerOptions has List<ListenOptions> where each entry is a different endpoint to listen on + middleware to handle connections to that endpoint.

Both IConnectionListenerFactory.BindAsync & IConnectionFactory.ConnectAsync accept a System.Net.EndPoint to bind/connect to. The BCL has EndPoint implementations for IPEndPoint, DnsEndPoint, and UnixDomainSocketEndPoint. Users can add new transports which accept custom sub-classes of EndPoint, eg MyOverlayNetworkEndPoint.

Servers bind to endpoints using IConnectionListenerFactory, resulting in a IConnectionListener. The listener's AcceptAsync() is then called in a loop, each time resulting in a ConnectionContext instance which represent a newly accepted connection. When the server wants to stop accepting new connections, the listener is disposed by calling listener.DisposeAsync(). Connections which were previously accepted continue to operate until each is individually terminated.

For implementations of IConnectionListener which are backed by a callback system (eg, libuv), decoupling the lifetime of IConnectionListener and the (many) resulting ConnectionContexts poses a challenge. We need to consider whether or not this minimal interface allows graceful shutdown (including draining connections) to still be cleanly implemented in those cases.

Handling connections

The client/server decides how it handles the ConnectionContext. Typically it will be decorated (eg, via ConnectionDelegate) and pumped until the connection is terminated. Connections can be terminated by calling DisposeAsync() (from IAsyncDisposable) but can also be terminated out-of-band (eg, because the remote endpoint terminated it). In either case, clients/servers can subscribe to the ConnectionContext's IConnectionLifetimeFeature feature to learn about termination and take necessary action (eg, cleanup).

Connection metadata

Currently, IHttpConnectionFeature is used to expose local/remote endpoint information. We will add a new feature, IConnectionEndPointFeature as a uniform way to access this information.

public interface IConnectionEndPointFeature
{
  EndPoint RemoteEndPoint { get; set; }
  EndPoint LocalEndPoint { get; set; }
}

Metadata

Metadata

Assignees

Labels

area-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractionsenhancementThis issue represents an ask for new feature or an enhancement to an existing one

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions