Apache HttpClient 4.5.13 java.net.SocketException:管道损坏(写入失败)
Apache HttpClient 4.5.13 java.net.SocketException: Broken pipe (Write failed)
我有一个 Java 8 Http 客户端应用程序,它与服务器通信,在某些情况下会拒绝请求(例如无效或丢失 Authorization
header,负载大于 1MB ).当请求被拒绝时,我在使用 cURL 时看到预期的 HTTP 响应代码(例如 401 未授权、403 禁止、413 有效负载太大)。但是,在使用 Apache HttpClient 时,我得到了 java.net.SocketException: Broken pipe (Write failed)
。我相信当客户端想要写请求 body 但服务器已经关闭连接而没有完全读取请求时会发生这种情况 body.
理想情况下,我希望得到服务器的响应,以便我可以处理“真正的”错误(例如,通知用户他们的请求太大,或者让他们知道他们的 session 不再有效).如果没有来自服务器的响应,我将不得不假设这是一个网络错误并重试。有没有办法优雅地处理 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('[=12=]', '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)
我遇到了类似的问题,我修复了它,包括对 httpClient 的以下配置
RequestConfig requestConfig = RequestConfig.custom()
.setExpectContinueEnabled(true).build();
HttpClientBuilder clientBuilder = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig);
CloseableHttpClient httpClient = clientBuilder.build();
在接下来的 link 中,您可以找到相关说明 http://httpcomponents.10934.n7.nabble.com/Broken-pipe-Write-failed-when-making-Unauthorized-request-td34235.html。
编辑:上面的 link 被破坏了,所以这里有一个类似的 link 可以工作:https://lists.apache.org/thread/p38b3lrjt4vhyqthwhqcq3vsx3dpr3wj
这是乔要求的摘要:
场景是服务器拒绝基于 headers 的请求并关闭连接,因此客户端收到管道损坏错误。 expect-continued header 告诉客户端查找 100 状态,指示服务器已接受 header 并且客户端应发送其余数据。请注意,并非所有服务器似乎都能正确执行此操作,并且在发送 100 状态后仍然可以关闭连接。
关于ExpectContinueEnabled的说明如下
这个摘要 class 是所有支持 'Expect: 100-continue' 握手的 HTTP 方法的基础。
100(继续)状态(有关详细信息,请参阅 RFC 2616 的第 10.1.1 节)的目的是允许发送带有请求 body 的请求消息的客户端确定如果源服务器愿意在客户端发送请求 body 之前接受请求(基于请求 headers)。在某些情况下,如果服务器在不查看 body.
的情况下拒绝消息,则客户端发送 body 可能是不合适的或效率极低的
'Expect: 100-continue' 握手应谨慎使用,因为它可能会导致不支持 HTTP/1.1 协议的 HTTP 服务器和代理出现问题。
我有一个 Java 8 Http 客户端应用程序,它与服务器通信,在某些情况下会拒绝请求(例如无效或丢失 Authorization
header,负载大于 1MB ).当请求被拒绝时,我在使用 cURL 时看到预期的 HTTP 响应代码(例如 401 未授权、403 禁止、413 有效负载太大)。但是,在使用 Apache HttpClient 时,我得到了 java.net.SocketException: Broken pipe (Write failed)
。我相信当客户端想要写请求 body 但服务器已经关闭连接而没有完全读取请求时会发生这种情况 body.
理想情况下,我希望得到服务器的响应,以便我可以处理“真正的”错误(例如,通知用户他们的请求太大,或者让他们知道他们的 session 不再有效).如果没有来自服务器的响应,我将不得不假设这是一个网络错误并重试。有没有办法优雅地处理 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('[=12=]', '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)
我遇到了类似的问题,我修复了它,包括对 httpClient 的以下配置
RequestConfig requestConfig = RequestConfig.custom()
.setExpectContinueEnabled(true).build();
HttpClientBuilder clientBuilder = HttpClientBuilder.create()
.setDefaultRequestConfig(requestConfig);
CloseableHttpClient httpClient = clientBuilder.build();
在接下来的 link 中,您可以找到相关说明 http://httpcomponents.10934.n7.nabble.com/Broken-pipe-Write-failed-when-making-Unauthorized-request-td34235.html。
编辑:上面的 link 被破坏了,所以这里有一个类似的 link 可以工作:https://lists.apache.org/thread/p38b3lrjt4vhyqthwhqcq3vsx3dpr3wj
这是乔要求的摘要: 场景是服务器拒绝基于 headers 的请求并关闭连接,因此客户端收到管道损坏错误。 expect-continued header 告诉客户端查找 100 状态,指示服务器已接受 header 并且客户端应发送其余数据。请注意,并非所有服务器似乎都能正确执行此操作,并且在发送 100 状态后仍然可以关闭连接。
关于ExpectContinueEnabled的说明如下
这个摘要 class 是所有支持 'Expect: 100-continue' 握手的 HTTP 方法的基础。
100(继续)状态(有关详细信息,请参阅 RFC 2616 的第 10.1.1 节)的目的是允许发送带有请求 body 的请求消息的客户端确定如果源服务器愿意在客户端发送请求 body 之前接受请求(基于请求 headers)。在某些情况下,如果服务器在不查看 body.
的情况下拒绝消息,则客户端发送 body 可能是不合适的或效率极低的'Expect: 100-continue' 握手应谨慎使用,因为它可能会导致不支持 HTTP/1.1 协议的 HTTP 服务器和代理出现问题。