/** * 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, };