Skip to content

Commit a5691ae

Browse files
authored
fix: make connection resets retryable in OkHttp engine (#896)
1 parent 751f451 commit a5691ae

File tree

3 files changed

+19
-1
lines changed

3 files changed

+19
-1
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"id": "d50afb27-0e5c-46b3-9c9c-865a3c75dd21",
3+
"type": "bugfix",
4+
"description": "Retry connection reset errors in OkHttp engine",
5+
"issues": [
6+
"awslabs/aws-sdk-kotlin#905"
7+
]
8+
}

runtime/protocol/http-client-engines/http-client-engine-okhttp/jvm/src/aws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpUtils.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import okhttp3.*
1919
import okhttp3.Authenticator
2020
import okhttp3.RequestBody.Companion.toRequestBody
2121
import okhttp3.internal.http.HttpMethod
22+
import java.io.EOFException
2223
import java.io.IOException
2324
import java.net.*
2425
import javax.net.ssl.SSLHandshakeException
@@ -191,16 +192,22 @@ internal inline fun<T> mapOkHttpExceptions(block: () -> T): T =
191192
throw HttpException(ex, ex.errCode(), ex.isRetryable())
192193
}
193194

194-
private fun Exception.isRetryable(): Boolean = isCauseOrSuppressed<ConnectException>()
195+
private fun Exception.isRetryable(): Boolean = isCauseOrSuppressed<ConnectException>() || isConnectionClosedException()
195196
private fun Exception.errCode(): HttpErrorCode = when {
196197
isConnectTimeoutException() -> HttpErrorCode.CONNECT_TIMEOUT
198+
isConnectionClosedException() -> HttpErrorCode.CONNECTION_CLOSED
197199
isCauseOrSuppressed<SocketTimeoutException>() -> HttpErrorCode.SOCKET_TIMEOUT
198200
isCauseOrSuppressed<SSLHandshakeException>() -> HttpErrorCode.TLS_NEGOTIATION_ERROR
199201
else -> HttpErrorCode.SDK_UNKNOWN
200202
}
201203

202204
private fun Exception.isConnectTimeoutException(): Boolean =
203205
findCauseOrSuppressed<SocketTimeoutException>()?.message?.contains("connect", ignoreCase = true) == true
206+
207+
private fun Exception.isConnectionClosedException(): Boolean =
208+
message?.contains("unexpected end of stream") == true &&
209+
(cause as? Exception)?.findCauseOrSuppressed<EOFException>()?.message?.contains("\\n not found: limit=0") == true
210+
204211
private inline fun <reified T> Exception.isCauseOrSuppressed(): Boolean = findCauseOrSuppressed<T>() != null
205212
private inline fun <reified T> Exception.findCauseOrSuppressed(): T? {
206213
if (this is T) return this

runtime/protocol/http-client-engines/http-client-engine-okhttp/jvm/test/aws/smithy/kotlin/runtime/http/engine/okhttp/OkHttpExceptionTest.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package aws.smithy.kotlin.runtime.http.engine.okhttp
77

88
import aws.smithy.kotlin.runtime.http.HttpErrorCode
99
import aws.smithy.kotlin.runtime.http.HttpException
10+
import java.io.EOFException
1011
import java.io.IOException
1112
import java.net.ConnectException
1213
import java.net.SocketTimeoutException
@@ -33,6 +34,8 @@ class OkHttpExceptionTest {
3334
ExceptionTest(SocketTimeoutException("read timeout"), expectedError = HttpErrorCode.SOCKET_TIMEOUT),
3435
ExceptionTest(IOException(SSLHandshakeException("negotiate error")), expectedError = HttpErrorCode.TLS_NEGOTIATION_ERROR),
3536
ExceptionTest(ConnectException("test connect error"), expectedError = HttpErrorCode.SDK_UNKNOWN, true),
37+
// see https://github.com/awslabs/aws-sdk-kotlin/issues/905
38+
ExceptionTest(IOException("unexpected end of stream on https://test.aws.com", EOFException("\\n not found: limit=0 content=...")), expectedError = HttpErrorCode.CONNECTION_CLOSED, expectedRetryable = true),
3639
)
3740

3841
for (test in tests) {

0 commit comments

Comments
 (0)