Skip to content

Instantly share code, notes, and snippets.

@lawrencecchen
Created December 4, 2025 04:52
Show Gist options
  • Select an option

  • Save lawrencecchen/755a82c2b391da7c8ab24cb0c940e506 to your computer and use it in GitHub Desktop.

Select an option

Save lawrencecchen/755a82c2b391da7c8ab24cb0c940e506 to your computer and use it in GitHub Desktop.
Freestyle WebSocket repro - headers stripped
/**
* Minimal WebSocket Reproduction for Freestyle
*
* ISSUE: WebSocket connections fail through Freestyle web deployments.
*
* Expected behavior: WebSocket upgrade should work
* Actual behavior: Connection/Upgrade headers are stripped by the platform
*
* Test endpoints:
* GET /debug-headers - Shows what headers arrive (demonstrates stripped headers)
* GET /ws-echo - WebSocket echo endpoint (never receives upgrade event)
* GET / - Simple health check
*
* Test commands:
* curl https://<deployment>/debug-headers -H "Connection: Upgrade" -H "Upgrade: websocket"
* websocat wss://<deployment>/ws-echo
*/
import { createServer, type IncomingMessage, type ServerResponse } from "node:http";
import { createHash } from "node:crypto";
import type { Socket } from "node:net";
function handleRequest(req: IncomingMessage, res: ServerResponse): void {
const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
// Health check
if (url.pathname === "/") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({ status: "ok", version: "ws-repro-1.0" }));
return;
}
// Debug headers - shows what the server receives
// Use: curl <url>/debug-headers -H "Connection: Upgrade" -H "Upgrade: websocket"
if (url.pathname === "/debug-headers") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({
info: "Headers received by server",
method: req.method,
headers: req.headers,
note: "Check if 'connection' and 'upgrade' headers are present",
}, null, 2));
return;
}
// WebSocket endpoint - will never receive upgrade event if headers are stripped
if (url.pathname === "/ws-echo") {
// If we reach here, the upgrade event was NOT triggered
// This means Connection/Upgrade headers were stripped
res.writeHead(400, { "Content-Type": "application/json" });
res.end(JSON.stringify({
error: "WebSocket upgrade failed",
reason: "Server received regular HTTP request instead of WebSocket upgrade",
headers_received: {
connection: req.headers.connection,
upgrade: req.headers.upgrade,
"sec-websocket-key": req.headers["sec-websocket-key"],
},
expected: {
connection: "Upgrade",
upgrade: "websocket",
},
}, null, 2));
return;
}
res.writeHead(404, { "Content-Type": "text/plain" });
res.end("Not found");
}
const server = createServer((req, res) => {
handleRequest(req, res);
});
// WebSocket upgrade handler - should be triggered for WebSocket requests
server.on("upgrade", (req: IncomingMessage, socket: Socket, head: Buffer) => {
console.log("[WS] Upgrade event received!");
const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
if (url.pathname === "/ws-echo") {
const wsKey = req.headers["sec-websocket-key"];
if (!wsKey) {
socket.write("HTTP/1.1 400 Bad Request\r\n\r\n");
socket.destroy();
return;
}
// Calculate WebSocket accept key
const acceptKey = createHash("sha1")
.update(wsKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
.digest("base64");
// Send 101 Switching Protocols
socket.write(
"HTTP/1.1 101 Switching Protocols\r\n" +
"Upgrade: websocket\r\n" +
"Connection: Upgrade\r\n" +
`Sec-WebSocket-Accept: ${acceptKey}\r\n` +
"\r\n"
);
console.log("[WS] Upgrade successful, echoing messages");
// Echo back any data received (raw frames)
socket.on("data", (data) => {
socket.write(data);
});
socket.on("error", (err) => {
console.log("[WS] Socket error:", err.message);
});
socket.on("close", () => {
console.log("[WS] Socket closed");
});
return;
}
socket.write("HTTP/1.1 404 Not Found\r\n\r\n");
socket.destroy();
});
server.listen(3000, () => {
console.log("WebSocket repro server running on port 3000");
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment