【问题标题】:Use java http connection instance after exception异常后使用java http连接实例
【发布时间】:2014-02-12 17:47:51
【问题描述】:

以下代码安全吗:

try {
    URL url = new URL(urlRequest);
    conn = (HttpURLConnection)url.openConnection();
    conn.setConnectTimeout(30000);
    conn.setReadTimeout(30000);
    conn.setRequestProperty("Accept-Encoding", "gzip, deflate");
    String encoding = conn.getContentEncoding();
    return Utils.wrapCompressedStream(conn.getInputStream(), encoding);
} catch (IOException e) {
    if(conn != null) {
        conn.getContentEncoding();
        conn.getErrorStream();
        conn.whateverOtherMethodThere();
        ...
    }
}

特别是,在 InterruptedIOException(例如,读取超时)的情况下调用 getContentEncoding() 之类的方法是否安全?据我了解,此方法需要实时连接才能读取 HTTP(S) 标头。

更新(附加信息):

这个问题源于真实系统的体验。我相信,该系统当时是在 Oracle/Sun JVM 1.6 上运行的。代码几乎一样:

...    
} catch (IOException e) {
    if(conn != null) {
         try {
             String response = tryGetResponse(conn);
...

问题发生在 HTTPS 请求上的tryGetResponse

 private static String tryGetResponse(HttpURLConnection conn) {
    if(conn == null) return "(failed to get)";
    InputStream in = null;
    try {
        InputStream err = conn.getErrorStream();
        if (err != null) {
            in = Utils.wrapCompressedStream(err, conn.getContentEncoding());
        }
        return Utils.inputStreamToString(in);
    } catch (IOException e) {
        return "(failed to get)";
    } finally {
        Utils.closeQuitely(in);
    }
}

getContentEncoding() 调用中,系统自发挂起连接(或读取)套接字:

in = Utils.wrapCompressedStream(err, conn.getContentEncoding());

恰好在初始代码中抛出SocketTimeoutException 之后。

因此,getContentEncoding() 似乎尝试(或在 Java 6 中尝试)建立新连接而没有设置超时。

【问题讨论】:

  • 不,不是,如果你尝试从不完整的输入流中获取内容编码之类的标头字段,你会得到另一个 IOException,检查 HttpURLConnection 源代码。

标签: java httpurlconnection urlconnection httpsurlconnection


【解决方案1】:

没有。一般情况下是不安全的。 JVM 实现之间的行为可能有所不同(想想 IBM J9 与 Oracle VM 与 Open JDK),并且在同一 VM 的版本之间发生更改,恕不另行通知。

原因是 API 规范不做任何保证。

如果您告诉我您正在使用哪个特定版本中的哪个具体实现,我可以查看源代码并尝试得出一些结论。但我强烈建议不要依赖您在那里发现的行为。

关于 HTTPS:SSL 似乎仍然存在一些错误。例如 OpenSSL 提出了一个public announcement,他们将在本周结束时揭示一个安全漏洞。在某些错误情况下,针对该问题的错误修复可能会改变 HTTPS 连接的行为。我们在消息来源中发现的任何东西都可能在本周末变得毫无意义。如果没有,它可能会随着下一次安全修复而改变。

更新:

我已尝试查找与您在更新后的问题中引用的 Java 版本相对应的来源。找到 Java 源代码不是什么大问题,但代码会很快进入本地部分。这本身就是一个很好的提示,答案不仅是特定于版本的,而且是特定于平台的(Linux、Windows、Mac 等)。您可以查看 Java 1.6 的 OpenJDK 源代码,例如for the network stack for Windows

注意:您很可能使用过 Sun JDK 1.6。 OpenJDK 1.6 基于 Sun/Oracle JDK,但基于 JDK 1.7。 Open JDK 1.6 是从 Sun/Oracle JDK 1.7 向后移植到 Java 1.6 的代码。因此,仍然可能存在一些小的差异,但对于发生错误后的连接使用情况而言,这可能是重要的。

【讨论】:

  • +1 您的回答非常有价值,因为有时开发人员可能会在 JVM 实现上工作而程序在不同的实现上运行。另一方面,EJP 的答案是实际的解决方案:您必须通过简单地尝试和捕获来处理同一代码中的任何 JVM 实现。
  • @stefan.schwetschke 我已更新问题以包含更多详细信息。
【解决方案2】:

尤其是在InterruptedIOException(比如读取超时)的情况下调用getContentEncoding()?之类的方法是否安全

你可以试试。最坏的情况是另一个IOException.IOException, 的几种情况下,例如FileNotFoundException,,调用getErrorStream() 并阅读错误页面的内容是完全安全的。

【讨论】:

  • 其实我试过了。系统在 https 请求上挂了很多。我已经更新了我的问题以包含更多详细信息。不过,不确定这种行为是否是 Java 错误,或者调用 getContentEncoding 通常不安全(仅在超时之后,连接肯定是死的?)。
  • 如果读取超时,(a) 内容很短,(b) 可能根本没有内容,(c) 也可能没有标题。我不明白你为什么认为这里有错误。
  • 连接超时怎么办?当您尝试读取错误流时,它会挂在getContentEncoding
  • 我必须查看它的堆栈跟踪。
【解决方案3】:

对于getErrorStream spec 说:

如果连接失败但服务器仍然发送了有用的数据,则返回错误流。典型的例子是当 HTTP 服务器响应 404 时,这将导致在连接中抛出 FileNotFoundException,但服务器发送了一个 HTML 帮助页面,其中包含有关如何操作的建议。 此方法不会导致启动连接。如果连接未连接,或者服务器在连接时没有错误,或者服务器有错误但没有发送错误数据,则此方法将返回 null。这是默认设置。

返回: 如果有错误流,则返回 null,如果没有错误、连接未连接或服务器未发送有用数据。

你也可以查看source来解惑,让我们看看一些方法:

public InputStream getErrorStream() {
    if (connected && responseCode >= 400) {
        // Client Error 4xx and Server Error 5xx
        if (errorStream != null) {
            return errorStream;
        } else if (inputStream != null) {
            return inputStream;
        }
    }
    return null;
}

调用它是安全的(不会抛出异常),errorStream 可以在某些IOException 情况下设置。源中的评论是buffer the error stream if bytes < 4k and it can be buffered within 1 second

getContentEncodings 规范中的行为是:

返回: URL 引用的资源的内容编码,如果未知,则为 null。

但是发生错误之后呢?让我们看看代码:

public String getContentEncoding() { //from the base class java.net.URLConnection
    return getHeaderField("content-encoding");
}

public String getHeaderField(String name) {
    try {
        getInputStream();
    } catch (IOException e) {} //ah exception is eaten

    if (cachedHeaders != null) {
        return cachedHeaders.findValue(name);
    }

    return responses.findValue(name);
}

因此,它会缓存标头并在已知的情况下返回它们,即使可能在 IOException 之后,它也不会传播异常,但如果之前没有成功获取标头,则可能返回 null。

【讨论】:

  • 规范比来源更权威。它说明了应该发生什么。源代码可能包含错误或您不应该依赖的实现细节。
  • 我的答案没有猜测。事实上,它与您在上面的回答中所说的一致。
  • @EJP 对不起,你说得对,在某些情况下可以设置 errorStream。
  • @EJP 你说得对,我从快速浏览代码中得出的两个结论都是错误的,并且与规范相矛盾!
  • @EJP 为了您评论的完整性:我绝对同意该规范是 Java 中的最高权威,所有未实现该规范的东西都可能而且必须被视为错误。但由于没有完美的文档,规范中没有涵盖的部分,在这种情况下,我们依赖源代码。
【解决方案4】:
catch (IOException e) {
    if(conn != null) {
        conn.getContentEncoding();
        conn.getErrorStream();
        conn.whateverOtherMethodThere();
        ...
    }

公共类 InterruptedIOException 扩展 IOException

表示 I/O 操作已被中断。抛出 InterruptedIOException 以指示输入或输出传输已终止,因为执行它的线程被中断。字段 bytesTransferred 表示在中断发生之前成功传输了多少字节。

这意味着如果发生中断的异常,您将无法对 HttpUrlConnection 进行进一步处理。

再次通过 IOException,您无法捕获其他类型的异常,例如 IllegalArgumentException、非法线程状态异常等等。

更多详情http://download.java.net/jdk7/archive/b123/docs/api/java/net/HttpURLConnection.html#getRequestMethod%28%29

【讨论】:

  • 我不对套接字本身进行任何处理,我使用的是 HttpUrlConnection 实例。问题是 - 使用 HttpUrlConnection 是否安全,而不是使用套接字。
  • 对不起,我有更新的答案。我错过了关键字。它将是 HttpUrlConnection。
  • 是否在某处指定?如果我不能使用套接字,并不意味着我也不能使用 UrlConnection。例如,在抛出 InterruptedException 之后,UrlConnection 可能会抛出异常或返回 null。例如。显然使用 getErrorStream 是安全的。
  • 它确实not意味着你不能继续使用连接。这意味着无论你正在做什么都被打断了。至少你必须关闭你从连接中获得的任何流,这构成使用它,或者断开它,同上。 getRequestMethod() 的 Javadoc 与它有什么关系完全是个谜。它只是局部变量的吸气剂。没有任何明示或暗示的网络操作,也没有任何与您在此处声明的内容一致的声明。关于IllegalArgumentException 等的部分完全无关紧要。 -1
猜你喜欢
  • 2011-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-08
相关资源
最近更新 更多