74 lines
2.0 KiB
JavaScript
Executable File
74 lines
2.0 KiB
JavaScript
Executable File
/**
|
|
* URL 重定向解析:在浏览器导航前尽可能解析 HTTP 级重定向链。
|
|
*/
|
|
'use strict';
|
|
|
|
const http = require('http');
|
|
const https = require('https');
|
|
|
|
const REDIRECT_STATUS = new Set([301, 302, 303, 307, 308]);
|
|
|
|
/**
|
|
* 发起单次 HEAD/GET,不自动跟随重定向。
|
|
*/
|
|
function fetchOnce(method, url, timeoutMs) {
|
|
return new Promise((resolve, reject) => {
|
|
const parsed = new URL(url);
|
|
const lib = parsed.protocol === 'https:' ? https : http;
|
|
const req = lib.request(
|
|
url,
|
|
{ method, timeout: timeoutMs, headers: { 'User-Agent': 'Mozilla/5.0 (compatible; puppeteer-api/1.0)' } },
|
|
(res) => {
|
|
res.resume();
|
|
resolve({ statusCode: res.statusCode || 0, headers: res.headers || {} });
|
|
}
|
|
);
|
|
req.on('timeout', () => {
|
|
req.destroy();
|
|
reject(new Error('REDIRECT_TIMEOUT'));
|
|
});
|
|
req.on('error', reject);
|
|
req.end();
|
|
});
|
|
}
|
|
|
|
/**
|
|
* 跟随 HTTP 重定向,返回最终 URL(无法解析时返回原始 URL)。
|
|
*/
|
|
async function resolveHttpRedirects(startUrl, { maxRedirects = 10, timeoutMs = 12000 } = {}) {
|
|
let current = startUrl;
|
|
for (let i = 0; i < maxRedirects; i++) {
|
|
let response;
|
|
try {
|
|
response = await fetchOnce('HEAD', current, timeoutMs);
|
|
} catch (_) {
|
|
response = await fetchOnce('GET', current, timeoutMs);
|
|
}
|
|
if (!REDIRECT_STATUS.has(response.statusCode)) {
|
|
return current;
|
|
}
|
|
const location = response.headers.location;
|
|
if (!location) {
|
|
return current;
|
|
}
|
|
current = new URL(location, current).href;
|
|
}
|
|
return current;
|
|
}
|
|
|
|
/**
|
|
* 从分享页 URL 提取 token 参数(星河等云控)。
|
|
*/
|
|
function extractShareTokenFromUrl(pageUrl) {
|
|
try {
|
|
return new URL(pageUrl).searchParams.get('token') || '';
|
|
} catch (_) {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
resolveHttpRedirects,
|
|
extractShareTokenFromUrl,
|
|
};
|