【问题标题】:Apache HttpClient 4.5.13 java.net.SocketException: Broken pipe (Write failed)Apache HttpClient 4.5.13 java.net.SocketException:管道损坏(写入失败)
【发布时间】:2022-02-15 21:47:29
【问题描述】:

我有一个 Java 8 Http 客户端应用程序,它与服务器通信,在某些情况下会拒绝请求(例如,Authorization 标头无效或丢失,有效负载大于 1MB)。当请求被拒绝时,我会在使用 cURL 时看到预期的 HTTP 响应代码(例如 401 Unauthorized、403 Forbidden、413 Payload Too Large)。但是,当使用 Apache HttpClient 时,我得到了一个java.net.SocketException: Broken pipe (Write failed)。我相信当客户端想要写入请求正文但服务器已经关闭连接而没有完全读取请求正文时会发生这种情况。

理想情况下,我希望获得服务器的响应,以便处理“真正的”错误(例如,通知用户他们的请求太大,或者让他们知道他们的会话不再有效)。如果没有来自服务器的响应,我将不得不假设这是一个网络错误并重试。有什么方法可以优雅地处理java.net.SocketException: Broken pipe (Write failed) 异常并仍然读取服务器响应?

示例 cURL 请求

$> printf 'x%.0s' {1..1048577} | curl -i --data @- https://my.server.com/anything
HTTP/1.1 100 Continue

HTTP/1.1 413 Request Entity Too Large
Content-Type: application/json
X-Frame-Options: DENY
Content-Length: 92
Connection: Close

示例代码

import org.apache.http.HttpResponse;
import org.apache.http.client.entity.EntityBuilder;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

public class Main {

    public final static void main(String[] args) throws Exception {
        CloseableHttpClient client = HttpClients.createDefault();
        try {
            HttpPost request = new HttpPost("https://my.server.com/anything");
            String largePayload = new String(new char[1024*1024+1]).replace('\0', 'x'); // >1MB
            request.setEntity(EntityBuilder.create().setText(largePayload).build());
            HttpResponse response = client.execute(request);
            System.out.println(response.getStatusLine());
        } finally {
            client.close();
        }
    }

}

全栈跟踪

Exception in thread "main" java.net.SocketException: Broken pipe (Write failed)
    at java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:111)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:155)
    at sun.security.ssl.OutputRecord.writeBuffer(OutputRecord.java:431)
    at sun.security.ssl.OutputRecord.write(OutputRecord.java:417)
    at sun.security.ssl.SSLSocketImpl.writeRecordInternal(SSLSocketImpl.java:886)
    at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:857)
    at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123)
    at org.apache.http.impl.conn.LoggingOutputStream.write(LoggingOutputStream.java:74)
    at org.apache.http.impl.io.SessionOutputBufferImpl.streamWrite(SessionOutputBufferImpl.java:124)
    at org.apache.http.impl.io.SessionOutputBufferImpl.write(SessionOutputBufferImpl.java:160)
    at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:113)
    at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:120)
    at org.apache.http.entity.StringEntity.writeTo(StringEntity.java:167)
    at org.apache.http.impl.DefaultBHttpClientConnection.sendRequestEntity(DefaultBHttpClientConnection.java:156)
    at org.apache.http.impl.conn.CPoolProxy.sendRequestEntity(CPoolProxy.java:152)
    at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:238)
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123)
    at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:272)
    at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
    at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
    at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
    at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
    at Main.main(main.java:15)

【问题讨论】:

  • 收到异常后,您是否尝试过获取请求的响应代码?它可能会起作用。另一方面,服务器可能只是关闭连接。如果发生这种情况,则根本不会有任何响应,并且您无法在客户端采取任何措施来解决此问题。
  • @StephenC 我实际上无法得到响应,因为这是抛出的方法(例如HttpResponse response = client.execute(request)
  • 向我们展示所有相关代码。以及完整的堆栈跟踪(包括任何嵌套的堆栈跟踪)。将它们放入问题中。
  • 您应该能够从 Java 代码中获得与 curl 相同的行为。您应该没有理由不能发送完全相同的请求,从而得到相同的响应。如果您的 Java 代码没有走得那么远,那么您可能会做一些不同/错误的事情。请提供您正在执行的 curl 命令、您的完整代码(最好是 MCVE)以及您遇到的完整错误,包括完整的堆栈跟踪。通过编辑和更新将所有这些内容放入您的问题中。
  • @StephenC 我已经更新了一些示例——如果您需要任何其他信息,请告诉我。感谢您的帮助!

标签: java apache-httpclient-4.x


【解决方案1】:

我遇到了类似的问题,我修复了它,包括对 httpClient 的以下配置

RequestConfig requestConfig = RequestConfig.custom()
            .setExpectContinueEnabled(true).build();
HttpClientBuilder clientBuilder = HttpClientBuilder.create()
            .setDefaultRequestConfig(requestConfig);
CloseableHttpClient httpClient = clientBuilder.build();

在下一个链接中,您可以找到执行此操作的说明http://httpcomponents.10934.n7.nabble.com/Broken-pipe-Write-failed-when-making-Unauthorized-request-td34235.html

编辑:上面的链接已损坏,所以这里有一个类似的链接有效:https://lists.apache.org/thread/p38b3lrjt4vhyqthwhqcq3vsx3dpr3wj

这是乔要求的摘要: 场景是服务器拒绝基于标头的请求并关闭连接,因此客户端收到损坏的管道错误。 expect-continued 标头告诉客户端查找 100 状态,指示服务器已接受标头,客户端应发送其余数据。请注意,并非所有服务器似乎都能正确执行此操作,并且在发送 100 状态后仍然可以关闭连接。

【讨论】:

  • 该链接现已断开。您能否解释一下为什么这可以解决问题?
  • 感谢这条线为我工作setExpectContinueEnabled(true)
【解决方案2】:

ExpectContinueEnabled解释如下

这个抽象类是所有支持“期望:100 次继续”握手的 HTTP 方法的基础。

100(继续)状态(有关详细信息,请参阅 RFC 2616 的第 10.1.1 节)的目的是允许正在发送带有请求正文的请求消息的客户端确定源服务器是否愿意在客户端发送请求正文之前接受请求(基于请求标头)。在某些情况下,如果服务器在不查看正文的情况下拒绝消息,则客户端发送正文可能不合适或效率极低。

应谨慎使用“预期:100 次继续”握手,因为它可能会导致不支持 HTTP/1.1 协议的 HTTP 服务器和代理出现问题。

参考: https://hc.apache.org/httpclient-legacy/apidocs/org/apache/commons/httpclient/methods/ExpectContinueMethod.html

【讨论】:

    猜你喜欢
    • 2011-07-13
    • 2011-03-19
    • 1970-01-01
    • 2016-10-05
    • 2018-04-20
    • 1970-01-01
    • 2011-01-19
    相关资源
    最近更新 更多