Slyrack
Slyrack

Reputation: 13

Dealing with OkHttp HTTP/2 REFUSED_STREAM errors

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

Answers (1)

Jesse Wilson
Jesse Wilson

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

Related Questions