49 lines
1.6 KiB
Dart
49 lines
1.6 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import 'net_guard.dart';
|
|
|
|
typedef BodyEncoder = Future<http.Response> Function();
|
|
|
|
class HttpRetry {
|
|
/// ريتراي لـ network/transient errors فقط.
|
|
static Future<http.Response> sendWithRetry(
|
|
BodyEncoder send, {
|
|
int maxRetries = 3,
|
|
Duration baseDelay = const Duration(milliseconds: 400),
|
|
Duration timeout = const Duration(seconds: 12),
|
|
}) async {
|
|
// ✅ Pre-flight check for internet connection
|
|
if (!await NetGuard().hasInternet()) {
|
|
// Immediately throw a specific exception if there's no internet.
|
|
// This avoids pointless retries.
|
|
throw const SocketException("No internet connection");
|
|
}
|
|
int attempt = 0;
|
|
while (true) {
|
|
attempt++;
|
|
try {
|
|
final res = await send().timeout(timeout);
|
|
return res;
|
|
} on TimeoutException catch (_) {
|
|
if (attempt >= maxRetries) rethrow;
|
|
} on SocketException catch (_) {
|
|
if (attempt >= maxRetries) rethrow;
|
|
} on HandshakeException catch (_) {
|
|
if (attempt >= maxRetries) rethrow;
|
|
} on http.ClientException catch (e) {
|
|
// مثال: Connection reset by peer
|
|
final msg = e.message.toLowerCase();
|
|
final transient = msg.contains('connection reset') ||
|
|
msg.contains('broken pipe') ||
|
|
msg.contains('timed out');
|
|
if (!transient || attempt >= maxRetries) rethrow;
|
|
}
|
|
// backoff: 0.4s, 0.8s, 1.6s
|
|
final delay = baseDelay * (1 << (attempt - 1));
|
|
await Future.delayed(delay);
|
|
}
|
|
}
|
|
}
|