webchat: improve logging and static serving
This commit is contained in:
@@ -50,6 +50,7 @@ class GatewaySocket {
|
|||||||
this.ws = ws;
|
this.ws = ws;
|
||||||
|
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
|
logStatus(`ws: open -> sending hello (${this.url})`);
|
||||||
const hello = {
|
const hello = {
|
||||||
type: "hello",
|
type: "hello",
|
||||||
minProtocol: 1,
|
minProtocol: 1,
|
||||||
@@ -65,9 +66,15 @@ class GatewaySocket {
|
|||||||
ws.send(JSON.stringify(hello));
|
ws.send(JSON.stringify(hello));
|
||||||
};
|
};
|
||||||
|
|
||||||
ws.onerror = (err) => reject(err);
|
ws.onerror = (err) => {
|
||||||
|
logStatus(`ws: error ${formatError(err)}`);
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
|
|
||||||
ws.onclose = (ev) => {
|
ws.onclose = (ev) => {
|
||||||
|
logStatus(
|
||||||
|
`ws: close code=${ev.code} reason=${ev.reason || "n/a"} clean=${ev.wasClean}`,
|
||||||
|
);
|
||||||
if (this.pending.size > 0) {
|
if (this.pending.size > 0) {
|
||||||
for (const [, p] of this.pending)
|
for (const [, p] of this.pending)
|
||||||
p.reject(new Error("gateway closed"));
|
p.reject(new Error("gateway closed"));
|
||||||
@@ -84,6 +91,9 @@ class GatewaySocket {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.type === "hello-ok") {
|
if (msg.type === "hello-ok") {
|
||||||
|
logStatus(
|
||||||
|
`ws: hello-ok presence=${msg?.snapshot?.presence?.length ?? 0} healthOk=${msg?.snapshot?.health?.ok ?? "n/a"}`,
|
||||||
|
);
|
||||||
this.handlers.set("snapshot", msg.snapshot);
|
this.handlers.set("snapshot", msg.snapshot);
|
||||||
resolve(msg);
|
resolve(msg);
|
||||||
return;
|
return;
|
||||||
@@ -267,14 +277,22 @@ const startChat = async () => {
|
|||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const sessionKey = params.get("session") || "main";
|
const sessionKey = params.get("session") || "main";
|
||||||
const wsUrl = (() => {
|
const wsUrl = (() => {
|
||||||
const u = new URL(window.location.href);
|
const loc = new URL(window.location.href);
|
||||||
u.protocol = u.protocol.replace("http", "ws");
|
const requestedPort = Number.parseInt(
|
||||||
u.port = params.get("gatewayPort") || "18789";
|
params.get("gatewayPort") ?? "",
|
||||||
u.pathname = "/";
|
10,
|
||||||
u.search = "";
|
);
|
||||||
|
const gatewayPort =
|
||||||
|
Number.isInteger(requestedPort) &&
|
||||||
|
requestedPort > 0 &&
|
||||||
|
requestedPort <= 65_535
|
||||||
|
? requestedPort
|
||||||
|
: 18_789;
|
||||||
|
const gatewayHost = params.get("gatewayHost") || loc.hostname || "127.0.0.1";
|
||||||
|
const u = new URL(`ws://${gatewayHost}:${gatewayPort}/`);
|
||||||
return u.toString();
|
return u.toString();
|
||||||
})();
|
})();
|
||||||
logStatus("boot: connecting gateway");
|
logStatus(`boot: connecting gateway (${wsUrl})`);
|
||||||
const gateway = new GatewaySocket(wsUrl);
|
const gateway = new GatewaySocket(wsUrl);
|
||||||
const hello = await gateway.connect();
|
const hello = await gateway.connect();
|
||||||
const healthOkRef = { current: Boolean(hello?.snapshot?.health?.ok ?? true) };
|
const healthOkRef = { current: Boolean(hello?.snapshot?.health?.ok ?? true) };
|
||||||
|
|||||||
@@ -196394,6 +196394,7 @@ var GatewaySocket = class {
|
|||||||
const ws = new WebSocket(this.url);
|
const ws = new WebSocket(this.url);
|
||||||
this.ws = ws;
|
this.ws = ws;
|
||||||
ws.onopen = () => {
|
ws.onopen = () => {
|
||||||
|
logStatus(`ws: open -> sending hello (${this.url})`);
|
||||||
const hello = {
|
const hello = {
|
||||||
type: "hello",
|
type: "hello",
|
||||||
minProtocol: 1,
|
minProtocol: 1,
|
||||||
@@ -196408,8 +196409,12 @@ var GatewaySocket = class {
|
|||||||
};
|
};
|
||||||
ws.send(JSON.stringify(hello));
|
ws.send(JSON.stringify(hello));
|
||||||
};
|
};
|
||||||
ws.onerror = (err) => reject(err);
|
ws.onerror = (err) => {
|
||||||
|
logStatus(`ws: error ${formatError(err)}`);
|
||||||
|
reject(err);
|
||||||
|
};
|
||||||
ws.onclose = (ev) => {
|
ws.onclose = (ev) => {
|
||||||
|
logStatus(`ws: close code=${ev.code} reason=${ev.reason || "n/a"} clean=${ev.wasClean}`);
|
||||||
if (this.pending.size > 0) {
|
if (this.pending.size > 0) {
|
||||||
for (const [, p$3] of this.pending) p$3.reject(new Error("gateway closed"));
|
for (const [, p$3] of this.pending) p$3.reject(new Error("gateway closed"));
|
||||||
this.pending.clear();
|
this.pending.clear();
|
||||||
@@ -196424,6 +196429,7 @@ var GatewaySocket = class {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (msg.type === "hello-ok") {
|
if (msg.type === "hello-ok") {
|
||||||
|
logStatus(`ws: hello-ok presence=${msg?.snapshot?.presence?.length ?? 0} healthOk=${msg?.snapshot?.health?.ok ?? "n/a"}`);
|
||||||
this.handlers.set("snapshot", msg.snapshot);
|
this.handlers.set("snapshot", msg.snapshot);
|
||||||
resolve(msg);
|
resolve(msg);
|
||||||
return;
|
return;
|
||||||
@@ -196592,14 +196598,14 @@ const startChat = async () => {
|
|||||||
const params = new URLSearchParams(window.location.search);
|
const params = new URLSearchParams(window.location.search);
|
||||||
const sessionKey = params.get("session") || "main";
|
const sessionKey = params.get("session") || "main";
|
||||||
const wsUrl = (() => {
|
const wsUrl = (() => {
|
||||||
const u$4 = new URL(window.location.href);
|
const loc = new URL(window.location.href);
|
||||||
u$4.protocol = u$4.protocol.replace("http", "ws");
|
const requestedPort = Number.parseInt(params.get("gatewayPort") ?? "", 10);
|
||||||
u$4.port = params.get("gatewayPort") || "18789";
|
const gatewayPort = Number.isInteger(requestedPort) && requestedPort > 0 && requestedPort <= 65535 ? requestedPort : 18789;
|
||||||
u$4.pathname = "/";
|
const gatewayHost = params.get("gatewayHost") || loc.hostname || "127.0.0.1";
|
||||||
u$4.search = "";
|
const u$4 = new URL(`ws://${gatewayHost}:${gatewayPort}/`);
|
||||||
return u$4.toString();
|
return u$4.toString();
|
||||||
})();
|
})();
|
||||||
logStatus("boot: connecting gateway");
|
logStatus(`boot: connecting gateway (${wsUrl})`);
|
||||||
const gateway = new GatewaySocket(wsUrl);
|
const gateway = new GatewaySocket(wsUrl);
|
||||||
const hello = await gateway.connect();
|
const hello = await gateway.connect();
|
||||||
const healthOkRef = { current: Boolean(hello?.snapshot?.health?.ok ?? true) };
|
const healthOkRef = { current: Boolean(hello?.snapshot?.health?.ok ?? true) };
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ const fetchText = (url: string) =>
|
|||||||
.on("error", reject);
|
.on("error", reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const fetchHeaders = (url: string) =>
|
||||||
|
new Promise<http.IncomingHttpHeaders>((resolve, reject) => {
|
||||||
|
http.get(url, (res) => resolve(res.headers)).on("error", reject);
|
||||||
|
});
|
||||||
|
|
||||||
describe("webchat server (static only)", () => {
|
describe("webchat server (static only)", () => {
|
||||||
test("serves index.html over loopback", { timeout: 8000 }, async () => {
|
test("serves index.html over loopback", { timeout: 8000 }, async () => {
|
||||||
const port = await getFreePort();
|
const port = await getFreePort();
|
||||||
@@ -41,4 +46,17 @@ describe("webchat server (static only)", () => {
|
|||||||
await stopWebChatServer();
|
await stopWebChatServer();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("serves bundled JS with module-friendly content type", async () => {
|
||||||
|
const port = await getFreePort();
|
||||||
|
await startWebChatServer(port);
|
||||||
|
try {
|
||||||
|
const headers = await fetchHeaders(
|
||||||
|
`http://127.0.0.1:${port}/webchat.bundle.js`,
|
||||||
|
);
|
||||||
|
expect(headers["content-type"]).toContain("application/javascript");
|
||||||
|
} finally {
|
||||||
|
await stopWebChatServer();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,6 +38,28 @@ function notFound(res: http.ServerResponse) {
|
|||||||
res.end("Not Found");
|
res.end("Not Found");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function contentTypeForExt(ext: string) {
|
||||||
|
switch (ext) {
|
||||||
|
case ".html":
|
||||||
|
return "text/html";
|
||||||
|
case ".js":
|
||||||
|
return "application/javascript";
|
||||||
|
case ".css":
|
||||||
|
return "text/css";
|
||||||
|
case ".json":
|
||||||
|
case ".map":
|
||||||
|
return "application/json";
|
||||||
|
case ".svg":
|
||||||
|
return "image/svg+xml";
|
||||||
|
case ".png":
|
||||||
|
return "image/png";
|
||||||
|
case ".ico":
|
||||||
|
return "image/x-icon";
|
||||||
|
default:
|
||||||
|
return "application/octet-stream";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function startWebChatServer(
|
export async function startWebChatServer(
|
||||||
port = WEBCHAT_DEFAULT_PORT,
|
port = WEBCHAT_DEFAULT_PORT,
|
||||||
): Promise<WebChatServerState | null> {
|
): Promise<WebChatServerState | null> {
|
||||||
@@ -67,15 +89,7 @@ export async function startWebChatServer(
|
|||||||
}
|
}
|
||||||
const data = fs.readFileSync(filePath);
|
const data = fs.readFileSync(filePath);
|
||||||
const ext = path.extname(filePath).toLowerCase();
|
const ext = path.extname(filePath).toLowerCase();
|
||||||
const type =
|
res.setHeader("Content-Type", contentTypeForExt(ext));
|
||||||
ext === ".html"
|
|
||||||
? "text/html"
|
|
||||||
: ext === ".js"
|
|
||||||
? "application/javascript"
|
|
||||||
: ext === ".css"
|
|
||||||
? "text/css"
|
|
||||||
: "application/octet-stream";
|
|
||||||
res.setHeader("Content-Type", type);
|
|
||||||
res.end(data);
|
res.end(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -93,7 +107,8 @@ export async function startWebChatServer(
|
|||||||
const filePath = path.join(root, relPath);
|
const filePath = path.join(root, relPath);
|
||||||
if (filePath.startsWith(root) && fs.existsSync(filePath)) {
|
if (filePath.startsWith(root) && fs.existsSync(filePath)) {
|
||||||
const data = fs.readFileSync(filePath);
|
const data = fs.readFileSync(filePath);
|
||||||
res.setHeader("Content-Type", "application/octet-stream");
|
const ext = path.extname(filePath).toLowerCase();
|
||||||
|
res.setHeader("Content-Type", contentTypeForExt(ext));
|
||||||
res.end(data);
|
res.end(data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -121,7 +136,6 @@ export async function startWebChatServer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
state = { server, port };
|
state = { server, port };
|
||||||
logDebug(`webchat server listening on 127.0.0.1:${port}`);
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user