@colyseus/uwebsockets-transport 0.17.19 → 0.17.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -304,7 +304,7 @@ var uWebSocketsTransport = class extends import_core.Transport {
|
|
|
304
304
|
(0, import_core.debugAndPrintError)(e);
|
|
305
305
|
client.error(e.code, e.message, () => {
|
|
306
306
|
try {
|
|
307
|
-
rawClient.end(reconnectionToken ? import_core.CloseCode.FAILED_TO_RECONNECT : import_core.CloseCode.WITH_ERROR);
|
|
307
|
+
rawClient.end(reconnectionToken ? import_core.isDevMode ? import_core.CloseCode.MAY_TRY_RECONNECT : import_core.CloseCode.FAILED_TO_RECONNECT : import_core.CloseCode.WITH_ERROR);
|
|
308
308
|
} catch (e2) {
|
|
309
309
|
}
|
|
310
310
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/uWebSocketsTransport.ts"],
|
|
4
|
-
"sourcesContent": ["import querystring, { type ParsedUrlQuery } from 'querystring';\nimport uWebSockets, { type WebSocket } from 'uWebSockets.js';\nimport type express from 'express';\n\nimport { type AuthContext, Transport, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom, CloseCode, type Router } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.ts';\nimport { Deferred } from '@colyseus/core';\n\nconst uWebSocketsExpress = new Deferred<typeof import('uwebsockets-express')>;\nlet uWebSocketsExpressModule: typeof import('uwebsockets-express') | undefined = undefined;\nimport('uwebsockets-express')\n .then((module) => uWebSocketsExpress.resolve(module))\n .catch((error) => uWebSocketsExpress.reject(error));\n\nexport type TransportOptions = Omit<uWebSockets.WebSocketBehavior<any>, \"upgrade\" | \"open\" | \"pong\" | \"close\" | \"message\">;\n\ntype RawWebSocketClient = uWebSockets.WebSocket<any> & {\n url: string,\n searchParams: ParsedUrlQuery,\n context: AuthContext,\n};\n\nexport class uWebSocketsTransport extends Transport {\n public app: uWebSockets.TemplatedApp;\n\n protected clients: RawWebSocketClient[] = [];\n protected clientWrappers = new WeakMap<RawWebSocketClient, uWebSocketWrapper>();\n\n private _listeningSocket: any;\n private _originalRawSend: typeof uWebSocketClient.prototype.raw | null = null;\n private _expressApp?: express.Application;\n\n constructor(options: TransportOptions = {}, appOptions: uWebSockets.AppOptions = {}) {\n super();\n\n this.app = (appOptions.cert_file_name && appOptions.key_file_name)\n ? uWebSockets.SSLApp(appOptions)\n : uWebSockets.App(appOptions);\n\n if (options.maxBackpressure === undefined) {\n options.maxBackpressure = 1024 * 1024;\n }\n\n if (options.compression === undefined) {\n options.compression = uWebSockets.DISABLED;\n }\n\n if (options.maxPayloadLength === undefined) {\n options.maxPayloadLength = 4 * 1024;\n }\n\n if (options.sendPingsAutomatically === undefined) {\n options.sendPingsAutomatically = true;\n }\n\n this.app.ws('/*', {\n ...options,\n\n upgrade: (res, req, context) => {\n // get all headers\n const headers: { [id: string]: string } = {};\n req.forEach((key, value) => headers[key] = value);\n\n const searchParams = querystring.parse(req.getQuery());\n\n /* This immediately calls open handler, you must not use res after this call */\n /* Spell these correctly */\n res.upgrade(\n {\n url: req.getUrl(),\n searchParams,\n context: {\n token: searchParams._authToken ?? getBearerToken(req.getHeader('authorization')),\n headers,\n ip: headers['x-real-ip'] ?? headers['x-forwarded-for'] ?? Buffer.from(res.getRemoteAddressAsText()).toString(),\n }\n },\n req.getHeader('sec-websocket-key'),\n req.getHeader('sec-websocket-protocol'),\n req.getHeader('sec-websocket-extensions'),\n context\n );\n },\n\n open: async (ws: WebSocket<any>) => {\n // ws.pingCount = 0;\n await this.onConnection(ws as RawWebSocketClient);\n },\n\n // pong: (ws: RawWebSocketClient) => {\n // ws.pingCount = 0;\n // },\n\n close: (ws: WebSocket<any>, code: number, message: ArrayBuffer) => {\n // remove from client list\n spliceOne(this.clients, this.clients.indexOf(ws as RawWebSocketClient));\n\n const clientWrapper = this.clientWrappers.get(ws as RawWebSocketClient);\n if (clientWrapper) {\n this.clientWrappers.delete(ws as RawWebSocketClient);\n\n // emit 'close' on wrapper\n clientWrapper.emit('close', code);\n }\n },\n\n message: (ws: WebSocket<any>, message: ArrayBuffer, isBinary: boolean) => {\n // emit 'message' on wrapper\n this.clientWrappers.get(ws as RawWebSocketClient)?.emit('message', Buffer.from(message));\n },\n\n });\n }\n\n public getExpressApp(): Promise<express.Application> | express.Application {\n if (!this._expressApp) {\n return new Promise(async (resolve, reject) => {\n try {\n const module = await uWebSocketsExpress;\n uWebSocketsExpressModule = module;\n\n // Temporarily stub `app.any` to prevent uwebsockets-express Application.init()\n // from registering its own catch-all handler \u2014 we manage HTTP routing ourselves\n // in bindRouter().\n const originalAny = this.app.any;\n this.app.any = (() => this.app) as any;\n this._expressApp = (module.default(this.app) as unknown) as express.Application;\n this.app.any = originalAny;\n resolve(this._expressApp);\n } catch (error) {\n reject(error);\n console.warn(\"\");\n console.warn(\"\u274C Error: could not initialize express.\");\n console.warn(\"\");\n console.warn(\" For Express v5, use:\");\n console.warn(\" \uD83D\uDC49 npm install --save uwebsockets-express@^2.0.1\");\n console.warn(\"\");\n console.warn(\" For Express v4, use:\");\n console.warn(\" \uD83D\uDC49 npm install --save uwebsockets-express@^1.4.1\");\n console.warn(\"\");\n process.exit();\n }\n });\n }\n return this._expressApp;\n }\n\n public bindRouter(router: Router) {\n const getCorsHeaders = (requestHeaders: Headers) => {\n return Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders(requestHeaders)\n );\n }\n\n const writeCorsHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {\n // skip if aborted\n if (res.aborted) { return; }\n\n const headers = getCorsHeaders(requestHeaders);\n\n for (const header in headers) {\n res.writeHeader(header, headers[header].toString());\n }\n\n return true;\n }\n\n this.app.options(\"/*\", (res, req) => {\n res.onAborted(() => res.aborted = true);\n\n // cache all headers\n const reqHeaders = new Headers();\n req.forEach((key, value) => reqHeaders.set(key, value));\n\n res.cork(() => {\n res.writeStatus(\"204 No Content\");\n writeCorsHeaders(res, reqHeaders);\n res.end();\n });\n });\n\n this.app.any('/*', async (res, req) => {\n const abortController = new AbortController();\n\n res.onAborted(() => {\n abortController.abort();\n res.aborted = true;\n });\n\n // cache all headers and request info synchronously\n // (uWebSockets.js req is only valid in the synchronous callback scope)\n const headers = new Headers();\n req.forEach((key, value) => headers.set(key, value));\n\n const method = req.getMethod().toUpperCase();\n const url = req.getUrl();\n const query = req.getQuery();\n const remoteAddress = res.getRemoteAddressAsText();\n\n // check if the route is defined in the router\n // if so, use the router handler, otherwise fallback to express\n if (router.findRoute(method, url) !== undefined) {\n const requestInit: RequestInit = {\n method,\n referrer: headers.get('referer') || undefined,\n keepalive: headers.get('keep-alive') === 'true',\n headers,\n signal: abortController.signal,\n };\n\n // read request body\n if (method !== \"GET\" && method !== \"HEAD\") {\n let body: Buffer = undefined;\n\n // uWebSockets.js `HttpRequest` does not provide 'getData', must aggregate POST body via HttpResponse\n await new Promise<void>((resolve) => {\n res.onData((ab, isLast) => {\n const chunk = Buffer.from(ab);\n if (body === undefined) {\n body = Buffer.from(chunk);\n } else {\n body = Buffer.concat([body, chunk]);\n }\n if (isLast) {\n resolve();\n }\n });\n });\n\n requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer;\n }\n\n const fullUrl = `http://${headers.get('host') || 'localhost'}${url}${(query ? `?${query}` : '')}`;\n const response = await router.handler(new Request(fullUrl, requestInit));\n\n // skip if aborted\n if (res.aborted) { return; }\n\n // read response body before cork (cork callback must be synchronous)\n const responseBody = await response.arrayBuffer();\n\n // writeStatus() must be called before writeHeader() in uWebSockets.js\n res.cork(() => {\n res.writeStatus(`${response.status} ${response.statusText}`);\n writeCorsHeaders(res, headers);\n response.headers.forEach((value, key) => {\n if (key.toLowerCase() !== 'content-length') {\n res.writeHeader(key, value);\n }\n });\n res.end(responseBody);\n });\n\n } else if (this._expressApp) {\n // skip if already aborted\n if (res.aborted) { return; }\n\n const corsHeaders = getCorsHeaders(headers);\n\n const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp as any, {\n headers: Object.fromEntries((headers as any).entries()),\n method,\n url,\n query,\n remoteAddress\n });\n const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);\n\n // Propagate uWS abort to the Express response wrapper.\n // When the client disconnects, mark the wrapper as finished\n // so it won't try to write to the already-aborted uWS response.\n // (fixes: \"uWS.HttpResponse must not be accessed after onAborted callback\")\n abortController.signal.addEventListener('abort', () => {\n eres.finished = true;\n // @ts-ignore\n eres.writableEnded = true;\n });\n\n // Apply CORS headers through the Express response wrapper\n for (const header in corsHeaders) {\n eres.setHeader(header, corsHeaders[header].toString());\n }\n\n // Read the request body from uWebSockets before passing to express\n // (uWebSockets requires res.onData() to be called to consume the body)\n await ereq._readBody();\n\n // skip if aborted during body read\n if (res.aborted) { return; }\n\n this._expressApp['handle'](ereq, eres);\n }\n });\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n const callback = (listeningSocket: any) => {\n this._listeningSocket = listeningSocket;\n listeningListener?.();\n };\n\n if (typeof (port) === \"string\") {\n this.app.listen_unix(callback, port);\n\n } else {\n this.app.listen(port, callback);\n\n }\n return this;\n }\n\n public shutdown() {\n if (this._listeningSocket) {\n uWebSockets.us_listen_socket_close(this._listeningSocket);\n }\n }\n\n public simulateLatency(milliseconds: number) {\n if (this._originalRawSend == null) {\n this._originalRawSend = uWebSocketClient.prototype.raw;\n }\n\n const originalRawSend = this._originalRawSend;\n uWebSocketClient.prototype.raw = milliseconds <= Number.EPSILON ? originalRawSend : function (...args: any[]) {\n // copy buffer\n let [buf, ...rest] = args;\n buf = Buffer.from(buf);\n // @ts-ignore\n setTimeout(() => originalRawSend.apply(this, [buf, ...rest]), milliseconds);\n };\n }\n\n protected async onConnection(rawClient: RawWebSocketClient) {\n const wrapper = new uWebSocketWrapper(rawClient);\n // keep reference to client and its wrapper\n this.clients.push(rawClient);\n this.clientWrappers.set(rawClient, wrapper);\n\n const url = rawClient.url;\n const searchParams = rawClient.searchParams;\n\n const sessionId = searchParams.sessionId as string;\n const processAndRoomId = url.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n // If sessionId is not provided, allow ping-pong utility.\n if (!sessionId && !roomId) {\n // Disconnect automatically after 1 second if no message is received.\n // uWS throws \"Invalid access of closed uWS.WebSocket\" if the socket closed between the readyState check and the end()/close() call.\n const timeout = setTimeout(() => {\n try { rawClient.close(); } catch (e: any) {}\n }, 1000);\n wrapper.on('message', (_) => {\n try { rawClient.send(new Uint8Array([Protocol.PING]), true); } catch (e: any) {}\n });\n wrapper.on('close', () => clearTimeout(timeout));\n return;\n }\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new uWebSocketClient(sessionId, wrapper);\n const reconnectionToken = searchParams.reconnectionToken as string;\n const skipHandshake = (searchParams.skipHandshake !== undefined);\n\n try {\n await connectClientToRoom(room, client, rawClient.context, {\n reconnectionToken,\n skipHandshake\n });\n\n } catch (e: any) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => {\n // uWS throws \"Invalid access of closed uWS.WebSocket\" if the socket closed between the readyState check and the end()/close() call.\n try {\n rawClient.end(reconnectionToken\n ? CloseCode.FAILED_TO_RECONNECT\n : CloseCode.WITH_ERROR);\n } catch (e: any) {}\n });\n }\n }\n\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAiD;AACjD,yBAA4C;AAG5C,
|
|
4
|
+
"sourcesContent": ["import querystring, { type ParsedUrlQuery } from 'querystring';\nimport uWebSockets, { type WebSocket } from 'uWebSockets.js';\nimport type express from 'express';\n\nimport { type AuthContext, Transport, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom, CloseCode, isDevMode, type Router } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.ts';\nimport { Deferred } from '@colyseus/core';\n\nconst uWebSocketsExpress = new Deferred<typeof import('uwebsockets-express')>;\nlet uWebSocketsExpressModule: typeof import('uwebsockets-express') | undefined = undefined;\nimport('uwebsockets-express')\n .then((module) => uWebSocketsExpress.resolve(module))\n .catch((error) => uWebSocketsExpress.reject(error));\n\nexport type TransportOptions = Omit<uWebSockets.WebSocketBehavior<any>, \"upgrade\" | \"open\" | \"pong\" | \"close\" | \"message\">;\n\ntype RawWebSocketClient = uWebSockets.WebSocket<any> & {\n url: string,\n searchParams: ParsedUrlQuery,\n context: AuthContext,\n};\n\nexport class uWebSocketsTransport extends Transport {\n public app: uWebSockets.TemplatedApp;\n\n protected clients: RawWebSocketClient[] = [];\n protected clientWrappers = new WeakMap<RawWebSocketClient, uWebSocketWrapper>();\n\n private _listeningSocket: any;\n private _originalRawSend: typeof uWebSocketClient.prototype.raw | null = null;\n private _expressApp?: express.Application;\n\n constructor(options: TransportOptions = {}, appOptions: uWebSockets.AppOptions = {}) {\n super();\n\n this.app = (appOptions.cert_file_name && appOptions.key_file_name)\n ? uWebSockets.SSLApp(appOptions)\n : uWebSockets.App(appOptions);\n\n if (options.maxBackpressure === undefined) {\n options.maxBackpressure = 1024 * 1024;\n }\n\n if (options.compression === undefined) {\n options.compression = uWebSockets.DISABLED;\n }\n\n if (options.maxPayloadLength === undefined) {\n options.maxPayloadLength = 4 * 1024;\n }\n\n if (options.sendPingsAutomatically === undefined) {\n options.sendPingsAutomatically = true;\n }\n\n this.app.ws('/*', {\n ...options,\n\n upgrade: (res, req, context) => {\n // get all headers\n const headers: { [id: string]: string } = {};\n req.forEach((key, value) => headers[key] = value);\n\n const searchParams = querystring.parse(req.getQuery());\n\n /* This immediately calls open handler, you must not use res after this call */\n /* Spell these correctly */\n res.upgrade(\n {\n url: req.getUrl(),\n searchParams,\n context: {\n token: searchParams._authToken ?? getBearerToken(req.getHeader('authorization')),\n headers,\n ip: headers['x-real-ip'] ?? headers['x-forwarded-for'] ?? Buffer.from(res.getRemoteAddressAsText()).toString(),\n }\n },\n req.getHeader('sec-websocket-key'),\n req.getHeader('sec-websocket-protocol'),\n req.getHeader('sec-websocket-extensions'),\n context\n );\n },\n\n open: async (ws: WebSocket<any>) => {\n // ws.pingCount = 0;\n await this.onConnection(ws as RawWebSocketClient);\n },\n\n // pong: (ws: RawWebSocketClient) => {\n // ws.pingCount = 0;\n // },\n\n close: (ws: WebSocket<any>, code: number, message: ArrayBuffer) => {\n // remove from client list\n spliceOne(this.clients, this.clients.indexOf(ws as RawWebSocketClient));\n\n const clientWrapper = this.clientWrappers.get(ws as RawWebSocketClient);\n if (clientWrapper) {\n this.clientWrappers.delete(ws as RawWebSocketClient);\n\n // emit 'close' on wrapper\n clientWrapper.emit('close', code);\n }\n },\n\n message: (ws: WebSocket<any>, message: ArrayBuffer, isBinary: boolean) => {\n // emit 'message' on wrapper\n this.clientWrappers.get(ws as RawWebSocketClient)?.emit('message', Buffer.from(message));\n },\n\n });\n }\n\n public getExpressApp(): Promise<express.Application> | express.Application {\n if (!this._expressApp) {\n return new Promise(async (resolve, reject) => {\n try {\n const module = await uWebSocketsExpress;\n uWebSocketsExpressModule = module;\n\n // Temporarily stub `app.any` to prevent uwebsockets-express Application.init()\n // from registering its own catch-all handler \u2014 we manage HTTP routing ourselves\n // in bindRouter().\n const originalAny = this.app.any;\n this.app.any = (() => this.app) as any;\n this._expressApp = (module.default(this.app) as unknown) as express.Application;\n this.app.any = originalAny;\n resolve(this._expressApp);\n } catch (error) {\n reject(error);\n console.warn(\"\");\n console.warn(\"\u274C Error: could not initialize express.\");\n console.warn(\"\");\n console.warn(\" For Express v5, use:\");\n console.warn(\" \uD83D\uDC49 npm install --save uwebsockets-express@^2.0.1\");\n console.warn(\"\");\n console.warn(\" For Express v4, use:\");\n console.warn(\" \uD83D\uDC49 npm install --save uwebsockets-express@^1.4.1\");\n console.warn(\"\");\n process.exit();\n }\n });\n }\n return this._expressApp;\n }\n\n public bindRouter(router: Router) {\n const getCorsHeaders = (requestHeaders: Headers) => {\n return Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders(requestHeaders)\n );\n }\n\n const writeCorsHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {\n // skip if aborted\n if (res.aborted) { return; }\n\n const headers = getCorsHeaders(requestHeaders);\n\n for (const header in headers) {\n res.writeHeader(header, headers[header].toString());\n }\n\n return true;\n }\n\n this.app.options(\"/*\", (res, req) => {\n res.onAborted(() => res.aborted = true);\n\n // cache all headers\n const reqHeaders = new Headers();\n req.forEach((key, value) => reqHeaders.set(key, value));\n\n res.cork(() => {\n res.writeStatus(\"204 No Content\");\n writeCorsHeaders(res, reqHeaders);\n res.end();\n });\n });\n\n this.app.any('/*', async (res, req) => {\n const abortController = new AbortController();\n\n res.onAborted(() => {\n abortController.abort();\n res.aborted = true;\n });\n\n // cache all headers and request info synchronously\n // (uWebSockets.js req is only valid in the synchronous callback scope)\n const headers = new Headers();\n req.forEach((key, value) => headers.set(key, value));\n\n const method = req.getMethod().toUpperCase();\n const url = req.getUrl();\n const query = req.getQuery();\n const remoteAddress = res.getRemoteAddressAsText();\n\n // check if the route is defined in the router\n // if so, use the router handler, otherwise fallback to express\n if (router.findRoute(method, url) !== undefined) {\n const requestInit: RequestInit = {\n method,\n referrer: headers.get('referer') || undefined,\n keepalive: headers.get('keep-alive') === 'true',\n headers,\n signal: abortController.signal,\n };\n\n // read request body\n if (method !== \"GET\" && method !== \"HEAD\") {\n let body: Buffer = undefined;\n\n // uWebSockets.js `HttpRequest` does not provide 'getData', must aggregate POST body via HttpResponse\n await new Promise<void>((resolve) => {\n res.onData((ab, isLast) => {\n const chunk = Buffer.from(ab);\n if (body === undefined) {\n body = Buffer.from(chunk);\n } else {\n body = Buffer.concat([body, chunk]);\n }\n if (isLast) {\n resolve();\n }\n });\n });\n\n requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer;\n }\n\n const fullUrl = `http://${headers.get('host') || 'localhost'}${url}${(query ? `?${query}` : '')}`;\n const response = await router.handler(new Request(fullUrl, requestInit));\n\n // skip if aborted\n if (res.aborted) { return; }\n\n // read response body before cork (cork callback must be synchronous)\n const responseBody = await response.arrayBuffer();\n\n // writeStatus() must be called before writeHeader() in uWebSockets.js\n res.cork(() => {\n res.writeStatus(`${response.status} ${response.statusText}`);\n writeCorsHeaders(res, headers);\n response.headers.forEach((value, key) => {\n if (key.toLowerCase() !== 'content-length') {\n res.writeHeader(key, value);\n }\n });\n res.end(responseBody);\n });\n\n } else if (this._expressApp) {\n // skip if already aborted\n if (res.aborted) { return; }\n\n const corsHeaders = getCorsHeaders(headers);\n\n const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp as any, {\n headers: Object.fromEntries((headers as any).entries()),\n method,\n url,\n query,\n remoteAddress\n });\n const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);\n\n // Propagate uWS abort to the Express response wrapper.\n // When the client disconnects, mark the wrapper as finished\n // so it won't try to write to the already-aborted uWS response.\n // (fixes: \"uWS.HttpResponse must not be accessed after onAborted callback\")\n abortController.signal.addEventListener('abort', () => {\n eres.finished = true;\n // @ts-ignore\n eres.writableEnded = true;\n });\n\n // Apply CORS headers through the Express response wrapper\n for (const header in corsHeaders) {\n eres.setHeader(header, corsHeaders[header].toString());\n }\n\n // Read the request body from uWebSockets before passing to express\n // (uWebSockets requires res.onData() to be called to consume the body)\n await ereq._readBody();\n\n // skip if aborted during body read\n if (res.aborted) { return; }\n\n this._expressApp['handle'](ereq, eres);\n }\n });\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n const callback = (listeningSocket: any) => {\n this._listeningSocket = listeningSocket;\n listeningListener?.();\n };\n\n if (typeof (port) === \"string\") {\n this.app.listen_unix(callback, port);\n\n } else {\n this.app.listen(port, callback);\n\n }\n return this;\n }\n\n public shutdown() {\n if (this._listeningSocket) {\n uWebSockets.us_listen_socket_close(this._listeningSocket);\n }\n }\n\n public simulateLatency(milliseconds: number) {\n if (this._originalRawSend == null) {\n this._originalRawSend = uWebSocketClient.prototype.raw;\n }\n\n const originalRawSend = this._originalRawSend;\n uWebSocketClient.prototype.raw = milliseconds <= Number.EPSILON ? originalRawSend : function (...args: any[]) {\n // copy buffer\n let [buf, ...rest] = args;\n buf = Buffer.from(buf);\n // @ts-ignore\n setTimeout(() => originalRawSend.apply(this, [buf, ...rest]), milliseconds);\n };\n }\n\n protected async onConnection(rawClient: RawWebSocketClient) {\n const wrapper = new uWebSocketWrapper(rawClient);\n // keep reference to client and its wrapper\n this.clients.push(rawClient);\n this.clientWrappers.set(rawClient, wrapper);\n\n const url = rawClient.url;\n const searchParams = rawClient.searchParams;\n\n const sessionId = searchParams.sessionId as string;\n const processAndRoomId = url.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n // If sessionId is not provided, allow ping-pong utility.\n if (!sessionId && !roomId) {\n // Disconnect automatically after 1 second if no message is received.\n // uWS throws \"Invalid access of closed uWS.WebSocket\" if the socket closed between the readyState check and the end()/close() call.\n const timeout = setTimeout(() => {\n try { rawClient.close(); } catch (e: any) {}\n }, 1000);\n wrapper.on('message', (_) => {\n try { rawClient.send(new Uint8Array([Protocol.PING]), true); } catch (e: any) {}\n });\n wrapper.on('close', () => clearTimeout(timeout));\n return;\n }\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new uWebSocketClient(sessionId, wrapper);\n const reconnectionToken = searchParams.reconnectionToken as string;\n const skipHandshake = (searchParams.skipHandshake !== undefined);\n\n try {\n await connectClientToRoom(room, client, rawClient.context, {\n reconnectionToken,\n skipHandshake\n });\n\n } catch (e: any) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => {\n // uWS throws \"Invalid access of closed uWS.WebSocket\" if the socket closed between the readyState check and the end()/close() call.\n // Use MAY_TRY_RECONNECT in devMode so the SDK retries \u2014 the seat\n // may not be reserved yet during HMR reload.\n try {\n rawClient.end(reconnectionToken\n ? (isDevMode)\n ? CloseCode.MAY_TRY_RECONNECT\n : CloseCode.FAILED_TO_RECONNECT\n : CloseCode.WITH_ERROR);\n } catch (e: any) {}\n });\n }\n }\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_core", "module", "uWebSockets", "querystring", "e"]
|
|
7
7
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// packages/transport/uwebsockets-transport/src/uWebSocketsTransport.ts
|
|
2
2
|
import querystring from "querystring";
|
|
3
3
|
import uWebSockets from "uWebSockets.js";
|
|
4
|
-
import { Transport, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom, CloseCode } from "@colyseus/core";
|
|
4
|
+
import { Transport, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom, CloseCode, isDevMode } from "@colyseus/core";
|
|
5
5
|
import { uWebSocketClient, uWebSocketWrapper } from "./uWebSocketClient.mjs";
|
|
6
6
|
import { Deferred } from "@colyseus/core";
|
|
7
7
|
var uWebSocketsExpress = new Deferred();
|
|
@@ -270,7 +270,7 @@ var uWebSocketsTransport = class extends Transport {
|
|
|
270
270
|
debugAndPrintError(e);
|
|
271
271
|
client.error(e.code, e.message, () => {
|
|
272
272
|
try {
|
|
273
|
-
rawClient.end(reconnectionToken ? CloseCode.FAILED_TO_RECONNECT : CloseCode.WITH_ERROR);
|
|
273
|
+
rawClient.end(reconnectionToken ? isDevMode ? CloseCode.MAY_TRY_RECONNECT : CloseCode.FAILED_TO_RECONNECT : CloseCode.WITH_ERROR);
|
|
274
274
|
} catch (e2) {
|
|
275
275
|
}
|
|
276
276
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/uWebSocketsTransport.ts"],
|
|
4
|
-
"sourcesContent": ["import querystring, { type ParsedUrlQuery } from 'querystring';\nimport uWebSockets, { type WebSocket } from 'uWebSockets.js';\nimport type express from 'express';\n\nimport { type AuthContext, Transport, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom, CloseCode, type Router } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.ts';\nimport { Deferred } from '@colyseus/core';\n\nconst uWebSocketsExpress = new Deferred<typeof import('uwebsockets-express')>;\nlet uWebSocketsExpressModule: typeof import('uwebsockets-express') | undefined = undefined;\nimport('uwebsockets-express')\n .then((module) => uWebSocketsExpress.resolve(module))\n .catch((error) => uWebSocketsExpress.reject(error));\n\nexport type TransportOptions = Omit<uWebSockets.WebSocketBehavior<any>, \"upgrade\" | \"open\" | \"pong\" | \"close\" | \"message\">;\n\ntype RawWebSocketClient = uWebSockets.WebSocket<any> & {\n url: string,\n searchParams: ParsedUrlQuery,\n context: AuthContext,\n};\n\nexport class uWebSocketsTransport extends Transport {\n public app: uWebSockets.TemplatedApp;\n\n protected clients: RawWebSocketClient[] = [];\n protected clientWrappers = new WeakMap<RawWebSocketClient, uWebSocketWrapper>();\n\n private _listeningSocket: any;\n private _originalRawSend: typeof uWebSocketClient.prototype.raw | null = null;\n private _expressApp?: express.Application;\n\n constructor(options: TransportOptions = {}, appOptions: uWebSockets.AppOptions = {}) {\n super();\n\n this.app = (appOptions.cert_file_name && appOptions.key_file_name)\n ? uWebSockets.SSLApp(appOptions)\n : uWebSockets.App(appOptions);\n\n if (options.maxBackpressure === undefined) {\n options.maxBackpressure = 1024 * 1024;\n }\n\n if (options.compression === undefined) {\n options.compression = uWebSockets.DISABLED;\n }\n\n if (options.maxPayloadLength === undefined) {\n options.maxPayloadLength = 4 * 1024;\n }\n\n if (options.sendPingsAutomatically === undefined) {\n options.sendPingsAutomatically = true;\n }\n\n this.app.ws('/*', {\n ...options,\n\n upgrade: (res, req, context) => {\n // get all headers\n const headers: { [id: string]: string } = {};\n req.forEach((key, value) => headers[key] = value);\n\n const searchParams = querystring.parse(req.getQuery());\n\n /* This immediately calls open handler, you must not use res after this call */\n /* Spell these correctly */\n res.upgrade(\n {\n url: req.getUrl(),\n searchParams,\n context: {\n token: searchParams._authToken ?? getBearerToken(req.getHeader('authorization')),\n headers,\n ip: headers['x-real-ip'] ?? headers['x-forwarded-for'] ?? Buffer.from(res.getRemoteAddressAsText()).toString(),\n }\n },\n req.getHeader('sec-websocket-key'),\n req.getHeader('sec-websocket-protocol'),\n req.getHeader('sec-websocket-extensions'),\n context\n );\n },\n\n open: async (ws: WebSocket<any>) => {\n // ws.pingCount = 0;\n await this.onConnection(ws as RawWebSocketClient);\n },\n\n // pong: (ws: RawWebSocketClient) => {\n // ws.pingCount = 0;\n // },\n\n close: (ws: WebSocket<any>, code: number, message: ArrayBuffer) => {\n // remove from client list\n spliceOne(this.clients, this.clients.indexOf(ws as RawWebSocketClient));\n\n const clientWrapper = this.clientWrappers.get(ws as RawWebSocketClient);\n if (clientWrapper) {\n this.clientWrappers.delete(ws as RawWebSocketClient);\n\n // emit 'close' on wrapper\n clientWrapper.emit('close', code);\n }\n },\n\n message: (ws: WebSocket<any>, message: ArrayBuffer, isBinary: boolean) => {\n // emit 'message' on wrapper\n this.clientWrappers.get(ws as RawWebSocketClient)?.emit('message', Buffer.from(message));\n },\n\n });\n }\n\n public getExpressApp(): Promise<express.Application> | express.Application {\n if (!this._expressApp) {\n return new Promise(async (resolve, reject) => {\n try {\n const module = await uWebSocketsExpress;\n uWebSocketsExpressModule = module;\n\n // Temporarily stub `app.any` to prevent uwebsockets-express Application.init()\n // from registering its own catch-all handler \u2014 we manage HTTP routing ourselves\n // in bindRouter().\n const originalAny = this.app.any;\n this.app.any = (() => this.app) as any;\n this._expressApp = (module.default(this.app) as unknown) as express.Application;\n this.app.any = originalAny;\n resolve(this._expressApp);\n } catch (error) {\n reject(error);\n console.warn(\"\");\n console.warn(\"\u274C Error: could not initialize express.\");\n console.warn(\"\");\n console.warn(\" For Express v5, use:\");\n console.warn(\" \uD83D\uDC49 npm install --save uwebsockets-express@^2.0.1\");\n console.warn(\"\");\n console.warn(\" For Express v4, use:\");\n console.warn(\" \uD83D\uDC49 npm install --save uwebsockets-express@^1.4.1\");\n console.warn(\"\");\n process.exit();\n }\n });\n }\n return this._expressApp;\n }\n\n public bindRouter(router: Router) {\n const getCorsHeaders = (requestHeaders: Headers) => {\n return Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders(requestHeaders)\n );\n }\n\n const writeCorsHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {\n // skip if aborted\n if (res.aborted) { return; }\n\n const headers = getCorsHeaders(requestHeaders);\n\n for (const header in headers) {\n res.writeHeader(header, headers[header].toString());\n }\n\n return true;\n }\n\n this.app.options(\"/*\", (res, req) => {\n res.onAborted(() => res.aborted = true);\n\n // cache all headers\n const reqHeaders = new Headers();\n req.forEach((key, value) => reqHeaders.set(key, value));\n\n res.cork(() => {\n res.writeStatus(\"204 No Content\");\n writeCorsHeaders(res, reqHeaders);\n res.end();\n });\n });\n\n this.app.any('/*', async (res, req) => {\n const abortController = new AbortController();\n\n res.onAborted(() => {\n abortController.abort();\n res.aborted = true;\n });\n\n // cache all headers and request info synchronously\n // (uWebSockets.js req is only valid in the synchronous callback scope)\n const headers = new Headers();\n req.forEach((key, value) => headers.set(key, value));\n\n const method = req.getMethod().toUpperCase();\n const url = req.getUrl();\n const query = req.getQuery();\n const remoteAddress = res.getRemoteAddressAsText();\n\n // check if the route is defined in the router\n // if so, use the router handler, otherwise fallback to express\n if (router.findRoute(method, url) !== undefined) {\n const requestInit: RequestInit = {\n method,\n referrer: headers.get('referer') || undefined,\n keepalive: headers.get('keep-alive') === 'true',\n headers,\n signal: abortController.signal,\n };\n\n // read request body\n if (method !== \"GET\" && method !== \"HEAD\") {\n let body: Buffer = undefined;\n\n // uWebSockets.js `HttpRequest` does not provide 'getData', must aggregate POST body via HttpResponse\n await new Promise<void>((resolve) => {\n res.onData((ab, isLast) => {\n const chunk = Buffer.from(ab);\n if (body === undefined) {\n body = Buffer.from(chunk);\n } else {\n body = Buffer.concat([body, chunk]);\n }\n if (isLast) {\n resolve();\n }\n });\n });\n\n requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer;\n }\n\n const fullUrl = `http://${headers.get('host') || 'localhost'}${url}${(query ? `?${query}` : '')}`;\n const response = await router.handler(new Request(fullUrl, requestInit));\n\n // skip if aborted\n if (res.aborted) { return; }\n\n // read response body before cork (cork callback must be synchronous)\n const responseBody = await response.arrayBuffer();\n\n // writeStatus() must be called before writeHeader() in uWebSockets.js\n res.cork(() => {\n res.writeStatus(`${response.status} ${response.statusText}`);\n writeCorsHeaders(res, headers);\n response.headers.forEach((value, key) => {\n if (key.toLowerCase() !== 'content-length') {\n res.writeHeader(key, value);\n }\n });\n res.end(responseBody);\n });\n\n } else if (this._expressApp) {\n // skip if already aborted\n if (res.aborted) { return; }\n\n const corsHeaders = getCorsHeaders(headers);\n\n const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp as any, {\n headers: Object.fromEntries((headers as any).entries()),\n method,\n url,\n query,\n remoteAddress\n });\n const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);\n\n // Propagate uWS abort to the Express response wrapper.\n // When the client disconnects, mark the wrapper as finished\n // so it won't try to write to the already-aborted uWS response.\n // (fixes: \"uWS.HttpResponse must not be accessed after onAborted callback\")\n abortController.signal.addEventListener('abort', () => {\n eres.finished = true;\n // @ts-ignore\n eres.writableEnded = true;\n });\n\n // Apply CORS headers through the Express response wrapper\n for (const header in corsHeaders) {\n eres.setHeader(header, corsHeaders[header].toString());\n }\n\n // Read the request body from uWebSockets before passing to express\n // (uWebSockets requires res.onData() to be called to consume the body)\n await ereq._readBody();\n\n // skip if aborted during body read\n if (res.aborted) { return; }\n\n this._expressApp['handle'](ereq, eres);\n }\n });\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n const callback = (listeningSocket: any) => {\n this._listeningSocket = listeningSocket;\n listeningListener?.();\n };\n\n if (typeof (port) === \"string\") {\n this.app.listen_unix(callback, port);\n\n } else {\n this.app.listen(port, callback);\n\n }\n return this;\n }\n\n public shutdown() {\n if (this._listeningSocket) {\n uWebSockets.us_listen_socket_close(this._listeningSocket);\n }\n }\n\n public simulateLatency(milliseconds: number) {\n if (this._originalRawSend == null) {\n this._originalRawSend = uWebSocketClient.prototype.raw;\n }\n\n const originalRawSend = this._originalRawSend;\n uWebSocketClient.prototype.raw = milliseconds <= Number.EPSILON ? originalRawSend : function (...args: any[]) {\n // copy buffer\n let [buf, ...rest] = args;\n buf = Buffer.from(buf);\n // @ts-ignore\n setTimeout(() => originalRawSend.apply(this, [buf, ...rest]), milliseconds);\n };\n }\n\n protected async onConnection(rawClient: RawWebSocketClient) {\n const wrapper = new uWebSocketWrapper(rawClient);\n // keep reference to client and its wrapper\n this.clients.push(rawClient);\n this.clientWrappers.set(rawClient, wrapper);\n\n const url = rawClient.url;\n const searchParams = rawClient.searchParams;\n\n const sessionId = searchParams.sessionId as string;\n const processAndRoomId = url.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n // If sessionId is not provided, allow ping-pong utility.\n if (!sessionId && !roomId) {\n // Disconnect automatically after 1 second if no message is received.\n // uWS throws \"Invalid access of closed uWS.WebSocket\" if the socket closed between the readyState check and the end()/close() call.\n const timeout = setTimeout(() => {\n try { rawClient.close(); } catch (e: any) {}\n }, 1000);\n wrapper.on('message', (_) => {\n try { rawClient.send(new Uint8Array([Protocol.PING]), true); } catch (e: any) {}\n });\n wrapper.on('close', () => clearTimeout(timeout));\n return;\n }\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new uWebSocketClient(sessionId, wrapper);\n const reconnectionToken = searchParams.reconnectionToken as string;\n const skipHandshake = (searchParams.skipHandshake !== undefined);\n\n try {\n await connectClientToRoom(room, client, rawClient.context, {\n reconnectionToken,\n skipHandshake\n });\n\n } catch (e: any) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => {\n // uWS throws \"Invalid access of closed uWS.WebSocket\" if the socket closed between the readyState check and the end()/close() call.\n try {\n rawClient.end(reconnectionToken\n ? CloseCode.FAILED_TO_RECONNECT\n : CloseCode.WITH_ERROR);\n } catch (e: any) {}\n });\n }\n }\n\n}\n"],
|
|
5
|
-
"mappings": ";AAAA,OAAO,iBAA0C;AACjD,OAAO,iBAAqC;AAG5C,SAA2B,WAAW,YAAY,UAAU,gBAAgB,oBAAoB,WAAW,qBAAqB,iBAA8B;
|
|
4
|
+
"sourcesContent": ["import querystring, { type ParsedUrlQuery } from 'querystring';\nimport uWebSockets, { type WebSocket } from 'uWebSockets.js';\nimport type express from 'express';\n\nimport { type AuthContext, Transport, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom, CloseCode, isDevMode, type Router } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.ts';\nimport { Deferred } from '@colyseus/core';\n\nconst uWebSocketsExpress = new Deferred<typeof import('uwebsockets-express')>;\nlet uWebSocketsExpressModule: typeof import('uwebsockets-express') | undefined = undefined;\nimport('uwebsockets-express')\n .then((module) => uWebSocketsExpress.resolve(module))\n .catch((error) => uWebSocketsExpress.reject(error));\n\nexport type TransportOptions = Omit<uWebSockets.WebSocketBehavior<any>, \"upgrade\" | \"open\" | \"pong\" | \"close\" | \"message\">;\n\ntype RawWebSocketClient = uWebSockets.WebSocket<any> & {\n url: string,\n searchParams: ParsedUrlQuery,\n context: AuthContext,\n};\n\nexport class uWebSocketsTransport extends Transport {\n public app: uWebSockets.TemplatedApp;\n\n protected clients: RawWebSocketClient[] = [];\n protected clientWrappers = new WeakMap<RawWebSocketClient, uWebSocketWrapper>();\n\n private _listeningSocket: any;\n private _originalRawSend: typeof uWebSocketClient.prototype.raw | null = null;\n private _expressApp?: express.Application;\n\n constructor(options: TransportOptions = {}, appOptions: uWebSockets.AppOptions = {}) {\n super();\n\n this.app = (appOptions.cert_file_name && appOptions.key_file_name)\n ? uWebSockets.SSLApp(appOptions)\n : uWebSockets.App(appOptions);\n\n if (options.maxBackpressure === undefined) {\n options.maxBackpressure = 1024 * 1024;\n }\n\n if (options.compression === undefined) {\n options.compression = uWebSockets.DISABLED;\n }\n\n if (options.maxPayloadLength === undefined) {\n options.maxPayloadLength = 4 * 1024;\n }\n\n if (options.sendPingsAutomatically === undefined) {\n options.sendPingsAutomatically = true;\n }\n\n this.app.ws('/*', {\n ...options,\n\n upgrade: (res, req, context) => {\n // get all headers\n const headers: { [id: string]: string } = {};\n req.forEach((key, value) => headers[key] = value);\n\n const searchParams = querystring.parse(req.getQuery());\n\n /* This immediately calls open handler, you must not use res after this call */\n /* Spell these correctly */\n res.upgrade(\n {\n url: req.getUrl(),\n searchParams,\n context: {\n token: searchParams._authToken ?? getBearerToken(req.getHeader('authorization')),\n headers,\n ip: headers['x-real-ip'] ?? headers['x-forwarded-for'] ?? Buffer.from(res.getRemoteAddressAsText()).toString(),\n }\n },\n req.getHeader('sec-websocket-key'),\n req.getHeader('sec-websocket-protocol'),\n req.getHeader('sec-websocket-extensions'),\n context\n );\n },\n\n open: async (ws: WebSocket<any>) => {\n // ws.pingCount = 0;\n await this.onConnection(ws as RawWebSocketClient);\n },\n\n // pong: (ws: RawWebSocketClient) => {\n // ws.pingCount = 0;\n // },\n\n close: (ws: WebSocket<any>, code: number, message: ArrayBuffer) => {\n // remove from client list\n spliceOne(this.clients, this.clients.indexOf(ws as RawWebSocketClient));\n\n const clientWrapper = this.clientWrappers.get(ws as RawWebSocketClient);\n if (clientWrapper) {\n this.clientWrappers.delete(ws as RawWebSocketClient);\n\n // emit 'close' on wrapper\n clientWrapper.emit('close', code);\n }\n },\n\n message: (ws: WebSocket<any>, message: ArrayBuffer, isBinary: boolean) => {\n // emit 'message' on wrapper\n this.clientWrappers.get(ws as RawWebSocketClient)?.emit('message', Buffer.from(message));\n },\n\n });\n }\n\n public getExpressApp(): Promise<express.Application> | express.Application {\n if (!this._expressApp) {\n return new Promise(async (resolve, reject) => {\n try {\n const module = await uWebSocketsExpress;\n uWebSocketsExpressModule = module;\n\n // Temporarily stub `app.any` to prevent uwebsockets-express Application.init()\n // from registering its own catch-all handler \u2014 we manage HTTP routing ourselves\n // in bindRouter().\n const originalAny = this.app.any;\n this.app.any = (() => this.app) as any;\n this._expressApp = (module.default(this.app) as unknown) as express.Application;\n this.app.any = originalAny;\n resolve(this._expressApp);\n } catch (error) {\n reject(error);\n console.warn(\"\");\n console.warn(\"\u274C Error: could not initialize express.\");\n console.warn(\"\");\n console.warn(\" For Express v5, use:\");\n console.warn(\" \uD83D\uDC49 npm install --save uwebsockets-express@^2.0.1\");\n console.warn(\"\");\n console.warn(\" For Express v4, use:\");\n console.warn(\" \uD83D\uDC49 npm install --save uwebsockets-express@^1.4.1\");\n console.warn(\"\");\n process.exit();\n }\n });\n }\n return this._expressApp;\n }\n\n public bindRouter(router: Router) {\n const getCorsHeaders = (requestHeaders: Headers) => {\n return Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders(requestHeaders)\n );\n }\n\n const writeCorsHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {\n // skip if aborted\n if (res.aborted) { return; }\n\n const headers = getCorsHeaders(requestHeaders);\n\n for (const header in headers) {\n res.writeHeader(header, headers[header].toString());\n }\n\n return true;\n }\n\n this.app.options(\"/*\", (res, req) => {\n res.onAborted(() => res.aborted = true);\n\n // cache all headers\n const reqHeaders = new Headers();\n req.forEach((key, value) => reqHeaders.set(key, value));\n\n res.cork(() => {\n res.writeStatus(\"204 No Content\");\n writeCorsHeaders(res, reqHeaders);\n res.end();\n });\n });\n\n this.app.any('/*', async (res, req) => {\n const abortController = new AbortController();\n\n res.onAborted(() => {\n abortController.abort();\n res.aborted = true;\n });\n\n // cache all headers and request info synchronously\n // (uWebSockets.js req is only valid in the synchronous callback scope)\n const headers = new Headers();\n req.forEach((key, value) => headers.set(key, value));\n\n const method = req.getMethod().toUpperCase();\n const url = req.getUrl();\n const query = req.getQuery();\n const remoteAddress = res.getRemoteAddressAsText();\n\n // check if the route is defined in the router\n // if so, use the router handler, otherwise fallback to express\n if (router.findRoute(method, url) !== undefined) {\n const requestInit: RequestInit = {\n method,\n referrer: headers.get('referer') || undefined,\n keepalive: headers.get('keep-alive') === 'true',\n headers,\n signal: abortController.signal,\n };\n\n // read request body\n if (method !== \"GET\" && method !== \"HEAD\") {\n let body: Buffer = undefined;\n\n // uWebSockets.js `HttpRequest` does not provide 'getData', must aggregate POST body via HttpResponse\n await new Promise<void>((resolve) => {\n res.onData((ab, isLast) => {\n const chunk = Buffer.from(ab);\n if (body === undefined) {\n body = Buffer.from(chunk);\n } else {\n body = Buffer.concat([body, chunk]);\n }\n if (isLast) {\n resolve();\n }\n });\n });\n\n requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer;\n }\n\n const fullUrl = `http://${headers.get('host') || 'localhost'}${url}${(query ? `?${query}` : '')}`;\n const response = await router.handler(new Request(fullUrl, requestInit));\n\n // skip if aborted\n if (res.aborted) { return; }\n\n // read response body before cork (cork callback must be synchronous)\n const responseBody = await response.arrayBuffer();\n\n // writeStatus() must be called before writeHeader() in uWebSockets.js\n res.cork(() => {\n res.writeStatus(`${response.status} ${response.statusText}`);\n writeCorsHeaders(res, headers);\n response.headers.forEach((value, key) => {\n if (key.toLowerCase() !== 'content-length') {\n res.writeHeader(key, value);\n }\n });\n res.end(responseBody);\n });\n\n } else if (this._expressApp) {\n // skip if already aborted\n if (res.aborted) { return; }\n\n const corsHeaders = getCorsHeaders(headers);\n\n const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp as any, {\n headers: Object.fromEntries((headers as any).entries()),\n method,\n url,\n query,\n remoteAddress\n });\n const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);\n\n // Propagate uWS abort to the Express response wrapper.\n // When the client disconnects, mark the wrapper as finished\n // so it won't try to write to the already-aborted uWS response.\n // (fixes: \"uWS.HttpResponse must not be accessed after onAborted callback\")\n abortController.signal.addEventListener('abort', () => {\n eres.finished = true;\n // @ts-ignore\n eres.writableEnded = true;\n });\n\n // Apply CORS headers through the Express response wrapper\n for (const header in corsHeaders) {\n eres.setHeader(header, corsHeaders[header].toString());\n }\n\n // Read the request body from uWebSockets before passing to express\n // (uWebSockets requires res.onData() to be called to consume the body)\n await ereq._readBody();\n\n // skip if aborted during body read\n if (res.aborted) { return; }\n\n this._expressApp['handle'](ereq, eres);\n }\n });\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n const callback = (listeningSocket: any) => {\n this._listeningSocket = listeningSocket;\n listeningListener?.();\n };\n\n if (typeof (port) === \"string\") {\n this.app.listen_unix(callback, port);\n\n } else {\n this.app.listen(port, callback);\n\n }\n return this;\n }\n\n public shutdown() {\n if (this._listeningSocket) {\n uWebSockets.us_listen_socket_close(this._listeningSocket);\n }\n }\n\n public simulateLatency(milliseconds: number) {\n if (this._originalRawSend == null) {\n this._originalRawSend = uWebSocketClient.prototype.raw;\n }\n\n const originalRawSend = this._originalRawSend;\n uWebSocketClient.prototype.raw = milliseconds <= Number.EPSILON ? originalRawSend : function (...args: any[]) {\n // copy buffer\n let [buf, ...rest] = args;\n buf = Buffer.from(buf);\n // @ts-ignore\n setTimeout(() => originalRawSend.apply(this, [buf, ...rest]), milliseconds);\n };\n }\n\n protected async onConnection(rawClient: RawWebSocketClient) {\n const wrapper = new uWebSocketWrapper(rawClient);\n // keep reference to client and its wrapper\n this.clients.push(rawClient);\n this.clientWrappers.set(rawClient, wrapper);\n\n const url = rawClient.url;\n const searchParams = rawClient.searchParams;\n\n const sessionId = searchParams.sessionId as string;\n const processAndRoomId = url.match(/\\/[a-zA-Z0-9_\\-]+\\/([a-zA-Z0-9_\\-]+)$/);\n const roomId = processAndRoomId && processAndRoomId[1];\n\n // If sessionId is not provided, allow ping-pong utility.\n if (!sessionId && !roomId) {\n // Disconnect automatically after 1 second if no message is received.\n // uWS throws \"Invalid access of closed uWS.WebSocket\" if the socket closed between the readyState check and the end()/close() call.\n const timeout = setTimeout(() => {\n try { rawClient.close(); } catch (e: any) {}\n }, 1000);\n wrapper.on('message', (_) => {\n try { rawClient.send(new Uint8Array([Protocol.PING]), true); } catch (e: any) {}\n });\n wrapper.on('close', () => clearTimeout(timeout));\n return;\n }\n\n const room = matchMaker.getLocalRoomById(roomId);\n const client = new uWebSocketClient(sessionId, wrapper);\n const reconnectionToken = searchParams.reconnectionToken as string;\n const skipHandshake = (searchParams.skipHandshake !== undefined);\n\n try {\n await connectClientToRoom(room, client, rawClient.context, {\n reconnectionToken,\n skipHandshake\n });\n\n } catch (e: any) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => {\n // uWS throws \"Invalid access of closed uWS.WebSocket\" if the socket closed between the readyState check and the end()/close() call.\n // Use MAY_TRY_RECONNECT in devMode so the SDK retries \u2014 the seat\n // may not be reserved yet during HMR reload.\n try {\n rawClient.end(reconnectionToken\n ? (isDevMode)\n ? CloseCode.MAY_TRY_RECONNECT\n : CloseCode.FAILED_TO_RECONNECT\n : CloseCode.WITH_ERROR);\n } catch (e: any) {}\n });\n }\n }\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["e"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colyseus/uwebsockets-transport",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"input": "./src/index.ts",
|
|
6
6
|
"main": "./build/index.cjs",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"uwebsockets-express": "^2.0.1",
|
|
28
|
-
"@colyseus/core": "^0.17.
|
|
28
|
+
"@colyseus/core": "^0.17.41"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
31
|
"@colyseus/core": "0.17.x",
|
|
@@ -2,7 +2,7 @@ import querystring, { type ParsedUrlQuery } from 'querystring';
|
|
|
2
2
|
import uWebSockets, { type WebSocket } from 'uWebSockets.js';
|
|
3
3
|
import type express from 'express';
|
|
4
4
|
|
|
5
|
-
import { type AuthContext, Transport, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom, CloseCode, type Router } from '@colyseus/core';
|
|
5
|
+
import { type AuthContext, Transport, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom, CloseCode, isDevMode, type Router } from '@colyseus/core';
|
|
6
6
|
import { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.ts';
|
|
7
7
|
import { Deferred } from '@colyseus/core';
|
|
8
8
|
|
|
@@ -376,9 +376,13 @@ export class uWebSocketsTransport extends Transport {
|
|
|
376
376
|
// send error code to client then terminate
|
|
377
377
|
client.error(e.code, e.message, () => {
|
|
378
378
|
// uWS throws "Invalid access of closed uWS.WebSocket" if the socket closed between the readyState check and the end()/close() call.
|
|
379
|
+
// Use MAY_TRY_RECONNECT in devMode so the SDK retries — the seat
|
|
380
|
+
// may not be reserved yet during HMR reload.
|
|
379
381
|
try {
|
|
380
382
|
rawClient.end(reconnectionToken
|
|
381
|
-
?
|
|
383
|
+
? (isDevMode)
|
|
384
|
+
? CloseCode.MAY_TRY_RECONNECT
|
|
385
|
+
: CloseCode.FAILED_TO_RECONNECT
|
|
382
386
|
: CloseCode.WITH_ERROR);
|
|
383
387
|
} catch (e: any) {}
|
|
384
388
|
});
|