Handle dev TrackerHub poll timeouts in proxy
This commit is contained in:
+33
-8
@@ -10,7 +10,7 @@
|
||||
* /* → localhost:8081 (Modern.js SSR + HMR)
|
||||
*/
|
||||
import express from "express";
|
||||
import { createProxyMiddleware } from "http-proxy-middleware";
|
||||
import { createProxyMiddleware, responseInterceptor } from "http-proxy-middleware";
|
||||
import { HttpsProxyAgent } from "https-proxy-agent";
|
||||
import { execFile, spawn } from "node:child_process";
|
||||
import { resolve } from "node:path";
|
||||
@@ -159,6 +159,25 @@ function normalizeTrackerCookie(cookie) {
|
||||
return [...parts, "Path=/tracker/hub", "SameSite=Lax"].join("; ");
|
||||
}
|
||||
|
||||
function applyTrackerCookieHeaders(proxyRes, res) {
|
||||
const setCookie = proxyRes.headers["set-cookie"];
|
||||
if (Array.isArray(setCookie)) {
|
||||
res.setHeader("set-cookie", setCookie.map(normalizeTrackerCookie));
|
||||
} else if (typeof setCookie === "string") {
|
||||
res.setHeader("set-cookie", normalizeTrackerCookie(setCookie));
|
||||
}
|
||||
}
|
||||
|
||||
function isTrackerLongPollTimeout(proxyRes, req) {
|
||||
const requestUrl = req.originalUrl ?? req.url ?? "";
|
||||
return (
|
||||
req.method === "GET" &&
|
||||
(requestUrl.startsWith("/tracker/hub?id=") ||
|
||||
requestUrl.startsWith("/hub?id=")) &&
|
||||
proxyRes.statusCode === 504
|
||||
);
|
||||
}
|
||||
|
||||
// --- SignalR TrackerHub proxy ---
|
||||
// Browser-direct localhost → platform.test.aeroflot.ru fails CORS. Keep the
|
||||
// hub same-origin in development and let proxy-helper / gost route the
|
||||
@@ -170,15 +189,21 @@ const trackerProxy = createProxyMiddleware({
|
||||
ws: true,
|
||||
logLevel: "warn",
|
||||
pathRewrite: (path) => `/tracker${path}`,
|
||||
selfHandleResponse: true,
|
||||
on: {
|
||||
proxyRes(proxyRes) {
|
||||
const setCookie = proxyRes.headers["set-cookie"];
|
||||
if (Array.isArray(setCookie)) {
|
||||
proxyRes.headers["set-cookie"] = setCookie.map(normalizeTrackerCookie);
|
||||
} else if (typeof setCookie === "string") {
|
||||
proxyRes.headers["set-cookie"] = normalizeTrackerCookie(setCookie);
|
||||
proxyRes: responseInterceptor(async (buffer, proxyRes, req, res) => {
|
||||
applyTrackerCookieHeaders(proxyRes, res);
|
||||
|
||||
if (isTrackerLongPollTimeout(proxyRes, req)) {
|
||||
console.warn(`Tracker long poll timed out upstream, returning empty 200 for ${req.originalUrl ?? req.url}`);
|
||||
res.statusCode = 200;
|
||||
res.statusMessage = "OK";
|
||||
res.setHeader("content-type", "text/plain");
|
||||
return "";
|
||||
}
|
||||
},
|
||||
|
||||
return buffer;
|
||||
}),
|
||||
},
|
||||
...(SYSTEM_PROXY ? { agent: new HttpsProxyAgent(SYSTEM_PROXY) } : {}),
|
||||
});
|
||||
|
||||
@@ -224,8 +224,6 @@ export function _resetSharedConnections(): void {
|
||||
|
||||
// ---- Default builder (dynamic import) ----
|
||||
|
||||
const DEV_TRACKER_LONG_POLL_TIMEOUT_MS = 10000;
|
||||
|
||||
export function _shouldUseLongPollingForDevTrackerProxy(url: string): boolean {
|
||||
try {
|
||||
const parsed = new URL(url, "http://localhost");
|
||||
@@ -243,39 +241,15 @@ async function defaultBuildConnection(
|
||||
url: string,
|
||||
delays: number[],
|
||||
): Promise<HubConnectionLike> {
|
||||
const {
|
||||
DefaultHttpClient,
|
||||
HttpClient,
|
||||
HubConnectionBuilder,
|
||||
HttpTransportType,
|
||||
LogLevel,
|
||||
NullLogger,
|
||||
} = await import("@microsoft/signalr");
|
||||
const { HubConnectionBuilder, HttpTransportType, LogLevel } = await import(
|
||||
"@microsoft/signalr"
|
||||
);
|
||||
const builder = new HubConnectionBuilder();
|
||||
const useDevTrackerLongPolling = _shouldUseLongPollingForDevTrackerProxy(url);
|
||||
const withUrlOptions = useDevTrackerLongPolling
|
||||
? {
|
||||
transport: HttpTransportType.LongPolling,
|
||||
httpClient: new (class extends HttpClient {
|
||||
private readonly inner = new DefaultHttpClient(NullLogger.instance);
|
||||
|
||||
send(request: Parameters<InstanceType<typeof HttpClient>["send"]>[0]) {
|
||||
if (request.method !== "GET") {
|
||||
return this.inner.send(request);
|
||||
}
|
||||
|
||||
return this.inner.send({
|
||||
...request,
|
||||
timeout: DEV_TRACKER_LONG_POLL_TIMEOUT_MS,
|
||||
});
|
||||
}
|
||||
})(),
|
||||
}
|
||||
const withUrlOptions = _shouldUseLongPollingForDevTrackerProxy(url)
|
||||
? { transport: HttpTransportType.LongPolling }
|
||||
: undefined;
|
||||
|
||||
return (withUrlOptions
|
||||
? builder.withUrl(url, withUrlOptions)
|
||||
: builder.withUrl(url))
|
||||
return (withUrlOptions ? builder.withUrl(url, withUrlOptions) : builder.withUrl(url))
|
||||
.withAutomaticReconnect(delays)
|
||||
// Suppress SignalR's internal console logging. The hub is optional
|
||||
// (failures degrade to polling) and we already handle status via
|
||||
|
||||
Reference in New Issue
Block a user