feedback5 R1: startServer and main become wiring — PORT / HOST / TRUST_PROXY and the option picks go to http/config.js, the session stack and the /ws upgrade to http/websocket.js, the request listener and /healthz to http/routes.js (new), the security headers and the clientError 400 to http/common.js, the banner, signals and entry check to http/boot.js
Same order of construction, listeners and headers as before; a scripted fingerprint (export list, 51 HTTP requests' status / header order / body hash, raw upgrade and malformed-request replies) and the process run (banner, port in use, SIGINT) match the pre-split server exactly. test/version.test.js reads the banner and /healthz from their new files; module headers describe each piece.
This commit is contained in:
1 parent
b48a67caec
commit
1b38e30819
11 files changed
+323
-202
No files matched your search
@@ -1,4 +1,62 @@
|
||||
// server/http/websocket.js — the WebSocket endpoint (moved from server/index.js).
|
||||
// server/http/websocket.js — the real-time side of the server:
|
||||
//
|
||||
// * session wiring: SessionRegistry (reconnect tokens) → Lobby (rooms, server/lobby.js) → Network (the socket
|
||||
// protocol, server/net.js), built from the startServer() options (config.js decides which go where);
|
||||
// * WebSocket (ws) at /ws, maxPayload 64 KB, no per-message deflate → Network.handleConnection. Refused at the
|
||||
// upgrade: any other path 404; per-network socket limit for internet clients (maxConnectionsPerAddr, see net.js
|
||||
// clientAddress; local/LAN peers are exempt) 429; server full (maxConnections) or shutting down 503.
|
||||
|
||||
import { WebSocketServer } from 'ws';
|
||||
import { Network, SessionRegistry, NET_DEFAULTS } from '../net.js';
|
||||
import { Lobby } from '../lobby.js';
|
||||
import { splitUrl } from './common.js';
|
||||
import { netOptionsFrom, lobbyOptionsFrom } from './config.js';
|
||||
|
||||
/** Inbound WebSocket frame limit (DESIGN §8). */
|
||||
export const WS_MAX_PAYLOAD = 64 * 1024;
|
||||
|
||||
/**
|
||||
* The session stack of one server.
|
||||
* @param {{ MatchClass?: Function, seedFn?: () => number, [option: string]: any }} opts startServer() options
|
||||
* @param {{ data: object, log: object }} deps the game data the lobby's matches use, the logger
|
||||
* @returns {{ registry: SessionRegistry, lobby: Lobby, network: Network }}
|
||||
*/
|
||||
export function createSessionStack(opts, { data, log }) {
|
||||
const netOptions = netOptionsFrom(opts);
|
||||
const registry = new SessionRegistry({ reconnectWindowMs: netOptions.reconnectWindowMs ?? NET_DEFAULTS.reconnectWindowMs });
|
||||
const lobbyOptions = lobbyOptionsFrom(opts);
|
||||
const lobby = new Lobby({ registry, log, MatchClass: opts.MatchClass, getData: () => data, seedFn: opts.seedFn, options: lobbyOptions });
|
||||
const network = new Network({ registry, handler: lobby, log, options: netOptions });
|
||||
return { registry, lobby, network };
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve the WebSocket endpoint /ws on `server` (its 'upgrade' event).
|
||||
* @param {import('node:http').Server} server
|
||||
* @param {{ network: Network, log: object }} deps
|
||||
* @returns {WebSocketServer}
|
||||
*/
|
||||
export function attachWebSocket(server, { network, log }) {
|
||||
const wss = new WebSocketServer({ noServer: true, maxPayload: WS_MAX_PAYLOAD, perMessageDeflate: false, clientTracking: false });
|
||||
wss.on('connection', (ws, req) => network.handleConnection(ws, req));
|
||||
wss.on('error', (e) => log.error('[ws] server error', e));
|
||||
|
||||
server.on('upgrade', (req, socket, head) => {
|
||||
socket.on('error', () => {});
|
||||
const parts = splitUrl(req.url || '/');
|
||||
const reject = (status, text) => {
|
||||
try { socket.end(`HTTP/1.1 ${status} ${text}\r\nConnection: close\r\nContent-Length: 0\r\n\r\n`); } catch { socket.destroy(); }
|
||||
};
|
||||
if (!parts || parts.rawPath !== '/ws') { reject(404, 'Not Found'); return; }
|
||||
const refused = network.admission(req);
|
||||
if (refused === 'per-address') { reject(429, 'Too Many Requests'); return; }
|
||||
if (refused) { reject(503, 'Service Unavailable'); return; }
|
||||
try {
|
||||
wss.handleUpgrade(req, socket, head, (ws) => wss.emit('connection', ws, req));
|
||||
} catch (e) {
|
||||
log.error('[ws] upgrade failed', e);
|
||||
socket.destroy();
|
||||
}
|
||||
});
|
||||
return wss;
|
||||
}
|
||||
Reference in new issue
Block a user