ASP.NET Core 中的 HTTP.sys Web 服务器实现

作者:Tom DykstraChris Ross

HTTP.sys 是仅在 Windows 上运行的适用于 ASP.NET Core 的 Web 服务器。 HTTP.sys 是 Kestrel 服务器的替代选择,提供了一些 Kestrel 不提供的功能。

重要

HTTP.sys 与 ASP.NET Core 模块不兼容,无法与 IIS 或 IIS Express 结合使用。

HTTP.sys 支持以下功能:

  • Windows 身份验证
  • 端口共享
  • 具有 SNI 的 HTTPS
  • 基于 TLS 的 HTTP/2(Windows 10 或更高版本)
  • 直接文件传输
  • 响应缓存
  • WebSocket(Windows 8 或更高版本)

受支持的 Windows 版本:

  • Windows 7 或更高版本
  • Windows Server 2008 R2 或更高版本

查看或下载示例代码如何下载

何时使用 HTTP.sys

HTTP.sys 对于以下情形的部署来说很有用:

  • 需要将服务器直接公开到 Internet 而不使用 IIS 的部署。

    HTTP.sys communicates directly with the Internet

  • 内部部署需要 Kestrel 中没有的功能。 有关详细信息,请参阅本文档中的 Kestrel 与HTTP.sys

    HTTP.sys communicates directly with the internal network

HTTP.sys 是一项成熟的技术,可以抵御多种攻击,并提供可靠、安全、可伸缩的全功能 Web 服务器。 IIS 本身作为 HTTP.sys 之上的 HTTP 侦听器运行。

HTTP/2 支持

如果满足以下基本要求,将为 ASP.NET Core 应用启用 HTTP/2

如果已建立 HTTP/2 连接,HttpRequest.Protocol 会报告 HTTP/2

默认情况下将启用 HTTP/2。 如果未建立 HTTP/2 连接,连接会回退到 HTTP/1.1。 在 Windows 的未来版本中,将提供 HTTP/2 配置标志,包括使用 HTTP.sys 禁用 HTTP/2 的功能。

HTTP/3 支持

如果满足以下基本要求,将为 ASP.NET Core 应用启用 HTTP/3

上述 Windows 11 内部版本可能需要使用 Windows 预览体验成员内部版本。

HTTP/3 是通过 alt-svc 标头作为从 HTTP/1.1 或 HTTP/2 的升级发现的。 这意味着,在切换到 HTTP/3 之前,第一个请求通常使用 HTTP/1.1 或 HTTP/2。 Http.Sys 不会自动添加 alt-svc 标头,它必须由应用程序添加。 以下代码是添加 alt-svc 响应头的中间件示例。

app.Use((context, next) =>
{
    context.Response.Headers.AltSvc = "h3=\":443\"";
    return next(context);
});

将前面的代码提前放在请求管道中。

Http.Sys 还支持发送 AltSvc HTTP/2 协议消息,而不是通过响应标头来通知客户端 HTTP/3 可用。 请参阅 EnableAltSvc 注册表项。 这需要使用主机名而不是 IP 地址的 netsh sslcert 绑定。

对 Kerberos 进行内核模式身份验证

HTTP.sys 通过 Kerberos 身份验证协议委托给内核模式身份验证。 Kerberos 和 HTTP.sys 不支持用户模式身份验证。 必须使用计算机帐户来解密从 Active Directory 获取的并由客户端转发到服务器的 Kerberos 令牌/票证,以便对用户进行身份验证。 注册主机的服务主体名称 (SPN),而不是应用的用户。

对内核模式响应缓冲的支持

在某些场景下,高延迟的大量小型写入可能会对 HTTP.sys 造成重大的性能影响。 这种影响是由于 HTTP.sys 实现中缺少 Pipe 缓冲区。 为了在这些场景中提高性能,HTTP.sys 中包含了对响应缓冲的支持。 通过将 HttpSysOptions.EnableKernelResponseBuffering 设置为 true 来启用缓冲。

响应缓冲应由执行同步 I/O 或异步 I/O 且一次不超过一个未完成写入的应用来启用。 在这些方案中,响应缓冲可以在高延迟连接上显著提高吞吐量。

使用异步 I/O 且一次可能有多个未完成的写入的应用应使用此标志。 通过 HTTP.Sys 启用此标志可能会导致 CPU 和内存使用率升高。

如何使用 HTTP.sys

配置 ASP.NET Core 应用以使用 HTTP.sys

生成主机时调用 UseHttpSys 扩展方法,指定所有必需的 HttpSysOptions。 以下示例将选项设置为其默认值:

using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.UseHttpSys(options =>
{
    options.AllowSynchronousIO = false;
    options.Authentication.Schemes = AuthenticationSchemes.None;
    options.Authentication.AllowAnonymous = true;
    options.MaxConnections = null;
    options.MaxRequestBodySize = 30_000_000;
    options.UrlPrefixes.Add("http://localhost:5005");
});

builder.Services.AddRazorPages();

var app = builder.Build();

通过注册表设置处理其他 HTTP.sys 配置。

有关 HTTP.sys 选项的详细信息,请参阅 HttpSysOptions

MaxRequestBodySize

允许的请求正文的最大大小(以字节计)。 当设置为 null 时,最大请求正文大小不受限制。 此限制不会影响升级后的连接,这始终不受限制。

在 ASP.NET Core MVC 应用中为单个 IActionResult 替代限制的推荐方法是在操作方法上使用 RequestSizeLimitAttribute 属性:

[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()

如果在应用开始读取请求后尝试配置请求限制,则会引发异常。 IsReadOnly 属性可用于指示 MaxRequestBodySize 属性是否处于只读状态。只读状态意味着已经太迟了,无法配置限制。

如果应用应替代每个请求的 MaxRequestBodySize,请使用 IHttpMaxRequestBodySizeFeature

app.Use((context, next) =>
{
    context.Features.GetRequiredFeature<IHttpMaxRequestBodySizeFeature>()
                                             .MaxRequestBodySize = 10 * 1024;

    var server = context.RequestServices
        .GetRequiredService<IServer>();
    var serverAddressesFeature = server.Features
                                 .GetRequiredFeature<IServerAddressesFeature>();

    var addresses = string.Join(", ", serverAddressesFeature.Addresses);

    var loggerFactory = context.RequestServices
        .GetRequiredService<ILoggerFactory>();
    var logger = loggerFactory.CreateLogger("Sample");

    logger.LogInformation("Addresses: {addresses}", addresses);

    return next(context);
});

如果使用的是 Visual Studio,请确保应用未经配置以运行 IIS 或 IIS Express。

在 Visual Studio 中,默认启动配置文件是针对 IIS Express 的。 若要作为控制台应用运行该项目,请手动更改所选配置文件,如以下屏幕截图中所示:

Select console app profile

配置 Windows Server

  1. 确定要为应用打开的端口,并使用 Windows 防火墙New-NetFirewallRule PowerShell cmdlet 打开防火墙端口,以允许流量到达 HTTP.sys。 在以下命令和应用配置中,使用的是端口 443。

  2. 在部署到 Azure VM 时,在网络安全组中打开端口。 在以下命令和应用配置中,使用的是端口 443。

  3. 如果需要,获取并安装 X.509 证书。

    在 Windows 上,可使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

    在服务器的“本地计算机”>“个人”存储中,安装自签名证书或 CA 签名证书

  4. 如果应用为框架相关部署,则安装 .NET Core、.NET Framework 或两者(如果应用是面向 .NET Framework 的 .NET Core 应用)。

    • .NET Core:如果应用需要 .NET Core,请从 .NET Core 下载页获取并运行 .NET Core 运行时安装程序。 请勿在服务器上安装完整 SDK。
    • .NET Framework:如果应用需要 .NET Framework,请参阅 .NET Framework 安装指南。 安装所需的 .NET Framework。 可以从 .NET Core 下载页获取最新 .NET Framework 的安装程序。

    如果应用是独立式部署,应用在部署中包含运行时。 无需在服务器上安装任何框架。

  5. 在应用中配置 URL 和端口。

    默认情况下,ASP.NET Core 绑定到 http://localhost:5000。 若要配置 URL 前缀和端口,可采用以下方法:

    下面的代码示例展示了如何对端口 443 结合使用 UrlPrefixes 和服务器的本地 IP 地址 10.0.0.4

    var builder = WebApplication.CreateBuilder(args);
    
    builder.WebHost.UseHttpSys(options =>
    {
        options.UrlPrefixes.Add("https://10.0.0.4:443");
    });
    
    builder.Services.AddRazorPages();
    
    var app = builder.Build();
    

    UrlPrefixes 的一个优点是会为格式不正确的前缀立即生成一条错误消息。

    UrlPrefixes 中的设置替代 UseUrls/urls/ASPNETCORE_URLS 设置。 因此,UseUrlsurlsASPNETCORE_URLS 环境变量的一个优点是在 Kestrel 和 HTTP.sys 之间切换变得更加容易。

    HTTP.sys 可以识别 URL 前缀中的两种通配符:

    • * 是一个弱绑定,也称为回退绑定。 如果 URL 前缀为 http://*:5000,而其他内容绑定到端口 5000,则不会使用此绑定。
    • + 是一个强绑定。 如果 URL 前缀为 http://+:5000,此绑定将在其他端口 5000 绑定之前使用。

    有关详细信息,请参阅 UrlPrefix 字符串

    警告

    不应使用顶级通配符绑定(http://*:80/http://+:80)。 顶级通配符绑定会带来应用安全漏洞。 此行为同时适用于强通配符和弱通配符。 请使用显式主机名或 IP 地址,而不是通配符。 如果可控制整个父域(相对于易受攻击的 *.com),子域通配符绑定(例如,*.mysub.com)不会构成安全风险。 有关详细信息,请参阅 RFC 9110:第 7.2 节:托管和授权

    应用和容器通常只能侦听一个端口(比如端口 80),且没有其他限制(比如主机或路径)。 HTTP_PORTS 和 HTTPS_PORTS 是为 Kestrel 和 HTTP.sys 服务器指定侦听端口的配置密钥。 可以将这些密钥指定为使用 DOTNET_ASPNETCORE_ 前缀定义的环境变量,也可以直接通过任何其他配置输入(例如 appsettings.json)指定这些密钥。 每个密钥都是一个以分号分隔的端口值列表,如下例所示:

    ASPNETCORE_HTTP_PORTS=80;8080
    ASPNETCORE_HTTPS_PORTS=443;8081
    

    上例是以下配置的简写,它指定了方案(HTTP 或 HTTPS)和任何主机或 IP。

    ASPNETCORE_URLS=http://*:80/;http://*:8080/;https://*:443/;https://*:8081/
    

    HTTP_PORTS 和 HTTPS_PORTS 配置密钥的优先级较低,会被 URLS 或代码中直接提供的值覆盖。 证书仍需通过特定于服务器的 HTTPS 机制单独配置。

    这些配置密钥等同于常用的通配符绑定。 对于开发和容器方案来说,这些配置密钥很方便,但运行可能还托管了其他服务的计算机时请避免使用通配符。

  6. 在服务器上预注册 URL 前缀。

    用于配置 HTTP.sys 的内置工具为 netsh.exenetsh.exe 用于保留 URL 前缀并分配 X.509 证书。 此工具需要管理员特权。

    使用 netsh.exe 工具为应用注册 URL:

    netsh http add urlacl url=<URL> user=<USER>
    
    • <URL>:完全限定的统一资源定位器 (URL)。 不要使用通配符绑定。 请使用有效主机名或本地 IP 地址。 URL 必须包含尾部反斜杠。
    • <USER>:指定用户名或用户组名称。

    在以下示例中,服务器的本地 IP 地址是 10.0.0.4

    netsh http add urlacl url=https://10.0.0.4:443/ user=Users
    

    在 URL 注册后,工具响应返回 URL reservation successfully added

    若要删除已注册的 URL,请使用 delete urlacl 命令:

    netsh http delete urlacl url=<URL>
    
  7. 在服务器上注册 X.509 证书。

    使用 netsh.exe 工具为应用注册证书:

    netsh http add sslcert ipport=<IP>:<PORT> certhash=<THUMBPRINT> appid="{<GUID>}"
    
    • <IP>:指定绑定的本地 IP 地址。 不要使用通配符绑定。 请使用有效 IP 地址。
    • <PORT>:指定绑定的端口。
    • <THUMBPRINT>:X.509 证书指纹。
    • <GUID>:开发人员生成的表示应用的 GUID,以供参考。

    为了便于参考,将 GUID 作为包标记存储在应用中:

    • 在 Visual Studio 中:
      • 在“解决方案资源管理器”中,右键单击应用,并选择“属性”,以打开应用的项目属性。
      • 选择“包”选项卡。
      • 在“标记”字段中输入已创建的 GUID。
    • 如果使用的不是 Visual Studio:
      • 打开应用的项目文件。

      • 使用已创建的 GUID,将 <PackageTags> 属性添加到新的或现有的 <PropertyGroup>

        <PropertyGroup>
          <PackageTags>9412ee86-c21b-4eb8-bd89-f650fbf44931</PackageTags>
        </PropertyGroup>
        

    如下示例中:

    • 服务器的本地 IP 地址是 10.0.0.4
    • 联机随机 GUID 生成器提供 appid 值。
    netsh http add sslcert 
        ipport=10.0.0.4:443 
        certhash=b66ee04419d4ee37464ab8785ff02449980eae10 
        appid="{9412ee86-c21b-4eb8-bd89-f650fbf44931}"
    

    在证书注册后,工具响应返回 SSL Certificate successfully added

    若要删除证书注册,请使用 delete sslcert 命令:

    netsh http delete sslcert ipport=<IP>:<PORT>
    

    netsh.exe 的参考文档:

  8. 运行应用。

    结合使用 HTTP(而不是 HTTPS)和大于 1024 的端口号绑定到 localhost,无需管理员权限,即可运行应用。 对于其他配置(例如,使用本地 IP 地址或绑定到端口 443),必须有管理员权限才能运行应用。

    应用在服务器的公共 IP 地址处响应。 此示例在 Internet 上的公共 IP 地址 104.214.79.47 处访问服务器。

    此示例使用的是开发证书。 在绕过浏览器的不受信任证书警告后,页面安全加载。

    Browser window showing the app's Index page loaded

代理服务器和负载均衡器方案

如果应用由 HTTP.sys 托管并且与来自 Internet 或公司网络的请求进行交互,当在代理服务器和负载均衡器后托管时,可能需要其他配置。 有关详细信息,请参阅配置 ASP.NET Core 以使用代理服务器和负载均衡器

使用 IHttpSysRequestTimingFeature 获取详细的时间信息

IHttpSysRequestTimingFeature 为请求提供详细的时间信息:

using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.UseHttpSys();

var app = builder.Build();

app.Use((context, next) =>
{
    var feature = context.Features.GetRequiredFeature<IHttpSysRequestTimingFeature>();
    
    var loggerFactory = context.RequestServices.GetRequiredService<ILoggerFactory>();
    var logger = loggerFactory.CreateLogger("Sample");

    var timestamps = feature.Timestamps;

    for (var i = 0; i < timestamps.Length; i++)
    {
        var timestamp = timestamps[i];
        var timingType = (HttpSysRequestTimingType)i;

        logger.LogInformation("Timestamp {timingType}: {timestamp}",
                                          timingType, timestamp);
    }

    return next(context);
});

app.MapGet("/", () => Results.Ok());

app.Run();

IHttpSysRequestTimingFeature.TryGetTimestamp 可检索提供的时间类型的时间戳:

using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;
var builder = WebApplication.CreateBuilder(args);

builder.WebHost.UseHttpSys();

var app = builder.Build();

app.Use((context, next) =>
{
    var feature = context.Features.GetRequiredFeature<IHttpSysRequestTimingFeature>();

    var loggerFactory = context.RequestServices.GetRequiredService<ILoggerFactory>();
    var logger = loggerFactory.CreateLogger("Sample");

    var timingType = HttpSysRequestTimingType.RequestRoutingEnd;

    if (feature.TryGetTimestamp(timingType, out var timestamp))
    {
        logger.LogInformation("Timestamp {timingType}: {timestamp}",
                                          timingType, timestamp);
    }
    else
    {
        logger.LogInformation("Timestamp {timingType}: not available for the "
                                           + "current request",    timingType);
    }

    return next(context);
});

app.MapGet("/", () => Results.Ok());

app.Run();

IHttpSysRequestTimingFeature.TryGetElapsedTime 提供两个指定的时间之间的时间差:

using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Server.HttpSys;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.UseHttpSys();

var app = builder.Build();

app.Use((context, next) =>
{
    var feature = context.Features.GetRequiredFeature<IHttpSysRequestTimingFeature>();

    var loggerFactory = context.RequestServices.GetRequiredService<ILoggerFactory>();
    var logger = loggerFactory.CreateLogger("Sample");

    var startingTimingType = HttpSysRequestTimingType.RequestRoutingStart;
    var endingTimingType = HttpSysRequestTimingType.RequestRoutingEnd;

    if (feature.TryGetElapsedTime(startingTimingType, endingTimingType, out var elapsed))
    {
        logger.LogInformation(
            "Elapsed time {startingTimingType} to {endingTimingType}: {elapsed}",
            startingTimingType,
            endingTimingType,
            elapsed);
    }
    else
    {
        logger.LogInformation(
            "Elapsed time {startingTimingType} to {endingTimingType}:"
            + " not available for the current request.",
            startingTimingType,
            endingTimingType);
    }

    return next(context);
});

app.MapGet("/", () => Results.Ok());

app.Run();

用于支持 gRPC 的高级 HTTP/2 功能

HTTP.sys 中的其他 HTTP/2 功能支持 gRPC,包括对响应尾部和发送重置帧的支持。

使用 HTTP.sys 运行 gRPC 的要求:

  • Windows 11 内部版本 22000 或更高版本、Windows Server 2022 内部版本 20348 或更高版本。
  • TLS 1.2 或更高版本的连接。

预告片

HTTP 尾部类似于 HTTP 标头,只不过它是在发送响应正文后发送的。 IIS 和 HTTP.sys 仅支持 HTTP/2 响应尾部。

if (httpContext.Response.SupportsTrailers())
{
    httpContext.Response.DeclareTrailer("trailername");	

    // Write body
    httpContext.Response.WriteAsync("Hello world");

    httpContext.Response.AppendTrailer("trailername", "TrailerValue");
}

在前面的示例代码中:

  • SupportsTrailers 确保响应支持尾部。
  • DeclareTrailer 将给定的尾部名称添加到 Trailer 响应头。 虽然并不是必须要声明响应尾部,但是建议这样做。 如果要调用 DeclareTrailer,则必须在发送响应标头之前进行此操作。
  • AppendTrailer 追加尾部。

重置

通过“Reset”,服务器可以使用指定的错误代码重置 HTTP/2 请求。 重置请求被视为中止。

var resetFeature = httpContext.Features.Get<IHttpResetFeature>();
resetFeature.Reset(errorCode: 2);

前述代码示例中的 Reset 指定 INTERNAL_ERROR 错误代码。 有关 HTTP/2 错误代码的详细信息,请访问“HTTP/2 规范错误代码”部分

跟踪

有关如何从 HTTP.sys 获取跟踪的信息,请参阅 HTTP.sys 可管理性方案

其他资源

HTTP.sys 是仅在 Windows 上运行的适用于 ASP.NET Core 的 Web 服务器。 HTTP.sys 是 Kestrel 服务器的替代选择,提供了一些 Kestrel 不提供的功能。

重要

HTTP.sys 与 ASP.NET Core 模块不兼容,无法与 IIS 或 IIS Express 结合使用。

HTTP.sys 支持以下功能:

  • Windows 身份验证
  • 端口共享
  • 具有 SNI 的 HTTPS
  • 基于 TLS 的 HTTP/2(Windows 10 或更高版本)
  • 直接文件传输
  • 响应缓存
  • WebSocket(Windows 8 或更高版本)

受支持的 Windows 版本:

  • Windows 7 或更高版本
  • Windows Server 2008 R2 或更高版本

查看或下载示例代码如何下载

何时使用 HTTP.sys

HTTP.sys 对于以下情形的部署来说很有用:

  • 需要将服务器直接公开到 Internet 而不使用 IIS 的部署。

    HTTP.sys communicates directly with the Internet

  • 内部部署需要 Kestrel 中没有的功能。 有关详细信息,请参阅本文档中的 Kestrel 与HTTP.sys

    HTTP.sys communicates directly with the internal network

HTTP.sys 是一项成熟的技术,可以抵御多种攻击,并提供可靠、安全、可伸缩的全功能 Web 服务器。 IIS 本身作为 HTTP.sys 之上的 HTTP 侦听器运行。

HTTP/2 支持

如果满足以下基本要求,将为 ASP.NET Core 应用启用 HTTP/2

如果已建立 HTTP/2 连接,HttpRequest.Protocol 会报告 HTTP/2

默认情况下将启用 HTTP/2。 如果未建立 HTTP/2 连接,连接会回退到 HTTP/1.1。 在 Windows 的未来版本中,将提供 HTTP/2 配置标志,包括使用 HTTP.sys 禁用 HTTP/2 的功能。

HTTP/3 支持

如果满足以下基本要求,将为 ASP.NET Core 应用启用 HTTP/3

上述 Windows 11 内部版本可能需要使用 Windows 预览体验成员内部版本。

HTTP/3 是通过 alt-svc 标头作为从 HTTP/1.1 或 HTTP/2 的升级发现的。 这意味着,在切换到 HTTP/3 之前,第一个请求通常使用 HTTP/1.1 或 HTTP/2。 Http.Sys 不会自动添加 alt-svc 标头,它必须由应用程序添加。 以下代码是添加 alt-svc 响应头的中间件示例。

app.Use((context, next) =>
{
    context.Response.Headers.AltSvc = "h3=\":443\"";
    return next(context);
});

将前面的代码提前放在请求管道中。

Http.Sys 还支持发送 AltSvc HTTP/2 协议消息,而不是通过响应标头来通知客户端 HTTP/3 可用。 请参阅 EnableAltSvc 注册表项。 这需要使用主机名而不是 IP 地址的 netsh sslcert 绑定。

对 Kerberos 进行内核模式身份验证

HTTP.sys 通过 Kerberos 身份验证协议委托给内核模式身份验证。 Kerberos 和 HTTP.sys 不支持用户模式身份验证。 必须使用计算机帐户来解密从 Active Directory 获取的并由客户端转发到服务器的 Kerberos 令牌/票证,以便对用户进行身份验证。 注册主机的服务主体名称 (SPN),而不是应用的用户。

如何使用 HTTP.sys

配置 ASP.NET Core 应用以使用 HTTP.sys

生成主机时调用 UseHttpSys 扩展方法,指定所有必需的 HttpSysOptions。 以下示例将选项设置为其默认值:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseHttpSys(options =>
            {
                options.AllowSynchronousIO = false;
                options.Authentication.Schemes = AuthenticationSchemes.None;
                options.Authentication.AllowAnonymous = true;
                options.MaxConnections = null;
                options.MaxRequestBodySize = 30000000;
                options.UrlPrefixes.Add("http://localhost:5005");
            });
            webBuilder.UseStartup<Startup>();
        });

通过注册表设置处理其他 HTTP.sys 配置。

有关 HTTP.sys 选项的详细信息,请参阅 HttpSysOptions

MaxRequestBodySize

允许的请求正文的最大大小(以字节计)。 当设置为 null 时,最大请求正文大小不受限制。 此限制不会影响升级后的连接,这始终不受限制。

在 ASP.NET Core MVC 应用中为单个 IActionResult 替代限制的推荐方法是在操作方法上使用 RequestSizeLimitAttribute 属性:

[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()

如果在应用开始读取请求后尝试配置请求限制,则会引发异常。 IsReadOnly 属性可用于指示 MaxRequestBodySize 属性是否处于只读状态。只读状态意味着已经太迟了,无法配置限制。

如果应用应替代每个请求的 MaxRequestBodySize,请使用 IHttpMaxRequestBodySizeFeature

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, 
    ILogger<Startup> logger, IServer server)
{
    app.Use(async (context, next) =>
    {
        context.Features.Get<IHttpMaxRequestBodySizeFeature>()
            .MaxRequestBodySize = 10 * 1024;

        var serverAddressesFeature = 
            app.ServerFeatures.Get<IServerAddressesFeature>();
        var addresses = string.Join(", ", serverAddressesFeature?.Addresses);

        logger.LogInformation("Addresses: {Addresses}", addresses);

        await next.Invoke();
    });

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
    }

    app.UseStaticFiles();
    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

如果使用的是 Visual Studio,请确保应用未经配置以运行 IIS 或 IIS Express。

在 Visual Studio 中,默认启动配置文件是针对 IIS Express 的。 若要作为控制台应用运行该项目,请手动更改所选配置文件,如以下屏幕截图中所示:

Select console app profile

配置 Windows Server

  1. 确定要为应用打开的端口,并使用 Windows 防火墙New-NetFirewallRule PowerShell cmdlet 打开防火墙端口,以允许流量到达 HTTP.sys。 在以下命令和应用配置中,使用的是端口 443。

  2. 在部署到 Azure VM 时,在网络安全组中打开端口。 在以下命令和应用配置中,使用的是端口 443。

  3. 如果需要,获取并安装 X.509 证书。

    在 Windows 上,可使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

    在服务器的“本地计算机”>“个人”存储中,安装自签名证书或 CA 签名证书

  4. 如果应用为框架相关部署,则安装 .NET Core、.NET Framework 或两者(如果应用是面向 .NET Framework 的 .NET Core 应用)。

    • .NET Core:如果应用需要 .NET Core,请从 .NET Core 下载页获取并运行 .NET Core 运行时安装程序。 请勿在服务器上安装完整 SDK。
    • .NET Framework:如果应用需要 .NET Framework,请参阅 .NET Framework 安装指南。 安装所需的 .NET Framework。 可以从 .NET Core 下载页获取最新 .NET Framework 的安装程序。

    如果应用是独立式部署,应用在部署中包含运行时。 无需在服务器上安装任何框架。

  5. 在应用中配置 URL 和端口。

    默认情况下,ASP.NET Core 绑定到 http://localhost:5000。 若要配置 URL 前缀和端口,可采用以下方法:

    下面的代码示例展示了如何对端口 443 结合使用 UrlPrefixes 和服务器的本地 IP 地址 10.0.0.4

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseHttpSys(options =>
                {
                    options.UrlPrefixes.Add("https://10.0.0.4:443");
                });
                webBuilder.UseStartup<Startup>();
            });
    

    UrlPrefixes 的一个优点是会为格式不正确的前缀立即生成一条错误消息。

    UrlPrefixes 中的设置替代 UseUrls/urls/ASPNETCORE_URLS 设置。 因此,UseUrlsurlsASPNETCORE_URLS 环境变量的一个优点是在 Kestrel 和 HTTP.sys 之间切换变得更加容易。

    HTTP.sys 使用 HTTP 服务器 API UrlPrefix 字符串格式

    警告

    不应使用顶级通配符绑定(http://*:80/http://+:80)。 顶级通配符绑定会带来应用安全漏洞。 此行为同时适用于强通配符和弱通配符。 请使用显式主机名或 IP 地址,而不是通配符。 如果可控制整个父域(相对于易受攻击的 *.com),子域通配符绑定(例如,*.mysub.com)不会构成安全风险。 有关详细信息,请参阅 RFC 9110:第 7.2 节:托管和授权

  6. 在服务器上预注册 URL 前缀。

    用于配置 HTTP.sys 的内置工具为 netsh.exenetsh.exe 用于保留 URL 前缀并分配 X.509 证书。 此工具需要管理员特权。

    使用 netsh.exe 工具为应用注册 URL:

    netsh http add urlacl url=<URL> user=<USER>
    
    • <URL>:完全限定的统一资源定位器 (URL)。 不要使用通配符绑定。 请使用有效主机名或本地 IP 地址。 URL 必须包含尾部反斜杠。
    • <USER>:指定用户名或用户组名称。

    在以下示例中,服务器的本地 IP 地址是 10.0.0.4

    netsh http add urlacl url=https://10.0.0.4:443/ user=Users
    

    在 URL 注册后,工具响应返回 URL reservation successfully added

    若要删除已注册的 URL,请使用 delete urlacl 命令:

    netsh http delete urlacl url=<URL>
    
  7. 在服务器上注册 X.509 证书。

    使用 netsh.exe 工具为应用注册证书:

    netsh http add sslcert ipport=<IP>:<PORT> certhash=<THUMBPRINT> appid="{<GUID>}"
    
    • <IP>:指定绑定的本地 IP 地址。 不要使用通配符绑定。 请使用有效 IP 地址。
    • <PORT>:指定绑定的端口。
    • <THUMBPRINT>:X.509 证书指纹。
    • <GUID>:开发人员生成的表示应用的 GUID,以供参考。

    为了便于参考,将 GUID 作为包标记存储在应用中:

    • 在 Visual Studio 中:
      • 在“解决方案资源管理器”中,右键单击应用,并选择“属性”,以打开应用的项目属性。
      • 选择“包”选项卡。
      • 在“标记”字段中输入已创建的 GUID。
    • 如果使用的不是 Visual Studio:
      • 打开应用的项目文件。

      • 使用已创建的 GUID,将 <PackageTags> 属性添加到新的或现有的 <PropertyGroup>

        <PropertyGroup>
          <PackageTags>9412ee86-c21b-4eb8-bd89-f650fbf44931</PackageTags>
        </PropertyGroup>
        

    如下示例中:

    • 服务器的本地 IP 地址是 10.0.0.4
    • 联机随机 GUID 生成器提供 appid 值。
    netsh http add sslcert 
        ipport=10.0.0.4:443 
        certhash=b66ee04419d4ee37464ab8785ff02449980eae10 
        appid="{9412ee86-c21b-4eb8-bd89-f650fbf44931}"
    

    在证书注册后,工具响应返回 SSL Certificate successfully added

    若要删除证书注册,请使用 delete sslcert 命令:

    netsh http delete sslcert ipport=<IP>:<PORT>
    

    netsh.exe 的参考文档:

  8. 运行应用。

    结合使用 HTTP(而不是 HTTPS)和大于 1024 的端口号绑定到 localhost,无需管理员权限,即可运行应用。 对于其他配置(例如,使用本地 IP 地址或绑定到端口 443),必须有管理员权限才能运行应用。

    应用在服务器的公共 IP 地址处响应。 此示例在 Internet 上的公共 IP 地址 104.214.79.47 处访问服务器。

    此示例使用的是开发证书。 在绕过浏览器的不受信任证书警告后,页面安全加载。

    Browser window showing the app's Index page loaded

代理服务器和负载均衡器方案

如果应用由 HTTP.sys 托管并且与来自 Internet 或公司网络的请求进行交互,当在代理服务器和负载均衡器后托管时,可能需要其他配置。 有关详细信息,请参阅配置 ASP.NET Core 以使用代理服务器和负载均衡器

用于支持 gRPC 的高级 HTTP/2 功能

HTTP.sys 中的其他 HTTP/2 功能支持 gRPC,包括对响应尾部和发送重置帧的支持。

使用 HTTP.sys 运行 gRPC 的要求:

  • Windows 11 内部版本 22000 或更高版本、Windows Server 2022 内部版本 20348 或更高版本。
  • TLS 1.2 或更高版本的连接。

预告片

HTTP 尾部类似于 HTTP 标头,只不过它是在发送响应正文后发送的。 IIS 和 HTTP.sys 仅支持 HTTP/2 响应尾部。

if (httpContext.Response.SupportsTrailers())
{
    httpContext.Response.DeclareTrailer("trailername");	

    // Write body
    httpContext.Response.WriteAsync("Hello world");

    httpContext.Response.AppendTrailer("trailername", "TrailerValue");
}

在前面的示例代码中:

  • SupportsTrailers 确保响应支持尾部。
  • DeclareTrailer 将给定的尾部名称添加到 Trailer 响应头。 虽然并不是必须要声明响应尾部,但是建议这样做。 如果要调用 DeclareTrailer,则必须在发送响应标头之前进行此操作。
  • AppendTrailer 追加尾部。

重置

通过“Reset”,服务器可以使用指定的错误代码重置 HTTP/2 请求。 重置请求被视为中止。

var resetFeature = httpContext.Features.Get<IHttpResetFeature>();
resetFeature.Reset(errorCode: 2);

前述代码示例中的 Reset 指定 INTERNAL_ERROR 错误代码。 有关 HTTP/2 错误代码的详细信息,请访问“HTTP/2 规范错误代码”部分

其他资源

HTTP.sys 是仅在 Windows 上运行的适用于 ASP.NET Core 的 Web 服务器。 HTTP.sys 是 Kestrel 服务器的替代选择,提供了一些 Kestrel 不提供的功能。

重要

HTTP.sys 与 ASP.NET Core 模块不兼容,无法与 IIS 或 IIS Express 结合使用。

HTTP.sys 支持以下功能:

  • Windows 身份验证
  • 端口共享
  • 具有 SNI 的 HTTPS
  • 基于 TLS 的 HTTP/2(Windows 10 或更高版本)
  • 直接文件传输
  • 响应缓存
  • WebSocket(Windows 8 或更高版本)

受支持的 Windows 版本:

  • Windows 7 或更高版本
  • Windows Server 2008 R2 或更高版本

查看或下载示例代码如何下载

何时使用 HTTP.sys

HTTP.sys 对于以下情形的部署来说很有用:

  • 需要将服务器直接公开到 Internet 而不使用 IIS 的部署。

    HTTP.sys communicates directly with the Internet

  • 内部部署需要 Kestrel 中没有的功能。 有关详细信息,请参阅本文档中的 Kestrel 与HTTP.sys

    HTTP.sys communicates directly with the internal network

HTTP.sys 是一项成熟的技术,可以抵御多种攻击,并提供可靠、安全、可伸缩的全功能 Web 服务器。 IIS 本身作为 HTTP.sys 之上的 HTTP 侦听器运行。

HTTP/2 支持

如果满足以下基本要求,将为 ASP.NET Core 应用启用 HTTP/2

如果已建立 HTTP/2 连接,HttpRequest.Protocol 会报告 HTTP/2

默认情况下将启用 HTTP/2。 如果未建立 HTTP/2 连接,连接会回退到 HTTP/1.1。 在 Windows 的未来版本中,将提供 HTTP/2 配置标志,包括使用 HTTP.sys 禁用 HTTP/2 的功能。

对 Kerberos 进行内核模式身份验证

HTTP.sys 通过 Kerberos 身份验证协议委托给内核模式身份验证。 Kerberos 和 HTTP.sys 不支持用户模式身份验证。 必须使用计算机帐户来解密从 Active Directory 获取的并由客户端转发到服务器的 Kerberos 令牌/票证,以便对用户进行身份验证。 注册主机的服务主体名称 (SPN),而不是应用的用户。

如何使用 HTTP.sys

配置 ASP.NET Core 应用以使用 HTTP.sys

生成主机时调用 UseHttpSys 扩展方法,指定所有必需的 HttpSysOptions。 以下示例将选项设置为其默认值:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseHttpSys(options =>
            {
                options.AllowSynchronousIO = false;
                options.Authentication.Schemes = AuthenticationSchemes.None;
                options.Authentication.AllowAnonymous = true;
                options.MaxConnections = null;
                options.MaxRequestBodySize = 30000000;
                options.UrlPrefixes.Add("http://localhost:5005");
            });
            webBuilder.UseStartup<Startup>();
        });

通过注册表设置处理其他 HTTP.sys 配置。

HTTP.sys 选项

Property 说明 默认
AllowSynchronousIO 控制是否允许 HttpContext.Request.BodyHttpContext.Response.Body 的同步输入/输出。 false
Authentication.AllowAnonymous 允许匿名请求。 true
Authentication.Schemes 指定允许的身份验证方案。 可能在处理侦听器之前随时修改。 通过 AuthenticationSchemes 枚举BasicKerberosNegotiateNoneNTLM 提供值。 None
EnableResponseCaching 尝试内核模式缓存,响应合格的标头。 该响应可能不包括 Set-CookieVaryPragma 标头。 它必须包括属性为 publicCache-Control 标头和 shared-max-agemax-age 值,或 Expires 标头。 true
Http503Verbosity 由于限制条件而拒绝请求时的 HTTP.sys 行为。 Http503VerbosityLevel。
基本
MaxAccepts 最大并发接受数量。 5 × Environment。
ProcessorCount
MaxConnections 要接受的最大并发连接数。 使用 -1 实现无限。 通过 null 使用注册表的计算机范围内的设置。 null
(计算机范围内的
设置)
MaxRequestBodySize 请参阅 MaxRequestBodySize 部分。 30000000 个字节
(~28.6 MB)
RequestQueueLimit 队列中允许的最大请求数。 1000
RequestQueueMode 这指示服务器是否负责创建和配置请求队列,或是否应附加到现有队列。
附加到现有队列时,大多数现有配置选项不适用。
RequestQueueMode.Create
RequestQueueName HTTP.sys 请求队列的名称。 null(匿名队列)
ThrowWriteExceptions 指示由于客户端断开连接而失败的响应主体写入应引发异常还是正常完成。 false
(正常完成)
Timeouts 公开 HTTP.sys TimeoutManager 配置,也可以在注册表中进行配置。 请访问 API 链接详细了解每个设置,包括默认值:
UrlPrefixes 指定要向 HTTP.sys 注册的 UrlPrefixCollection。 最有用的是 UrlPrefixCollection.Add,它用于将前缀添加到集合中。 可能在处理侦听器之前随时对这些设置进行修改。

MaxRequestBodySize

允许的请求正文的最大大小(以字节计)。 当设置为 null 时,最大请求正文大小不受限制。 此限制不会影响升级后的连接,这始终不受限制。

在 ASP.NET Core MVC 应用中为单个 IActionResult 替代限制的推荐方法是在操作方法上使用 RequestSizeLimitAttribute 属性:

[RequestSizeLimit(100000000)]
public IActionResult MyActionMethod()

如果在应用开始读取请求后尝试配置请求限制,则会引发异常。 IsReadOnly 属性可用于指示 MaxRequestBodySize 属性是否处于只读状态。只读状态意味着已经太迟了,无法配置限制。

如果应用应替代每个请求的 MaxRequestBodySize,请使用 IHttpMaxRequestBodySizeFeature

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, 
    ILogger<Startup> logger, IServer server)
{
    app.Use(async (context, next) =>
    {
        context.Features.Get<IHttpMaxRequestBodySizeFeature>()
            .MaxRequestBodySize = 10 * 1024;

        var serverAddressesFeature = 
            app.ServerFeatures.Get<IServerAddressesFeature>();
        var addresses = string.Join(", ", serverAddressesFeature?.Addresses);

        logger.LogInformation("Addresses: {Addresses}", addresses);

        await next.Invoke();
    });

    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
    }

    app.UseStaticFiles();
    app.UseRouting();

    app.UseEndpoints(endpoints =>
    {
        endpoints.MapRazorPages();
    });
}

如果使用的是 Visual Studio,请确保应用未经配置以运行 IIS 或 IIS Express。

在 Visual Studio 中,默认启动配置文件是针对 IIS Express 的。 若要作为控制台应用运行该项目,请手动更改所选配置文件,如以下屏幕截图中所示:

Select console app profile

配置 Windows Server

  1. 确定要为应用打开的端口,并使用 Windows 防火墙New-NetFirewallRule PowerShell cmdlet 打开防火墙端口,以允许流量到达 HTTP.sys。 在以下命令和应用配置中,使用的是端口 443。

  2. 在部署到 Azure VM 时,在网络安全组中打开端口。 在以下命令和应用配置中,使用的是端口 443。

  3. 如果需要,获取并安装 X.509 证书。

    在 Windows 上,可使用 New-SelfSignedCertificate PowerShell cmdlet 创建自签名证书。 有关不支持的示例,请参阅 UpdateIISExpressSSLForChrome.ps1

    在服务器的“本地计算机”>“个人”存储中,安装自签名证书或 CA 签名证书

  4. 如果应用为框架相关部署,则安装 .NET Core、.NET Framework 或两者(如果应用是面向 .NET Framework 的 .NET Core 应用)。

    • .NET Core:如果应用需要 .NET Core,请从 .NET Core 下载页获取并运行 .NET Core 运行时安装程序。 请勿在服务器上安装完整 SDK。
    • .NET Framework:如果应用需要 .NET Framework,请参阅 .NET Framework 安装指南。 安装所需的 .NET Framework。 可以从 .NET Core 下载页获取最新 .NET Framework 的安装程序。

    如果应用是独立式部署,应用在部署中包含运行时。 无需在服务器上安装任何框架。

  5. 在应用中配置 URL 和端口。

    默认情况下,ASP.NET Core 绑定到 http://localhost:5000。 若要配置 URL 前缀和端口,可采用以下方法:

    下面的代码示例展示了如何对端口 443 结合使用 UrlPrefixes 和服务器的本地 IP 地址 10.0.0.4

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseHttpSys(options =>
                {
                    options.UrlPrefixes.Add("https://10.0.0.4:443");
                });
                webBuilder.UseStartup<Startup>();
            });
    

    UrlPrefixes 的一个优点是会为格式不正确的前缀立即生成一条错误消息。

    UrlPrefixes 中的设置替代 UseUrls/urls/ASPNETCORE_URLS 设置。 因此,UseUrlsurlsASPNETCORE_URLS 环境变量的一个优点是在 Kestrel 和 HTTP.sys 之间切换变得更加容易。

    HTTP.sys 使用 HTTP 服务器 API UrlPrefix 字符串格式

    警告

    不应使用顶级通配符绑定(http://*:80/http://+:80)。 顶级通配符绑定会带来应用安全漏洞。 此行为同时适用于强通配符和弱通配符。 请使用显式主机名或 IP 地址,而不是通配符。 如果可控制整个父域(相对于易受攻击的 *.com),子域通配符绑定(例如,*.mysub.com)不会构成安全风险。 有关详细信息,请参阅 RFC 9110:第 7.2 节:托管和授权

  6. 在服务器上预注册 URL 前缀。

    用于配置 HTTP.sys 的内置工具为 netsh.exenetsh.exe 用于保留 URL 前缀并分配 X.509 证书。 此工具需要管理员特权。

    使用 netsh.exe 工具为应用注册 URL:

    netsh http add urlacl url=<URL> user=<USER>
    
    • <URL>:完全限定的统一资源定位器 (URL)。 不要使用通配符绑定。 请使用有效主机名或本地 IP 地址。 URL 必须包含尾部反斜杠。
    • <USER>:指定用户名或用户组名称。

    在以下示例中,服务器的本地 IP 地址是 10.0.0.4

    netsh http add urlacl url=https://10.0.0.4:443/ user=Users
    

    在 URL 注册后,工具响应返回 URL reservation successfully added

    若要删除已注册的 URL,请使用 delete urlacl 命令:

    netsh http delete urlacl url=<URL>
    
  7. 在服务器上注册 X.509 证书。

    使用 netsh.exe 工具为应用注册证书:

    netsh http add sslcert ipport=<IP>:<PORT> certhash=<THUMBPRINT> appid="{<GUID>}"
    
    • <IP>:指定绑定的本地 IP 地址。 不要使用通配符绑定。 请使用有效 IP 地址。
    • <PORT>:指定绑定的端口。
    • <THUMBPRINT>:X.509 证书指纹。
    • <GUID>:开发人员生成的表示应用的 GUID,以供参考。

    为了便于参考,将 GUID 作为包标记存储在应用中:

    • 在 Visual Studio 中:
      • 在“解决方案资源管理器”中,右键单击应用,并选择“属性”,以打开应用的项目属性。
      • 选择“包”选项卡。
      • 在“标记”字段中输入已创建的 GUID。
    • 如果使用的不是 Visual Studio:
      • 打开应用的项目文件。

      • 使用已创建的 GUID,将 <PackageTags> 属性添加到新的或现有的 <PropertyGroup>

        <PropertyGroup>
          <PackageTags>9412ee86-c21b-4eb8-bd89-f650fbf44931</PackageTags>
        </PropertyGroup>
        

    如下示例中:

    • 服务器的本地 IP 地址是 10.0.0.4
    • 联机随机 GUID 生成器提供 appid 值。
    netsh http add sslcert 
        ipport=10.0.0.4:443 
        certhash=b66ee04419d4ee37464ab8785ff02449980eae10 
        appid="{9412ee86-c21b-4eb8-bd89-f650fbf44931}"
    

    在证书注册后,工具响应返回 SSL Certificate successfully added

    若要删除证书注册,请使用 delete sslcert 命令:

    netsh http delete sslcert ipport=<IP>:<PORT>
    

    netsh.exe 的参考文档:

  8. 运行应用。

    结合使用 HTTP(而不是 HTTPS)和大于 1024 的端口号绑定到 localhost,无需管理员权限,即可运行应用。 对于其他配置(例如,使用本地 IP 地址或绑定到端口 443),必须有管理员权限才能运行应用。

    应用在服务器的公共 IP 地址处响应。 此示例在 Internet 上的公共 IP 地址 104.214.79.47 处访问服务器。

    此示例使用的是开发证书。 在绕过浏览器的不受信任证书警告后,页面安全加载。

    Browser window showing the app's Index page loaded

代理服务器和负载均衡器方案

如果应用由 HTTP.sys 托管并且与来自 Internet 或公司网络的请求进行交互,当在代理服务器和负载均衡器后托管时,可能需要其他配置。 有关详细信息,请参阅配置 ASP.NET Core 以使用代理服务器和负载均衡器

用于支持 gRPC 的高级 HTTP/2 功能

HTTP.sys 中的其他 HTTP/2 功能支持 gRPC,包括对响应尾部和发送重置帧的支持。

使用 HTTP.sys 运行 gRPC 的要求:

  • Windows 10,OS 内部版本 19041.508 或更高版本
  • TLS 1.2 或更高版本的连接

预告片

HTTP 尾部类似于 HTTP 标头,只不过它是在发送响应正文后发送的。 IIS 和 HTTP.sys 仅支持 HTTP/2 响应尾部。

if (httpContext.Response.SupportsTrailers())
{
    httpContext.Response.DeclareTrailer("trailername");	

    // Write body
    httpContext.Response.WriteAsync("Hello world");

    httpContext.Response.AppendTrailer("trailername", "TrailerValue");
}

在前面的示例代码中:

  • SupportsTrailers 确保响应支持尾部。
  • DeclareTrailer 将给定的尾部名称添加到 Trailer 响应头。 虽然并不是必须要声明响应尾部,但是建议这样做。 如果要调用 DeclareTrailer,则必须在发送响应标头之前进行此操作。
  • AppendTrailer 追加尾部。

重置

通过“Reset”,服务器可以使用指定的错误代码重置 HTTP/2 请求。 重置请求被视为中止。

var resetFeature = httpContext.Features.Get<IHttpResetFeature>();
resetFeature.Reset(errorCode: 2);

前述代码示例中的 Reset 指定 INTERNAL_ERROR 错误代码。 有关 HTTP/2 错误代码的详细信息,请访问“HTTP/2 规范错误代码”部分

其他资源