写入响应标头后服务器关闭连接时的 HttpClient 异常

Posted

技术标签:

【中文标题】写入响应标头后服务器关闭连接时的 HttpClient 异常【英文标题】:HttpClient exception when server closes connection after writing response headers 【发布时间】:2014-01-22 14:14:23 【问题描述】:

我正在使用HttpClient 将数据发送到使用PutAsync 的HTTP 服务器。

当服务器发回错误代码(例如 401 Unauthorized)时,它会关闭连接(在其响应中提供了 Connection: close 标头)。发生这种情况时,HttpClient 会引发 System.Net.Http.HttpRequestException(将内容复制到流时出错),并带有下面列出的内部异常。这似乎与写出数据有关(尽管它在 BeginRead 中),因为如果我说服服务器读取所有正在发送的数据,则不会引发异常。

我可以通过使用HttpClient.SendAsync 来解决此问题,并使用HttpCompletionOption.ResponseHeadersRead 参数执行相同的任务。在这种情况下,不会抛出异常,我可以读取HttpResponseMessage.StatusCode

我的问题是,这个例外是否正确?即服务器是否没有通过关闭连接而不读取它不需要的所有数据来遵循标准?如果不是,那么PutAsync 是否存在问题,在这种情况下它不允许访问返回的StatusCode

示例代码:

using (FileStream stream = File.OpenRead(FilePath))
using (StreamContent content = new StreamContent(stream))
using (HttpRequestMessage req = new HttpRequestMessage
    
        Method = HttpMethod.Put,
        RequestUri = new Uri(baseUri, Path),
        Content = content,
    )
using (HttpClient client = new HttpClient())

    req.Content.Headers.ContentType = new MediaTypeHeaderValue(FileContentType);
    // This version works:
    //using (HttpResponseMessage resp = await client.SendAsync(req,
    //    HttpCompletionOption.ResponseHeadersRead))
    // This version throws an exception:
    using (HttpResponseMessage resp = await client.PutAsync(
        new Uri(baseUri, Path), content))
    
        if (resp.StatusCode == HttpStatusCode.Unauthorized)
        
            // Try again with authorisation credentials based on resp.Headers.WwwAuthenticate
        
    

内部异常:System.IO.IOException(无法从传输连接读取数据:现有连接被远程主机强行关闭。)

at System.Net.Sockets.NetworkStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
at System.Net.PooledStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
at System.Net.ConnectStream.BeginReadWithoutValidation(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
at System.Net.ConnectStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)
at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead(Byte[] buffer, Int32 offset, Int32 count, AsyncCallback callback, Object state)
at System.Net.Http.StreamToStreamCopy.StartRead()

内部异常:System.Net.Sockets.SocketException(现有连接被远程主机强行关闭)

at System.Net.Sockets.Socket.BeginReceive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, AsyncCallback callback, Object state)
at System.Net.Sockets.NetworkStream.BeginRead(Byte[] buffer, Int32 offset, Int32 size, AsyncCallback callback, Object state)

【问题讨论】:

【参考方案1】:

如果适合您的需要,请尝试使用 HTTP 协议版本 1.0:

using (HttpRequestMessage req = new HttpRequestMessage
    
        Method = HttpMethod.Put,
        RequestUri = new Uri(baseUri, Path),
        Content = content,
        Version = HttpVersion.Version10
    )

【讨论】:

以上是关于写入响应标头后服务器关闭连接时的 HttpClient 异常的主要内容,如果未能解决你的问题,请参考以下文章

我们应该在发送响应时关闭飞行前 Cors 请求的连接吗?

AWS Nginx“从上游读取响应标头时上游过早关闭连接”

Nginx后缺少响应标头[关闭]

错误:从上游 [uWSGI/Django/NGINX] 读取响应标头时,上游过早关闭连接

响应没有“内容长度”标头时的 AVURLAsset

nginx uwsgi websockets 502 Bad Gateway上游过早关闭连接,同时从上游读取响应标头