Okhttp: Distinguish between connect timeout and read timeout

Created on 3 May 2017  Â·  13Comments  Â·  Source: square/okhttp

Feature request: Throw identifiable exceptions for read timeout and connect timeout.
_We wish to retry on connect timeout errors and not on read timeouts. The okHttp client throws the same exception in both cases. The only way to distinguish between the two is the through the error message, which is not clean or reliable._

On read timeout:
java.net.SocketTimeoutException: timeout at okio.Okio$3.newTimeoutException(Okio.java:212) at okio.AsyncTimeout.exit(AsyncTimeout.java:288) at okio.AsyncTimeout$2.read(AsyncTimeout.java:242) at okio.RealBufferedSource.indexOf(RealBufferedSource.java:325) at okio.RealBufferedSource.indexOf(RealBufferedSource.java:314) at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:210) at okhttp3.internal.http.Http1xStream.readResponse(Http1xStream.java:184) at okhttp3.internal.http.Http1xStream.readResponseHeaders(Http1xStream.java:125) at okhttp3.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:775) at okhttp3.internal.http.HttpEngine.access$200(HttpEngine.java:86) at okhttp3.internal.http.HttpEngine$NetworkInterceptorChain.proceed(HttpEngine.java:760) at okhttp3.internal.http.HttpEngine.readResponse(HttpEngine.java:613) at okhttp3.RealCall.getResponse(RealCall.java:244) at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:201) at com.bluejeans.sample.test.OkHttpConnect$1.intercept(OkHttpConnect.java:25) at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:190) at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:163) at okhttp3.RealCall.execute(RealCall.java:57) at com.bluejeans.sample.test.OkHttpConnect.main(OkHttpConnect.java:39) Caused by: java.net.SocketTimeoutException: Read timed out at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read(SocketInputStream.java:170) at java.net.SocketInputStream.read(SocketInputStream.java:141) at okio.Okio$2.read(Okio.java:140) at okio.AsyncTimeout$2.read(AsyncTimeout.java:238) ... 16 more

On connect timeout:
java.net.SocketTimeoutException: connect timed out at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:345) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:589) at okhttp3.internal.Platform.connectSocket(Platform.java:121) at okhttp3.internal.io.RealConnection.connectSocket(RealConnection.java:185) at okhttp3.internal.io.RealConnection.buildConnection(RealConnection.java:170) at okhttp3.internal.io.RealConnection.connect(RealConnection.java:111) at okhttp3.internal.http.StreamAllocation.findConnection(StreamAllocation.java:187) at okhttp3.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:123) at okhttp3.internal.http.StreamAllocation.newStream(StreamAllocation.java:93) at okhttp3.internal.http.HttpEngine.connect(HttpEngine.java:296) at okhttp3.internal.http.HttpEngine.sendRequest(HttpEngine.java:248) at okhttp3.RealCall.getResponse(RealCall.java:243) at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:201) at com.bluejeans.sample.test.OkHttpConnect$1.intercept(OkHttpConnect.java:25) at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:190) at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:163) at okhttp3.RealCall.execute(RealCall.java:57) at com.bluejeans.sample.test.OkHttpConnect.main(OkHttpConnect.java:39)

enhancement needs info

Most helpful comment

I would appreciate this feature very much. I would like to help a little, so I tried to simulate both scenarios on the latest version of okhttp (3.10.0).

Connect timeout

To simulate connection timeout, I tried to connect to http://10.255.255.1 (see) and the exception I get is:

java.net.ConnectException: Failed to connect to /10.255.255.1:80

    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:242)
    at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:160)
    at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
    at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
    at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
    at okhttp3.RealCall.execute(RealCall.java:77)
    at com.project.shared.utils.http.Client.send(Client.java:85)
    at com.project.shared.utils.http.ClientTest.Send(ClientTest.groovy:13)
Caused by: java.net.ConnectException: Connection timed out: connect
    at java.base/java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
    at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:400)
    at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:243)
    at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:225)
    at java.base/java.net.PlainSocketImpl.connect(PlainSocketImpl.java:148)
    at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:402)
    at java.base/java.net.Socket.connect(Socket.java:591)
    at okhttp3.internal.platform.Platform.connectSocket(Platform.java:129)
    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:240)
    ... 19 more

Read timeout

For read timeout, the exception is as follows:

java.net.SocketTimeoutException: Read timed out

    at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:171)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:141)
    at okio.Okio$2.read(Okio.java:140)
    at okio.AsyncTimeout$2.read(AsyncTimeout.java:237)
    at okio.RealBufferedSource.indexOf(RealBufferedSource.java:355)
    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:227)
    at okhttp3.internal.http1.Http1Codec.readHeaderLine(Http1Codec.java:215)
    at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:189)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
    at okhttp3.RealCall.execute(RealCall.java:77)
    at com.project.shared.utils.http.Client.send(Client.java:85)
    at com.project.shared.utils.http.ClientTest.read timeout(ClientTest.groovy:27)

(I set readTimeout(10, TimeUnit.MILLISECONDS) to simulate this)

My environment:

  • okhttp 3.10
  • java 9.0.4

All 13 comments

we meet the same issue!

I would appreciate this feature very much. I would like to help a little, so I tried to simulate both scenarios on the latest version of okhttp (3.10.0).

Connect timeout

To simulate connection timeout, I tried to connect to http://10.255.255.1 (see) and the exception I get is:

java.net.ConnectException: Failed to connect to /10.255.255.1:80

    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:242)
    at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:160)
    at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
    at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
    at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
    at okhttp3.RealCall.execute(RealCall.java:77)
    at com.project.shared.utils.http.Client.send(Client.java:85)
    at com.project.shared.utils.http.ClientTest.Send(ClientTest.groovy:13)
Caused by: java.net.ConnectException: Connection timed out: connect
    at java.base/java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
    at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:400)
    at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:243)
    at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:225)
    at java.base/java.net.PlainSocketImpl.connect(PlainSocketImpl.java:148)
    at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:402)
    at java.base/java.net.Socket.connect(Socket.java:591)
    at okhttp3.internal.platform.Platform.connectSocket(Platform.java:129)
    at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:240)
    ... 19 more

Read timeout

For read timeout, the exception is as follows:

java.net.SocketTimeoutException: Read timed out

    at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:171)
    at java.base/java.net.SocketInputStream.read(SocketInputStream.java:141)
    at okio.Okio$2.read(Okio.java:140)
    at okio.AsyncTimeout$2.read(AsyncTimeout.java:237)
    at okio.RealBufferedSource.indexOf(RealBufferedSource.java:355)
    at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:227)
    at okhttp3.internal.http1.Http1Codec.readHeaderLine(Http1Codec.java:215)
    at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:189)
    at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
    at okhttp3.RealCall.execute(RealCall.java:77)
    at com.project.shared.utils.http.Client.send(Client.java:85)
    at com.project.shared.utils.http.ClientTest.read timeout(ClientTest.groovy:27)

(I set readTimeout(10, TimeUnit.MILLISECONDS) to simulate this)

My environment:

  • okhttp 3.10
  • java 9.0.4

how to solve this problem?

I’m not 100% sure we’re going to be able to solve this problem in a satisfying way. We can differentiate between whether we’ve send the request bytes or not, but we can’t differentiate between whether or not the server has received them.

I’d love to learn more about what your application will do differently on connect vs. read timeouts.

... this is especially true as we migrate towards QUIC which doesn’t have a distinct “connected” phase.

A read timeout implies that the network is intact and the server is still processing the request.
A connect timeout would mean that the server didn't receive the request. We'd want to retry the request in this case

Yeah, but how do we know whether the server received the request? The only reliable way is to receive a response back from the server.

As per my understanding, the transport layer library throws a java.net.ConnectException: Connection timed out: connect if it does not receive an ACK during the connect timeout.
Refer to the exception provided in https://github.com/square/okhttp/issues/3318#issuecomment-375999956

@tanmaybinaykiya that’s _true_ but you shouldn’t make application behavior depend upon it. If any part of your route between client and web server has a proxy (client-side proxy, server-side load balancer, etc.) then the connect timeout tells you whether it successfully connected to that proxy, and not whether your actual webserver received any data.

Agreed!
In our case, we use this conditional retry when we're guaranteed that there is no such proxy in the route to the server.

No obvious action to take here.

I know this is closed and all, but just to let some help if anyone falls in here and needs something like that:

Maybe a better approach to this issue is using Correlation IDs, generated in the client per request, sent in your request header, and making your backend reject requests with Correlation IDs already received. You can also use them in your logs, and get a real nice logging where you can see from the client till the backend all logs that ID touched. (Still nothing to be done by the library)

I would appreciate this feature very much. I would like to help a little, so I tried to simulate both scenarios on the latest version of okhttp (3.10.0).

Connect timeout

To simulate connection timeout, I tried to connect to http://10.255.255.1 (see) and the exception I get is:

java.net.ConnectException: Failed to connect to /10.255.255.1:80

  at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:242)
  at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:160)
  at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:257)
  at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
  at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
  at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
  at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
  at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
  at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
  at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
  at okhttp3.RealCall.execute(RealCall.java:77)
  at com.project.shared.utils.http.Client.send(Client.java:85)
  at com.project.shared.utils.http.ClientTest.Send(ClientTest.groovy:13)
Caused by: java.net.ConnectException: Connection timed out: connect
  at java.base/java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
  at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:400)
  at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:243)
  at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:225)
  at java.base/java.net.PlainSocketImpl.connect(PlainSocketImpl.java:148)
  at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:402)
  at java.base/java.net.Socket.connect(Socket.java:591)
  at okhttp3.internal.platform.Platform.connectSocket(Platform.java:129)
  at okhttp3.internal.connection.RealConnection.connectSocket(RealConnection.java:240)
  ... 19 more

Read timeout

For read timeout, the exception is as follows:

java.net.SocketTimeoutException: Read timed out

  at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
  at java.base/java.net.SocketInputStream.read(SocketInputStream.java:171)
  at java.base/java.net.SocketInputStream.read(SocketInputStream.java:141)
  at okio.Okio$2.read(Okio.java:140)
  at okio.AsyncTimeout$2.read(AsyncTimeout.java:237)
  at okio.RealBufferedSource.indexOf(RealBufferedSource.java:355)
  at okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:227)
  at okhttp3.internal.http1.Http1Codec.readHeaderLine(Http1Codec.java:215)
  at okhttp3.internal.http1.Http1Codec.readResponseHeaders(Http1Codec.java:189)
  at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.java:88)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
  at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:45)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
  at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
  at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
  at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
  at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
  at okhttp3.RealCall.execute(RealCall.java:77)
  at com.project.shared.utils.http.Client.send(Client.java:85)
  at com.project.shared.utils.http.ClientTest.read timeout(ClientTest.groovy:27)

(I set readTimeout(10, TimeUnit.MILLISECONDS) to simulate this)

My environment:

  • okhttp 3.10
  • java 9.0.4

To be more rigorous, I tried 4 cases below.

  1. connect timeout & not set connectTimeout
    java.net.ConnectException: Failed to connect to.....
  2. connect timeout & already set connectTimeout
    java.net.SocketTimeoutException: connect timed out
  3. read timeout & not set readTimeout
    java.net.SocketTimeoutException: Read timed out
  4. read timeout & already set readTimeout
    java.net.SocketTimeoutException: timeout
Was this page helpful?
0 / 5 - 0 ratings