Reputation: 13
We are Using OkHttp3 (v4.9.1) to establish h2c (HTTP/2 without TLS) connections in a highly concurrent fashion from a Spring Boot application. To do so, we have narrowed down the supported protocols using:
builder.protocols(List.of(Protocol.H2_PRIOR_KNOWLEDGE))
Establishing connections usually works fine and HTTP/2 streams are used instead of dedicated connections. However, we observe sporadic error bursts when the server (based on nginx, single node, single address) closes the connection after a certain number of requests has been reached (as instructed by its keepalive_request option). When this happens, OkHttp does not seem to attempt to retry the connection, but instead just throws an exception to the caller:
okhttp3.internal.http2.StreamResetException: stream was reset: REFUSED_STREAM
at okhttp3.internal.http2.Http2Stream.takeHeaders(Http2Stream.kt:148)
at okhttp3.internal.http2.Http2ExchangeCodec.readResponseHeaders(Http2ExchangeCodec.kt:96)
at okhttp3.internal.connection.Exchange.readResponseHeaders(Exchange.kt:106)
at okhttp3.internal.http.CallServerInterceptor.intercept(CallServerInterceptor.kt:79)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.kt:34)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.kt:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.kt:83)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.kt:76)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at org.example.OkHttpAutoConfiguration.lambda$authenticate$3(OkHttpAutoConfiguration.java:95)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.kt:109)
at okhttp3.internal.connection.RealCall.getResponseWithInterceptorChain$okhttp(RealCall.kt:201)
at okhttp3.internal.connection.RealCall.execute(RealCall.kt:154)
[...]
Requests are initiated like this:
httpClient.newCall(buildRequest(uri)).execute()
What is the recommended way to deal with these errors?
Is there an option (we may have missed) so OkHttp takes care of this transparently to the application?
Upvotes: 1
Views: 2243
Reputation: 40603
With this issue you've made the case for us to fix it in OkHttp. https://github.com/square/okhttp/issues/6700
In the interim, you'll want an interceptor that uses a try/catch block, and attempts again in the catch clause if the exception matches this criteria.
Upvotes: 1