Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ class AWSRetryer implements Retryer {
if (exception is TimeoutException) {
return true;
}
// Transport-level failures never reached the server, so are safe to retry.
if (exception is AWSHttpException) {
return true;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point. AWSHttpException has no status code so we can't check codes, and those two are response errors, not transport. Instead of trusting the type, the HTTP client now marks the real transport failures with AWSHttpException.retryable(...) and the retryer only retries the flagged ones, so those two stay non-retryable.

if (exception is! SmithyException) {
return false;
}
Expand Down
41 changes: 41 additions & 0 deletions packages/smithy/smithy_aws/test/http/aws_retryer_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,47 @@ void main() {
});
}
});

group('isRetryable', () {
final retryer = AWSRetryer();
final request = AWSHttpRequest.get(Uri.parse('https://example.com'));

test('retries transient transport failures (AWSHttpException)', () {
expect(retryer.isRetryable(AWSHttpException(request)), isTrue);
});

test('retries TimeoutException', () {
expect(retryer.isRetryable(TimeoutException('slow')), isTrue);
});

test('does not retry non-transport, non-Smithy exceptions', () {
expect(retryer.isRetryable(const FormatException('bad')), isFalse);
});
});

group('retry recovery', () {
test('recovers after a transient AWSHttpException (retries then '
'succeeds)', () async {
await runZoned(() async {
// exponentialBase: 0 => zero backoff, so the test is fast.
final retryer = AWSRetryer(exponentialBase: 0);
final request = AWSHttpRequest.get(Uri.parse('https://example.com'));
var attempts = 0;
final result = await retryer.retry<int>(() {
attempts++;
final completer = CancelableCompleter<int>();
if (attempts < 2) {
completer.completeError(AWSHttpException(request));
} else {
completer.complete(42);
}
return completer.operation;
}).valueOrCancellation();
expect(attempts, 2, reason: 'should retry once, then succeed');
expect(result, 42, reason: 'should return the retried result');
}, zoneValues: {AWSConfigValue.maxAttempts: 3});
});
});
});
}

Expand Down
Loading