Tho*_*que 12 .net c# tomcat http httpwebrequest
TL; DR版本
在写入请求流时发生传输错误时,即使服务器发送了响应,我也无法访问响应.
完整版本
我有一个.NET应用程序,使用它将文件上传到Tomcat服务器HttpWebRequest.在某些情况下,服务器过早地关闭请求流(因为它因某种原因拒绝文件,例如文件无效),并发送带有自定义标头的400响应以指示错误的原因.
问题是,如果上传的文件很大,请求流在我写完请求主体之前关闭,我得到一个IOException:
消息:无法将数据写入传输连接:远程主机强制关闭现有连接.
InnerException ::SocketException远程主机强制关闭现有连接
我可以捕获这个异常,但是当我调用时GetResponse,我得到一个WebException前面IOException的内部异常和一个null Response属性.所以我永远无法得到响应,即使服务器发送它(使用WireShark检查).
由于我无法得到答复,我不知道实际问题是什么.从我的应用程序的角度来看,看起来连接被中断了,所以我将其视为与网络相关的错误并重试上传...当然,这再次失败.
如何解决此问题并从服务器检索实际响应?它甚至可能吗?对我来说,当前的行为看起来像是一个bug 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");
}
}
}
Run Code Online (Sandbox Code Playgroud)
补充说明:
如果我正确理解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
我不知道什么是客户端解决问题的方法.但我仍然认为使用自定义tomcat阀门的服务器端解决方案可以在这里提供帮助.我目前没有tomcat设置,我可以测试这个,但我认为这里的服务器端解决方案将沿着以下几行:
RFC第8.2.3节明确规定:HTTP/1.1源服务器的要求:
- Upon receiving a request which includes an Expect request-header
field with the "100-continue" expectation, an origin server MUST
either respond with 100 (Continue) status and continue to read
from the input stream, or respond with a final status code. The
origin server MUST NOT wait for the request body before sending
the 100 (Continue) response. If it responds with a final status
code, it MAY close the transport connection or it MAY continue
to read and discard the rest of the request. It MUST NOT
perform the requested method if it returns a final status code.
Run Code Online (Sandbox Code Playgroud)
因此,假设tomcat确认RFC,而在自定义阀门中,您将收到HTTP请求标头,但由于控件尚未在读取正文的servlet中,因此不会发送请求正文.
所以你可以实现一个自定义阀门,类似于:
import org.apache.catalina.connector.Request;
import org.apache.catalina.connector.Response;
import org.apache.catalina.valves.ErrorReportValve;
public class CustomUploadHandlerValve extends ValveBase {
@Override
public void invoke(Request request, Response response) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String fileName = httpRequest.getHeader("Filename"); // get the filename or whatever other parameters required as per your code
bool validationSuccess = Validate(); // perform filename check or anyother validation here
if(!validationSuccess)
{
response = CreateResponse(); //create your custom 400 response here
request.SetResponse(response);
// return the response here
}
else
{
getNext().invoke(request, response); // to pass to the next valve/ servlet in the chain
}
}
...
}
Run Code Online (Sandbox Code Playgroud)
免责声明:我再没有尝试过这个成功,需要一段时间和一个tomcat设置来试试它;).认为这可能是你的起点.
| 归档时间: |
|
| 查看次数: |
3181 次 |
| 最近记录: |