-
Notifications
You must be signed in to change notification settings - Fork 28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: treat IO exceptions as retryable in HTTP engines #979
Changes from 3 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"id": "0645c929-bdfe-4d07-a131-b0b447422fff", | ||
"type": "bugfix", | ||
"description": "Treat all `IOException` errors in OkHttp engine as retryable (e.g., socket errors, DNS lookup failures, etc.)" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* | ||
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
package aws.smithy.kotlin.runtime.http.engine.crt | ||
|
||
import kotlin.test.Test | ||
import kotlin.test.assertFalse | ||
import kotlin.test.assertTrue | ||
|
||
class RequestUtilTest { | ||
@Test | ||
fun testExceptionRetryability() { | ||
setOf( | ||
// All IO errors are retryable | ||
"AWS_IO_SOCKET_NETWORK_DOWN", | ||
"AWS_ERROR_IO_OPERATION_CANCELLED", | ||
"AWS_IO_DNS_NO_ADDRESS_FOR_HOST", | ||
|
||
// Any connection closure is retryable | ||
"AWS_ERROR_HTTP_CONNECTION_CLOSED", | ||
"AWS_ERROR_HTTP_SERVER_CLOSED", | ||
"AWS_IO_BROKEN_PIPE", | ||
|
||
// All proxy errors are retryable | ||
"AWS_ERROR_HTTP_PROXY_CONNECT_FAILED", | ||
"AWS_ERROR_HTTP_PROXY_STRATEGY_NTLM_CHALLENGE_TOKEN_MISSING", | ||
"AWS_ERROR_HTTP_PROXY_STRATEGY_TOKEN_RETRIEVAL_FAILURE", | ||
|
||
// Any connection manager issues are retryable | ||
"AWS_ERROR_HTTP_CONNECTION_MANAGER_INVALID_STATE_FOR_ACQUIRE", | ||
"AWS_ERROR_HTTP_CONNECTION_MANAGER_VENDED_CONNECTION_UNDERFLOW", | ||
"AWS_ERROR_HTTP_CONNECTION_MANAGER_SHUTTING_DOWN", | ||
).forEach { name -> assertTrue(isRetryable(name), "Expected $name to be retryable!") } | ||
|
||
setOf( | ||
// Any other errors are not retryable | ||
"AWS_ERROR_HTTP_INVALID_METHOD", | ||
"AWS_ERROR_PKCS11_CKR_CANCEL", | ||
"AWS_ERROR_PEM_MALFORMED", | ||
|
||
// Unknown error codes are not retryable | ||
null, | ||
).forEach { name -> assertFalse(isRetryable(name), "Expected $name to not be retryable!") } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -208,10 +208,9 @@ internal inline fun<T> mapOkHttpExceptions(block: () -> T): T = | |
try { | ||
block() | ||
} catch (ex: IOException) { | ||
throw HttpException(ex, ex.errCode(), ex.isRetryable()) | ||
throw HttpException(ex, ex.errCode(), retryable = true) // All IOExceptions are retryable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Question/Correctness: Is there any reason we wouldn't just make all There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A few reasons came to mind:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
We already have an expect/actual for it FYI: https://github.com/awslabs/smithy-kotlin/blob/main/runtime/runtime-core/common/src/aws/smithy/kotlin/runtime/io/Exceptions.kt#L8
It entirely depends on how we wrap the exceptions. To your point though CRT JVM bindings throw generic exceptions with a CRT specific error code IIRC. I wasn't thinking of CRT though I was thinking of other JVM specific bindings that customers may writer or that we may provide in the future. Doing it at the retry policy level would capture all of them.
I think we are wrapping all of our HTTP related exceptions into an HttpException with an error code if we can map it. I suppose because of this handling it at the specific engine level makes sense. We just have to remember to do it for each engine. suggestion : If we are handling this at the specific engine level I think we ought to probably make the CRT exceptions retryable as well if we can in this PR. It would require looking at specific error codes probably. |
||
} | ||
|
||
private fun Exception.isRetryable(): Boolean = isCauseOrSuppressed<ConnectException>() || isConnectionClosedException() | ||
private fun Exception.errCode(): HttpErrorCode = when { | ||
isConnectTimeoutException() -> HttpErrorCode.CONNECT_TIMEOUT | ||
isConnectionClosedException() -> HttpErrorCode.CONNECTION_CLOSED | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and now also the CRT engine! 🚀