ASP.NET CORE在“应用”层将针对请求的处理抽象成由中间件构建的管道,实际上KestrelServer面向“传输”层的连接也采用了这样的设计。当代表连接的ConnectionContext上下文创建出来之后,后续的处理将交给由连接中间件构建的管道进行处理。我们可以根据需要注册任意的中间件来处理连接,比如可以将并发连结的控制实现在专门的连接中间件中。ASP.NET CORE管道利用RequestDelegate委托来表示请求处理器,连接管道同样定义了如下这个ConnectionDelegate委托。
public delegate Task ConnectionDelegate(ConnectionContext connection);二、IConnectionBuilder
ASP.NET CORE管道中的中间件体现为一个Func<RequestDelegate, RequestDelegate>委托,连接管道的中间件同样可以利用Func<ConnectionDelegate, ConnectionDelegate>委托来表示。ASP.NET CORE管道中的中间件注册到IApplicationBuilder对象上并利用它将管道构建出来。连接管道依然具有如下这个IConnectionBuilder接口,ConnectionBuilder实现了该接口。
public interface IConnectionBuilder
{
IServiceProvider ApplicationServices { get; }
IConnectionBuilder Use(Func<ConnectionDelegate, ConnectionDelegate> middleware);
ConnectionDelegate Build();
}
public class ConnectionBuilder : IConnectionBuilder
{
public IServiceProvider ApplicationServices { get; }
public ConnectionDelegate Build();
public IConnectionBuilder Use(Func<ConnectionDelegate, ConnectionDelegate> middleware);
}
public class MiniKestrelServer : IServer
{
private readonly KestrelServerOptions _options;
private readonly IConnectionListenerFactory _factory;
private readonly List<IConnectionListener> _listeners = new();
public IFeatureCollection Features { get; } = new FeatureCollection();
public MiniKestrelServer(IOptions<KestrelServerOptions> optionsAccessor, IConnectionListenerFactory factory)
{
_factory = factory;
_options = optionsAccessor.Value;
Features.Set<IServerAddressesFeature>(new ServerAddressesFeature());
}
public void Dispose() => StopAsync(CancellationToken.None).GetAwaiter().GetResult();
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) where TContext : notnull
{
var feature = Features.Get<IServerAddressesFeature>()!;
IEnumerable<ListenOptions> listenOptions;
if (feature.PreferHostingUrls)
{
listenOptions = BuildListenOptions(feature);
}
else
{
listenOptions = _options.GetListenOptions();
if (!listenOptions.Any())
{
listenOptions = BuildListenOptions(feature);
}
}
foreach (var options in listenOptions)
{
_ = StartAsync(options);
}
return Task.CompletedTask;
async Task StartAsync(ListenOptions litenOptions)
{
var listener = await _factory.BindAsync(litenOptions.EndPoint,cancellationToken);
_listeners.Add(listener!);
var hostedApplication = new HostedApplication<TContext>(application);
var pipeline = litenOptions.Use(next => context => hostedApplication.OnConnectedAsync(context)).Build();
while (true)
{
var connection = await listener.AcceptAsync();
if (connection != null)
{
_ = pipeline(connection);
}
}
}
IEnumerable<ListenOptions> BuildListenOptions(IServerAddressesFeature feature)
{
var options = new KestrelServerOptions();
foreach (var address in feature.Addresses)
{
var url = new Uri(address);
if (string.Compare("localhost", url.Host, true) == 0)
{
options.ListenLocalhost(url.Port);
}
else
{
options.Listen(IPAddress.Parse(url.Host), url.Port);
}
}
return options.GetListenOptions();
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(_listeners.Select(it => it.DisposeAsync().AsTask()));
}
using App;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.DependencyInjection.Extensions;
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseKestrel(kestrel => kestrel.ListenLocalhost(5000));
builder.Services.Replace(ServiceDescriptor.Singleton<IServer, MiniKestrelServer>());
var app = builder.Build();
app.Run(context => context.Response.WriteAsync("Hello World!"));
app.Run();
ASP.NET CORE在“应用”层将针对请求的处理抽象成由中间件构建的管道,实际上KestrelServer面向“传输”层的连接也采用了这样的设计。当代表连接的ConnectionContext上下文创建出来之后,后续的处理将交给由连接中间件构建的管道进行处理。我们可以根据需要注册任意的中间件来处理连接,比如可以将并发连结的控制实现在专门的连接中间件中。ASP.NET CORE管道利用RequestDelegate委托来表示请求处理器,连接管道同样定义了如下这个ConnectionDelegate委托。
public delegate Task ConnectionDelegate(ConnectionContext connection);二、IConnectionBuilder
ASP.NET CORE管道中的中间件体现为一个Func<RequestDelegate, RequestDelegate>委托,连接管道的中间件同样可以利用Func<ConnectionDelegate, ConnectionDelegate>委托来表示。ASP.NET CORE管道中的中间件注册到IApplicationBuilder对象上并利用它将管道构建出来。连接管道依然具有如下这个IConnectionBuilder接口,ConnectionBuilder实现了该接口。
public interface IConnectionBuilder
{
IServiceProvider ApplicationServices { get; }
IConnectionBuilder Use(Func<ConnectionDelegate, ConnectionDelegate> middleware);
ConnectionDelegate Build();
}
public class ConnectionBuilder : IConnectionBuilder
{
public IServiceProvider ApplicationServices { get; }
public ConnectionDelegate Build();
public IConnectionBuilder Use(Func<ConnectionDelegate, ConnectionDelegate> middleware);
}
public class MiniKestrelServer : IServer
{
private readonly KestrelServerOptions _options;
private readonly IConnectionListenerFactory _factory;
private readonly List<IConnectionListener> _listeners = new();
public IFeatureCollection Features { get; } = new FeatureCollection();
public MiniKestrelServer(IOptions<KestrelServerOptions> optionsAccessor, IConnectionListenerFactory factory)
{
_factory = factory;
_options = optionsAccessor.Value;
Features.Set<IServerAddressesFeature>(new ServerAddressesFeature());
}
public void Dispose() => StopAsync(CancellationToken.None).GetAwaiter().GetResult();
public Task StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken) where TContext : notnull
{
var feature = Features.Get<IServerAddressesFeature>()!;
IEnumerable<ListenOptions> listenOptions;
if (feature.PreferHostingUrls)
{
listenOptions = BuildListenOptions(feature);
}
else
{
listenOptions = _options.GetListenOptions();
if (!listenOptions.Any())
{
listenOptions = BuildListenOptions(feature);
}
}
foreach (var options in listenOptions)
{
_ = StartAsync(options);
}
return Task.CompletedTask;
async Task StartAsync(ListenOptions litenOptions)
{
var listener = await _factory.BindAsync(litenOptions.EndPoint,cancellationToken);
_listeners.Add(listener!);
var hostedApplication = new HostedApplication<TContext>(application);
var pipeline = litenOptions.Use(next => context => hostedApplication.OnConnectedAsync(context)).Build();
while (true)
{
var connection = await listener.AcceptAsync();
if (connection != null)
{
_ = pipeline(connection);
}
}
}
IEnumerable<ListenOptions> BuildListenOptions(IServerAddressesFeature feature)
{
var options = new KestrelServerOptions();
foreach (var address in feature.Addresses)
{
var url = new Uri(address);
if (string.Compare("localhost", url.Host, true) == 0)
{
options.ListenLocalhost(url.Port);
}
else
{
options.Listen(IPAddress.Parse(url.Host), url.Port);
}
}
return options.GetListenOptions();
}
}
public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(_listeners.Select(it => it.DisposeAsync().AsTask()));
}
using App;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.Extensions.DependencyInjection.Extensions;
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseKestrel(kestrel => kestrel.ListenLocalhost(5000));
builder.Services.Replace(ServiceDescriptor.Singleton<IServer, MiniKestrelServer>());
var app = builder.Build();
app.Run(context => context.Response.WriteAsync("Hello World!"));
app.Run();