KestrelServer詳解[1]:註冊監聽終結點(Endpoint)

具有跨平臺能力的KestrelServer是最重要的服務器類型。針對KestrelServer的設置均體現在KestrelServerOptions配置選項上,註冊的終結點是它承載的最重要的配置選項。這裏所謂的終結點(Endpoint)與“路由”介紹的終結點不是一回事,這裏表示的就是服務器在監聽請求時綁定的網絡地址,對應着一個System.Net.Endpoint對象。我們知道ASP.NET Core應用承載API也提供了註冊監聽地址的方法,其本質其實也是爲了註冊終結點,那麼兩種註冊方式如何取捨呢?本文提供的示例演示已經同步到《ASP.NET Core 6框架揭祕-實例演示版》)

一、UseKestrel擴展方法
二、兩種終結點的取捨
三、終結點配置
四、針對HTTPS的設置
五、限制約束
六、其他設置

一、UseKestrel擴展方法

IWebHostBuilder接口如下三個UseKestrel擴展方法重載會幫助我們完成KestrelServer的註冊並對KestrelServerOptions配置選項作相應設置,我們先來看看如何利用它們來註冊終結點。

public static class WebHostBuilderKestrelExtensions
{
    public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder);
    public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder,Action<KestrelServerOptions> options);
    public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder, Action<WebHostBuilderContext, KestrelServerOptions> configureOptions);
}

註冊到KestrelServer上的終結點體現爲如下這個Endpoint對象。Endpoint是對網絡地址的抽象,它們在大部分下體現爲“IP地址+端口”或者“域名+端口”,對應的類型分別爲IPEndPoint和DnsEndPoint。UnixDomainSocketEndPoint表示基於Unix Domain Socket/IPC Socket的終結點,它旨在實現同一臺機器上多個進程之間的通信(IPC)。FileHandleEndPoint表示指向某個文件句柄(比如TCP或者Pipe類型的文件句柄)的終結點。

public abstract class EndPoint
{
    public virtual AddressFamily AddressFamily { get; }

    public virtual EndPoint Create(SocketAddress socketAddress);
    public virtual SocketAddress Serialize();
}

public class IPEndPoint : EndPoint
public class DnsEndPoint : EndPoint
public sealed class UnixDomainSocketEndPoint : EndPoint
public class FileHandleEndPoint : EndPoint

終結點註冊利用如下這個ListenOptions配置選項來描述。該類型實現的IConnectionBuilder和IMultiplexedConnectionBuilder接口涉及針對連接的構建,我們將在後面討論這個話題。註冊的終結點體現爲該配置選項的EndPoint屬性,如果是一個IPEndPoint對象,該對象也會體現在IPEndPoint屬性上。如果終結點類型爲UnixDomainSocketEndPoint和FileHandleEndPoint,我們可以利用配置選項的SocketPath和FileHandle得到對應的Socket路徑和文件句柄。

public class ListenOptions : IConnectionBuilder, IMultiplexedConnectionBuilder
{
    public EndPoint EndPoint { get; }

    public IPEndPoint IPEndPoint { get; }
    public string SocketPath { get; }
    public ulong FileHandle { get; }

    public HttpProtocols Protocols { get; set; }
    public bool DisableAltSvcHeader { get; set; }

    public IServiceProvider ApplicationServices { get; }
    public KestrelServerOptions KestrelServerOptions { get; }
    ...
}

同一個終結點可以同時支持HTTP 1.x、HTTP 2 和HTTP 3三種協議,具體設置體現在Protocols屬性上,該屬性返回如下這個HttpProtocols枚舉。由於枚舉項Http3和Http1AndHttp2AndHttp3上面標註了RequiresPreviewFeaturesAttribute特性,如果需要採用HTTP 3協議,項目文件中必須添加“<EnablePreviewFeatures>true</EnablePreviewFeatures>”屬性。如果HTTP3終結點同時支持HTTP 1.X和HTTP 2,針對HTTP 1.X和HTTP 2的請求的響應一般會添加一個alt-svc (Alternative Service)報頭指示可以升級到HTTP 3,我們可以設置DisableAltSvcHeader屬性關閉此特性。該屬性默認值爲Http1AndHttp2。

[Flags]
public enum HttpProtocols
{
    None = 0,
    Http1 = 1,
    Http2 = 2,
    Http1AndHttp2 = 3,
    [RequiresPreviewFeatures]
    Http3 = 4,
    [RequiresPreviewFeatures]
    Http1AndHttp2AndHttp3 = 7
}

KestrelServerOptions的ListenOptions屬性返回的ListenOptions列表代表所有註冊的終結點,它由CodeBackedListenOptions和ConfigurationBackedListenOptions屬性合併而成,這兩個屬性分別表示通過代碼和配置註冊的終結點。基於“代碼”的終結點註冊由如下所示的一系列Listen和以“Listen”爲前綴的方法來完成。除了這些註冊單個終結點的方法, ConfigureEndpointDefaults方法爲註冊的所有終結點提供基礎設置。

public class KestrelServerOptions
{
    internal List<ListenOptions>  CodeBackedListenOptions { get; }
    internal List<ListenOptions>  ConfigurationBackedListenOptions { get; }
    internal IEnumerable<ListenOptions> 	ListenOptions { get; }

    public void Listen(EndPoint endPoint);
    public void Listen(IPEndPoint endPoint);
    public void Listen(EndPoint endPoint, Action<ListenOptions> configure);
    public void Listen(IPAddress address, int port);
    public void Listen(IPEndPoint endPoint, Action<ListenOptions> configure);
    public void Listen(IPAddress address, int port, Action<ListenOptions> configure);
    public void ListenAnyIP(int port);
    public void ListenAnyIP(int port, Action<ListenOptions> configure);
    public void ListenHandle(ulong handle);
    public void ListenHandle(ulong handle, Action<ListenOptions> configure);
    public void ListenLocalhost(int port);
    public void ListenLocalhost(int port, Action<ListenOptions> configure);
    public void ListenUnixSocket(string socketPath);
    public void ListenUnixSocket(string socketPath, Action<ListenOptions> configure);

    public void ConfigureEndpointDefaults(Action<ListenOptions> configureOptions)
    ...
}

二、兩種終結點的取捨

我們知道監聽地址不僅可以添加到WebApplication對象的Urls屬性中,WebApplication類型用來啓動應用的RunAsync和Run方法也提供了可缺省的參數url來指定監聽地址。從如下的代碼片段可以看出,這三種方式提供的監聽地址都被添加到了IServerAddressesFeature特性的Addresses屬性中。

public sealed class WebApplication : IHost { private readonly IHost _host; public ICollection<string> Urls => _host.Services.GetRequiredService<IServer>().Features .Get<IServerAddressesFeature>()?.Addresses
?? throw new InvalidOperationException("IServerAddressesFeature could not be found."); public Task RunAsync(string? url = null) { Listen(url); return ((IHost)this).RunAsync(); } public void Run(string? url = null) { Listen(url); ((IHost)this).Run(); } private void Listen(string? url) { if (url != null) { var addresses = ServerFeatures.Get<IServerAddressesFeature>()?.Addresses?? throw new InvalidOperationException("No valid IServerAddressesFeature is found"); addresses.Clear(); addresses.Add(url); } } }

如果KestrelServerOptions配置選項不能提供註冊的終結點,那麼KestrelServer就會使用IServerAddressesFeature特性提供的地址來創建對應的終結點,否則就會根據它的PreferHostingUrls屬性來進行取捨。如果IServerAddressesFeature特性的PreferHostingUrls屬性返回True,它提供的地址會被選擇,否則就使用直接註冊到KestrelServerOptions配置選項的終結點。針對監聽地址的註冊和PreferHostingUrls的設置可以利用IWebHostBuilder接口如下兩個擴展方法來完成。從給出的代碼片段可以看出這兩個方法會將提供的設置存儲配置上,配置項名稱分別爲“urls”和“preferHostingUrls”,對應着WebHostDefaults定義的兩個靜態只讀字段ServerUrlsKey和PreferHostingUrlsKey。既然這兩個設置來源於配置,我們自然可以利用命令行參數、環境變量或者直接修改對應配置項的方式來指定它們。

public static class HostingAbstractionsWebHostBuilderExtensions
{
    public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls)
        => hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, string.Join(';', urls));
    public static IWebHostBuilder PreferHostingUrls(this IWebHostBuilder hostBuilder, bool preferHostingUrls)
        => hostBuilder.UseSetting(WebHostDefaults.PreferHostingUrlsKey, preferHostingUrls ? "true" : "false");
}

如果服務器的特性集合提供的IServerAddressesFeature特性包含監聽地址,以配置方式設置的監聽地址和針對PreferHostingUrls的設置將會被忽略,這一個特性體現在GenericWebHostService的StartAsync方法中。如下面的代碼片段所示,該方法會從服務器中提取IServerAddressesFeature特性,只有該特性不能提供監聽地址的情況下,利用配置註冊的監聽地址和針對PreferHostingUrls的設置纔會應用到該特性中。

internal sealed class GenericWebHostService : IHostedService
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        ...
        var serverAddressesFeature = Server.Features.Get<IServerAddressesFeature>();
        var addresses = serverAddressesFeature?.Addresses;
        if (addresses != null && !addresses.IsReadOnly && addresses.Count == 0)
        {
            var text = Configuration[WebHostDefaults.ServerUrlsKey];
            if (!string.IsNullOrEmpty(text))
            {
                serverAddressesFeature.PreferHostingUrls = WebHostUtilities.ParseBool(Configuration, WebHostDefaults.PreferHostingUrlsKey);
                string[] array = text.Split(';', StringSplitOptions.RemoveEmptyEntries);
                foreach (string item in array)
                {
                    addresses.Add(item);
                }
            }
        }
    }
}

下面的演示程序通過調用IWebHostBuilder接口的UseKestrel擴展方法註冊了一個採用8000端口的本地終結點,通過調用UseUrls擴展方法註冊了一個採用9000端口的監聽地址。

var builder = WebApplication.CreateBuilder(args);
builder.WebHost
    .UseKestrel(kestrel => kestrel.ListenLocalhost(8000))
    .UseUrls("http://localhost:9000");
var app = builder.Build();
app.Run();

我們以命令行的方式兩次啓動了該程序。默認情況下應用會選擇調用UseKestrel擴展方法註冊的終結點。如果指定了命令行參數“preferHostingUrls=1”,那麼最終使用的都是將是調用UseUrls擴展方法註冊的監聽地址。由於兩種情況都涉及到放棄某種設置,所以輸出了相應的日誌。

image
圖1 兩種終結點的選擇

三、終結點配置

KestrelServerOptions承載的很多設置都可以利用配置來提供。由於該配置選項類型的定義與配置的結構存在差異, KestrelServerOptions配置選項無法直接使用對應的IConfiguration對象進行綁定,所以KestrelServerOptions類型定義如下三個Configure方法。後面兩個方法提供了承載配置內容的IConfiguration對象,最後一個重載還提供了reloadOnChange參數來決定是否自動加載更新後的配置。第一個重載提供的其實是一個空的IConfiguration對象。

public class KestrelServerOptions
{
    public KestrelConfigurationLoader Configure();
    public KestrelConfigurationLoader Configure(IConfiguration config);
    public KestrelConfigurationLoader Configure(IConfiguration config, bool reloadOnChange)
}

三個Configure方法都返回KestrelConfigurationLoader對象,後者是對當前KestrelServerOptions配置選項和指定IConfiguration對象的封裝。KestrelConfigurationLoader的Load方法會讀取配置的內容並將其應用到KestrelServerOptions配置選項上,該類型還提供了一系列註冊各類終結點的方法。

public class KestrelConfigurationLoader
{
    public KestrelServerOptions Options { get; }
    public IConfiguration Configuration { get; }

    public KestrelConfigurationLoader Endpoint(string name, Action<EndpointConfiguration> configureOptions);
    public KestrelConfigurationLoader Endpoint(IPAddress address, int port);
    public KestrelConfigurationLoader Endpoint(IPAddress address, int port, Action<ListenOptions> configure);
    public KestrelConfigurationLoader Endpoint(IPEndPoint endPoint);
    public KestrelConfigurationLoader Endpoint(IPEndPoint endPoint, Action<ListenOptions> configure);
    public KestrelConfigurationLoader LocalhostEndpoint(int port);
    public KestrelConfigurationLoader LocalhostEndpoint(int port, Action<ListenOptions> configure);
    public KestrelConfigurationLoader AnyIPEndpoint(int port);
    public KestrelConfigurationLoader AnyIPEndpoint(int port, Action<ListenOptions> configure);
    public KestrelConfigurationLoader UnixSocketEndpoint(string socketPath);
    public KestrelConfigurationLoader UnixSocketEndpoint(string socketPath, Action<ListenOptions> configure);
    public KestrelConfigurationLoader HandleEndpoint(ulong handle);
    public KestrelConfigurationLoader HandleEndpoint(ulong handle, Action<ListenOptions> configure);

    public void Load();
}

ASP.NET Core應用在啓動時會調用IHostBuilder接口如下這個ConfigureWebHostDefaults擴展方法進行初始化設置,該方法會從當前配置中提取出“Kestrel”配置節,並將其作爲參數調用Configure方法將配置內容應用到KestrelServerOptions配置選項上。由於reloadOnChange參數被設置成了True,所以更新後的配置會自動被重新加載。

public static class GenericHostBuilderExtensions
{
    public static IHostBuilder ConfigureWebHostDefaults(this IHostBuilder builder, Action<IWebHostBuilder> configure) => builder.ConfigureWebHost(webHostBuilder => {
            WebHost.ConfigureWebDefaults(webHostBuilder);
            configure(webHostBuilder);
        });
}

public static class WebHost
{
    internal static void ConfigureWebDefaults(IWebHostBuilder builder)
    {
        ...
        builder.UseKestrel((builderContext, options) => {
            options.Configure(builderContext.Configuration.GetSection("Kestrel"), reloadOnChange: true);
        })
	...
    }
}

如下的代碼片段展現了針對終結點的配置。我們在“Kestrel:Endpoints”配置了兩個分別命名爲“endpoint1”和“endpoint2”終結點,它們採用的監聽地址分別爲“http://localhost:9000”和“https://localhost:9001”。KestrelServerOptions絕大部分配置選項都可以定義在配置文件中,具體的配置定義方法可以參閱官方文檔。

{
  "Kestrel": {
    "Endpoints": {
      "endpoint1": {
        "Url": "http://localhost:9000"
      },
      "endpoint2": {
        "Url": "https://localhost:9001"
      }
    }
  }
}

四、針對HTTPS的設置

較之普通的終結點,HTTPS(SSL/TLS)終結點需要提供額外的設置,這些設置大都體現在如下這個HttpsConnectionAdapterOptions配置選項上。KestrelServerOptions的ConfigureHttpsDefaults方法爲所有HTTPS終結點提供了默認的設置。

public class HttpsConnectionAdapterOptions
{
    public X509Certificate2? ServerCertificate { get; set; }
    public Func<ConnectionContext?, string?, X509Certificate2?>? ServerCertificateSelector { get; set; }
    public TimeSpan HandshakeTimeout { get; set; }
    public SslProtocols SslProtocols { get; set; }
    public Action<ConnectionContext, SslServerAuthenticationOptions>? OnAuthenticate { get; set; }

    public ClientCertificateMode ClientCertificateMode { get; set; }
    public Func<X509Certificate2, X509Chain?, SslPolicyErrors, bool>? ClientCertificateValidation { get; set; }
    public bool CheckCertificateRevocation { get; set; }
    public void AllowAnyClientCertificate() { get; set; }
}

public static class KestrelServerOptions
{
    public void ConfigureHttpsDefaults(Action<HttpsConnectionAdapterOptions> configureOptions);
    ...
}

表示服務端證書的X509Certificate2對象可以直接設置到ServerCertificate屬性上,我們也可以在ServerCertificateSelector屬性上設置一個根據當前連結動態選擇證書的委託。SslProtocols屬性用來設置採用的協議(SSL或者TLS),對應的類型爲如下這個SslProtocols枚舉。HandshakeTimeout屬性用來設置TLS/SSL“握手”的超時時間,默認爲10秒。

[Flags]
public enum SslProtocols
{
    None = 0x0,
    [Obsolete("SslProtocols.Ssl2 has been deprecated and is not supported.")]
    Ssl2 = 0xC,
    [Obsolete("SslProtocols.Ssl3 has been deprecated and is not supported.")]
    Ssl3 = 0x30,
    Tls = 0xC0,
    [Obsolete("SslProtocols.Default has been deprecated and is not supported.")]
    Default = 0xF0,
    Tls11 = 0x300,
    Tls12 = 0xC00,
    Tls13 = 0x3000
}

HTTPS主要解決的是服務端的認證和傳輸安全問題,所以服務端的認證信息需要在前期“協商”階段利用建立的安全通道傳遞給客戶端,具體的認證信息是如下這個SslServerAuthenticationOptions配置選項格式化後的結果。HttpsConnectionAdapterOptions的OnAuthenticate屬性提供的委託可以幫助我們對這個配置選項進行設置,所以絕大部分HTTPS相關的設置都可以利用該屬性來完成。

public class SslServerAuthenticationOptions
{
    public bool AllowRenegotiation { get; set; }
    public bool ClientCertificateRequired { get; set; }
    public List<SslApplicationProtocol>? ApplicationProtocols { get; set; }
    public RemoteCertificateValidationCallback? RemoteCertificateValidationCallback { get; set; }
    public ServerCertificateSelectionCallback? ServerCertificateSelectionCallback { get; set; }
    public X509Certificate? ServerCertificate { get; set; }
    public SslStreamCertificateContext? ServerCertificateContext { get; set; }
    public SslProtocols EnabledSslProtocols { get; set; }
    public X509RevocationMode CertificateRevocationCheckMode { get; set; }
    public EncryptionPolicy EncryptionPolicy { get; set; }
    public CipherSuitesPolicy? CipherSuitesPolicy { get; set; }
}

HTTPS不僅僅能夠幫助客戶端來驗證服務端的身份,還能幫助服務端來對客戶端身份進行驗證。服務端驗證利用服務端證書來完成,與之類似,服務端要識別客戶端的身份,同樣需要客戶端提供證書。我們可以利用HttpsConnectionAdapterOptions的ClientCertificateMode屬性來決定是否要求客戶端提供證書,該屬性類型爲如下這個ClientCertificateMode枚舉。針對客戶端認證的驗證可以利用ClientCertificateValidation屬性設置的委託來完成。

public enum ClientCertificateMode
{
    NoCertificate,
    AllowCertificate,
    RequireCertificate,
    DelayCertificate
}

由權威機構(Certificate Authority)頒發的證書可能會由於某種原因被撤銷,我們有兩種途徑來確定某張證書是否處於被撤銷的狀態:證書頒發機構可以採用標準的OCSP(Online Certificate Status Protocol)協議提供用於確定證書狀態的API,也可以直接提供一份撤銷的證書清單(CRL:Certificate Revocation List)。HttpsConnectionAdapterOptions的CheckCertificateRevocation屬性用來決定是否需要對證書的撤銷狀態進行驗證。如果不需要對客戶端證書作任何驗證,我們可以調用HttpsConnectionAdapterOptions的AllowAnyClientCertificate方法。

當我們將某個終結點註冊到KestrelServer上並生成對應ListenOptions配置選項後,我們可以調用後者的UseHttps擴展方法(註冊終結點的很多方法都提供一個Action<ListenOptions>參數)完成針對HTTPS的設置,我們有如下這一系列UseHttps重載可供選擇。對於證書的設置,我們可以直接指定一個X509Certificate2對象,也可以指定證書文件的路徑(一般還需要提供讀取證書的密碼),還可以指定證書的存儲(Certificate Store)。我們可以利用部分方法重載提供的委託對HttpsConnectionAdapterOptions配置選項進行設置。部分方法重載還提供了一個ServerOptionsSelectionCallback委託直接返回SslServerAuthenticationOptions配置選項。

public static class ListenOptionsHttpsExtensions
{
    public static ListenOptions UseHttps(this ListenOptions listenOptions);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string? password);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, string fileName, string? password, Action<HttpsConnectionAdapterOptions> configureOptions);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject, bool allowInvalid);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject, bool allowInvalid, StoreLocation location);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, StoreName storeName, string subject, bool allowInvalid, StoreLocation location, Action<HttpsConnectionAdapterOptions> configureOptions);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, X509Certificate2 serverCertificate);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, X509Certificate2 serverCertificate, Action<HttpsConnectionAdapterOptions> configureOptions);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, Action<HttpsConnectionAdapterOptions> configureOptions);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, HttpsConnectionAdapterOptions httpsOptions);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, ServerOptionsSelectionCallback serverOptionsSelectionCallback, object state);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, ServerOptionsSelectionCallback serverOptionsSelectionCallback, object state, TimeSpan handshakeTimeout);
    public static ListenOptions UseHttps(this ListenOptions listenOptions, TlsHandshakeCallbackOptions callbackOptions);
}

public delegate ValueTask<SslServerAuthenticationOptions> ServerOptionsSelectionCallback(SslStream stream, SslClientHelloInfo clientHelloInfo, object? state, CancellationToken cancellationToken);

除了調用上述這些方法來爲註冊的終結點提供HTTPS相關的設置外,這些設置也可以按照如下的方式放在終結點的配置中。

{
  "Kestrel": {
    "Endpoints": {
      "MyHttpsEndpoint": {
        "Url": "https://localhost:5001",
        "ClientCertificateMode": "AllowCertificate",
        "Certificate": {
          "Path": "c:\\certificates\\foobar.pfx>",
          "Password": "password"
        }
      }
    }
  }
}

五、限制約束

爲了確保KestrelServer穩定可靠地運行,需要根據需要爲它設置相應的限制和約束,這些設置體現在KestrelServerOptions配置選項Limits屬性返回的KestrelServerLimits對象上。

public class KestrelServerOptions
{
    public KestrelServerLimits Limits { get; } = new KestrelServerLimits();
}

public class KestrelServerLimits
{
    public long? MaxConcurrentConnections { get; set; }
    public long? MaxConcurrentUpgradedConnections { get; set; }
    public TimeSpan KeepAliveTimeout { get; set; }

    public int MaxRequestHeaderCount { get; set; }
    public long? MaxRequestBufferSize { get; set; }
    public int MaxRequestHeadersTotalSize { get; set; }
    public int MaxRequestLineSize { get; set; }
    public long? MaxRequestBodySize { get; set; }
    public TimeSpan RequestHeadersTimeout { get; set; }
    public MinDataRate MinRequestBodyDataRate { get; set; }

    public long? MaxResponseBufferSize { get; set; }
    public MinDataRate MinResponseDataRate { get; set; }

    public Http2Limits Http2 { get; }
    public Http3Limits Http3 { get; }
}

KestrelServerLimits利用其豐富的屬性對連接、請求和響應進行了相應的限制。KestrelServer提供了針對HTTP 2和HTTP3的支持,針對性的限制設置體現在KestrelServerLimits類型的Http2和Http3屬性上。下表對定義在KestrelServerLimits類型中的這些屬性所體現的限制約束進行了簡單說明。

屬性

含  義

MaxConcurrentConnections

最大併發連接。如果設置爲Null(默認值),意味着不作限制。

MaxConcurrentUpgradedConnections

可升級連接(比如從HTTP升級到WebSocket)的最大併發數。如果設置爲Null(默認值),意味着不作限制。

KeepAliveTimeout

連接保持活動狀態的超時時間,默認值爲130秒。

MaxRequestHeaderCount

請求攜帶的最大報頭數量,默認值爲100。

MaxRequestBufferSize

請求緩衝區最大容量,默認值爲1,048,576字節(1M)。

MaxRequestHeadersTotalSize

請求攜帶報頭總字節數,默認值爲 32,768字節(32K)。

MaxRequestLineSize

對於HTTP 1.X來說就是請求的首行(Request Line)最大字節數。對於HTTP 2/3來說就是 :method, :scheme, :authority, and :path這些報頭的總字節數。默認值爲8,192 字節(8K)。

MaxRequestBodySize

請求主體最大字節數,默認值爲30,000,000 字節(約28.6M)。如果設置爲Null,意味着不作限制。

RequestHeadersTimeout

接收請求報頭的超時時間,默認爲30秒。

MinRequestBodyDataRate

請求主體內容最低傳輸率。

MaxResponseBufferSize

響應緩衝區最大容量,默認值爲65,536(1M)。

MinResponseDataRate

響應最低傳輸率。

KestrelServerLimits的MinRequestBodyDataRate和MinResponseDataRate屬性返回的最低傳輸率體現爲如下這個MinDataRate對象。如果沒有達到設定的傳輸率,當前連接就會被重置。MinDataRate對象除了提供表示傳輸率的BytesPerSecond屬性外,還提供了一個表示“寬限時間”的GracePeriod屬性。並非傳輸率下降到設定的閾值的那一刻就重置連接,只要在指定的時段內傳輸率上升到閾值以上也沒有問題。MinRequestBodyDataRate和MinResponseDataRate屬性的默認值均爲“240 bytes/second(5 seconds)”。

public class MinDataRate
{
    public double 	BytesPerSecond { get; }
    public TimeSpan 	GracePeriod { get; }

    public MinDataRate(double bytesPerSecond, TimeSpan gracePeriod);
}

HTTP 1.X建立在TCP之上,客戶端和服務端之間的交互依賴預先創建的TCP連接。雖然HTTP 1.1引入的流水線技術允許客戶端可以隨時向服務端發送請求,而無需等待接收到上一個請求的響應,但是響應依然只能按照請求的接收順序返回的。真正意義上的“併發”請求只能利用多個連接來完成,但是針對同一個域名支持的TCP連接的數量又是有限的。這個問題在HTTP 2得到了一定程度的解決。

與採用文本編碼的HTTP 1.X相比, HTTP 2採用更加高效的二進制編碼。幀(Frame)成爲了基本通信單元,單個請求和響應可以分解成多個幀進行發送。客戶端和服務端之間額消息交換在一個支持雙向通信的信道(Channel)中完成,該信道被稱爲“流(Stream)”。每一個流具有一個唯一標識,同一個TCP連接可以承載成百上千的流。每個幀攜帶着所屬流的標識,所以它可以隨時被“亂序”發送,接收端可以利用流的標識進行重組,所以HTTP 2在同一個TCP連接上實現了“多路複用”。

使用同一個連接發送的請求和響應都存在很多重複的報頭,爲了減少報頭內容佔據的帶寬,HTTP 2會採用一種名爲HPACK的壓縮算法對報頭文本進行編碼。HPACK會在發送和接收端維護一個索引表來存儲編碼的文本,報頭內容在發送前會被替換成在該表的索引,接收端這利用此索引在本地壓縮表中找到原始的內容。

public class Http2Limits
{
    public int 	MaxStreamsPerConnection { get; set; }
    public int 	HeaderTableSize { get; set; }
    public int 	MaxFrameSize { get; set; }
    public int 	MaxRequestHeaderFieldSize { get; set; }
    public int 	InitialConnectionWindowSize { get; set; }
    public int 	InitialStreamWindowSize { get; set; }
    public TimeSpan 	KeepAlivePingDelay { get; set; }
    public TimeSpan 	KeepAlivePingTimeout { get; set; }
}

於HTTP 2相關限制和約束的設置體現在KestrelServerLimits的Http2屬性上,該屬性返回如上所示的Http2Limits對象。下表對定義在Http2Limits類型中的這些屬性所體現的限制約束進行了簡單說明。

屬性

含  義

MaxStreamsPerConnection

連接能夠承載的流數量,默認值爲100。

HeaderTableSize

HPACK報頭壓縮表的容量,默認值爲4096。

MaxFrameSize

幀的最大字節數,有效值在[214~224 – 1]區間範圍內,默認值爲214(16384)。

MaxRequestHeaderFieldSize

最大請求報頭(含報頭名稱)的最大字節數,默認值爲214(16384)。

InitialConnectionWindowSize

連接的初始化請求主體緩存區的大小,有效值在[65535~231]區間範圍內,默認爲131072。

InitialStreamWindowSize

流的初始化請求主體緩存區的大小,有效值在[65535~231]區間範圍內,默認爲98304。

KeepAlivePingDelay

如果服務端在該屬性設定的時間跨度內沒有接收到來自客戶端的有效幀,它會主動發送Ping請求確定客戶端的是否保持活動狀態,默認值爲1秒。

KeepAlivePingTimeout

發送Ping請求的超時時間,如果客戶端在該時限內一直處於爲活動狀態,當前連接將被關閉,默認值爲20秒。

由於HTTP 2的多路複用是在同一個TCP連接上實現的,這樣的實現並不“純粹”,因爲它不可能解決由於TCP的“擁塞控制”機制導致的“隊頭阻塞(Header-Of-Line Blocking)”問題。如果希望在得到併發支持的前提下還能在低延時上有更好的作爲,就不得不拋棄TCP。目前被正式確定爲HTTP 3的QUIC(Quick UDP Internet Connection)就將TCP替換成了UDP。如果KestrelServer支持HTTP 3,我們可以利用KestrelServerLimits的Http3屬性返回的Http3Limits對象都限制約束進行鍼對性設置。Http3Limits只包含如下這個表示最大請求報頭字節數的MaxRequestHeaderFieldSize屬性,它的默認值爲16384。

public class Http3Limits
{
    public int MaxRequestHeaderFieldSize { get; set;}
}

六、其他設置

除了註冊的終結點和基於通信的限制約束,KestrelServerOptions配置選項還利用如下的屬性承載着其他的設置。

public class KestrelServerOptions
{
    public bool AddServerHeader { get; set; }
    public bool AllowResponseHeaderCompression { get; set; }
    public bool AllowSynchronousIO { get; set; }
    public bool AllowAlternateSchemes { get; set; }
    public bool DisableStringReuse { get; set; }
    public Func<string, Encoding> RequestHeaderEncodingSelector { get; set; }
    public Func<string, Encoding> ResponseHeaderEncodingSelector { get; set; }
}

下表對定義在KestrelServerOptions類型中的上述這些屬性進行了簡單的說明。

屬性

含  義

AddServerHeader

是否會在回覆的響應中自動添加“Server: Kestrel”報頭,默認值爲True。

AllowResponseHeaderCompression

是否允許對響應報頭進行HPACK壓縮,默認值爲True。

AllowSynchronousIO

是否允許對請求和響應進行同步IO操作,默認值爲False,意味這個默認情況下以同步方式讀取請求和寫入響應都會拋出異常。

AllowAlternateSchemes

是否允許爲“:scheme”字段(針對HTTP 2和HTTP 3)提供一個與當前傳輸不匹配的值(“http”或者“https”),默認值爲False。如果將這個屬性設置爲True,意味着HttpRequest.Scheme屬性可能與採用的傳輸類型不匹配。

DisableStringReuse

創建的字符串是否可以在多個請求中複用。

RequestHeaderEncodingSelector

用於設置某個請求報頭採用的編碼方式,默認爲Utf8Encoding。

ResponseHeaderEncodingSelector

用於設置某個響應報頭採用的編碼方式,默認爲ASCIIEncoding。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章