TL;DR版本:
当向请求流写入数据时发生传输错误,即使服务器已发送响应,我也无法访问响应。
完整版本:
我有一个.NET应用程序,使用HttpWebRequest
将文件上传到Tomcat服务器。在某些情况下,服务器会提前关闭请求流(因为出于某种原因拒绝文件,例如无效的文件名),并发送一个400响应以及自定义头以指示错误原因。
问题在于,如果上传的文件很大,请求流在我完成请求正文的写入之前就被关闭了,并且我会收到一个IOException
:
Message: Unable to write data to the transport connection: An existing connection was forcibly closed by the remote host.
InnerException:SocketException
: An existing connection was forcibly closed by the remote host
尽管服务器已经发送了响应(通过WireShark检查),但我无法获取响应。因此我永远也得不到响应。虽然我可以捕获这个异常,但是当我调用GetResponse
时,我会得到一个WebException
,它的内部异常是之前的IOException
,而且Response
属性为空。因此,我不知道实际的问题是什么。从我的应用程序角度来看,它看起来像是连接被中断了,所以我将其视为与网络相关的错误并重试上传...当然,这又失败了。
如何解决这个问题并从服务器检索实际响应?这可能吗?对我来说,当前的行为看起来像是HttpWebRequest
中的一个错误或至少是一个严重的设计问题...
以下是我用于复现问题的代码:
var request = HttpWebRequest.CreateHttp(uri);
request.Method = "POST";
string filename = "foo\u00A0bar.dat"; // Invalid characters in filename, the server will refuse it
request.Headers["Content-Disposition"] = string.Format("attachment; filename*=utf-8''{0}", Uri.EscapeDataString(filename));
request.AllowWriteStreamBuffering = false;
request.ContentType = "application/octet-stream";
request.ContentLength = 100 * 1024 * 1024;
// Upload the "file" (just random data in this case)
try
{
using (var stream = request.GetRequestStream())
{
byte[] buffer = new byte[1024 * 1024];
new Random().NextBytes(buffer);
for (int i = 0; i < 100; i++)
{
stream.Write(buffer, 0, buffer.Length);
}
}
}
catch(Exception ex)
{
// here I get an IOException; InnerException is a SocketException
Console.WriteLine("Error writing to stream: {0}", ex);
}
// Now try to read the response
try
{
using (var response = (HttpWebResponse)request.GetResponse())
{
Console.WriteLine("{0} - {1}", (int)response.StatusCode, response.StatusDescription);
}
}
catch(Exception ex)
{
// here I get a WebException; InnerException is the IOException from the previous catch
Console.WriteLine("Error getting the response: {0}", ex);
var webEx = ex as WebException;
if (webEx != null)
{
Console.WriteLine(webEx.Status); // SendFailure
var response = (HttpWebResponse)webEx.Response;
if (response != null)
{
Console.WriteLine("{0} - {1}", (int)response.StatusCode, response.StatusDescription);
}
else
{
Console.WriteLine("No response");
}
}
}
附加说明:
如果我正确理解了100 Continue
状态的作用,那么如果服务器要拒绝文件,它不应该向我发送此状态。然而,似乎这个状态由Tomcat直接控制,并且无法被应用程序控制。理想情况下,我希望服务器在这种情况下不要向我发送100 Continue
,但是据我负责后端的同事说,目前没有简单的方法可以实现。因此,我现在正在寻找客户端解决方案;但是如果你碰巧知道如何在服务器端解决这个问题,那也会很感激。
遇到这个问题的应用程序针对.NET 4.0,但我也使用4.5重现了这个问题。
我没有超时。异常在超时之前就被抛出。
我尝试了异步请求。结果并没有改变什么。
我尝试将请求协议版本设置为HTTP 1.0,但结果相同。
已经有其他人在Connect上报告了这个问题:https://connect.microsoft.com/VisualStudio/feedback/details/779622/unable-to-get-servers-error-response-when-uploading-file-with-httpwebrequest