mirror of
https://github.com/kodjodevf/mangayomi.git
synced 2026-05-21 01:32:49 +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
|
/// Resolve using specific provider via hostname
|
||||||
/// This avoids circular DNS dependency by connecting to bootstrap IP directly
|
|
||||||
static Future<List<String>> _resolveFromProvider(
|
static Future<List<String>> _resolveFromProvider(
|
||||||
String host,
|
String host,
|
||||||
DoHProvider provider, {
|
DoHProvider provider, {
|
||||||
|
|
@ -119,58 +118,34 @@ class DoHResolver {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
final uri = Uri.parse(provider.url);
|
try {
|
||||||
final providerHost = uri.host;
|
final directResult = await _queryDoHViaHostname(
|
||||||
|
host,
|
||||||
// Validate bootstrap IPs format
|
provider,
|
||||||
final validBootstrapIps = provider.bootstrapIPs
|
recordType,
|
||||||
.where((ip) => _isValidIp(ip))
|
);
|
||||||
.toList();
|
if (directResult.isNotEmpty) {
|
||||||
if (validBootstrapIps.isEmpty) {
|
_log('Resolved $host via ${provider.name} (hostname mode)');
|
||||||
_log('No valid bootstrap IPs for ${provider.name}', isError: true);
|
stats.successCount++;
|
||||||
return [];
|
return directResult;
|
||||||
}
|
}
|
||||||
|
} catch (e) {
|
||||||
// Try each bootstrap IP until one succeeds
|
_log('Hostname mode failed for ${provider.name}: $e');
|
||||||
for (int i = 0; i < validBootstrapIps.length; i++) {
|
stats.failureCount++;
|
||||||
final bootstrapIp = validBootstrapIps[i];
|
stats.lastFailure = DateTime.now();
|
||||||
try {
|
if (stats.failureCount >= 3) {
|
||||||
final result = await _queryDoHViaBootstrapIp(
|
stats.isCircuitOpen = true;
|
||||||
host,
|
_log('Circuit breaker opened for ${provider.name}', isError: true);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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 [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Query DoH via specific bootstrap IP (direct IP connection, no DNS lookup)
|
/// Query DoH by provider hostname (relies on platform DNS for provider host)
|
||||||
static Future<List<String>> _queryDoHViaBootstrapIp(
|
static Future<List<String>> _queryDoHViaHostname(
|
||||||
String targetHost,
|
String targetHost,
|
||||||
DoHProvider provider,
|
DoHProvider provider,
|
||||||
String bootstrapIp,
|
|
||||||
String providerHost,
|
|
||||||
String recordType,
|
String recordType,
|
||||||
) async {
|
) async {
|
||||||
final uri = Uri.parse(provider.url);
|
final uri = Uri.parse(provider.url);
|
||||||
|
|
@ -178,19 +153,16 @@ class DoHResolver {
|
||||||
client.connectionTimeout = _requestTimeout;
|
client.connectionTimeout = _requestTimeout;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create request with bootstrap IP instead of hostname
|
|
||||||
final request = await client.getUrl(
|
final request = await client.getUrl(
|
||||||
Uri(
|
Uri(
|
||||||
scheme: uri.scheme,
|
scheme: uri.scheme,
|
||||||
host: bootstrapIp, // Use bootstrap IP directly
|
host: uri.host,
|
||||||
port: uri.port,
|
port: uri.port,
|
||||||
path: uri.path,
|
path: uri.path,
|
||||||
query: 'name=$targetHost&type=$recordType',
|
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('Accept', 'application/dns-json');
|
||||||
request.headers.set('User-Agent', 'Mangayomi/1.0');
|
request.headers.set('User-Agent', 'Mangayomi/1.0');
|
||||||
|
|
||||||
|
|
@ -201,18 +173,10 @@ class DoHResolver {
|
||||||
return _parseDoHResponse(body);
|
return _parseDoHResponse(body);
|
||||||
}
|
}
|
||||||
|
|
||||||
_log('HTTP ${response.statusCode} from $bootstrapIp (${provider.name})');
|
_log('HTTP ${response.statusCode} from hostname (${provider.name})');
|
||||||
return [];
|
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 {
|
} finally {
|
||||||
client.close();
|
client.close(force: true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -272,6 +236,7 @@ class DoHResolver {
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.whereType<String>()
|
.whereType<String>()
|
||||||
|
.where(_isValidIp)
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (ips.isNotEmpty) {
|
if (ips.isNotEmpty) {
|
||||||
|
|
|
||||||
12
pubspec.lock
12
pubspec.lock
|
|
@ -1699,26 +1699,26 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test
|
name: test
|
||||||
sha256: "77cc98ea27006c84e71a7356cf3daf9ddbde2d91d84f77dbfe64cf0e4d9611ae"
|
sha256: "54c516bbb7cee2754d327ad4fca637f78abfc3cbcc5ace83b3eda117e42cd71a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.28.0"
|
version: "1.29.0"
|
||||||
test_api:
|
test_api:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "19a78f63e83d3a61f00826d09bc2f60e191bf3504183c001262be6ac75589fb8"
|
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.8"
|
version: "0.7.9"
|
||||||
test_core:
|
test_core:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_core
|
name: test_core
|
||||||
sha256: f1072617a6657e5fc09662e721307f7fb009b4ed89b19f47175d11d5254a62d4
|
sha256: "394f07d21f0f2255ec9e3989f21e54d3c7dc0e6e9dbce160e5a9c1a6be0e2943"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.6.14"
|
version: "0.6.15"
|
||||||
time:
|
time:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue