1

My logic calls two consecutive POST requests from RestTemplate in SpringBoot. The first call always succeeds but the second call always fails with:

I/O error on POST request for "https://httpbin.org/post": httpbin.org:443 failed to respond

Everything works fine, if I either

  • disable keep-alive by setting Connection=close
  • put a sleep between the request calls
  • remove the proxy

I've tested with spring-boot 3.3.5 and apache-httpclient 5.4.1 but the same issue occurs with spring-boot 2.7.8 and apache-httpclient 4.5.14 I'm using jdk17 and burp as proxy. Finally I've stripped down the code only using httpclient.

public class RestTemplateDemo {

  private static Logger logger = Logger.getLogger(RestTemplateDemo.class.getName());

  public static void main(String[] args) {
    System.setProperty("javax.net.debug", "all");
    HttpHost proxy = new HttpHost("localhost", 8888);
    CloseableHttpClient client = HttpClients.custom()
        .setProxy(proxy)
        .build();

    logger.info("1. call");
    callPost(client);
    logger.info("2. call");
    callPost(client);
  }

  static void callPost(CloseableHttpClient client) {
    String url = "https://httpbin.org/post";
    HttpPost post = new HttpPost(url);
    post.setEntity(new StringEntity("test", Charset.defaultCharset()));
    try (CloseableHttpResponse response = client.execute(post)) {
      logger.info(response.getCode() + " " + response.getReasonPhrase());
    } catch (IOException e) {
      logger.log(Level.SEVERE, e.getMessage(), e);
    }
  }

Here's the log and stack trace:

...
INFO: 2. call
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.657 CET|SSLSocketOutputRecord.java:334|WRITE: TLSv1.3 application_data, length = 222
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.657 CET|SSLCipher.java:2066|Plaintext before ENCRYPTION (
...
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.657 CET|SSLSocketOutputRecord.java:349|Raw write (
...
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.657 CET|SSLSocketInputRecord.java:494|Raw read (
...
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.657 CET|SSLSocketInputRecord.java:214|READ: TLSv1.2 application_data, length = 35
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.657 CET|SSLSocketInputRecord.java:494|Raw read (
...
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.657 CET|SSLSocketInputRecord.java:247|READ: TLSv1.2 application_data, length = 35
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.657 CET|SSLCipher.java:1961|Plaintext after DECRYPTION (
...
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|Alert.java:238|Received alert message (
"Alert": {
  "level"      : "warning",
  "description": "user_canceled"
}
)
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|SSLSocketInputRecord.java:494|Raw read (
...
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|SSLSocketInputRecord.java:214|READ: TLSv1.2 application_data, length = 35
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|SSLSocketInputRecord.java:494|Raw read (
...
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|SSLSocketInputRecord.java:247|READ: TLSv1.2 application_data, length = 35
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|SSLCipher.java:1961|Plaintext after DECRYPTION (
...
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|Alert.java:238|Received alert message (
"Alert": {
  "level"      : "warning",
  "description": "close_notify"
}
)
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|SSLSocketOutputRecord.java:71|WRITE: TLSv1.3 alert(close_notify), length = 2
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|SSLCipher.java:2066|Plaintext before ENCRYPTION (
..
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.658 CET|SSLSocketOutputRecord.java:85|Raw write (
...
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.659 CET|SSLSocketImpl.java:577|duplex close of SSLSocket
javax.net.ssl|DEBUG|10|main|2024-11-20 14:09:17.659 CET|SSLSocketImpl.java:1785|close the SSL connection (passive)
Nov 20, 2024 2:09:17 PM demo.RestTemplateDemo callPost
SEVERE: httpbin.org:443 failed to respond
org.apache.hc.core5.http.NoHttpResponseException: httpbin.org:443 failed to respond
    at org.apache.hc.core5.http.impl.io.DefaultBHttpClientConnection.receiveResponseHeader(DefaultBHttpClientConnection.java:333)
    at org.apache.hc.core5.http.impl.io.HttpRequestExecutor.execute(HttpRequestExecutor.java:193)
    at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.lambda$execute$0(InternalExecRuntime.java:236)
    at org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager$InternalConnectionEndpoint.execute(PoolingHttpClientConnectionManager.java:791)
    at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.execute(InternalExecRuntime.java:233)
    at org.apache.hc.client5.http.impl.classic.MainClientExec.execute(MainClientExec.java:121)
    at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
    at org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:199)
    at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
    at org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:192)
    at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
    at org.apache.hc.client5.http.impl.classic.ContentCompressionExec.execute(ContentCompressionExec.java:150)
    at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
    at org.apache.hc.client5.http.impl.classic.HttpRequestRetryExec.execute(HttpRequestRetryExec.java:113)
    at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
    at org.apache.hc.client5.http.impl.classic.RedirectExec.execute(RedirectExec.java:110)
    at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
    at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:174)
    at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:123)
    at demo.RestTemplateDemo.callPost(RestTemplateDemo.java:36)
    at demo.RestTemplateDemo.main(RestTemplateDemo.java:29)


Process finished with exit code 0

https://github.com/publeorg/spring-boot-rest-demo/tree/main

PS I know, that RestTemplate is deprected, but my problem occurs with legacy code. This is only a stripped down reproducable sample. In real live I also call two POST requests at different endpoints.

9
  • Would it be possible to provide stacktrace of the error? or enable debug level at java network and see what exactly proxy is responding back for 2nd call? Commented Nov 20, 2024 at 2:00
  • @Imran activated java network logging and added stacktrace Commented Nov 20, 2024 at 12:07
  • I've stripped down the code to use only httpclient. I've left the spring and RestTemplate parts in the description, because in my real live code I'm using Spring + RestTemplate and I think mentioning this might be helpfull for other users. Commented Nov 20, 2024 at 13:15
  • I was trying to replicate same behavior on my side. I tried with docker mitmproxy proxy and did not get the error. are you using burp in your real applications?. it seems to be something with proxy causing this behavior for you. Let me know if you want to provide the steps I did. Commented Nov 21, 2024 at 3:14
  • @Imran Thanks for trying to reproduce my error. I also can't reproduce with mitmproxy. Of course I'm not using burp in production :-) It's only, that I could locallly reproduce the error (establishing connection in 2nd request) which I'm getting in production. Production means a complex infracstructure with kubernetes cluster and firewalls. Commented Nov 21, 2024 at 13:54

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.