resolve method
host is the claimed host the code arrived on. It is required rather
than configured once because the resolver operates the entry host
domain (§2): the host says which registry this code belongs to. A build
that claims two hosts and asks one fixed resolver would be asking the
first issuer about the second issuer's code.
locale is sent so the issuer's own words come back translated — a host
must never machine-translate an issuer's identity (§4.1.2).
Implementation
@override
Future<EntryTarget> resolve(
String code, {
required String host,
required String locale,
}) async {
if (host.isEmpty) {
throw ArgumentError.value(host, 'host', 'entry host must not be empty');
}
// Always https: a resolver reached over plain http can be answered by
// anyone on the path.
//
// The code is a path segment, not a query value: it is an identifier of a
// resource, and encoding it keeps a partitioned code space intact.
final url = Uri(
scheme: 'https',
host: host,
pathSegments: <String>[
..._path.split('/').where((s) => s.isNotEmpty),
...code.split('/').where((s) => s.isNotEmpty),
],
);
try {
final body = await _fetch(
url,
headers: <String, String>{
'accept': 'application/json',
'accept-language': locale,
},
);
final decoded = jsonDecode(body);
if (decoded is! Map<String, dynamic>) {
return EntryTargetCodec.unreachable('malformed resolver response');
}
return EntryTargetCodec.fromJson(decoded);
} catch (e) {
// A resolver we cannot reach is not an entry we may guess at. The host
// renders a gate that says so; it never proceeds on an assumption.
_logger.warn('entry.resolve.failed', <String, dynamic>{
'url': url.toString(),
'error': e.toString(),
});
return EntryTargetCodec.unreachable('resolver unreachable');
}
}