HTTP post body spuriously丢失(HttpURLConnection和Jetty)

3

我有一个自己编写的协议,使用Java 1.6中的HttpURLConnection和Jetty 6.1.26来POST一块XML作为请求并接收一块XML作为响应。XML的大小约为5KB。

当在世界不同地方的Linux EC2实例上同时运行发送方和接收方时,我发现大约0.04%的请求中Jetty处理程序将XML请求(POST体)视为空字符串。我已经检查过,客户端输出表明它一直尝试发送正确(> 0长度)的XML请求字符串。

我还通过在本地(Win 8)循环我的JUnit测试来重现了这个问题。

我认为错误可能是以下原因之一:

  • 缓冲区的误用
  • HttpURLConnection的错误
  • 网络错误
  • Jetty的错误
  • 我在代码中做了一个随机的头脑发热的愚蠢的事情

相关代码如下:

客户端

        connection = (HttpURLConnection) (new URL (url)).openConnection();
        connection.setReadTimeout(readTimeoutMS);
        connection.setConnectTimeout(connectTimeoutMS);
        connection.setRequestMethod("POST");
        connection.setAllowUserInteraction(false);
        connection.setDoOutput(true);

        // Send request
        byte[] postBytes = requestXML.getBytes("UTF-8");
        connection.setRequestProperty("Content-length", "" + postBytes.length);
        OutputStream os = connection.getOutputStream();
        os.write(postBytes);
        os.flush();
        os.close();

        // Read response
        InputStream is = connection.getInputStream();
        StringWriter writer = new StringWriter();
        IOUtils.copy(is, writer, "UTF-8");
        is.close();
        connection.disconnect();
        return writer.toString();

服务器 (Jetty 处理程序)

    public void handle(java.lang.String target, javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, int dispatch) {
        InputStream is = request.getInputStream();
        StringWriter writer = new StringWriter();
        IOUtils.copy(is, writer, "UTF-8");
        is.close(); 
        String requestXML = writer.toString();
        // requestXML is 0 length string about 0.04% of time

有人能想到为什么我会随机收到一个空字符串的请求吗?

谢谢!

编辑

我增加了一些跟踪,当错误发生时getContentLength()返回-1,但客户端输出仍然显示发送的字节数是正确的。


getContentLength() 显示什么? - user314104
经过更多的测试运行,getContentLength()在失败的情况下为-1。将进行编辑以反映这一点。 - Jonathan
此时,我会在服务器上使用Wireshark。请告诉我们你发现了什么。 :) - user314104
我认为你是对的,虽然我现在没有时间。我会在某个时候完成并回报。 - Jonathan
2个回答

2

我想不出为什么你会得到一个空字符串。代码看起来是正确的。如果你更新你的代码来检查空字符串,并且在找到时报告请求的内容长度和传输编码,那将有助于确定罪魁祸首。Wireshark跟踪网络数据也是很好的。

但不幸的是,jetty-6已经真正的停止维护了,我们不太可能会对它进行更新。如果你今天编写代码,那么你真的应该使用jetty-7或8。如果你勇敢的话,甚至可以使用jetty-9里程碑版本。如果你在jetty-9中发现这样的错误,我会像皮疹一样全力以赴尝试修复它!


谢谢Greg,这是个好主意,我会尝试放入最新的Jetty 8 jar包并让测试程序通宵运行。 - Jonathan
很遗憾,在我的测试中,使用Jetty 8.1.7仍然偶尔出现了问题。 - Jonathan
我按照你的建议开始输出内容长度标头,但结果显示为-1。我想这意味着我应该看客户端方面了。 - Jonathan
你是否正在尝试异步扩展大量的这些连接?如果是的话,你可能想避免使用URLConnection,并使用一个通过设计处理扩展的库,比如jetty-client或netty。 - jesse mcconnell
我以后会扩展规模,但现在这是可重现的,没有并发;一次只处理一个请求。 - Jonathan
此时此刻,我想我只能接受偶尔出现的错误并使用重试。将来的某个时候我会再次查看。我授予您最详细答案的奖励,因为您的建议是良好的调试思路。 - Jonathan

1
请确保您设置了connection.setRequestProperty("Content-Type", "application/xml");。没有 Content-type 可能会导致 POST 数据被丢弃。当我在本地复制你的问题时(针对 Grails 嵌入式 Tomcat 实例),这就是情况,并提供此内容将其修复。

1
另外,您可能考虑使用类似于Apache HttpClient(现在位于http://hc.apache.org/)的东西。这样可以抽象出一些低级烦恼。 - Brian Henry
此外,关于这个主题的好资源:https://dev59.com/vHE85IYBdhLWcg3wZyqt - Brian Henry
我同意如果问题是一致的,但我不明白它如何解释发生在少数呼叫中。 - Jonathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接