fix: switch Spotify HTTP to https module with socket.setTimeout for OS-level timeout
undici cannot abort a TCP connect in progress even with AbortController. Node's https.request + socket.setTimeout fires at the OS socket level and aborts within ms even when the TCP SYN is silently dropped. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
1 parent
d2620519aa
commit
f4766acc74
1 file changed
+30
-6
@@ -67,12 +67,36 @@ function parseSpotifyUrl(url) {
|
|||||||
return { type: m[1], id: m[2] };
|
return { type: m[1], id: m[2] };
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbortSignal.timeout() is unreliable in Node/undici when TCP connect is blocked.
|
// undici (native fetch) cannot abort a pending TCP connect even with AbortController.
|
||||||
// Use AbortController + setTimeout instead — always fires from Node's event loop.
|
// Use Node's https module with socket.setTimeout() — fires at OS level during connect.
|
||||||
function _fetchTimeout(url, opts, ms = 10000) {
|
function _fetchTimeout(url, opts = {}, ms = 10000) {
|
||||||
const ctrl = new AbortController();
|
return new Promise((resolve, reject) => {
|
||||||
const timer = setTimeout(() => ctrl.abort(), ms);
|
const u = new URL(url);
|
||||||
return fetch(url, { ...opts, signal: ctrl.signal }).finally(() => clearTimeout(timer));
|
const req = https.request({
|
||||||
|
hostname: u.hostname, port: 443,
|
||||||
|
path: u.pathname + u.search,
|
||||||
|
method: opts.method || 'GET',
|
||||||
|
headers: opts.headers || {},
|
||||||
|
}, res => {
|
||||||
|
const chunks = [];
|
||||||
|
res.on('data', c => chunks.push(c));
|
||||||
|
res.on('end', () => {
|
||||||
|
const body = Buffer.concat(chunks).toString();
|
||||||
|
resolve({
|
||||||
|
ok: res.statusCode >= 200 && res.statusCode < 300,
|
||||||
|
status: res.statusCode,
|
||||||
|
headers: { get: h => res.headers[h.toLowerCase()] ?? null },
|
||||||
|
json: () => { try { return Promise.resolve(JSON.parse(body)); } catch(e) { return Promise.reject(e); } },
|
||||||
|
text: () => Promise.resolve(body),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
res.on('error', reject);
|
||||||
|
});
|
||||||
|
req.on('socket', s => s.setTimeout(ms, () => req.destroy(new Error(`Spotify nicht erreichbar (Timeout ${ms}ms) — api.spotify.com blockiert?`))));
|
||||||
|
req.on('error', reject);
|
||||||
|
if (opts.body) req.write(opts.body);
|
||||||
|
req.end();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let _spTokenCache = null;
|
let _spTokenCache = null;
|
||||||
|
|||||||
Reference in new issue
Block a user