@colyseus/uwebsockets-transport 0.15.0-preview.1 → 0.15.0

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.
@@ -1,256 +1,226 @@
1
- import querystring from 'querystring';
2
- import uWebSockets from 'uWebSockets.js';
3
- import { Transport, DummyServer, spliceOne, matchMaker, debugAndPrintError, ErrorCode } from '@colyseus/core';
4
- import { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.mjs';
5
-
1
+ import querystring from "querystring";
2
+ import uWebSockets from "uWebSockets.js";
3
+ import { DummyServer, ErrorCode, matchMaker, Transport, debugAndPrintError, spliceOne } from "@colyseus/core";
4
+ import { uWebSocketClient, uWebSocketWrapper } from "./uWebSocketClient";
6
5
  class uWebSocketsTransport extends Transport {
7
- app;
8
- clients = [];
9
- clientWrappers = new WeakMap();
10
- _listeningSocket;
11
- constructor(options = {}, appOptions = {}) {
12
- super();
13
- this.app = (appOptions.cert_file_name && appOptions.key_file_name)
14
- ? uWebSockets.SSLApp(appOptions)
15
- : uWebSockets.App(appOptions);
16
- if (!options.maxBackpressure) {
17
- options.maxBackpressure = 1024 * 1024;
18
- }
19
- if (!options.compression) {
20
- options.compression = uWebSockets.DISABLED;
21
- }
22
- if (!options.maxPayloadLength) {
23
- options.maxPayloadLength = 1024 * 1024;
24
- }
25
- // https://github.com/colyseus/colyseus/issues/458
26
- // Adding a mock object for Transport.server
27
- if (!this.server) {
28
- this.server = new DummyServer();
29
- }
30
- this.app.ws('/*', {
31
- ...options,
32
- upgrade: (res, req, context) => {
33
- // get all headers
34
- const headers = {};
35
- req.forEach((key, value) => headers[key] = value);
36
- /* This immediately calls open handler, you must not use res after this call */
37
- /* Spell these correctly */
38
- res.upgrade({
39
- url: req.getUrl(),
40
- query: req.getQuery(),
41
- // compatibility with @colyseus/ws-transport
42
- headers,
43
- connection: {
44
- remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString()
45
- }
46
- }, req.getHeader('sec-websocket-key'), req.getHeader('sec-websocket-protocol'), req.getHeader('sec-websocket-extensions'), context);
47
- },
48
- open: async (ws) => {
49
- // ws.pingCount = 0;
50
- await this.onConnection(ws);
51
- },
52
- // pong: (ws: RawWebSocketClient) => {
53
- // ws.pingCount = 0;
54
- // },
55
- close: (ws, code, message) => {
56
- // remove from client list
57
- spliceOne(this.clients, this.clients.indexOf(ws));
58
- const clientWrapper = this.clientWrappers.get(ws);
59
- if (clientWrapper) {
60
- this.clientWrappers.delete(ws);
61
- // emit 'close' on wrapper
62
- clientWrapper.emit('close', code);
63
- }
64
- },
65
- message: (ws, message, isBinary) => {
66
- // emit 'close' on wrapper
67
- this.clientWrappers.get(ws)?.emit('message', Buffer.from(message.slice(0)));
68
- },
69
- });
70
- this.registerMatchMakeRequest();
6
+ app;
7
+ clients = [];
8
+ clientWrappers = /* @__PURE__ */ new WeakMap();
9
+ _listeningSocket;
10
+ constructor(options = {}, appOptions = {}) {
11
+ super();
12
+ this.app = appOptions.cert_file_name && appOptions.key_file_name ? uWebSockets.SSLApp(appOptions) : uWebSockets.App(appOptions);
13
+ if (!options.maxBackpressure) {
14
+ options.maxBackpressure = 1024 * 1024;
71
15
  }
72
- listen(port, hostname, backlog, listeningListener) {
73
- this.app.listen(port, (listeningSocket) => {
74
- this._listeningSocket = listeningSocket;
75
- listeningListener?.();
76
- this.server.emit("listening"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458
77
- });
78
- return this;
16
+ if (!options.compression) {
17
+ options.compression = uWebSockets.DISABLED;
79
18
  }
80
- shutdown() {
81
- if (this._listeningSocket) {
82
- uWebSockets.us_listen_socket_close(this._listeningSocket);
83
- this.server.emit("close"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458
84
- }
19
+ if (!options.maxPayloadLength) {
20
+ options.maxPayloadLength = 1024 * 1024;
85
21
  }
86
- simulateLatency(milliseconds) {
87
- const originalRawSend = uWebSocketClient.prototype.raw;
88
- uWebSocketClient.prototype.raw = function () {
89
- setTimeout(() => originalRawSend.apply(this, arguments), milliseconds);
90
- };
22
+ if (!this.server) {
23
+ this.server = new DummyServer();
91
24
  }
92
- async onConnection(rawClient) {
93
- const wrapper = new uWebSocketWrapper(rawClient);
94
- // keep reference to client and its wrapper
95
- this.clients.push(rawClient);
96
- this.clientWrappers.set(rawClient, wrapper);
97
- const query = rawClient.query;
98
- const url = rawClient.url;
99
- const searchParams = querystring.parse(query);
100
- const sessionId = searchParams.sessionId;
101
- const processAndRoomId = url.match(/\/[a-zA-Z0-9_\-]+\/([a-zA-Z0-9_\-]+)$/);
102
- const roomId = processAndRoomId && processAndRoomId[1];
103
- const room = matchMaker.getRoomById(roomId);
104
- const client = new uWebSocketClient(sessionId, wrapper);
105
- //
106
- // TODO: DRY code below with all transports
107
- //
108
- try {
109
- if (!room || !room.hasReservedSeat(sessionId, searchParams.reconnectionToken)) {
110
- throw new Error('seat reservation expired.');
25
+ this.app.ws("/*", {
26
+ ...options,
27
+ upgrade: (res, req, context) => {
28
+ const headers = {};
29
+ req.forEach((key, value) => headers[key] = value);
30
+ res.upgrade(
31
+ {
32
+ url: req.getUrl(),
33
+ query: req.getQuery(),
34
+ headers,
35
+ connection: {
36
+ remoteAddress: Buffer.from(res.getRemoteAddressAsText()).toString()
111
37
  }
112
- await room._onJoin(client, rawClient);
113
- }
114
- catch (e) {
115
- debugAndPrintError(e);
116
- // send error code to client then terminate
117
- client.error(e.code, e.message, () => rawClient.close());
38
+ },
39
+ req.getHeader("sec-websocket-key"),
40
+ req.getHeader("sec-websocket-protocol"),
41
+ req.getHeader("sec-websocket-extensions"),
42
+ context
43
+ );
44
+ },
45
+ open: async (ws) => {
46
+ await this.onConnection(ws);
47
+ },
48
+ close: (ws, code, message) => {
49
+ spliceOne(this.clients, this.clients.indexOf(ws));
50
+ const clientWrapper = this.clientWrappers.get(ws);
51
+ if (clientWrapper) {
52
+ this.clientWrappers.delete(ws);
53
+ clientWrapper.emit("close", code);
118
54
  }
55
+ },
56
+ message: (ws, message, isBinary) => {
57
+ this.clientWrappers.get(ws)?.emit("message", Buffer.from(message.slice(0)));
58
+ }
59
+ });
60
+ this.registerMatchMakeRequest();
61
+ }
62
+ listen(port, hostname, backlog, listeningListener) {
63
+ this.app.listen(port, (listeningSocket) => {
64
+ this._listeningSocket = listeningSocket;
65
+ listeningListener?.();
66
+ this.server.emit("listening");
67
+ });
68
+ return this;
69
+ }
70
+ shutdown() {
71
+ if (this._listeningSocket) {
72
+ uWebSockets.us_listen_socket_close(this._listeningSocket);
73
+ this.server.emit("close");
119
74
  }
120
- registerMatchMakeRequest() {
121
- // TODO: DRY with Server.ts
122
- const matchmakeRoute = 'matchmake';
123
- const allowedRoomNameChars = /([a-zA-Z_\-0-9]+)/gi;
124
- const writeHeaders = (req, res) => {
125
- // skip if aborted
126
- if (res.aborted) {
127
- return;
128
- }
129
- const headers = Object.assign({}, matchMaker.controller.DEFAULT_CORS_HEADERS, matchMaker.controller.getCorsHeaders.call(undefined, req));
130
- for (const header in headers) {
131
- res.writeHeader(header, headers[header].toString());
132
- }
133
- return true;
134
- };
135
- const writeError = (res, error) => {
136
- // skip if aborted
137
- if (res.aborted) {
138
- return;
139
- }
140
- res.writeStatus("406 Not Acceptable");
141
- res.end(JSON.stringify(error));
142
- };
143
- const onAborted = (res) => {
144
- res.aborted = true;
145
- };
146
- this.app.options("/matchmake/*", (res, req) => {
147
- res.onAborted(() => onAborted(res));
148
- if (writeHeaders(req, res)) {
149
- res.writeStatus("204 No Content");
150
- res.end();
151
- }
152
- });
153
- this.app.post("/matchmake/*", (res, req) => {
154
- res.onAborted(() => onAborted(res));
155
- writeHeaders(req, res);
156
- res.writeHeader('Content-Type', 'application/json');
157
- const url = req.getUrl();
158
- const matchedParams = url.match(allowedRoomNameChars);
159
- const matchmakeIndex = matchedParams.indexOf(matchmakeRoute);
160
- // read json body
161
- this.readJson(res, async (clientOptions) => {
162
- const method = matchedParams[matchmakeIndex + 1];
163
- const roomName = matchedParams[matchmakeIndex + 2] || '';
164
- try {
165
- const response = await matchMaker.controller.invokeMethod(method, roomName, clientOptions);
166
- if (!res.aborted) {
167
- res.writeStatus("200 OK");
168
- res.end(JSON.stringify(response));
169
- }
170
- }
171
- catch (e) {
172
- debugAndPrintError(e);
173
- writeError(res, {
174
- code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,
175
- error: e.message
176
- });
177
- }
178
- });
179
- });
180
- // this.app.any("/*", (res, req) => {
181
- // res.onAborted(() => onAborted(req));
182
- // res.writeStatus("200 OK");
183
- // });
184
- this.app.get("/matchmake/*", async (res, req) => {
185
- res.onAborted(() => onAborted(res));
186
- writeHeaders(req, res);
187
- res.writeHeader('Content-Type', 'application/json');
188
- const url = req.getUrl();
189
- const matchedParams = url.match(allowedRoomNameChars);
190
- const roomName = matchedParams.length > 1 ? matchedParams[matchedParams.length - 1] : "";
191
- try {
192
- const response = await matchMaker.controller.getAvailableRooms(roomName || '');
193
- if (!res.aborted) {
194
- res.writeStatus("200 OK");
195
- res.end(JSON.stringify(response));
196
- }
197
- }
198
- catch (e) {
199
- debugAndPrintError(e);
200
- writeError(res, {
201
- code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,
202
- error: e.message
203
- });
204
- }
205
- });
75
+ }
76
+ simulateLatency(milliseconds) {
77
+ const originalRawSend = uWebSocketClient.prototype.raw;
78
+ uWebSocketClient.prototype.raw = function() {
79
+ setTimeout(() => originalRawSend.apply(this, arguments), milliseconds);
80
+ };
81
+ }
82
+ async onConnection(rawClient) {
83
+ const wrapper = new uWebSocketWrapper(rawClient);
84
+ this.clients.push(rawClient);
85
+ this.clientWrappers.set(rawClient, wrapper);
86
+ const query = rawClient.query;
87
+ const url = rawClient.url;
88
+ const searchParams = querystring.parse(query);
89
+ const sessionId = searchParams.sessionId;
90
+ const processAndRoomId = url.match(/\/[a-zA-Z0-9_\-]+\/([a-zA-Z0-9_\-]+)$/);
91
+ const roomId = processAndRoomId && processAndRoomId[1];
92
+ const room = matchMaker.getRoomById(roomId);
93
+ const client = new uWebSocketClient(sessionId, wrapper);
94
+ try {
95
+ if (!room || !room.hasReservedSeat(sessionId, searchParams.reconnectionToken)) {
96
+ throw new Error("seat reservation expired.");
97
+ }
98
+ await room._onJoin(client, rawClient);
99
+ } catch (e) {
100
+ debugAndPrintError(e);
101
+ client.error(e.code, e.message, () => rawClient.close());
206
102
  }
207
- /* Helper function for reading a posted JSON body */
208
- /* Extracted from https://github.com/uNetworking/uWebSockets.js/blob/master/examples/JsonPost.js */
209
- readJson(res, cb) {
210
- let buffer;
211
- /* Register data cb */
212
- res.onData((ab, isLast) => {
213
- let chunk = Buffer.from(ab);
214
- if (isLast) {
215
- let json;
216
- if (buffer) {
217
- try {
218
- // @ts-ignore
219
- json = JSON.parse(Buffer.concat([buffer, chunk]));
220
- }
221
- catch (e) {
222
- /* res.close calls onAborted */
223
- res.close();
224
- cb(undefined);
225
- return;
226
- }
227
- cb(json);
228
- }
229
- else {
230
- try {
231
- // @ts-ignore
232
- json = JSON.parse(chunk);
233
- }
234
- catch (e) {
235
- /* res.close calls onAborted */
236
- res.close();
237
- cb(undefined);
238
- return;
239
- }
240
- cb(json);
241
- }
242
- }
243
- else {
244
- if (buffer) {
245
- buffer = Buffer.concat([buffer, chunk]);
246
- }
247
- else {
248
- buffer = Buffer.concat([chunk]);
249
- }
250
- }
103
+ }
104
+ registerMatchMakeRequest() {
105
+ const matchmakeRoute = "matchmake";
106
+ const allowedRoomNameChars = /([a-zA-Z_\-0-9]+)/gi;
107
+ const writeHeaders = (req, res) => {
108
+ if (res.aborted) {
109
+ return;
110
+ }
111
+ const headers = Object.assign(
112
+ {},
113
+ matchMaker.controller.DEFAULT_CORS_HEADERS,
114
+ matchMaker.controller.getCorsHeaders.call(void 0, req)
115
+ );
116
+ for (const header in headers) {
117
+ res.writeHeader(header, headers[header].toString());
118
+ }
119
+ return true;
120
+ };
121
+ const writeError = (res, error) => {
122
+ if (res.aborted) {
123
+ return;
124
+ }
125
+ res.writeStatus("406 Not Acceptable");
126
+ res.end(JSON.stringify(error));
127
+ };
128
+ const onAborted = (res) => {
129
+ res.aborted = true;
130
+ };
131
+ this.app.options("/matchmake/*", (res, req) => {
132
+ res.onAborted(() => onAborted(res));
133
+ if (writeHeaders(req, res)) {
134
+ res.writeStatus("204 No Content");
135
+ res.end();
136
+ }
137
+ });
138
+ this.app.post("/matchmake/*", (res, req) => {
139
+ res.onAborted(() => onAborted(res));
140
+ if (matchMaker.isGracefullyShuttingDown) {
141
+ return res.close();
142
+ }
143
+ writeHeaders(req, res);
144
+ res.writeHeader("Content-Type", "application/json");
145
+ const url = req.getUrl();
146
+ const matchedParams = url.match(allowedRoomNameChars);
147
+ const matchmakeIndex = matchedParams.indexOf(matchmakeRoute);
148
+ this.readJson(res, async (clientOptions) => {
149
+ try {
150
+ if (clientOptions === void 0) {
151
+ throw new Error("invalid JSON input");
152
+ }
153
+ const method = matchedParams[matchmakeIndex + 1];
154
+ const roomName = matchedParams[matchmakeIndex + 2] || "";
155
+ const response = await matchMaker.controller.invokeMethod(method, roomName, clientOptions);
156
+ if (!res.aborted) {
157
+ res.writeStatus("200 OK");
158
+ res.end(JSON.stringify(response));
159
+ }
160
+ } catch (e) {
161
+ debugAndPrintError(e);
162
+ writeError(res, {
163
+ code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,
164
+ error: e.message
165
+ });
166
+ }
167
+ });
168
+ });
169
+ this.app.get("/matchmake/*", async (res, req) => {
170
+ res.onAborted(() => onAborted(res));
171
+ writeHeaders(req, res);
172
+ res.writeHeader("Content-Type", "application/json");
173
+ const url = req.getUrl();
174
+ const matchedParams = url.match(allowedRoomNameChars);
175
+ const roomName = matchedParams.length > 1 ? matchedParams[matchedParams.length - 1] : "";
176
+ try {
177
+ const response = await matchMaker.controller.getAvailableRooms(roomName || "");
178
+ if (!res.aborted) {
179
+ res.writeStatus("200 OK");
180
+ res.end(JSON.stringify(response));
181
+ }
182
+ } catch (e) {
183
+ debugAndPrintError(e);
184
+ writeError(res, {
185
+ code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,
186
+ error: e.message
251
187
  });
252
- }
188
+ }
189
+ });
190
+ }
191
+ readJson(res, cb) {
192
+ let buffer;
193
+ res.onData((ab, isLast) => {
194
+ let chunk = Buffer.from(ab);
195
+ if (isLast) {
196
+ let json;
197
+ if (buffer) {
198
+ try {
199
+ json = JSON.parse(Buffer.concat([buffer, chunk]));
200
+ } catch (e) {
201
+ cb(void 0);
202
+ return;
203
+ }
204
+ cb(json);
205
+ } else {
206
+ try {
207
+ json = JSON.parse(chunk);
208
+ } catch (e) {
209
+ cb(void 0);
210
+ return;
211
+ }
212
+ cb(json);
213
+ }
214
+ } else {
215
+ if (buffer) {
216
+ buffer = Buffer.concat([buffer, chunk]);
217
+ } else {
218
+ buffer = Buffer.concat([chunk]);
219
+ }
220
+ }
221
+ });
222
+ }
253
223
  }
254
-
255
- export { uWebSocketsTransport };
256
- //# sourceMappingURL=uWebSocketsTransport.mjs.map
224
+ export {
225
+ uWebSocketsTransport
226
+ };
@@ -1 +1,7 @@
1
- {"version":3,"file":"uWebSocketsTransport.mjs","sources":["../src/uWebSocketsTransport.ts"],"sourcesContent":["import http from 'http';\nimport querystring from 'querystring';\nimport uWebSockets from 'uWebSockets.js';\n\nimport { DummyServer, ErrorCode, matchMaker, Transport, debugAndPrintError, spliceOne } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient';\n\nexport type TransportOptions = Omit<uWebSockets.WebSocketBehavior, \"upgrade\" | \"open\" | \"pong\" | \"close\" | \"message\">;\n\ntype RawWebSocketClient = uWebSockets.WebSocket & {\n headers: {[key: string]: string},\n connection: { remoteAddress: string },\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\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) {\n options.maxBackpressure = 1024 * 1024;\n }\n\n if (!options.compression) {\n options.compression = uWebSockets.DISABLED;\n }\n\n if (!options.maxPayloadLength) {\n options.maxPayloadLength = 1024 * 1024;\n }\n\n // https://github.com/colyseus/colyseus/issues/458\n // Adding a mock object for Transport.server\n if(!this.server) {\n this.server = new DummyServer();\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 /* 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 query: req.getQuery(),\n\n // compatibility with @colyseus/ws-transport\n headers,\n connection: {\n remoteAddress: 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: RawWebSocketClient) => {\n // ws.pingCount = 0;\n await this.onConnection(ws);\n },\n\n // pong: (ws: RawWebSocketClient) => {\n // ws.pingCount = 0;\n // },\n\n close: (ws: RawWebSocketClient, code: number, message: ArrayBuffer) => {\n // remove from client list\n spliceOne(this.clients, this.clients.indexOf(ws));\n\n const clientWrapper = this.clientWrappers.get(ws);\n if (clientWrapper) {\n this.clientWrappers.delete(ws);\n\n // emit 'close' on wrapper\n clientWrapper.emit('close', code);\n }\n },\n\n message: (ws: RawWebSocketClient, message: ArrayBuffer, isBinary: boolean) => {\n // emit 'close' on wrapper\n this.clientWrappers.get(ws)?.emit('message', Buffer.from(message.slice(0)));\n },\n\n });\n\n this.registerMatchMakeRequest();\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n this.app.listen(port, (listeningSocket: any) => {\n this._listeningSocket = listeningSocket;\n listeningListener?.();\n this.server.emit(\"listening\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n });\n return this;\n }\n\n public shutdown() {\n if (this._listeningSocket) {\n uWebSockets.us_listen_socket_close(this._listeningSocket);\n this.server.emit(\"close\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n }\n }\n\n public simulateLatency(milliseconds: number) {\n const originalRawSend = uWebSocketClient.prototype.raw;\n uWebSocketClient.prototype.raw = function() {\n setTimeout(() => originalRawSend.apply(this, arguments), 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 query = rawClient.query;\n const url = rawClient.url;\n const searchParams = querystring.parse(query);\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 const room = matchMaker.getRoomById(roomId);\n const client = new uWebSocketClient(sessionId, wrapper);\n\n //\n // TODO: DRY code below with all transports\n //\n\n try {\n if (!room || !room.hasReservedSeat(sessionId, searchParams.reconnectionToken as string)) {\n throw new Error('seat reservation expired.');\n }\n\n await room._onJoin(client, rawClient as unknown as http.IncomingMessage);\n\n } catch (e) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => rawClient.close());\n }\n }\n\n protected registerMatchMakeRequest() {\n\n // TODO: DRY with Server.ts\n const matchmakeRoute = 'matchmake';\n const allowedRoomNameChars = /([a-zA-Z_\\-0-9]+)/gi;\n\n const writeHeaders = (req: uWebSockets.HttpRequest, res: uWebSockets.HttpResponse) => {\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.call(undefined, req)\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.writeStatus(\"406 Not Acceptable\");\n res.end(JSON.stringify(error));\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 if (writeHeaders(req, res)) {\n res.writeStatus(\"204 No Content\");\n res.end();\n }\n });\n\n this.app.post(\"/matchmake/*\", (res, req) => {\n res.onAborted(() => onAborted(res));\n\n writeHeaders(req, res);\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 // read json body\n this.readJson(res, async (clientOptions) => {\n const method = matchedParams[matchmakeIndex + 1];\n const roomName = matchedParams[matchmakeIndex + 2] || '';\n\n try {\n const response = await matchMaker.controller.invokeMethod(method, roomName, clientOptions);\n if (!res.aborted) {\n res.writeStatus(\"200 OK\");\n res.end(JSON.stringify(response));\n }\n\n } catch (e) {\n debugAndPrintError(e);\n writeError(res, {\n code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n error: e.message\n });\n }\n\n });\n });\n\n // this.app.any(\"/*\", (res, req) => {\n // res.onAborted(() => onAborted(req));\n // res.writeStatus(\"200 OK\");\n // });\n\n this.app.get(\"/matchmake/*\", async (res, req) => {\n res.onAborted(() => onAborted(res));\n\n writeHeaders(req, res);\n res.writeHeader('Content-Type', 'application/json');\n\n const url = req.getUrl();\n const matchedParams = url.match(allowedRoomNameChars);\n const roomName = matchedParams.length > 1 ? matchedParams[matchedParams.length - 1] : \"\";\n\n try {\n const response = await matchMaker.controller.getAvailableRooms(roomName || '')\n if (!res.aborted) {\n res.writeStatus(\"200 OK\");\n res.end(JSON.stringify(response));\n }\n\n } catch (e) {\n debugAndPrintError(e);\n writeError(res, {\n code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n error: e.message\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: any;\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"],"names":[],"mappings":"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"}
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/uWebSocketsTransport.ts"],
4
+ "sourcesContent": ["import http from 'http';\nimport querystring from 'querystring';\nimport uWebSockets from 'uWebSockets.js';\n\nimport { DummyServer, ErrorCode, matchMaker, Transport, debugAndPrintError, spliceOne } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient';\n\nexport type TransportOptions = Omit<uWebSockets.WebSocketBehavior<any>, \"upgrade\" | \"open\" | \"pong\" | \"close\" | \"message\">;\n\ntype RawWebSocketClient = uWebSockets.WebSocket<any> & {\n url: string,\n query: string,\n headers: {[key: string]: string},\n connection: { remoteAddress: string },\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\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) {\n options.maxBackpressure = 1024 * 1024;\n }\n\n if (!options.compression) {\n options.compression = uWebSockets.DISABLED;\n }\n\n if (!options.maxPayloadLength) {\n options.maxPayloadLength = 1024 * 1024;\n }\n\n // https://github.com/colyseus/colyseus/issues/458\n // Adding a mock object for Transport.server\n if(!this.server) {\n this.server = new DummyServer();\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 /* 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 query: req.getQuery(),\n\n // compatibility with @colyseus/ws-transport\n headers,\n connection: {\n remoteAddress: 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: RawWebSocketClient) => {\n // ws.pingCount = 0;\n await this.onConnection(ws);\n },\n\n // pong: (ws: RawWebSocketClient) => {\n // ws.pingCount = 0;\n // },\n\n close: (ws: RawWebSocketClient, code: number, message: ArrayBuffer) => {\n // remove from client list\n spliceOne(this.clients, this.clients.indexOf(ws));\n\n const clientWrapper = this.clientWrappers.get(ws);\n if (clientWrapper) {\n this.clientWrappers.delete(ws);\n\n // emit 'close' on wrapper\n clientWrapper.emit('close', code);\n }\n },\n\n message: (ws: RawWebSocketClient, message: ArrayBuffer, isBinary: boolean) => {\n // emit 'close' on wrapper\n this.clientWrappers.get(ws)?.emit('message', Buffer.from(message.slice(0)));\n },\n\n });\n\n this.registerMatchMakeRequest();\n }\n\n public listen(port: number, hostname?: string, backlog?: number, listeningListener?: () => void) {\n this.app.listen(port, (listeningSocket: any) => {\n this._listeningSocket = listeningSocket;\n listeningListener?.();\n this.server.emit(\"listening\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n });\n return this;\n }\n\n public shutdown() {\n if (this._listeningSocket) {\n uWebSockets.us_listen_socket_close(this._listeningSocket);\n this.server.emit(\"close\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n }\n }\n\n public simulateLatency(milliseconds: number) {\n const originalRawSend = uWebSocketClient.prototype.raw;\n uWebSocketClient.prototype.raw = function() {\n setTimeout(() => originalRawSend.apply(this, arguments), 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 query = rawClient.query;\n const url = rawClient.url;\n const searchParams = querystring.parse(query);\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 const room = matchMaker.getRoomById(roomId);\n const client = new uWebSocketClient(sessionId, wrapper);\n\n //\n // TODO: DRY code below with all transports\n //\n\n try {\n if (!room || !room.hasReservedSeat(sessionId, searchParams.reconnectionToken as string)) {\n throw new Error('seat reservation expired.');\n }\n\n await room._onJoin(client, rawClient as unknown as http.IncomingMessage);\n\n } catch (e) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => rawClient.close());\n }\n }\n\n protected registerMatchMakeRequest() {\n\n // TODO: DRY with Server.ts\n const matchmakeRoute = 'matchmake';\n const allowedRoomNameChars = /([a-zA-Z_\\-0-9]+)/gi;\n\n const writeHeaders = (req: uWebSockets.HttpRequest, res: uWebSockets.HttpResponse) => {\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.call(undefined, req)\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.writeStatus(\"406 Not Acceptable\");\n res.end(JSON.stringify(error));\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 if (writeHeaders(req, res)) {\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.isGracefullyShuttingDown) {\n return res.close();\n }\n\n writeHeaders(req, res);\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 // 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(method, roomName, clientOptions);\n if (!res.aborted) {\n res.writeStatus(\"200 OK\");\n res.end(JSON.stringify(response));\n }\n\n } catch (e) {\n debugAndPrintError(e);\n writeError(res, {\n code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n error: e.message\n });\n }\n\n });\n });\n\n // this.app.any(\"/*\", (res, req) => {\n // res.onAborted(() => onAborted(req));\n // res.writeStatus(\"200 OK\");\n // });\n\n this.app.get(\"/matchmake/*\", async (res, req) => {\n res.onAborted(() => onAborted(res));\n\n writeHeaders(req, res);\n res.writeHeader('Content-Type', 'application/json');\n\n const url = req.getUrl();\n const matchedParams = url.match(allowedRoomNameChars);\n const roomName = matchedParams.length > 1 ? matchedParams[matchedParams.length - 1] : \"\";\n\n try {\n const response = await matchMaker.controller.getAvailableRooms(roomName || '')\n if (!res.aborted) {\n res.writeStatus(\"200 OK\");\n res.end(JSON.stringify(response));\n }\n\n } catch (e) {\n debugAndPrintError(e);\n writeError(res, {\n code: e.code || ErrorCode.MATCHMAKE_UNHANDLED,\n error: e.message\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: any;\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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",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,14 +1,16 @@
1
1
  {
2
2
  "name": "@colyseus/uwebsockets-transport",
3
- "version": "0.15.0-preview.1",
3
+ "version": "0.15.0",
4
4
  "input": "./src/index.ts",
5
5
  "main": "./build/index.js",
6
6
  "module": "./build/index.mjs",
7
7
  "typings": "./build/index.d.ts",
8
8
  "dependencies": {
9
- "@colyseus/core": "^0.15.0-preview.2",
10
- "@colyseus/schema": "^2.0.0",
11
- "uWebSockets.js": "github:uNetworking/uWebSockets.js#v19.2.0"
9
+ "@colyseus/core": "^0.15.0",
10
+ "uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.24.0"
11
+ },
12
+ "peerDependencies": {
13
+ "@colyseus/schema": ">=1.0.0"
12
14
  },
13
15
  "author": "Endel Dreyer",
14
16
  "license": "MIT",
@@ -32,5 +34,5 @@
32
34
  "publishConfig": {
33
35
  "access": "public"
34
36
  },
35
- "gitHead": "50c7cac08e2fb7d17aa5bd57c45a7dad9be84722"
37
+ "gitHead": "0ec5c17379936d74f4fa18ba68d16cbb7ed2c298"
36
38
  }
@@ -1,31 +0,0 @@
1
- 'use strict';
2
-
3
- Object.defineProperty(exports, '__esModule', { value: true });
4
-
5
- /*! *****************************************************************************
6
- Copyright (c) Microsoft Corporation.
7
-
8
- Permission to use, copy, modify, and/or distribute this software for any
9
- purpose with or without fee is hereby granted.
10
-
11
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
12
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
14
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
16
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17
- PERFORMANCE OF THIS SOFTWARE.
18
- ***************************************************************************** */
19
-
20
- function __awaiter(thisArg, _arguments, P, generator) {
21
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
22
- return new (P || (P = Promise))(function (resolve, reject) {
23
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
24
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
25
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
26
- step((generator = generator.apply(thisArg, _arguments || [])).next());
27
- });
28
- }
29
-
30
- exports.__awaiter = __awaiter;
31
- //# sourceMappingURL=tslib.es6.js.map