I have a Java 8 Http client application that communicates with a server that will reject requests under a few circumstances (e.g. invalid or missing Authorization header, payload larger than 1MB). When a request is rejected I see expected HTTP response codes when using cURL (e.g. 401 Unauthorized, 403 Forbidden, 413 Payload Too Large). However, when using the Apache HttpClient I am getting a java.net.SocketException: Broken pipe (Write failed). I believe this happens when the client wants to write the request body but the server has already closed the connection without fully reading the request body.
Ideally, I'd like to get the server's response so I can handle the "real" error (e.g. inform the user their request is too large, or let them know their session is no longer valid). Without the response from the server I would have to assume that this is a network error and retry. Is there any way to gracefully handle the java.net.SocketException: Broken pipe (Write failed) exception and still read the server response?
Example cURL request
$> 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
Example Code
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();
}
}
}
Full Stack Trace
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)