@colyseus/uwebsockets-transport 0.17.13 → 0.17.15

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/LICENSE CHANGED
@@ -1,6 +1,4 @@
1
- Copyright (c) 2023 Endel Dreyer
2
- Copyright (c) 2021-2022 Lucid Sight
3
- Copyright (c) 2015-2021 Endel Dreyer
1
+ Copyright (c) 2015-2026 Endel Dreyer
4
2
 
5
3
  MIT License:
6
4
 
package/README.md CHANGED
@@ -7,19 +7,19 @@
7
7
  <a href="https://npmjs.com/package/colyseus">
8
8
  <img src="https://img.shields.io/npm/dm/colyseus.svg?style=for-the-badge&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAHdElNRQfjAgETESWYxR33AAAAtElEQVQoz4WQMQrCQBRE38Z0QoTcwF4Qg1h4BO0sxGOk80iCtViksrIQRRBTewWxMI1mbELYjYu+4rPMDPtn12ChMT3gavb4US5Jym0tcBIta3oDHv4Gwmr7nC4QAxBrCdzM2q6XqUnm9m9r59h7Rc0n2pFv24k4ttGMUXW+sGELTJjSr7QDKuqLS6UKFChVWWuFkZw9Z2AAvAirKT+JTlppIRnd6XgaP4goefI2Shj++OnjB3tBmHYK8z9zAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDE5LTAyLTAxVDE4OjE3OjM3KzAxOjAwGQQixQAAACV0RVh0ZGF0ZTptb2RpZnkAMjAxOS0wMi0wMVQxODoxNzozNyswMTowMGhZmnkAAAAZdEVYdFNvZnR3YXJlAHd3dy5pbmtzY2FwZS5vcmeb7jwaAAAAAElFTkSuQmCC">
9
9
  </a>
10
- <a href="https://github.com/colyseus/colyseus/discussions" title="Discuss on Forum">
11
- <img src="https://img.shields.io/badge/discuss-on%20forum-brightgreen.svg?style=for-the-badge&colorB=0069b8&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAHdElNRQfjAgETDROxCNUzAAABB0lEQVQoz4WRvyvEARjGP193CnWRH+dHQmGwKZtFGcSmxHAL400GN95ktIpV2dzlLzDJgsGgGNRdDAzoQueS/PgY3HXHyT3T+/Y87/s89UANBKXBdoZo5J6L4K1K5ZxHfnjnlQUf3bKvkgy57a0r9hS3cXfMO1kWJMza++tj3Ac7/LY343x1NA9cNmYMwnSS/SP8JVFuSJmr44iFqvtmpjhmhBCrOOazCesq6H4P3bPBjFoIBydOk2bUA17I080Es+wSZ51B4DIA2zgjSpYcEe44Js01G0XjRcCU+y4ZMrDeLmfc9EnVd5M/o0VMeu6nJZxWJivLmhyw1WHTvrr2b4+2OFqra+ALwouTMDcqmjMAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDItMDFUMTg6MTM6MTkrMDE6MDAC9f6fAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTAyLTAxVDE4OjEzOjE5KzAxOjAwc6hGIwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAASUVORK5CYII=" alt="Discussion forum" />
12
- </a>
13
10
  <a href="http://chat.colyseus.io">
14
11
  <img src="https://img.shields.io/discord/525739117951320081.svg?style=for-the-badge&colorB=7581dc&logo=discord&logoColor=white">
15
12
  </a>
13
+ <a href="https://github.com/colyseus/colyseus/discussions" title="Discuss Forum">
14
+ <img src="https://img.shields.io/badge/discuss-forum-brightgreen.svg?style=for-the-badge&colorB=0069b8&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QAAKqNIzIAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAAHdElNRQfjAgETDROxCNUzAAABB0lEQVQoz4WRvyvEARjGP193CnWRH+dHQmGwKZtFGcSmxHAL400GN95ktIpV2dzlLzDJgsGgGNRdDAzoQueS/PgY3HXHyT3T+/Y87/s89UANBKXBdoZo5J6L4K1K5ZxHfnjnlQUf3bKvkgy57a0r9hS3cXfMO1kWJMza++tj3Ac7/LY343x1NA9cNmYMwnSS/SP8JVFuSJmr44iFqvtmpjhmhBCrOOazCesq6H4P3bPBjFoIBydOk2bUA17I080Es+wSZ51B4DIA2zgjSpYcEe44Js01G0XjRcCU+y4ZMrDeLmfc9EnVd5M/o0VMeu6nJZxWJivLmhyw1WHTvrr2b4+2OFqra+ALwouTMDcqmjMAAAAldEVYdGRhdGU6Y3JlYXRlADIwMTktMDItMDFUMTg6MTM6MTkrMDE6MDAC9f6fAAAAJXRFWHRkYXRlOm1vZGlmeQAyMDE5LTAyLTAxVDE4OjEzOjE5KzAxOjAwc6hGIwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAAASUVORK5CYII=" alt="Discussion forum" />
15
+ </a>
16
16
  <h3>
17
17
  Multiplayer Framework for Node.js. <br /><a href="https://docs.colyseus.io/">View documentation</a>
18
18
  </h3>
19
19
  </div>
20
20
 
21
- Colyseus is an Authoritative Multiplayer Framework for Node.js, with clients
22
- available for the Web, Unity3d, Defold, Haxe, and Cocos. ([See official clients](#%EF%B8%8F-official-client-integration))
21
+ Colyseus is an Authoritative Multiplayer Framework for Node.js, with SDKs
22
+ available for the Web, Unity, Defold, Haxe, Cocos and Construct3. ([See official SDKs](https://docs.colyseus.io/getting-started))
23
23
 
24
24
  The project focuses on providing synchronizable data structures for realtime and
25
25
  turn-based games, matchmaking, and ease of usage both on the server-side and
@@ -28,23 +28,24 @@ client-side.
28
28
  The mission of the framework is to be a standard netcode & matchmaking solution
29
29
  for any kind of project you can think of!
30
30
 
31
- ## Key features:
31
+ ## Why developers choose Colyseus:
32
32
 
33
- - WebSocket-based communication
34
- - Simple API in the server-side and client-side.
35
- - Automatic state synchronization from server-to-client (delta compressed)
36
- - Matchmaking clients into game rooms/sessions
37
- - Scale vertically or horizontally
33
+ - ⚡️ **Real-time state sync that just works** → Define your state on the server and it automatically synchronizes to all clients, delta-compressed and binary-encoded.
34
+ - ⚔️ **Built-in matchmaking** → Room-based architecture with filtering, queuing, and reconnection support out of the box.
35
+ - 📈 **Scalable** Go from 10 to 10,000+ CCU by scaling vertically or horizontally with Redis and load balancers.
36
+ - 🛡️ **Cheat-proof by design** → Authoritative server model ensures game logic runs on the server, not the client.
37
+ - 🛠️ **Use the tools you already know** → Built on Node.js and TypeScript with a simple, familiar API on both server and client.
38
+ - 💙 **Free forever** → MIT licensed, even for commercial games.
38
39
 
39
- See [public roadmap](https://github.com/colyseus/colyseus/wiki/Public-Roadmap) for future plans.
40
+ See [public roadmap](https://docs.colyseus.io/roadmap) for version 1.0.
40
41
 
41
42
  # 🚀 Quickstart
42
43
 
43
- Create a bare-bones Colyseus server by using `npm create colyseus-app@latest`:
44
+ Set up your own Colyseus server project for your game using `npm create colyseus-app@latest`:
44
45
 
45
46
  ```
46
- npm create colyseus-app@latest my-colyseus-server
47
- cd my-colyseus-server
47
+ npm create colyseus-app@latest ./my-server
48
+ cd my-server
48
49
  npm start
49
50
  ```
50
51
 
@@ -87,7 +88,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
87
88
  <td align="center"><a href="https://github.com/TinyDobbins"><img src="https://avatars2.githubusercontent.com/u/20824844?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nikita Borisov</b></sub></a><br /><a href="https://github.com/colyseus/colyseus/issues?q=author%3ATinyDobbins" title="Bug reports">🐛</a> <a href="https://github.com/colyseus/colyseus/commits?author=TinyDobbins" title="Code">💻</a> <a href="#business-TinyDobbins" title="Business development">💼</a> <a href="#ideas-TinyDobbins" title="Ideas, Planning, & Feedback">🤔</a></td>
88
89
  <td align="center"><a href="https://acemobe.com/"><img src="https://avatars2.githubusercontent.com/u/232101?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Phil Harvey</b></sub></a><br /><a href="https://github.com/colyseus/colyseus/commits?author=filharvey" title="Documentation">📖</a></td>
89
90
  <td align="center"><a href="https://github.com/serjek"><img src="https://avatars2.githubusercontent.com/u/18265157?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sergey</b></sub></a><br /><a href="https://github.com/colyseus/colyseus/issues?q=author%3Aserjek" title="Bug reports">🐛</a> <a href="https://github.com/colyseus/colyseus/commits?author=serjek" title="Code">💻</a></td>
90
- <td align="center"><a href="https://oyed.io"><img src="https://avatars0.githubusercontent.com/u/853683?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tom</b></sub></a><br /><a href="#question-oyed" title="Answering Questions">💬</a> <a href="https://github.com/colyseus/colyseus/issues?q=author%3Aoyed" title="Bug reports">🐛</a> <a href="#ideas-oyed" title="Ideas, Planning, & Feedback">🤔</a></td>
91
+ <td align="center"><a href="https://devlsh.com"><img src="https://avatars0.githubusercontent.com/u/853683?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sophie</b></sub></a><br /><a href="#question-devlsh" title="Answering Questions">💬</a> <a href="https://github.com/colyseus/colyseus/issues?q=author%3Adevlsh" title="Bug reports">🐛</a> <a href="#ideas-devlsh" title="Ideas, Planning, & Feedback">🤔</a></td>
91
92
  <td align="center"><a href="https://github.com/supertommy"><img src="https://avatars0.githubusercontent.com/u/2236153?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tommy Leung</b></sub></a><br /><a href="#mentoring-supertommy" title="Mentoring">🧑‍🏫</a></td>
92
93
  <td align="center"><a href="https://github.com/digimbyte"><img src="https://avatars2.githubusercontent.com/u/6645396?v=4?s=100" width="100px;" alt=""/><br /><sub><b>digimbyte</b></sub></a><br /><a href="https://github.com/colyseus/colyseus/commits?author=digimbyte" title="Documentation">📖</a></td>
93
94
  </tr>
@@ -107,9 +107,13 @@ var uWebSocketsTransport = class extends import_core.Transport {
107
107
  try {
108
108
  const module2 = await uWebSocketsExpress;
109
109
  uWebSocketsExpressModule = module2;
110
+ const originalAny = this.app.any;
111
+ this.app.any = (() => this.app);
110
112
  this._expressApp = module2.default(this.app);
113
+ this.app.any = originalAny;
111
114
  resolve(this._expressApp);
112
115
  } catch (error) {
116
+ reject(error);
113
117
  console.warn("");
114
118
  console.warn("\u274C Error: could not initialize express.");
115
119
  console.warn("");
@@ -126,15 +130,18 @@ var uWebSocketsTransport = class extends import_core.Transport {
126
130
  return this._expressApp;
127
131
  }
128
132
  bindRouter(router) {
129
- const writeHeaders = (res, requestHeaders) => {
130
- if (res.aborted) {
131
- return;
132
- }
133
- const headers = Object.assign(
133
+ const getCorsHeaders = (requestHeaders) => {
134
+ return Object.assign(
134
135
  {},
135
136
  import_core.matchMaker.controller.DEFAULT_CORS_HEADERS,
136
137
  import_core.matchMaker.controller.getCorsHeaders(requestHeaders)
137
138
  );
139
+ };
140
+ const writeCorsHeaders = (res, requestHeaders) => {
141
+ if (res.aborted) {
142
+ return;
143
+ }
144
+ const headers = getCorsHeaders(requestHeaders);
138
145
  for (const header in headers) {
139
146
  res.writeHeader(header, headers[header].toString());
140
147
  }
@@ -144,10 +151,11 @@ var uWebSocketsTransport = class extends import_core.Transport {
144
151
  res.onAborted(() => res.aborted = true);
145
152
  const reqHeaders = new Headers();
146
153
  req.forEach((key, value) => reqHeaders.set(key, value));
147
- if (writeHeaders(res, reqHeaders)) {
154
+ res.cork(() => {
148
155
  res.writeStatus("204 No Content");
156
+ writeCorsHeaders(res, reqHeaders);
149
157
  res.end();
150
- }
158
+ });
151
159
  });
152
160
  this.app.any("/*", async (res, req) => {
153
161
  const abortController = new AbortController();
@@ -157,59 +165,65 @@ var uWebSocketsTransport = class extends import_core.Transport {
157
165
  });
158
166
  const headers = new Headers();
159
167
  req.forEach((key, value) => headers.set(key, value));
160
- writeHeaders(res, headers);
161
- const requestInit = {
162
- method: req.getMethod().toUpperCase(),
163
- referrer: req.getHeader("referer"),
164
- keepalive: req.getHeader("keep-alive") === "true",
165
- headers,
166
- signal: abortController.signal
167
- };
168
+ const method = req.getMethod().toUpperCase();
168
169
  const url = req.getUrl();
169
170
  const query = req.getQuery();
170
171
  const remoteAddress = res.getRemoteAddressAsText();
171
- if (requestInit.method.toUpperCase() !== "GET" && requestInit.method.toUpperCase() !== "HEAD") {
172
- let body = void 0;
173
- await new Promise((resolve) => {
174
- res.onData((ab, isLast) => {
175
- const chunk = Buffer.from(ab);
176
- if (body === void 0) {
177
- body = Buffer.from(chunk);
178
- } else {
179
- body = Buffer.concat([body, chunk]);
180
- }
181
- if (isLast) {
182
- resolve();
183
- }
172
+ if (router.findRoute(method, url) !== void 0) {
173
+ const requestInit = {
174
+ method,
175
+ referrer: headers.get("referer") || void 0,
176
+ keepalive: headers.get("keep-alive") === "true",
177
+ headers,
178
+ signal: abortController.signal
179
+ };
180
+ if (method !== "GET" && method !== "HEAD") {
181
+ let body = void 0;
182
+ await new Promise((resolve) => {
183
+ res.onData((ab, isLast) => {
184
+ const chunk = Buffer.from(ab);
185
+ if (body === void 0) {
186
+ body = Buffer.from(chunk);
187
+ } else {
188
+ body = Buffer.concat([body, chunk]);
189
+ }
190
+ if (isLast) {
191
+ resolve();
192
+ }
193
+ });
194
+ });
195
+ requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
196
+ }
197
+ const fullUrl = `http://${headers.get("host") || "localhost"}${url}${query ? `?${query}` : ""}`;
198
+ const response = await router.handler(new Request(fullUrl, requestInit));
199
+ if (res.aborted) {
200
+ return;
201
+ }
202
+ const responseBody = await response.arrayBuffer();
203
+ res.cork(() => {
204
+ res.writeStatus(`${response.status} ${response.statusText}`);
205
+ writeCorsHeaders(res, headers);
206
+ response.headers.forEach((value, key) => {
207
+ res.writeHeader(key, value);
184
208
  });
209
+ res.end(responseBody);
185
210
  });
186
- requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
187
- }
188
- const fullUrl = `http://${headers.get("host") || "localhost"}${url}${query ? `?${query}` : ""}`;
189
- const response = await router.handler(new Request(fullUrl, requestInit));
190
- if (response.status === 404 && this._expressApp) {
211
+ } else if (this._expressApp) {
212
+ const corsHeaders = getCorsHeaders(headers);
191
213
  const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp, {
192
214
  headers: Object.fromEntries(headers.entries()),
193
- method: requestInit.method,
215
+ method,
194
216
  url,
195
217
  query,
196
218
  remoteAddress
197
219
  });
198
220
  const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);
221
+ for (const header in corsHeaders) {
222
+ eres.setHeader(header, corsHeaders[header].toString());
223
+ }
224
+ await ereq._readBody();
199
225
  this._expressApp["handle"](ereq, eres);
200
- return;
201
226
  }
202
- if (res.aborted) {
203
- return;
204
- }
205
- const responseBody = await response.arrayBuffer();
206
- res.cork(() => {
207
- res.writeStatus(`${response.status} ${response.statusText}`);
208
- response.headers.forEach((value, key) => {
209
- res.writeHeader(key, value);
210
- });
211
- res.end(responseBody);
212
- });
213
227
  });
214
228
  }
215
229
  listen(port, hostname, backlog, listeningListener) {
@@ -269,127 +283,6 @@ var uWebSocketsTransport = class extends import_core.Transport {
269
283
  client.error(e.code, e.message, () => rawClient.end(reconnectionToken ? import_core.CloseCode.FAILED_TO_RECONNECT : import_core.CloseCode.WITH_ERROR));
270
284
  }
271
285
  }
272
- // protected registerMatchMakeRequest() {
273
- // const matchmakeRoute = 'matchmake';
274
- // const allowedRoomNameChars = /([a-zA-Z_\-0-9]+)/gi;
275
- // const writeHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {
276
- // // skip if aborted
277
- // if (res.aborted) { return; }
278
- // const headers = Object.assign(
279
- // {},
280
- // matchMaker.controller.DEFAULT_CORS_HEADERS,
281
- // matchMaker.controller.getCorsHeaders(requestHeaders)
282
- // );
283
- // for (const header in headers) {
284
- // res.writeHeader(header, headers[header].toString());
285
- // }
286
- // return true;
287
- // }
288
- // const writeError = (res: uWebSockets.HttpResponse, error: { code: number, error: string }) => {
289
- // // skip if aborted
290
- // if (res.aborted) { return; }
291
- // res.cork(() => {
292
- // res.writeStatus("406 Not Acceptable");
293
- // res.end(JSON.stringify(error));
294
- // });
295
- // }
296
- // const onAborted = (res: uWebSockets.HttpResponse) => {
297
- // res.aborted = true;
298
- // };
299
- // this.app.options("/matchmake/*", (res, req) => {
300
- // res.onAborted(() => onAborted(res));
301
- // // cache all headers
302
- // const reqHeaders = new Headers();
303
- // req.forEach((key, value) => reqHeaders.set(key, value));
304
- // if (writeHeaders(res, reqHeaders)) {
305
- // res.writeStatus("204 No Content");
306
- // res.end();
307
- // }
308
- // });
309
- // // @ts-ignore
310
- // this.app.post("/matchmake/*", (res, req) => {
311
- // res.onAborted(() => onAborted(res));
312
- // // do not accept matchmaking requests if already shutting down
313
- // if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {
314
- // return res.close();
315
- // }
316
- // // cache all headers
317
- // const headers = new Headers();
318
- // req.forEach((key, value) => headers.set(key, value));
319
- // writeHeaders(res, headers);
320
- // res.writeHeader('Content-Type', 'application/json');
321
- // const url = req.getUrl();
322
- // const matchedParams = url.match(allowedRoomNameChars);
323
- // const matchmakeIndex = matchedParams.indexOf(matchmakeRoute);
324
- // const token = getBearerToken(headers['authorization']);
325
- // // read json body
326
- // this.readJson(res, async (clientOptions) => {
327
- // try {
328
- // if (clientOptions === undefined) {
329
- // throw new Error("invalid JSON input");
330
- // }
331
- // const method = matchedParams[matchmakeIndex + 1];
332
- // const roomName = matchedParams[matchmakeIndex + 2] || '';
333
- // const response = await matchMaker.controller.invokeMethod(
334
- // method,
335
- // roomName,
336
- // clientOptions,
337
- // {
338
- // token,
339
- // headers,
340
- // ip: headers.get('x-real-ip') ?? headers.get('x-forwarded-for') ?? Buffer.from(res.getRemoteAddressAsText()).toString()
341
- // }
342
- // );
343
- // if (!res.aborted) {
344
- // res.cork(() => {
345
- // res.writeStatus("200 OK");
346
- // res.end(JSON.stringify(response));
347
- // });
348
- // }
349
- // } catch (e: any) {
350
- // debugAndPrintError(e);
351
- // writeError(res, {
352
- // code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,
353
- // error: e.message
354
- // });
355
- // }
356
- // });
357
- // });
358
- // }
359
- /* Helper function for reading a posted JSON body */
360
- /* Extracted from https://github.com/uNetworking/uWebSockets.js/blob/master/examples/JsonPost.js */
361
- readJson(res, cb) {
362
- let buffer;
363
- res.onData((ab, isLast) => {
364
- let chunk = Buffer.from(ab);
365
- if (isLast) {
366
- let json;
367
- if (buffer) {
368
- try {
369
- json = JSON.parse(Buffer.concat([buffer, chunk]));
370
- } catch (e) {
371
- cb(void 0);
372
- return;
373
- }
374
- cb(json);
375
- } else {
376
- try {
377
- json = JSON.parse(chunk);
378
- } catch (e) {
379
- cb(void 0);
380
- return;
381
- }
382
- cb(json);
383
- }
384
- } else {
385
- if (buffer) {
386
- buffer = Buffer.concat([buffer, chunk]);
387
- } else {
388
- buffer = Buffer.concat([chunk]);
389
- }
390
- }
391
- });
392
- }
393
286
  };
394
287
  // Annotate the CommonJS export names for ESM import in node:
395
288
  0 && (module.exports = {
@@ -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 this._expressApp = (module.default(this.app) as unknown) as express.Application;\n resolve(this._expressApp);\n } catch (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 writeHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {\n // skip if aborted\n if (res.aborted) { return; }\n\n const headers = Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders(requestHeaders)\n );\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 if (writeHeaders(res, reqHeaders)) {\n res.writeStatus(\"204 No Content\");\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 const headers = new Headers();\n req.forEach((key, value) => headers.set(key, value));\n\n // write cors headers\n writeHeaders(res, headers);\n\n const requestInit: RequestInit = {\n method: req.getMethod().toUpperCase(),\n referrer: req.getHeader('referer'),\n keepalive: req.getHeader('keep-alive') === 'true',\n headers,\n signal: abortController.signal,\n };\n\n // Construct full URL (Request constructor requires absolute URL)\n const url = req.getUrl();\n const query = req.getQuery();\n const remoteAddress = res.getRemoteAddressAsText();\n\n // read request body\n if (requestInit.method.toUpperCase() !== \"GET\" && requestInit.method.toUpperCase() !== \"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 // fallback to express stack if 404\n if (response.status === 404 && this._expressApp) {\n const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp as any, {\n headers: Object.fromEntries((headers as any).entries()),\n method: requestInit.method,\n url,\n query,\n remoteAddress\n });\n const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);\n this._expressApp['handle'](ereq, eres);\n return;\n }\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 res.cork(() => {\n res.writeStatus(`${response.status} ${response.statusText}`);\n response.headers.forEach((value, key) => {\n res.writeHeader(key, value);\n });\n res.end(responseBody);\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 // protected registerMatchMakeRequest() {\n // const matchmakeRoute = 'matchmake';\n // const allowedRoomNameChars = /([a-zA-Z_\\-0-9]+)/gi;\n\n // const writeHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {\n // // skip if aborted\n // if (res.aborted) { return; }\n\n // const headers = Object.assign(\n // {},\n // matchMaker.controller.DEFAULT_CORS_HEADERS,\n // matchMaker.controller.getCorsHeaders(requestHeaders)\n // );\n\n // for (const header in headers) {\n // res.writeHeader(header, headers[header].toString());\n // }\n\n // return true;\n // }\n\n // const writeError = (res: uWebSockets.HttpResponse, error: { code: number, error: string }) => {\n // // skip if aborted\n // if (res.aborted) { return; }\n\n // res.cork(() => {\n // res.writeStatus(\"406 Not Acceptable\");\n // res.end(JSON.stringify(error));\n // });\n // }\n\n // const onAborted = (res: uWebSockets.HttpResponse) => {\n // res.aborted = true;\n // };\n\n // this.app.options(\"/matchmake/*\", (res, req) => {\n // res.onAborted(() => onAborted(res));\n\n // // cache all headers\n // const reqHeaders = new Headers();\n // req.forEach((key, value) => reqHeaders.set(key, value));\n\n // if (writeHeaders(res, reqHeaders)) {\n // res.writeStatus(\"204 No Content\");\n // res.end();\n // }\n // });\n\n\n // // @ts-ignore\n // this.app.post(\"/matchmake/*\", (res, req) => {\n // res.onAborted(() => onAborted(res));\n\n // // do not accept matchmaking requests if already shutting down\n // if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\n // return res.close();\n // }\n\n // // cache all headers\n // const headers = new Headers();\n // req.forEach((key, value) => headers.set(key, value));\n\n // writeHeaders(res, headers);\n // res.writeHeader('Content-Type', 'application/json');\n\n // const url = req.getUrl();\n // const matchedParams = url.match(allowedRoomNameChars);\n // const matchmakeIndex = matchedParams.indexOf(matchmakeRoute);\n\n // const token = getBearerToken(headers['authorization']);\n\n // // read json body\n // this.readJson(res, async (clientOptions) => {\n // try {\n // if (clientOptions === undefined) {\n // throw new Error(\"invalid JSON input\");\n // }\n\n // const method = matchedParams[matchmakeIndex + 1];\n // const roomName = matchedParams[matchmakeIndex + 2] || '';\n\n // const response = await matchMaker.controller.invokeMethod(\n // method,\n // roomName,\n // clientOptions,\n // {\n // token,\n // headers,\n // ip: headers.get('x-real-ip') ?? headers.get('x-forwarded-for') ?? Buffer.from(res.getRemoteAddressAsText()).toString()\n // }\n // );\n\n // if (!res.aborted) {\n // res.cork(() => {\n // res.writeStatus(\"200 OK\");\n // res.end(JSON.stringify(response));\n // });\n // }\n\n // } catch (e: any) {\n // debugAndPrintError(e);\n // writeError(res, {\n // code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n // error: e.message\n // });\n // }\n\n // });\n // });\n // }\n\n /* Helper function for reading a posted JSON body */\n /* Extracted from https://github.com/uNetworking/uWebSockets.js/blob/master/examples/JsonPost.js */\n private readJson(res: uWebSockets.HttpResponse, cb: (json: any) => void) {\n let buffer: Buffer;\n /* Register data cb */\n res.onData((ab, isLast) => {\n let chunk = Buffer.from(ab);\n if (isLast) {\n let json;\n if (buffer) {\n try {\n // @ts-ignore\n json = JSON.parse(Buffer.concat([buffer, chunk]));\n } catch (e) {\n /* res.close calls onAborted */\n // res.close();\n cb(undefined);\n return;\n }\n cb(json);\n } else {\n try {\n // @ts-ignore\n json = JSON.parse(chunk);\n } catch (e) {\n /* res.close calls onAborted */\n // res.close();\n cb(undefined);\n return;\n }\n cb(json);\n }\n } else {\n if (buffer) {\n buffer = Buffer.concat([buffer, chunk]);\n } else {\n buffer = Buffer.concat([chunk]);\n }\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 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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",
6
6
  "names": ["import_core", "module", "uWebSockets", "querystring"]
7
7
  }
@@ -23,6 +23,5 @@ export declare class uWebSocketsTransport extends Transport {
23
23
  shutdown(): void;
24
24
  simulateLatency(milliseconds: number): void;
25
25
  protected onConnection(rawClient: RawWebSocketClient): Promise<void>;
26
- private readJson;
27
26
  }
28
27
  export {};
@@ -73,9 +73,13 @@ var uWebSocketsTransport = class extends Transport {
73
73
  try {
74
74
  const module = await uWebSocketsExpress;
75
75
  uWebSocketsExpressModule = module;
76
+ const originalAny = this.app.any;
77
+ this.app.any = (() => this.app);
76
78
  this._expressApp = module.default(this.app);
79
+ this.app.any = originalAny;
77
80
  resolve(this._expressApp);
78
81
  } catch (error) {
82
+ reject(error);
79
83
  console.warn("");
80
84
  console.warn("\u274C Error: could not initialize express.");
81
85
  console.warn("");
@@ -92,15 +96,18 @@ var uWebSocketsTransport = class extends Transport {
92
96
  return this._expressApp;
93
97
  }
94
98
  bindRouter(router) {
95
- const writeHeaders = (res, requestHeaders) => {
96
- if (res.aborted) {
97
- return;
98
- }
99
- const headers = Object.assign(
99
+ const getCorsHeaders = (requestHeaders) => {
100
+ return Object.assign(
100
101
  {},
101
102
  matchMaker.controller.DEFAULT_CORS_HEADERS,
102
103
  matchMaker.controller.getCorsHeaders(requestHeaders)
103
104
  );
105
+ };
106
+ const writeCorsHeaders = (res, requestHeaders) => {
107
+ if (res.aborted) {
108
+ return;
109
+ }
110
+ const headers = getCorsHeaders(requestHeaders);
104
111
  for (const header in headers) {
105
112
  res.writeHeader(header, headers[header].toString());
106
113
  }
@@ -110,10 +117,11 @@ var uWebSocketsTransport = class extends Transport {
110
117
  res.onAborted(() => res.aborted = true);
111
118
  const reqHeaders = new Headers();
112
119
  req.forEach((key, value) => reqHeaders.set(key, value));
113
- if (writeHeaders(res, reqHeaders)) {
120
+ res.cork(() => {
114
121
  res.writeStatus("204 No Content");
122
+ writeCorsHeaders(res, reqHeaders);
115
123
  res.end();
116
- }
124
+ });
117
125
  });
118
126
  this.app.any("/*", async (res, req) => {
119
127
  const abortController = new AbortController();
@@ -123,59 +131,65 @@ var uWebSocketsTransport = class extends Transport {
123
131
  });
124
132
  const headers = new Headers();
125
133
  req.forEach((key, value) => headers.set(key, value));
126
- writeHeaders(res, headers);
127
- const requestInit = {
128
- method: req.getMethod().toUpperCase(),
129
- referrer: req.getHeader("referer"),
130
- keepalive: req.getHeader("keep-alive") === "true",
131
- headers,
132
- signal: abortController.signal
133
- };
134
+ const method = req.getMethod().toUpperCase();
134
135
  const url = req.getUrl();
135
136
  const query = req.getQuery();
136
137
  const remoteAddress = res.getRemoteAddressAsText();
137
- if (requestInit.method.toUpperCase() !== "GET" && requestInit.method.toUpperCase() !== "HEAD") {
138
- let body = void 0;
139
- await new Promise((resolve) => {
140
- res.onData((ab, isLast) => {
141
- const chunk = Buffer.from(ab);
142
- if (body === void 0) {
143
- body = Buffer.from(chunk);
144
- } else {
145
- body = Buffer.concat([body, chunk]);
146
- }
147
- if (isLast) {
148
- resolve();
149
- }
138
+ if (router.findRoute(method, url) !== void 0) {
139
+ const requestInit = {
140
+ method,
141
+ referrer: headers.get("referer") || void 0,
142
+ keepalive: headers.get("keep-alive") === "true",
143
+ headers,
144
+ signal: abortController.signal
145
+ };
146
+ if (method !== "GET" && method !== "HEAD") {
147
+ let body = void 0;
148
+ await new Promise((resolve) => {
149
+ res.onData((ab, isLast) => {
150
+ const chunk = Buffer.from(ab);
151
+ if (body === void 0) {
152
+ body = Buffer.from(chunk);
153
+ } else {
154
+ body = Buffer.concat([body, chunk]);
155
+ }
156
+ if (isLast) {
157
+ resolve();
158
+ }
159
+ });
160
+ });
161
+ requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
162
+ }
163
+ const fullUrl = `http://${headers.get("host") || "localhost"}${url}${query ? `?${query}` : ""}`;
164
+ const response = await router.handler(new Request(fullUrl, requestInit));
165
+ if (res.aborted) {
166
+ return;
167
+ }
168
+ const responseBody = await response.arrayBuffer();
169
+ res.cork(() => {
170
+ res.writeStatus(`${response.status} ${response.statusText}`);
171
+ writeCorsHeaders(res, headers);
172
+ response.headers.forEach((value, key) => {
173
+ res.writeHeader(key, value);
150
174
  });
175
+ res.end(responseBody);
151
176
  });
152
- requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength);
153
- }
154
- const fullUrl = `http://${headers.get("host") || "localhost"}${url}${query ? `?${query}` : ""}`;
155
- const response = await router.handler(new Request(fullUrl, requestInit));
156
- if (response.status === 404 && this._expressApp) {
177
+ } else if (this._expressApp) {
178
+ const corsHeaders = getCorsHeaders(headers);
157
179
  const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp, {
158
180
  headers: Object.fromEntries(headers.entries()),
159
- method: requestInit.method,
181
+ method,
160
182
  url,
161
183
  query,
162
184
  remoteAddress
163
185
  });
164
186
  const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);
187
+ for (const header in corsHeaders) {
188
+ eres.setHeader(header, corsHeaders[header].toString());
189
+ }
190
+ await ereq._readBody();
165
191
  this._expressApp["handle"](ereq, eres);
166
- return;
167
192
  }
168
- if (res.aborted) {
169
- return;
170
- }
171
- const responseBody = await response.arrayBuffer();
172
- res.cork(() => {
173
- res.writeStatus(`${response.status} ${response.statusText}`);
174
- response.headers.forEach((value, key) => {
175
- res.writeHeader(key, value);
176
- });
177
- res.end(responseBody);
178
- });
179
193
  });
180
194
  }
181
195
  listen(port, hostname, backlog, listeningListener) {
@@ -235,127 +249,6 @@ var uWebSocketsTransport = class extends Transport {
235
249
  client.error(e.code, e.message, () => rawClient.end(reconnectionToken ? CloseCode.FAILED_TO_RECONNECT : CloseCode.WITH_ERROR));
236
250
  }
237
251
  }
238
- // protected registerMatchMakeRequest() {
239
- // const matchmakeRoute = 'matchmake';
240
- // const allowedRoomNameChars = /([a-zA-Z_\-0-9]+)/gi;
241
- // const writeHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {
242
- // // skip if aborted
243
- // if (res.aborted) { return; }
244
- // const headers = Object.assign(
245
- // {},
246
- // matchMaker.controller.DEFAULT_CORS_HEADERS,
247
- // matchMaker.controller.getCorsHeaders(requestHeaders)
248
- // );
249
- // for (const header in headers) {
250
- // res.writeHeader(header, headers[header].toString());
251
- // }
252
- // return true;
253
- // }
254
- // const writeError = (res: uWebSockets.HttpResponse, error: { code: number, error: string }) => {
255
- // // skip if aborted
256
- // if (res.aborted) { return; }
257
- // res.cork(() => {
258
- // res.writeStatus("406 Not Acceptable");
259
- // res.end(JSON.stringify(error));
260
- // });
261
- // }
262
- // const onAborted = (res: uWebSockets.HttpResponse) => {
263
- // res.aborted = true;
264
- // };
265
- // this.app.options("/matchmake/*", (res, req) => {
266
- // res.onAborted(() => onAborted(res));
267
- // // cache all headers
268
- // const reqHeaders = new Headers();
269
- // req.forEach((key, value) => reqHeaders.set(key, value));
270
- // if (writeHeaders(res, reqHeaders)) {
271
- // res.writeStatus("204 No Content");
272
- // res.end();
273
- // }
274
- // });
275
- // // @ts-ignore
276
- // this.app.post("/matchmake/*", (res, req) => {
277
- // res.onAborted(() => onAborted(res));
278
- // // do not accept matchmaking requests if already shutting down
279
- // if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {
280
- // return res.close();
281
- // }
282
- // // cache all headers
283
- // const headers = new Headers();
284
- // req.forEach((key, value) => headers.set(key, value));
285
- // writeHeaders(res, headers);
286
- // res.writeHeader('Content-Type', 'application/json');
287
- // const url = req.getUrl();
288
- // const matchedParams = url.match(allowedRoomNameChars);
289
- // const matchmakeIndex = matchedParams.indexOf(matchmakeRoute);
290
- // const token = getBearerToken(headers['authorization']);
291
- // // read json body
292
- // this.readJson(res, async (clientOptions) => {
293
- // try {
294
- // if (clientOptions === undefined) {
295
- // throw new Error("invalid JSON input");
296
- // }
297
- // const method = matchedParams[matchmakeIndex + 1];
298
- // const roomName = matchedParams[matchmakeIndex + 2] || '';
299
- // const response = await matchMaker.controller.invokeMethod(
300
- // method,
301
- // roomName,
302
- // clientOptions,
303
- // {
304
- // token,
305
- // headers,
306
- // ip: headers.get('x-real-ip') ?? headers.get('x-forwarded-for') ?? Buffer.from(res.getRemoteAddressAsText()).toString()
307
- // }
308
- // );
309
- // if (!res.aborted) {
310
- // res.cork(() => {
311
- // res.writeStatus("200 OK");
312
- // res.end(JSON.stringify(response));
313
- // });
314
- // }
315
- // } catch (e: any) {
316
- // debugAndPrintError(e);
317
- // writeError(res, {
318
- // code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,
319
- // error: e.message
320
- // });
321
- // }
322
- // });
323
- // });
324
- // }
325
- /* Helper function for reading a posted JSON body */
326
- /* Extracted from https://github.com/uNetworking/uWebSockets.js/blob/master/examples/JsonPost.js */
327
- readJson(res, cb) {
328
- let buffer;
329
- res.onData((ab, isLast) => {
330
- let chunk = Buffer.from(ab);
331
- if (isLast) {
332
- let json;
333
- if (buffer) {
334
- try {
335
- json = JSON.parse(Buffer.concat([buffer, chunk]));
336
- } catch (e) {
337
- cb(void 0);
338
- return;
339
- }
340
- cb(json);
341
- } else {
342
- try {
343
- json = JSON.parse(chunk);
344
- } catch (e) {
345
- cb(void 0);
346
- return;
347
- }
348
- cb(json);
349
- }
350
- } else {
351
- if (buffer) {
352
- buffer = Buffer.concat([buffer, chunk]);
353
- } else {
354
- buffer = Buffer.concat([chunk]);
355
- }
356
- }
357
- });
358
- }
359
252
  };
360
253
  export {
361
254
  uWebSocketsTransport
@@ -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 this._expressApp = (module.default(this.app) as unknown) as express.Application;\n resolve(this._expressApp);\n } catch (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 writeHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {\n // skip if aborted\n if (res.aborted) { return; }\n\n const headers = Object.assign(\n {},\n matchMaker.controller.DEFAULT_CORS_HEADERS,\n matchMaker.controller.getCorsHeaders(requestHeaders)\n );\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 if (writeHeaders(res, reqHeaders)) {\n res.writeStatus(\"204 No Content\");\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 const headers = new Headers();\n req.forEach((key, value) => headers.set(key, value));\n\n // write cors headers\n writeHeaders(res, headers);\n\n const requestInit: RequestInit = {\n method: req.getMethod().toUpperCase(),\n referrer: req.getHeader('referer'),\n keepalive: req.getHeader('keep-alive') === 'true',\n headers,\n signal: abortController.signal,\n };\n\n // Construct full URL (Request constructor requires absolute URL)\n const url = req.getUrl();\n const query = req.getQuery();\n const remoteAddress = res.getRemoteAddressAsText();\n\n // read request body\n if (requestInit.method.toUpperCase() !== \"GET\" && requestInit.method.toUpperCase() !== \"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 // fallback to express stack if 404\n if (response.status === 404 && this._expressApp) {\n const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp as any, {\n headers: Object.fromEntries((headers as any).entries()),\n method: requestInit.method,\n url,\n query,\n remoteAddress\n });\n const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);\n this._expressApp['handle'](ereq, eres);\n return;\n }\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 res.cork(() => {\n res.writeStatus(`${response.status} ${response.statusText}`);\n response.headers.forEach((value, key) => {\n res.writeHeader(key, value);\n });\n res.end(responseBody);\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 // protected registerMatchMakeRequest() {\n // const matchmakeRoute = 'matchmake';\n // const allowedRoomNameChars = /([a-zA-Z_\\-0-9]+)/gi;\n\n // const writeHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {\n // // skip if aborted\n // if (res.aborted) { return; }\n\n // const headers = Object.assign(\n // {},\n // matchMaker.controller.DEFAULT_CORS_HEADERS,\n // matchMaker.controller.getCorsHeaders(requestHeaders)\n // );\n\n // for (const header in headers) {\n // res.writeHeader(header, headers[header].toString());\n // }\n\n // return true;\n // }\n\n // const writeError = (res: uWebSockets.HttpResponse, error: { code: number, error: string }) => {\n // // skip if aborted\n // if (res.aborted) { return; }\n\n // res.cork(() => {\n // res.writeStatus(\"406 Not Acceptable\");\n // res.end(JSON.stringify(error));\n // });\n // }\n\n // const onAborted = (res: uWebSockets.HttpResponse) => {\n // res.aborted = true;\n // };\n\n // this.app.options(\"/matchmake/*\", (res, req) => {\n // res.onAborted(() => onAborted(res));\n\n // // cache all headers\n // const reqHeaders = new Headers();\n // req.forEach((key, value) => reqHeaders.set(key, value));\n\n // if (writeHeaders(res, reqHeaders)) {\n // res.writeStatus(\"204 No Content\");\n // res.end();\n // }\n // });\n\n\n // // @ts-ignore\n // this.app.post(\"/matchmake/*\", (res, req) => {\n // res.onAborted(() => onAborted(res));\n\n // // do not accept matchmaking requests if already shutting down\n // if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\n // return res.close();\n // }\n\n // // cache all headers\n // const headers = new Headers();\n // req.forEach((key, value) => headers.set(key, value));\n\n // writeHeaders(res, headers);\n // res.writeHeader('Content-Type', 'application/json');\n\n // const url = req.getUrl();\n // const matchedParams = url.match(allowedRoomNameChars);\n // const matchmakeIndex = matchedParams.indexOf(matchmakeRoute);\n\n // const token = getBearerToken(headers['authorization']);\n\n // // read json body\n // this.readJson(res, async (clientOptions) => {\n // try {\n // if (clientOptions === undefined) {\n // throw new Error(\"invalid JSON input\");\n // }\n\n // const method = matchedParams[matchmakeIndex + 1];\n // const roomName = matchedParams[matchmakeIndex + 2] || '';\n\n // const response = await matchMaker.controller.invokeMethod(\n // method,\n // roomName,\n // clientOptions,\n // {\n // token,\n // headers,\n // ip: headers.get('x-real-ip') ?? headers.get('x-forwarded-for') ?? Buffer.from(res.getRemoteAddressAsText()).toString()\n // }\n // );\n\n // if (!res.aborted) {\n // res.cork(() => {\n // res.writeStatus(\"200 OK\");\n // res.end(JSON.stringify(response));\n // });\n // }\n\n // } catch (e: any) {\n // debugAndPrintError(e);\n // writeError(res, {\n // code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n // error: e.message\n // });\n // }\n\n // });\n // });\n // }\n\n /* Helper function for reading a posted JSON body */\n /* Extracted from https://github.com/uNetworking/uWebSockets.js/blob/master/examples/JsonPost.js */\n private readJson(res: uWebSockets.HttpResponse, cb: (json: any) => void) {\n let buffer: Buffer;\n /* Register data cb */\n res.onData((ab, isLast) => {\n let chunk = Buffer.from(ab);\n if (isLast) {\n let json;\n if (buffer) {\n try {\n // @ts-ignore\n json = JSON.parse(Buffer.concat([buffer, chunk]));\n } catch (e) {\n /* res.close calls onAborted */\n // res.close();\n cb(undefined);\n return;\n }\n cb(json);\n } else {\n try {\n // @ts-ignore\n json = JSON.parse(chunk);\n } catch (e) {\n /* res.close calls onAborted */\n // res.close();\n cb(undefined);\n return;\n }\n cb(json);\n }\n } else {\n if (buffer) {\n buffer = Buffer.concat([buffer, chunk]);\n } else {\n buffer = Buffer.concat([chunk]);\n }\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 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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",
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.13",
3
+ "version": "0.17.15",
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.30"
27
+ "@colyseus/core": "^0.17.34"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "@colyseus/core": "0.17.x",
@@ -118,9 +118,17 @@ export class uWebSocketsTransport extends Transport {
118
118
  try {
119
119
  const module = await uWebSocketsExpress;
120
120
  uWebSocketsExpressModule = module;
121
+
122
+ // Temporarily stub `app.any` to prevent uwebsockets-express Application.init()
123
+ // from registering its own catch-all handler — we manage HTTP routing ourselves
124
+ // in bindRouter().
125
+ const originalAny = this.app.any;
126
+ this.app.any = (() => this.app) as any;
121
127
  this._expressApp = (module.default(this.app) as unknown) as express.Application;
128
+ this.app.any = originalAny;
122
129
  resolve(this._expressApp);
123
130
  } catch (error) {
131
+ reject(error);
124
132
  console.warn("");
125
133
  console.warn("❌ Error: could not initialize express.");
126
134
  console.warn("");
@@ -138,15 +146,19 @@ export class uWebSocketsTransport extends Transport {
138
146
  }
139
147
 
140
148
  public bindRouter(router: Router) {
141
- const writeHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {
142
- // skip if aborted
143
- if (res.aborted) { return; }
144
-
145
- const headers = Object.assign(
149
+ const getCorsHeaders = (requestHeaders: Headers) => {
150
+ return Object.assign(
146
151
  {},
147
152
  matchMaker.controller.DEFAULT_CORS_HEADERS,
148
153
  matchMaker.controller.getCorsHeaders(requestHeaders)
149
154
  );
155
+ }
156
+
157
+ const writeCorsHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {
158
+ // skip if aborted
159
+ if (res.aborted) { return; }
160
+
161
+ const headers = getCorsHeaders(requestHeaders);
150
162
 
151
163
  for (const header in headers) {
152
164
  res.writeHeader(header, headers[header].toString());
@@ -162,10 +174,11 @@ export class uWebSocketsTransport extends Transport {
162
174
  const reqHeaders = new Headers();
163
175
  req.forEach((key, value) => reqHeaders.set(key, value));
164
176
 
165
- if (writeHeaders(res, reqHeaders)) {
177
+ res.cork(() => {
166
178
  res.writeStatus("204 No Content");
179
+ writeCorsHeaders(res, reqHeaders);
167
180
  res.end();
168
- }
181
+ });
169
182
  });
170
183
 
171
184
  this.app.any('/*', async (res, req) => {
@@ -176,77 +189,91 @@ export class uWebSocketsTransport extends Transport {
176
189
  res.aborted = true;
177
190
  });
178
191
 
192
+ // cache all headers and request info synchronously
193
+ // (uWebSockets.js req is only valid in the synchronous callback scope)
179
194
  const headers = new Headers();
180
195
  req.forEach((key, value) => headers.set(key, value));
181
196
 
182
- // write cors headers
183
- writeHeaders(res, headers);
184
-
185
- const requestInit: RequestInit = {
186
- method: req.getMethod().toUpperCase(),
187
- referrer: req.getHeader('referer'),
188
- keepalive: req.getHeader('keep-alive') === 'true',
189
- headers,
190
- signal: abortController.signal,
191
- };
192
-
193
- // Construct full URL (Request constructor requires absolute URL)
197
+ const method = req.getMethod().toUpperCase();
194
198
  const url = req.getUrl();
195
199
  const query = req.getQuery();
196
200
  const remoteAddress = res.getRemoteAddressAsText();
197
201
 
198
- // read request body
199
- if (requestInit.method.toUpperCase() !== "GET" && requestInit.method.toUpperCase() !== "HEAD") {
200
- let body: Buffer = undefined;
201
-
202
- // uWebSockets.js `HttpRequest` does not provide 'getData', must aggregate POST body via HttpResponse
203
- await new Promise<void>((resolve) => {
204
- res.onData((ab, isLast) => {
205
- const chunk = Buffer.from(ab);
206
- if (body === undefined) {
207
- body = Buffer.from(chunk);
208
- } else {
209
- body = Buffer.concat([body, chunk]);
210
- }
211
- if (isLast) {
212
- resolve();
213
- }
202
+ // check if the route is defined in the router
203
+ // if so, use the router handler, otherwise fallback to express
204
+ if (router.findRoute(method, url) !== undefined) {
205
+ const requestInit: RequestInit = {
206
+ method,
207
+ referrer: headers.get('referer') || undefined,
208
+ keepalive: headers.get('keep-alive') === 'true',
209
+ headers,
210
+ signal: abortController.signal,
211
+ };
212
+
213
+ // read request body
214
+ if (method !== "GET" && method !== "HEAD") {
215
+ let body: Buffer = undefined;
216
+
217
+ // uWebSockets.js `HttpRequest` does not provide 'getData', must aggregate POST body via HttpResponse
218
+ await new Promise<void>((resolve) => {
219
+ res.onData((ab, isLast) => {
220
+ const chunk = Buffer.from(ab);
221
+ if (body === undefined) {
222
+ body = Buffer.from(chunk);
223
+ } else {
224
+ body = Buffer.concat([body, chunk]);
225
+ }
226
+ if (isLast) {
227
+ resolve();
228
+ }
229
+ });
214
230
  });
215
- });
216
231
 
217
- requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer;
218
- }
232
+ requestInit.body = body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) as ArrayBuffer;
233
+ }
234
+
235
+ const fullUrl = `http://${headers.get('host') || 'localhost'}${url}${(query ? `?${query}` : '')}`;
236
+ const response = await router.handler(new Request(fullUrl, requestInit));
219
237
 
220
- const fullUrl = `http://${headers.get('host') || 'localhost'}${url}${(query ? `?${query}` : '')}`
221
- const response = await router.handler(new Request(fullUrl, requestInit));
238
+ // skip if aborted
239
+ if (res.aborted) { return; }
240
+
241
+ // read response body before cork (cork callback must be synchronous)
242
+ const responseBody = await response.arrayBuffer();
243
+
244
+ // writeStatus() must be called before writeHeader() in uWebSockets.js
245
+ res.cork(() => {
246
+ res.writeStatus(`${response.status} ${response.statusText}`);
247
+ writeCorsHeaders(res, headers);
248
+ response.headers.forEach((value, key) => {
249
+ res.writeHeader(key, value);
250
+ });
251
+ res.end(responseBody);
252
+ });
253
+
254
+ } else if (this._expressApp) {
255
+ const corsHeaders = getCorsHeaders(headers);
222
256
 
223
- // fallback to express stack if 404
224
- if (response.status === 404 && this._expressApp) {
225
257
  const ereq = new uWebSocketsExpressModule.IncomingMessage(req, res, this._expressApp as any, {
226
258
  headers: Object.fromEntries((headers as any).entries()),
227
- method: requestInit.method,
259
+ method,
228
260
  url,
229
261
  query,
230
262
  remoteAddress
231
263
  });
232
264
  const eres = new uWebSocketsExpressModule.ServerResponse(res, req, this._expressApp);
233
- this._expressApp['handle'](ereq, eres);
234
- return;
235
- }
236
265
 
237
- // skip if aborted
238
- if (res.aborted) { return; }
266
+ // Apply CORS headers through the Express response wrapper
267
+ for (const header in corsHeaders) {
268
+ eres.setHeader(header, corsHeaders[header].toString());
269
+ }
239
270
 
240
- // read response body before cork (cork callback must be synchronous)
241
- const responseBody = await response.arrayBuffer();
271
+ // Read the request body from uWebSockets before passing to express
272
+ // (uWebSockets requires res.onData() to be called to consume the body)
273
+ await ereq._readBody();
242
274
 
243
- res.cork(() => {
244
- res.writeStatus(`${response.status} ${response.statusText}`);
245
- response.headers.forEach((value, key) => {
246
- res.writeHeader(key, value);
247
- });
248
- res.end(responseBody);
249
- });
275
+ this._expressApp['handle'](ereq, eres);
276
+ }
250
277
  });
251
278
  }
252
279
 
@@ -331,156 +358,4 @@ export class uWebSocketsTransport extends Transport {
331
358
  }
332
359
  }
333
360
 
334
- // protected registerMatchMakeRequest() {
335
- // const matchmakeRoute = 'matchmake';
336
- // const allowedRoomNameChars = /([a-zA-Z_\-0-9]+)/gi;
337
-
338
- // const writeHeaders = (res: uWebSockets.HttpResponse, requestHeaders: Headers) => {
339
- // // skip if aborted
340
- // if (res.aborted) { return; }
341
-
342
- // const headers = Object.assign(
343
- // {},
344
- // matchMaker.controller.DEFAULT_CORS_HEADERS,
345
- // matchMaker.controller.getCorsHeaders(requestHeaders)
346
- // );
347
-
348
- // for (const header in headers) {
349
- // res.writeHeader(header, headers[header].toString());
350
- // }
351
-
352
- // return true;
353
- // }
354
-
355
- // const writeError = (res: uWebSockets.HttpResponse, error: { code: number, error: string }) => {
356
- // // skip if aborted
357
- // if (res.aborted) { return; }
358
-
359
- // res.cork(() => {
360
- // res.writeStatus("406 Not Acceptable");
361
- // res.end(JSON.stringify(error));
362
- // });
363
- // }
364
-
365
- // const onAborted = (res: uWebSockets.HttpResponse) => {
366
- // res.aborted = true;
367
- // };
368
-
369
- // this.app.options("/matchmake/*", (res, req) => {
370
- // res.onAborted(() => onAborted(res));
371
-
372
- // // cache all headers
373
- // const reqHeaders = new Headers();
374
- // req.forEach((key, value) => reqHeaders.set(key, value));
375
-
376
- // if (writeHeaders(res, reqHeaders)) {
377
- // res.writeStatus("204 No Content");
378
- // res.end();
379
- // }
380
- // });
381
-
382
-
383
- // // @ts-ignore
384
- // this.app.post("/matchmake/*", (res, req) => {
385
- // res.onAborted(() => onAborted(res));
386
-
387
- // // do not accept matchmaking requests if already shutting down
388
- // if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {
389
- // return res.close();
390
- // }
391
-
392
- // // cache all headers
393
- // const headers = new Headers();
394
- // req.forEach((key, value) => headers.set(key, value));
395
-
396
- // writeHeaders(res, headers);
397
- // res.writeHeader('Content-Type', 'application/json');
398
-
399
- // const url = req.getUrl();
400
- // const matchedParams = url.match(allowedRoomNameChars);
401
- // const matchmakeIndex = matchedParams.indexOf(matchmakeRoute);
402
-
403
- // const token = getBearerToken(headers['authorization']);
404
-
405
- // // read json body
406
- // this.readJson(res, async (clientOptions) => {
407
- // try {
408
- // if (clientOptions === undefined) {
409
- // throw new Error("invalid JSON input");
410
- // }
411
-
412
- // const method = matchedParams[matchmakeIndex + 1];
413
- // const roomName = matchedParams[matchmakeIndex + 2] || '';
414
-
415
- // const response = await matchMaker.controller.invokeMethod(
416
- // method,
417
- // roomName,
418
- // clientOptions,
419
- // {
420
- // token,
421
- // headers,
422
- // ip: headers.get('x-real-ip') ?? headers.get('x-forwarded-for') ?? Buffer.from(res.getRemoteAddressAsText()).toString()
423
- // }
424
- // );
425
-
426
- // if (!res.aborted) {
427
- // res.cork(() => {
428
- // res.writeStatus("200 OK");
429
- // res.end(JSON.stringify(response));
430
- // });
431
- // }
432
-
433
- // } catch (e: any) {
434
- // debugAndPrintError(e);
435
- // writeError(res, {
436
- // code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,
437
- // error: e.message
438
- // });
439
- // }
440
-
441
- // });
442
- // });
443
- // }
444
-
445
- /* Helper function for reading a posted JSON body */
446
- /* Extracted from https://github.com/uNetworking/uWebSockets.js/blob/master/examples/JsonPost.js */
447
- private readJson(res: uWebSockets.HttpResponse, cb: (json: any) => void) {
448
- let buffer: Buffer;
449
- /* Register data cb */
450
- res.onData((ab, isLast) => {
451
- let chunk = Buffer.from(ab);
452
- if (isLast) {
453
- let json;
454
- if (buffer) {
455
- try {
456
- // @ts-ignore
457
- json = JSON.parse(Buffer.concat([buffer, chunk]));
458
- } catch (e) {
459
- /* res.close calls onAborted */
460
- // res.close();
461
- cb(undefined);
462
- return;
463
- }
464
- cb(json);
465
- } else {
466
- try {
467
- // @ts-ignore
468
- json = JSON.parse(chunk);
469
- } catch (e) {
470
- /* res.close calls onAborted */
471
- // res.close();
472
- cb(undefined);
473
- return;
474
- }
475
- cb(json);
476
- }
477
- } else {
478
- if (buffer) {
479
- buffer = Buffer.concat([buffer, chunk]);
480
- } else {
481
- buffer = Buffer.concat([chunk]);
482
- }
483
- }
484
- });
485
- }
486
361
  }