.NET 10 HttpClient 在 HandleAltSvc 空引用,先看空响应头
HTTPS 响应带空 Alt-Svc 时,.NET 10 的处理路径可能抛 NullReferenceException。修正响应头或提前关闭 HTTP/3,核对影响与版本。
.NET 10 的 HttpClient 请求一个 HTTPS 地址时,可能直接抛出 NullReferenceException,堆栈落在 HttpConnectionPool.HandleAltSvc。请求明明使用 HTTP/1.1,也没有开始 HTTP/3 连接,仍然会失败。
一个具体触发条件是响应带着空的 Alt-Svc,或只有逗号等分隔符:
Alt-Svc:
Alt-Svc: ,
这里的字段是服务器返回的响应头,不是客户端请求头。给 HttpClient 的请求加一个不同的 Alt-Svc,不能修正已经收到的空响应值。
先保留第一跳响应头
在 .NET 10 的这条处理路径中,Alt-Svc parser 对空的多值字段可能返回“解析成功”,同时给出 null。调用代码只做 Debug.Assert,随后把结果当作非空值访问;Release 构建不会因为那个断言而自动跳过 null。
因此异常可能从 SendAsync 直接冒出,而不是包装成普通的 HttpRequestException。状态码也不是必要条件:空头既可能出现在 302 上,也可能出现在 200 上。自动重定向时,应保留发生异常的那一跳,不能只检查最终目标服务器的正常头部。
.NET 7 起默认启用 HTTP/3 支持。启用这个能力后,HTTPS 响应中的 Alt-Svc 会被检查,不要求当前请求已经采用 HTTP/3。把请求版本固定成 HTTP/1.1,未必能关闭这条响应头处理。
能修改服务端时,修正响应头
不需要通告替代服务的服务器可以不发送 Alt-Svc。需要明确清除此前通告时,按协议使用 clear;需要通告 HTTP/3 时,生成有效的协议与 authority 值。不要让配置中的空变量渲染成一个空头,再期待所有客户端都忽略它。
改动可能来自应用、反向代理或 CDN。核对客户端实际收到的响应,不只检查某一层配置文件。若服务器并不归自己管理,则需要客户端侧的临时处理。
临时关闭 HTTP/3 处理,需要在初始化之前
应用不依赖 HTTP/3 时,可在最早的启动阶段、创建第一个 HttpClientHandler 或 SocketsHttpHandler 前设置:
AppContext.SetSwitch("System.Net.SocketsHttpHandler.Http3Support", false);
这个开关会影响该进程的 HTTP/3 支持,不是只修改出错地址。依赖 HTTP/3 的调用也会受到影响,应检查其是否能使用 HTTP/2 或 HTTP/1.1;它不会替客户端补上服务端根本没有提供的协议。
也可以合入现有项目文件的配置项,重新构建并重启进程:
<ItemGroup>
<RuntimeHostConfigurationOption
Include="System.Net.SocketsHttpHandler.Http3Support"
Value="false" />
</ItemGroup>
两种配置选一种即可,不要等第一个请求失败后才在异常处理中切换。内部设置可能已经被读取缓存。处理完服务端响应头或确认部署了包含正式修复的运行时后,按原需求移除临时开关,再重新验证。
已确认的故障包括 .NET 10.0.3,10.0.12 的相应正式源码也仍有这条非空假设;不能仅凭“小版本更新过”认定已修复。验证时同时覆盖空头、只有分隔符、不发送 Alt-Svc、有效通告及 clear,并检查真正的业务响应。
不要通过关闭证书验证来处理这个异常:TLS 握手成功后才会进入这里的响应头处理。若堆栈没有 HandleAltSvc 或响应没有这类头部,应重新定位自己的空引用,不把所有 HttpClient 的 NullReferenceException 都套进本题。


