UNPKG

@openevstack/ocpp-rpc

Version:

⚡ A lightweight, production-ready RPC server built with Express and WebSocket for handling OCPP-based EV charger communication. Part of the OpenEVStack ecosystem.

103 lines (102 loc) 3.49 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isValidStatusCode = exports.parseSubprotocols = exports.abortHandshake = void 0; const http_1 = __importDefault(require("http")); /** * Gracefully abort a WebSocket handshake. */ function abortHandshake(socket, code, message, headers = {}) { var _a; if (socket.writable) { const responseMessage = (_a = message !== null && message !== void 0 ? message : http_1.default.STATUS_CODES[code]) !== null && _a !== void 0 ? _a : "Unknown Error"; const responseHeaders = { Connection: "close", "Content-Type": "text/html", "Content-Length": Buffer.byteLength(responseMessage).toString(), ...headers, }; const headerString = Object.entries(responseHeaders) .map(([k, v]) => `${k}: ${v}`) .join("\r\n"); socket.write(`HTTP/1.1 ${code} ${http_1.default.STATUS_CODES[code]}\r\n${headerString}\r\n\r\n${responseMessage}`); } socket.removeAllListeners("error"); socket.destroy(); } exports.abortHandshake = abortHandshake; /** * Character lookup table for valid WebSocket subprotocol tokens. */ const tokenChars = new Array(128).fill(0); [ 0x21, ...Array.from({ length: 10 }, (_, i) => 0x23 + i), ...Array.from({ length: 26 }, (_, i) => 0x41 + i), ...Array.from({ length: 26 }, (_, i) => 0x61 + i), 0x2d, 0x2e, 0x5f, // -, ., _ ].forEach((code) => { tokenChars[code] = 1; }); /** * Parses a `Sec-WebSocket-Protocol` header value. */ function parseSubprotocols(header) { const protocols = new Set(); let start = -1; let end = -1; let i = 0; for (; i < header.length; i++) { const code = header.charCodeAt(i); if (end === -1 && tokenChars[code] === 1) { if (start === -1) start = i; } else if (i !== 0 && (code === 0x20 /* space */ || code === 0x09) /* tab */) { if (end === -1 && start !== -1) end = i; } else if (code === 0x2c /* comma */) { if (start === -1) throw new SyntaxError(`Unexpected character at index ${i}`); if (end === -1) end = i; const protocol = header.slice(start, end); if (protocols.has(protocol)) { throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`); } protocols.add(protocol); start = end = -1; } else { throw new SyntaxError(`Unexpected character at index ${i}`); } } if (start === -1 || end !== -1) { throw new SyntaxError("Unexpected end of input"); } const protocol = header.slice(start, i); if (protocols.has(protocol)) { throw new SyntaxError(`The "${protocol}" subprotocol is duplicated`); } protocols.add(protocol); return protocols; } exports.parseSubprotocols = parseSubprotocols; /** * Validates if a status code is allowed in a WebSocket close frame. */ function isValidStatusCode(code) { return ((code >= 1000 && code <= 1014 && code !== 1004 && code !== 1005 && code !== 1006) || (code >= 3000 && code <= 4999)); } exports.isValidStatusCode = isValidStatusCode;