繁体   English   中英

Apache HttpClient 4.5.13 java.net.SocketException: Broken pipe(写入失败)

[英]Apache HttpClient 4.5.13 java.net.SocketException: Broken pipe (Write failed)

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

理想情况下,我想得到服务器的响应,这样我就可以处理“真正的”错误(例如,通知用户他们的请求太大,或者让他们知道他们的 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('\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)

我遇到了类似的问题,我修复了它,包括对 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

关于ExpectContinueEnabled的解释如下

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

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

'Expect: 100-continue' 握手应该谨慎使用,因为它可能会导致 HTTP 服务器和不支持 HTTP/1.1 协议的代理出现问题。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM