resolveOnce function
执行一次 HTTP 请求(HEAD 或 GET)
method 请求方法:'HEAD' 或 'GET'
currentUrl 当前要请求的 URL
timeoutMs 超时时间(毫秒)
返回解析结果,包含状态码、下一个 URL(如果有重定向)和最终 URL
Implementation
Future<ResolveResult> resolveOnce(String method, String currentUrl, int timeoutMs) async {
if (!isHttpLike(currentUrl)) {
return ResolveResult(statusCode: 0, finalUrl: currentUrl);
}
final uri = Uri.parse(currentUrl);
final client = http.Client();
try {
final request = http.Request(method, uri)..followRedirects = false;
request.headers['User-Agent'] = 'expand-urls/1.0';
if (method == 'GET') {
request.headers['Range'] = 'bytes=0-0';
}
final response = await client
.send(request)
.timeout(
Duration(milliseconds: timeoutMs),
onTimeout: () {
throw TimeoutException('请求超时', Duration(milliseconds: timeoutMs));
},
);
final statusCode = response.statusCode;
// 获取所有响应头,检查 location 头(HTTP 头名称不区分大小写)
// Dart 的 Headers 类在访问时会自动进行大小写不敏感的匹配
String? location;
// 尝试多种可能的写法(虽然理论上不区分大小写,但为了确保兼容性)
location = response.headers['location'] ?? response.headers['Location'] ?? response.headers['LOCATION'];
// 如果还是找不到,遍历所有头查找(某些边缘情况)
if (location == null) {
for (final key in response.headers.keys) {
if (key.toLowerCase() == 'location') {
location = response.headers[key];
break;
}
}
}
// 获取实际请求的 URL(相当于 TypeScript 的 res.url)
final requestUrl = response.request?.url.toString() ?? currentUrl;
final finalUrl = requestUrl;
// 如果响应中有 location 头,处理它(与 TypeScript 代码保持一致:if (location))
if (location != null && location.isNotEmpty) {
try {
// 使用 Uri.resolve 来解析相对 URL,类似于 TypeScript 的 new URL(location, res.url || currentUrl)
// TypeScript: new URL(location, res.url || currentUrl)
// 这里 res.url 是 got 库返回的实际请求 URL,如果不存在则使用 currentUrl
final baseUri = Uri.parse(finalUrl);
final absoluteUrl = baseUri.resolve(location).toString();
if (isHttpLike(absoluteUrl)) {
return ResolveResult(statusCode: statusCode, nextUrl: absoluteUrl, finalUrl: finalUrl);
} else {
// 遇到非 HTTP(S) 协议(如 app://, intent:, shopee://),停止跟随
return ResolveResult(statusCode: statusCode, finalUrl: finalUrl);
}
} catch (e) {
// 位置头格式错误,视为最终 URL
return ResolveResult(statusCode: statusCode, finalUrl: finalUrl);
}
}
return ResolveResult(statusCode: statusCode, finalUrl: finalUrl);
} finally {
client.close();
}
}