mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-05-07 12:40:39 +00:00
refactor: update DoH resolution method to use hostname first; improve circuit breaker logic
This commit is contained in:
parent
cc69c9f275
commit
9d829a16e7
2 changed files with 31 additions and 66 deletions
|
|
@ -105,8 +105,7 @@ class DoHResolver {
|
|||
}
|
||||
}
|
||||
|
||||
/// Resolve using specific provider via bootstrap IP
|
||||
/// This avoids circular DNS dependency by connecting to bootstrap IP directly
|
||||
/// Resolve using specific provider via hostname
|
||||
static Future<List<String>> _resolveFromProvider(
|
||||
String host,
|
||||
DoHProvider provider, {
|
||||
|
|
@ -119,58 +118,34 @@ class DoHResolver {
|
|||
return [];
|
||||
}
|
||||
|
||||
final uri = Uri.parse(provider.url);
|
||||
final providerHost = uri.host;
|
||||
|
||||
// Validate bootstrap IPs format
|
||||
final validBootstrapIps = provider.bootstrapIPs
|
||||
.where((ip) => _isValidIp(ip))
|
||||
.toList();
|
||||
if (validBootstrapIps.isEmpty) {
|
||||
_log('No valid bootstrap IPs for ${provider.name}', isError: true);
|
||||
return [];
|
||||
}
|
||||
|
||||
// Try each bootstrap IP until one succeeds
|
||||
for (int i = 0; i < validBootstrapIps.length; i++) {
|
||||
final bootstrapIp = validBootstrapIps[i];
|
||||
try {
|
||||
final result = await _queryDoHViaBootstrapIp(
|
||||
host,
|
||||
provider,
|
||||
bootstrapIp,
|
||||
providerHost,
|
||||
recordType,
|
||||
);
|
||||
if (result.isNotEmpty) {
|
||||
_log(
|
||||
'Resolved $host via ${provider.name} (bootstrap IP $i+1/${validBootstrapIps.length})',
|
||||
);
|
||||
return result;
|
||||
}
|
||||
} catch (e) {
|
||||
_log('Bootstrap IP $i failed for ${provider.name}: $e');
|
||||
continue;
|
||||
try {
|
||||
final directResult = await _queryDoHViaHostname(
|
||||
host,
|
||||
provider,
|
||||
recordType,
|
||||
);
|
||||
if (directResult.isNotEmpty) {
|
||||
_log('Resolved $host via ${provider.name} (hostname mode)');
|
||||
stats.successCount++;
|
||||
return directResult;
|
||||
}
|
||||
} catch (e) {
|
||||
_log('Hostname mode failed for ${provider.name}: $e');
|
||||
stats.failureCount++;
|
||||
stats.lastFailure = DateTime.now();
|
||||
if (stats.failureCount >= 3) {
|
||||
stats.isCircuitOpen = true;
|
||||
_log('Circuit breaker opened for ${provider.name}', isError: true);
|
||||
}
|
||||
}
|
||||
|
||||
stats.failureCount++;
|
||||
stats.lastFailure = DateTime.now();
|
||||
// Open circuit after 3 consecutive failures
|
||||
if (stats.failureCount >= 3) {
|
||||
stats.isCircuitOpen = true;
|
||||
_log('Circuit breaker opened for ${provider.name}', isError: true);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/// Query DoH via specific bootstrap IP (direct IP connection, no DNS lookup)
|
||||
static Future<List<String>> _queryDoHViaBootstrapIp(
|
||||
/// Query DoH by provider hostname (relies on platform DNS for provider host)
|
||||
static Future<List<String>> _queryDoHViaHostname(
|
||||
String targetHost,
|
||||
DoHProvider provider,
|
||||
String bootstrapIp,
|
||||
String providerHost,
|
||||
String recordType,
|
||||
) async {
|
||||
final uri = Uri.parse(provider.url);
|
||||
|
|
@ -178,19 +153,16 @@ class DoHResolver {
|
|||
client.connectionTimeout = _requestTimeout;
|
||||
|
||||
try {
|
||||
// Create request with bootstrap IP instead of hostname
|
||||
final request = await client.getUrl(
|
||||
Uri(
|
||||
scheme: uri.scheme,
|
||||
host: bootstrapIp, // Use bootstrap IP directly
|
||||
host: uri.host,
|
||||
port: uri.port,
|
||||
path: uri.path,
|
||||
query: 'name=$targetHost&type=$recordType',
|
||||
),
|
||||
);
|
||||
|
||||
// Set Host header to the actual provider hostname (required for HTTPS SNI)
|
||||
request.headers.set('Host', providerHost);
|
||||
request.headers.set('Accept', 'application/dns-json');
|
||||
request.headers.set('User-Agent', 'Mangayomi/1.0');
|
||||
|
||||
|
|
@ -201,18 +173,10 @@ class DoHResolver {
|
|||
return _parseDoHResponse(body);
|
||||
}
|
||||
|
||||
_log('HTTP ${response.statusCode} from $bootstrapIp (${provider.name})');
|
||||
_log('HTTP ${response.statusCode} from hostname (${provider.name})');
|
||||
return [];
|
||||
} on SocketException {
|
||||
// Connection failed with this bootstrap IP
|
||||
_log('Socket error on $bootstrapIp');
|
||||
rethrow;
|
||||
} on TimeoutException {
|
||||
// Timeout with this bootstrap IP
|
||||
_log('Timeout on $bootstrapIp');
|
||||
rethrow;
|
||||
} finally {
|
||||
client.close();
|
||||
client.close(force: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -272,6 +236,7 @@ class DoHResolver {
|
|||
return null;
|
||||
})
|
||||
.whereType<String>()
|
||||
.where(_isValidIp)
|
||||
.toList();
|
||||
|
||||
if (ips.isNotEmpty) {
|
||||
|
|
|
|||
12
pubspec.lock
12
pubspec.lock
|
|
@ -1699,26 +1699,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test
|
||||
sha256: "77cc98ea27006c84e71a7356cf3daf9ddbde2d91d84f77dbfe64cf0e4d9611ae"
|
||||
sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.28.0"
|
||||
version: "1.29.0"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8"
|
||||
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.8"
|
||||
version: "0.7.9"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: f1072617a6657e5fc09662e721307f7fb009b4ed89b19f47175d11d5254a62d4
|
||||
sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.14"
|
||||
version: "0.6.15"
|
||||
time:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
|||
Loading…
Reference in a new issue