fix(qlik): send Origin/User-Agent on engine ws + verbose 403 diagnostics

Qlik proxy validates the WebSocket Origin header (whitelist) — a browser
always sends it. Add Origin + User-Agent to the engine ws. Also surface the
403 response (server/www-authenticate/body) for diagnosis.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MichaelandClaude Opus 4.8 committed 2026-06-20 23:35:09 +02:00
1 parent abed369bfb
commit 1281b0e21f
1 file changed
+15 -2
+15 -2
View File
@@ -168,7 +168,13 @@ function openEngine(appGuid: string, sess: QlikSession, cfg: QlikConfig): Promis
const url = `wss://${cfg.host}/app/${encodeURIComponent(appGuid)}?Xrfkey=${sess.xrfkey}`;
return new Promise((resolve, reject) => {
const ws = new WebSocket(url, {
headers: { Cookie: sess.cookie, "X-Qlik-Xrfkey": sess.xrfkey },
headers: {
Cookie: sess.cookie,
"X-Qlik-Xrfkey": sess.xrfkey,
// Le proxy Qlik valide l'Origin des websockets (whitelist) — un navigateur l'envoie toujours.
Origin: `https://${cfg.host}`,
"User-Agent": "Mozilla/5.0",
},
rejectUnauthorized: !cfg.tlsInsecure,
handshakeTimeout: cfg.timeoutMs,
});
@@ -188,7 +194,14 @@ function openEngine(appGuid: string, sess: QlikSession, cfg: QlikConfig): Promis
else p.resolve(msg.result as Record<string, unknown>);
}
});
ws.on("unexpected-response", (_req, res) => { clearTimeout(timer); reject(new Error(`[qlik] ws HTTP ${res.statusCode}`)); });
ws.on("unexpected-response", (_req, res) => {
clearTimeout(timer);
let body = "";
res.on("data", (d: Buffer) => (body += d.toString()));
res.on("end", () => reject(new Error(
`[qlik] ws HTTP ${res.statusCode} server=${res.headers["server"]} wwwauth=${res.headers["www-authenticate"]} body=${body.slice(0, 160)}`,
)));
});
ws.on("error", (e: Error) => { clearTimeout(timer); reject(e); });
ws.on("close", () => { for (const p of pending.values()) p.reject(new Error("[qlik] ws ferme")); pending.clear(); });