Skip to content

Introduce a new Server API #15006

@davidfowl

Description

@davidfowl

Servers typically need to do a set of common tasks:

  • They need to implement an accept loop per endpoint
  • They need to track connections
  • They need to implement graceful shutdown
  • They need an efficient way to implement timeouts (which integrates with connection tracking)

The goal of the server API is to:

  • Expose a common way for server bindings
  • This includes exposing a common way for wiring up transports
  • Implement the mechanics described above in a reliable manner
  • Provide an embeddable server API without a dependency on the HTTP stack
public class ServerBuilder
{
    public ServerBuilder(IServiceProvider serviceProvider);

    public IList<ServerBinding> Bindings { get; }

    public TimeSpan HeartBeatInterval { get; set; }

    public IServiceProvider ApplicationServices { get; }

    public Server Build();
}

public class ServerBinding
{
    public ServerBinding(EndPoint endPoint, IConnectionListenerFactory connectionListenerFactory, ConnectionDelegate application);

    public EndPoint EndPoint { get; }
    
    public IConnectionListenerFactory ConnectionListenerFactory { get; }
    
    public ConnectionDelegate Application { get; }
}

public class Server
{
    public Task StartAsync(CancellationToken cancellationToken = default);
    public Task StopAsync(CancellationToken cancellationToken = default);
}

Usage looks like this:

namespace ServerApplication
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            // Make a servier provider with a logger
            var serviceProvider = new ServiceCollection().AddLogging(builder =>
            {
                builder.AddConsole();
            })
            .BuildServiceProvider();

            var serverBuilder = new ServerBuilder(serviceProvider);
            // Create an instance of the sockets transport
            var socketConnectionListenerFactory = new SocketsConnectionListenerFactory();
            var endpoint = new IPEndPoint(IPAddress.LoopBack, 5050);
            // Simple echo server
            ConnectionDelegate application = connection => connection.Transport.Input.CopyToAsync(connection.Transport.Output);
            // Add the binding to the server
            serverBuilder.Bindings.Add(new ServerBinding(endpoint, socketConnectionListenerFactory, application));
            var server = serverBuilder.Build();
            await server.StartAsync();

            // Wait until a key is pressed
            Console.ReadLine();

            await server.StopAsync();
        }
    }
}

With some syntax sugar, I expect it to look like this:

namespace ServerApplication
{
    public class Program
    {
        public static async Task Main(string[] args)
        {
            // Make a servier provider with a logger
            var serviceProvider = new ServiceCollection().AddLogging(builder =>
            {
                builder.AddConsole();
            })
            .BuildServiceProvider();

            var server = new ServerBuilder(serviceProvider)
                        .UseSockets(sockets =>
                        {
                            sockets.Options.IOQueueCount = Environment.ProcessorCount;

                            sockets.Listen(IPAddress.Loopback, 5010, builder => builder.UseConnectionHandler<EchoServerApplication>());
                        })
                        .Build();

            await server.StartAsync();

            // Wait until a key is pressed
            Console.ReadLine();

            await server.StopAsync();
        }
    }
}

Metadata

Metadata

Assignees

Labels

api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-networkingIncludes servers, yarp, json patch, bedrock, websockets, http client factory, and http abstractions🥌 Bedrock

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions