@colyseus/uwebsockets-transport 0.17.15 → 0.17.17
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.
package/README.md
CHANGED
|
@@ -19,7 +19,21 @@
|
|
|
19
19
|
</div>
|
|
20
20
|
|
|
21
21
|
Colyseus is an Authoritative Multiplayer Framework for Node.js, with SDKs
|
|
22
|
-
available for
|
|
22
|
+
available for all major platforms and engines.
|
|
23
|
+
|
|
24
|
+
## Client SDKs
|
|
25
|
+
|
|
26
|
+
| Platform | Install | Documentation | Demo |
|
|
27
|
+
|----------|---------|---------------|------|
|
|
28
|
+
| TypeScript | `npm install @colyseus/sdk` | [Getting Started](https://docs.colyseus.io/getting-started/typescript) | [PlayCanvas](https://github.com/endel/tank-battle-multiplayer/tree/master/web-playcanvas) |
|
|
29
|
+
| React | `npm install @colyseus/react` | [Getting Started](https://docs.colyseus.io/getting-started/react) | [R3F Lobby](https://github.com/endel/r3f-lobby-car-prototype) |
|
|
30
|
+
| Unity | [Download](https://github.com/colyseus/colyseus-unity3d/releases/latest/download/Colyseus_Plugin.unitypackage) | [Getting Started](https://docs.colyseus.io/getting-started/unity) | [Tank Battle](https://github.com/endel/tank-battle-multiplayer/tree/master/unity) |
|
|
31
|
+
| Godot | [Download](https://github.com/colyseus/native-sdk/releases?q=godot+sdk&expanded=true) | [Getting Started](https://docs.colyseus.io/getting-started/godot) | [Tank Battle](https://github.com/endel/tank-battle-multiplayer/tree/master/godot) |
|
|
32
|
+
| GameMaker | [Download](https://github.com/colyseus/native-sdk/releases?q=gamemaker+sdk&expanded=true) | [Getting Started](https://docs.colyseus.io/getting-started/gamemaker) | [Tank Battle](https://github.com/endel/tank-battle-multiplayer/tree/master/gamemaker) |
|
|
33
|
+
| Defold | See documentation → | [Getting Started](https://docs.colyseus.io/getting-started/defold) | [Tank Battle](https://github.com/endel/tank-battle-multiplayer/tree/master/defold) |
|
|
34
|
+
| Construct | [Download](https://www.construct.net/en/make-games/addons/111/colyseus-multiplayer-sdk) | [Getting Started](https://docs.colyseus.io/getting-started/construct3) | [Raw Demo](https://github.com/colyseus/construct3-demo) |
|
|
35
|
+
| Haxe | `haxelib install colyseus` | [Getting Started](https://docs.colyseus.io/getting-started/haxe) | [Tank Battle](https://github.com/endel/tank-battle-multiplayer/tree/master/haxe) |
|
|
36
|
+
| C / Static Libraries | [Download](https://github.com/colyseus/native-sdk/releases?q=%22Colyseus+Native+SDK+-+Static+Library%22&expanded=true) | [GitHub](https://github.com/colyseus/native-sdk) | [raylib](https://github.com/colyseus/native-sdk/tree/main/platforms/raylib) |
|
|
23
37
|
|
|
24
38
|
The project focuses on providing synchronizable data structures for realtime and
|
|
25
39
|
turn-based games, matchmaking, and ease of usage both on the server-side and
|
|
@@ -204,11 +204,16 @@ var uWebSocketsTransport = class extends import_core.Transport {
|
|
|
204
204
|
res.writeStatus(`${response.status} ${response.statusText}`);
|
|
205
205
|
writeCorsHeaders(res, headers);
|
|
206
206
|
response.headers.forEach((value, key) => {
|
|
207
|
-
|
|
207
|
+
if (key.toLowerCase() !== "content-length") {
|
|
208
|
+
res.writeHeader(key, value);
|
|
209
|
+
}
|
|
208
210
|
});
|
|
209
211
|
res.end(responseBody);
|
|
210
212
|
});
|
|
211
213
|
} else if (this._expressApp) {
|
|
214
|
+
if (res.aborted) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
212
217
|
const corsHeaders = getCorsHeaders(headers);
|
|
213
218
|
const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp, {
|
|
214
219
|
headers: Object.fromEntries(headers.entries()),
|
|
@@ -218,10 +223,17 @@ var uWebSocketsTransport = class extends import_core.Transport {
|
|
|
218
223
|
remoteAddress
|
|
219
224
|
});
|
|
220
225
|
const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);
|
|
226
|
+
abortController.signal.addEventListener("abort", () => {
|
|
227
|
+
eres.finished = true;
|
|
228
|
+
eres.writableEnded = true;
|
|
229
|
+
});
|
|
221
230
|
for (const header in corsHeaders) {
|
|
222
231
|
eres.setHeader(header, corsHeaders[header].toString());
|
|
223
232
|
}
|
|
224
233
|
await ereq._readBody();
|
|
234
|
+
if (res.aborted) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
225
237
|
this._expressApp["handle"](ereq, eres);
|
|
226
238
|
}
|
|
227
239
|
});
|
|
@@ -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 res.writeHeader(key, value);\n });\n res.end(responseBody);\n });\n\n } else if (this._expressApp) {\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 // 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 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 const timeout = setTimeout(() => rawClient.close(), 1000);\n wrapper.on('message', (_) => rawClient.send(new Uint8Array([Protocol.PING]), true));\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 rawClient.end(reconnectionToken\n ? CloseCode.FAILED_TO_RECONNECT\n : CloseCode.WITH_ERROR));\n }\n }\n\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
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 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 const timeout = setTimeout(() => rawClient.close(), 1000);\n wrapper.on('message', (_) => rawClient.send(new Uint8Array([Protocol.PING]), true));\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 rawClient.end(reconnectionToken\n ? CloseCode.FAILED_TO_RECONNECT\n : CloseCode.WITH_ERROR));\n }\n }\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["import_core", "module", "uWebSockets", "querystring"]
|
|
7
7
|
}
|
|
@@ -170,11 +170,16 @@ var uWebSocketsTransport = class extends Transport {
|
|
|
170
170
|
res.writeStatus(`${response.status} ${response.statusText}`);
|
|
171
171
|
writeCorsHeaders(res, headers);
|
|
172
172
|
response.headers.forEach((value, key) => {
|
|
173
|
-
|
|
173
|
+
if (key.toLowerCase() !== "content-length") {
|
|
174
|
+
res.writeHeader(key, value);
|
|
175
|
+
}
|
|
174
176
|
});
|
|
175
177
|
res.end(responseBody);
|
|
176
178
|
});
|
|
177
179
|
} else if (this._expressApp) {
|
|
180
|
+
if (res.aborted) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
178
183
|
const corsHeaders = getCorsHeaders(headers);
|
|
179
184
|
const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp, {
|
|
180
185
|
headers: Object.fromEntries(headers.entries()),
|
|
@@ -184,10 +189,17 @@ var uWebSocketsTransport = class extends Transport {
|
|
|
184
189
|
remoteAddress
|
|
185
190
|
});
|
|
186
191
|
const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);
|
|
192
|
+
abortController.signal.addEventListener("abort", () => {
|
|
193
|
+
eres.finished = true;
|
|
194
|
+
eres.writableEnded = true;
|
|
195
|
+
});
|
|
187
196
|
for (const header in corsHeaders) {
|
|
188
197
|
eres.setHeader(header, corsHeaders[header].toString());
|
|
189
198
|
}
|
|
190
199
|
await ereq._readBody();
|
|
200
|
+
if (res.aborted) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
191
203
|
this._expressApp["handle"](ereq, eres);
|
|
192
204
|
}
|
|
193
205
|
});
|
|
@@ -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 res.writeHeader(key, value);\n });\n res.end(responseBody);\n });\n\n } else if (this._expressApp) {\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 // 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 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 const timeout = setTimeout(() => rawClient.close(), 1000);\n wrapper.on('message', (_) => rawClient.send(new Uint8Array([Protocol.PING]), true));\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 rawClient.end(reconnectionToken\n ? CloseCode.FAILED_TO_RECONNECT\n : CloseCode.WITH_ERROR));\n }\n }\n\n}\n"],
|
|
5
|
-
"mappings": "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
|
|
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 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 const timeout = setTimeout(() => rawClient.close(), 1000);\n wrapper.on('message', (_) => rawClient.send(new Uint8Array([Protocol.PING]), true));\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 rawClient.end(reconnectionToken\n ? CloseCode.FAILED_TO_RECONNECT\n : CloseCode.WITH_ERROR));\n }\n }\n\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
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.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"input": "./src/index.ts",
|
|
6
6
|
"main": "./build/index.cjs",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.57.0"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@colyseus/core": "^0.17.
|
|
27
|
+
"@colyseus/core": "^0.17.39"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
30
|
"@colyseus/core": "0.17.x",
|
|
@@ -246,12 +246,17 @@ export class uWebSocketsTransport extends Transport {
|
|
|
246
246
|
res.writeStatus(`${response.status} ${response.statusText}`);
|
|
247
247
|
writeCorsHeaders(res, headers);
|
|
248
248
|
response.headers.forEach((value, key) => {
|
|
249
|
-
|
|
249
|
+
if (key.toLowerCase() !== 'content-length') {
|
|
250
|
+
res.writeHeader(key, value);
|
|
251
|
+
}
|
|
250
252
|
});
|
|
251
253
|
res.end(responseBody);
|
|
252
254
|
});
|
|
253
255
|
|
|
254
256
|
} else if (this._expressApp) {
|
|
257
|
+
// skip if already aborted
|
|
258
|
+
if (res.aborted) { return; }
|
|
259
|
+
|
|
255
260
|
const corsHeaders = getCorsHeaders(headers);
|
|
256
261
|
|
|
257
262
|
const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp as any, {
|
|
@@ -263,6 +268,15 @@ export class uWebSocketsTransport extends Transport {
|
|
|
263
268
|
});
|
|
264
269
|
const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);
|
|
265
270
|
|
|
271
|
+
// Propagate uWS abort to the Express response wrapper.
|
|
272
|
+
// When the client disconnects, mark the wrapper as finished
|
|
273
|
+
// so it won't try to write to the already-aborted uWS response.
|
|
274
|
+
// (fixes: "uWS.HttpResponse must not be accessed after onAborted callback")
|
|
275
|
+
abortController.signal.addEventListener('abort', () => {
|
|
276
|
+
eres.finished = true;
|
|
277
|
+
eres.writableEnded = true;
|
|
278
|
+
});
|
|
279
|
+
|
|
266
280
|
// Apply CORS headers through the Express response wrapper
|
|
267
281
|
for (const header in corsHeaders) {
|
|
268
282
|
eres.setHeader(header, corsHeaders[header].toString());
|
|
@@ -272,6 +286,9 @@ export class uWebSocketsTransport extends Transport {
|
|
|
272
286
|
// (uWebSockets requires res.onData() to be called to consume the body)
|
|
273
287
|
await ereq._readBody();
|
|
274
288
|
|
|
289
|
+
// skip if aborted during body read
|
|
290
|
+
if (res.aborted) { return; }
|
|
291
|
+
|
|
275
292
|
this._expressApp['handle'](ereq, eres);
|
|
276
293
|
}
|
|
277
294
|
});
|