@colyseus/uwebsockets-transport 0.16.10 → 0.17.1
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/build/index.js +6 -5
- package/build/index.js.map +2 -2
- package/build/index.mjs.map +1 -1
- package/build/src/index.d.ts +2 -0
- package/build/{uWebSocketClient.d.ts → src/uWebSocketClient.d.ts} +9 -7
- package/build/{uWebSocketsTransport.d.ts → src/uWebSocketsTransport.d.ts} +3 -5
- package/build/uWebSocketClient.js +17 -17
- package/build/uWebSocketClient.js.map +3 -3
- package/build/uWebSocketClient.mjs +15 -16
- package/build/uWebSocketClient.mjs.map +3 -3
- package/build/uWebSocketsTransport.js +26 -16
- package/build/uWebSocketsTransport.js.map +3 -3
- package/build/uWebSocketsTransport.mjs +23 -14
- package/build/uWebSocketsTransport.mjs.map +2 -2
- package/package.json +11 -5
- package/build/index.d.ts +0 -2
package/build/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __defProp = Object.defineProperty;
|
|
2
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -15,14 +16,14 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
16
|
return to;
|
|
16
17
|
};
|
|
17
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
-
var
|
|
19
|
-
__export(
|
|
19
|
+
var index_exports = {};
|
|
20
|
+
__export(index_exports, {
|
|
20
21
|
uWebSocketClient: () => import_uWebSocketClient.uWebSocketClient,
|
|
21
22
|
uWebSocketsTransport: () => import_uWebSocketsTransport.uWebSocketsTransport
|
|
22
23
|
});
|
|
23
|
-
module.exports = __toCommonJS(
|
|
24
|
-
var import_uWebSocketClient = require("./uWebSocketClient.
|
|
25
|
-
var import_uWebSocketsTransport = require("./uWebSocketsTransport.
|
|
24
|
+
module.exports = __toCommonJS(index_exports);
|
|
25
|
+
var import_uWebSocketClient = require("./uWebSocketClient.ts");
|
|
26
|
+
var import_uWebSocketsTransport = require("./uWebSocketsTransport.ts");
|
|
26
27
|
// Annotate the CommonJS export names for ESM import in node:
|
|
27
28
|
0 && (module.exports = {
|
|
28
29
|
uWebSocketClient,
|
package/build/index.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export { uWebSocketClient } from './uWebSocketClient.
|
|
5
|
-
"mappings": "
|
|
4
|
+
"sourcesContent": ["export { uWebSocketClient } from './uWebSocketClient.ts';\nexport { uWebSocketsTransport, type TransportOptions } from './uWebSocketsTransport.ts'"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8BAAiC;AACjC,kCAA4D;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/build/index.mjs.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/index.ts"],
|
|
4
|
-
"sourcesContent": ["export { uWebSocketClient } from './uWebSocketClient.
|
|
4
|
+
"sourcesContent": ["export { uWebSocketClient } from './uWebSocketClient.ts';\nexport { uWebSocketsTransport, type TransportOptions } from './uWebSocketsTransport.ts'"],
|
|
5
5
|
"mappings": ";AAAA,SAAS,wBAAwB;AACjC,SAAS,4BAAmD;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import uWebSockets from 'uWebSockets.js';
|
|
3
|
-
import { Client, ClientPrivate, ClientState, ISendOptions } from '@colyseus/core';
|
|
3
|
+
import { type Client, type ClientPrivate, ClientState, type ISendOptions } from '@colyseus/core';
|
|
4
4
|
export declare class uWebSocketWrapper extends EventEmitter {
|
|
5
5
|
ws: uWebSockets.WebSocket<any>;
|
|
6
6
|
constructor(ws: uWebSockets.WebSocket<any>);
|
|
7
7
|
}
|
|
8
|
-
export declare
|
|
9
|
-
CONNECTING
|
|
10
|
-
OPEN
|
|
11
|
-
CLOSING
|
|
12
|
-
CLOSED
|
|
13
|
-
}
|
|
8
|
+
export declare const ReadyState: {
|
|
9
|
+
readonly CONNECTING: 0;
|
|
10
|
+
readonly OPEN: 1;
|
|
11
|
+
readonly CLOSING: 2;
|
|
12
|
+
readonly CLOSED: 3;
|
|
13
|
+
};
|
|
14
|
+
export type ReadyState = (typeof ReadyState)[keyof typeof ReadyState];
|
|
14
15
|
export declare class uWebSocketClient implements Client, ClientPrivate {
|
|
16
|
+
'~messages': any;
|
|
15
17
|
id: string;
|
|
16
18
|
_ref: uWebSocketWrapper;
|
|
17
19
|
sessionId: string;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { ParsedUrlQuery } from 'querystring';
|
|
1
|
+
import { type ParsedUrlQuery } from 'querystring';
|
|
2
2
|
import uWebSockets from 'uWebSockets.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { uWebSocketWrapper } from './uWebSocketClient.js';
|
|
3
|
+
import { type AuthContext, Transport } from '@colyseus/core';
|
|
4
|
+
import { uWebSocketWrapper } from './uWebSocketClient.ts';
|
|
6
5
|
export type TransportOptions = Omit<uWebSockets.WebSocketBehavior<any>, "upgrade" | "open" | "pong" | "close" | "message">;
|
|
7
6
|
type RawWebSocketClient = uWebSockets.WebSocket<any> & {
|
|
8
7
|
url: string;
|
|
@@ -11,7 +10,6 @@ type RawWebSocketClient = uWebSockets.WebSocket<any> & {
|
|
|
11
10
|
};
|
|
12
11
|
export declare class uWebSocketsTransport extends Transport {
|
|
13
12
|
app: uWebSockets.TemplatedApp;
|
|
14
|
-
expressApp: Application;
|
|
15
13
|
protected clients: RawWebSocketClient[];
|
|
16
14
|
protected clientWrappers: WeakMap<RawWebSocketClient, uWebSocketWrapper>;
|
|
17
15
|
private _listeningSocket;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -32,7 +33,8 @@ __export(uWebSocketClient_exports, {
|
|
|
32
33
|
uWebSocketWrapper: () => uWebSocketWrapper
|
|
33
34
|
});
|
|
34
35
|
module.exports = __toCommonJS(uWebSocketClient_exports);
|
|
35
|
-
var import_events = __toESM(require("events"));
|
|
36
|
+
var import_events = __toESM(require("events"), 1);
|
|
37
|
+
var import_uWebSockets = __toESM(require("uWebSockets.js"), 1);
|
|
36
38
|
var import_core = require("@colyseus/core");
|
|
37
39
|
class uWebSocketWrapper extends import_events.default {
|
|
38
40
|
constructor(ws) {
|
|
@@ -40,29 +42,27 @@ class uWebSocketWrapper extends import_events.default {
|
|
|
40
42
|
this.ws = ws;
|
|
41
43
|
}
|
|
42
44
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
})(ReadyState || {});
|
|
45
|
+
const ReadyState = {
|
|
46
|
+
CONNECTING: 0,
|
|
47
|
+
OPEN: 1,
|
|
48
|
+
CLOSING: 2,
|
|
49
|
+
CLOSED: 3
|
|
50
|
+
};
|
|
50
51
|
class uWebSocketClient {
|
|
51
52
|
constructor(id, _ref) {
|
|
52
|
-
this.id = id;
|
|
53
|
-
this._ref = _ref;
|
|
54
53
|
this.state = import_core.ClientState.JOINING;
|
|
55
|
-
this.readyState =
|
|
54
|
+
this.readyState = ReadyState.OPEN;
|
|
56
55
|
this._enqueuedMessages = [];
|
|
57
|
-
this.sessionId = id;
|
|
58
|
-
|
|
56
|
+
this.id = this.sessionId = id;
|
|
57
|
+
this._ref = _ref;
|
|
58
|
+
_ref.on("close", () => this.readyState = ReadyState.CLOSED);
|
|
59
59
|
}
|
|
60
60
|
get ref() {
|
|
61
61
|
return this._ref;
|
|
62
62
|
}
|
|
63
63
|
set ref(_ref) {
|
|
64
64
|
this._ref = _ref;
|
|
65
|
-
this.readyState =
|
|
65
|
+
this.readyState = ReadyState.OPEN;
|
|
66
66
|
}
|
|
67
67
|
sendBytes(type, bytes, options) {
|
|
68
68
|
(0, import_core.debugMessage)("send bytes(to %s): '%s' -> %j", this.sessionId, type, bytes);
|
|
@@ -90,7 +90,7 @@ class uWebSocketClient {
|
|
|
90
90
|
this.raw(data, options);
|
|
91
91
|
}
|
|
92
92
|
raw(data, options, cb) {
|
|
93
|
-
if (this.readyState !==
|
|
93
|
+
if (this.readyState !== ReadyState.OPEN) {
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
this._ref.ws.send(data, true, false);
|
|
@@ -102,10 +102,10 @@ class uWebSocketClient {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
leave(code, data) {
|
|
105
|
-
if (this.readyState !==
|
|
105
|
+
if (this.readyState !== ReadyState.OPEN) {
|
|
106
106
|
return;
|
|
107
107
|
}
|
|
108
|
-
this.readyState =
|
|
108
|
+
this.readyState = ReadyState.CLOSING;
|
|
109
109
|
if (code !== void 0) {
|
|
110
110
|
this._ref.ws.end(code, data);
|
|
111
111
|
} else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/uWebSocketClient.ts"],
|
|
4
|
-
"sourcesContent": ["import EventEmitter from 'events';\nimport uWebSockets from 'uWebSockets.js';\n\nimport { getMessageBytes, Protocol, Client, ClientPrivate, ClientState, ISendOptions, logger, debugMessage } from '@colyseus/core';\n\nexport class uWebSocketWrapper extends EventEmitter {\n
|
|
5
|
-
"mappings": "
|
|
6
|
-
"names": ["EventEmitter"
|
|
4
|
+
"sourcesContent": ["import EventEmitter from 'events';\nimport uWebSockets from 'uWebSockets.js';\n\nimport { getMessageBytes, Protocol, type Client, type ClientPrivate, ClientState, type ISendOptions, logger, debugMessage } from '@colyseus/core';\n\nexport class uWebSocketWrapper extends EventEmitter {\n public ws: uWebSockets.WebSocket<any>;\n constructor(ws: uWebSockets.WebSocket<any>) {\n super();\n this.ws = ws;\n }\n}\n\nexport const ReadyState = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\nexport type ReadyState = (typeof ReadyState)[keyof typeof ReadyState];\n\nexport class uWebSocketClient implements Client, ClientPrivate {\n '~messages': any;\n\n public id: string;\n public _ref: uWebSocketWrapper;\n\n public sessionId: string;\n public state: ClientState = ClientState.JOINING;\n public readyState: number = ReadyState.OPEN;\n public reconnectionToken: string;\n\n public _enqueuedMessages: any[] = [];\n public _afterNextPatchQueue;\n public _reconnectionToken: string;\n public _joinedAt: number;\n\n constructor(id: string, _ref: uWebSocketWrapper) {\n this.id = this.sessionId = id;\n this._ref = _ref;\n _ref.on('close', () => this.readyState = ReadyState.CLOSED);\n }\n\n get ref() { return this._ref; }\n set ref(_ref: uWebSocketWrapper) {\n this._ref = _ref;\n this.readyState = ReadyState.OPEN;\n }\n\n public sendBytes(type: string | number, bytes: Buffer | Uint8Array, options?: ISendOptions) {\n debugMessage(\"send bytes(to %s): '%s' -> %j\", this.sessionId, type, bytes);\n\n this.enqueueRaw(\n getMessageBytes.raw(Protocol.ROOM_DATA_BYTES, type, undefined, bytes),\n options,\n );\n }\n\n public send(messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions) {\n debugMessage(\"send(to %s): '%s' -> %O\", this.sessionId, messageOrType, messageOrOptions);\n\n this.enqueueRaw(\n getMessageBytes.raw(Protocol.ROOM_DATA, messageOrType, messageOrOptions),\n options,\n );\n }\n\n public enqueueRaw(data: Uint8Array | Buffer, options?: ISendOptions) {\n // use room's afterNextPatch queue\n if (options?.afterNextPatch) {\n this._afterNextPatchQueue.push([this, [data]]);\n return;\n }\n\n if (this.state === ClientState.JOINING) {\n // sending messages during `onJoin`.\n // - the client-side cannot register \"onMessage\" callbacks at this point.\n // - enqueue the messages to be send after JOIN_ROOM message has been sent\n // - create a new buffer for enqueued messages, as the underlying buffer might be modified\n this._enqueuedMessages.push(data);\n return;\n }\n\n this.raw(data, options);\n }\n\n public raw(data: Uint8Array | Buffer, options?: ISendOptions, cb?: (err?: Error) => void) {\n // skip if client not open\n if (this.readyState !== ReadyState.OPEN) {\n return;\n }\n\n this._ref.ws.send(data, true, false);\n }\n\n public error(code: number, message: string = '', cb?: (err?: Error) => void) {\n this.raw(getMessageBytes[Protocol.ERROR](code, message));\n\n if (cb) {\n // delay callback execution - uWS doesn't acknowledge when the message was sent\n // (same API as \"ws\" transport)\n setTimeout(cb, 1);\n }\n }\n\n public leave(code?: number, data?: string) {\n if (this.readyState !== ReadyState.OPEN) {\n // connection already closed. ignore.\n return;\n }\n\n this.readyState = ReadyState.CLOSING;\n\n if (code !== undefined) {\n this._ref.ws.end(code, data);\n\n } else {\n this._ref.ws.close();\n }\n }\n\n public close(code?: number, data?: string) {\n logger.warn('DEPRECATION WARNING: use client.leave() instead of client.close()');\n try {\n throw new Error();\n } catch (e: any) {\n logger.info(e.stack);\n }\n this.leave(code, data);\n }\n\n public toJSON() {\n return { sessionId: this.sessionId, readyState: this.readyState };\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAAyB;AACzB,yBAAwB;AAExB,kBAAiI;AAE1H,MAAM,0BAA0B,cAAAA,QAAa;AAAA,EAElD,YAAY,IAAgC;AAC1C,UAAM;AACN,SAAK,KAAK;AAAA,EACZ;AACF;AAEO,MAAM,aAAa;AAAA,EACxB,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AACV;AAGO,MAAM,iBAAkD;AAAA,EAgB7D,YAAY,IAAY,MAAyB;AATjD,SAAO,QAAqB,wBAAY;AACxC,SAAO,aAAqB,WAAW;AAGvC,SAAO,oBAA2B,CAAC;AAMjC,SAAK,KAAK,KAAK,YAAY;AAC3B,SAAK,OAAO;AACZ,SAAK,GAAG,SAAS,MAAM,KAAK,aAAa,WAAW,MAAM;AAAA,EAC5D;AAAA,EAEA,IAAI,MAAM;AAAE,WAAO,KAAK;AAAA,EAAM;AAAA,EAC9B,IAAI,IAAI,MAAyB;AAC/B,SAAK,OAAO;AACZ,SAAK,aAAa,WAAW;AAAA,EAC/B;AAAA,EAEO,UAAU,MAAuB,OAA4B,SAAwB;AAC1F,kCAAa,iCAAiC,KAAK,WAAW,MAAM,KAAK;AAEzE,SAAK;AAAA,MACH,4BAAgB,IAAI,qBAAS,iBAAiB,MAAM,QAAW,KAAK;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AAAA,EAEO,KAAK,eAAoB,kBAAuC,SAAwB;AAC7F,kCAAa,2BAA2B,KAAK,WAAW,eAAe,gBAAgB;AAEvF,SAAK;AAAA,MACH,4BAAgB,IAAI,qBAAS,WAAW,eAAe,gBAAgB;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AAAA,EAEO,WAAW,MAA2B,SAAwB;AAEnE,QAAI,SAAS,gBAAgB;AAC3B,WAAK,qBAAqB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC7C;AAAA,IACF;AAEA,QAAI,KAAK,UAAU,wBAAY,SAAS;AAKtC,WAAK,kBAAkB,KAAK,IAAI;AAChC;AAAA,IACF;AAEA,SAAK,IAAI,MAAM,OAAO;AAAA,EACxB;AAAA,EAEO,IAAI,MAA2B,SAAwB,IAA4B;AAExF,QAAI,KAAK,eAAe,WAAW,MAAM;AACvC;AAAA,IACF;AAEA,SAAK,KAAK,GAAG,KAAK,MAAM,MAAM,KAAK;AAAA,EACrC;AAAA,EAEO,MAAM,MAAc,UAAkB,IAAI,IAA4B;AAC3E,SAAK,IAAI,4BAAgB,qBAAS,KAAK,EAAE,MAAM,OAAO,CAAC;AAEvD,QAAI,IAAI;AAGN,iBAAW,IAAI,CAAC;AAAA,IAClB;AAAA,EACF;AAAA,EAEO,MAAM,MAAe,MAAe;AACzC,QAAI,KAAK,eAAe,WAAW,MAAM;AAEvC;AAAA,IACF;AAEA,SAAK,aAAa,WAAW;AAE7B,QAAI,SAAS,QAAW;AACtB,WAAK,KAAK,GAAG,IAAI,MAAM,IAAI;AAAA,IAE7B,OAAO;AACL,WAAK,KAAK,GAAG,MAAM;AAAA,IACrB;AAAA,EACF;AAAA,EAEO,MAAM,MAAe,MAAe;AACzC,uBAAO,KAAK,mEAAmE;AAC/E,QAAI;AACF,YAAM,IAAI,MAAM;AAAA,IAClB,SAAS,GAAQ;AACf,yBAAO,KAAK,EAAE,KAAK;AAAA,IACrB;AACA,SAAK,MAAM,MAAM,IAAI;AAAA,EACvB;AAAA,EAEO,SAAS;AACd,WAAO,EAAE,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW;AAAA,EAClE;AACF;",
|
|
6
|
+
"names": ["EventEmitter"]
|
|
7
7
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// packages/transport/uwebsockets-transport/src/uWebSocketClient.ts
|
|
2
2
|
import EventEmitter from "events";
|
|
3
|
+
import "uWebSockets.js";
|
|
3
4
|
import { getMessageBytes, Protocol, ClientState, logger, debugMessage } from "@colyseus/core";
|
|
4
5
|
var uWebSocketWrapper = class extends EventEmitter {
|
|
5
6
|
constructor(ws) {
|
|
@@ -7,29 +8,27 @@ var uWebSocketWrapper = class extends EventEmitter {
|
|
|
7
8
|
this.ws = ws;
|
|
8
9
|
}
|
|
9
10
|
};
|
|
10
|
-
var ReadyState =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
})(ReadyState || {});
|
|
11
|
+
var ReadyState = {
|
|
12
|
+
CONNECTING: 0,
|
|
13
|
+
OPEN: 1,
|
|
14
|
+
CLOSING: 2,
|
|
15
|
+
CLOSED: 3
|
|
16
|
+
};
|
|
17
17
|
var uWebSocketClient = class {
|
|
18
18
|
constructor(id, _ref) {
|
|
19
|
-
this.id = id;
|
|
20
|
-
this._ref = _ref;
|
|
21
19
|
this.state = ClientState.JOINING;
|
|
22
|
-
this.readyState =
|
|
20
|
+
this.readyState = ReadyState.OPEN;
|
|
23
21
|
this._enqueuedMessages = [];
|
|
24
|
-
this.sessionId = id;
|
|
25
|
-
|
|
22
|
+
this.id = this.sessionId = id;
|
|
23
|
+
this._ref = _ref;
|
|
24
|
+
_ref.on("close", () => this.readyState = ReadyState.CLOSED);
|
|
26
25
|
}
|
|
27
26
|
get ref() {
|
|
28
27
|
return this._ref;
|
|
29
28
|
}
|
|
30
29
|
set ref(_ref) {
|
|
31
30
|
this._ref = _ref;
|
|
32
|
-
this.readyState =
|
|
31
|
+
this.readyState = ReadyState.OPEN;
|
|
33
32
|
}
|
|
34
33
|
sendBytes(type, bytes, options) {
|
|
35
34
|
debugMessage("send bytes(to %s): '%s' -> %j", this.sessionId, type, bytes);
|
|
@@ -57,7 +56,7 @@ var uWebSocketClient = class {
|
|
|
57
56
|
this.raw(data, options);
|
|
58
57
|
}
|
|
59
58
|
raw(data, options, cb) {
|
|
60
|
-
if (this.readyState !==
|
|
59
|
+
if (this.readyState !== ReadyState.OPEN) {
|
|
61
60
|
return;
|
|
62
61
|
}
|
|
63
62
|
this._ref.ws.send(data, true, false);
|
|
@@ -69,10 +68,10 @@ var uWebSocketClient = class {
|
|
|
69
68
|
}
|
|
70
69
|
}
|
|
71
70
|
leave(code, data) {
|
|
72
|
-
if (this.readyState !==
|
|
71
|
+
if (this.readyState !== ReadyState.OPEN) {
|
|
73
72
|
return;
|
|
74
73
|
}
|
|
75
|
-
this.readyState =
|
|
74
|
+
this.readyState = ReadyState.CLOSING;
|
|
76
75
|
if (code !== void 0) {
|
|
77
76
|
this._ref.ws.end(code, data);
|
|
78
77
|
} else {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/uWebSocketClient.ts"],
|
|
4
|
-
"sourcesContent": ["import EventEmitter from 'events';\nimport uWebSockets from 'uWebSockets.js';\n\nimport { getMessageBytes, Protocol, Client, ClientPrivate, ClientState, ISendOptions, logger, debugMessage } from '@colyseus/core';\n\nexport class uWebSocketWrapper extends EventEmitter {\n
|
|
5
|
-
"mappings": ";AAAA,OAAO,kBAAkB;
|
|
6
|
-
"names": [
|
|
4
|
+
"sourcesContent": ["import EventEmitter from 'events';\nimport uWebSockets from 'uWebSockets.js';\n\nimport { getMessageBytes, Protocol, type Client, type ClientPrivate, ClientState, type ISendOptions, logger, debugMessage } from '@colyseus/core';\n\nexport class uWebSocketWrapper extends EventEmitter {\n public ws: uWebSockets.WebSocket<any>;\n constructor(ws: uWebSockets.WebSocket<any>) {\n super();\n this.ws = ws;\n }\n}\n\nexport const ReadyState = {\n CONNECTING: 0,\n OPEN: 1,\n CLOSING: 2,\n CLOSED: 3,\n} as const;\nexport type ReadyState = (typeof ReadyState)[keyof typeof ReadyState];\n\nexport class uWebSocketClient implements Client, ClientPrivate {\n '~messages': any;\n\n public id: string;\n public _ref: uWebSocketWrapper;\n\n public sessionId: string;\n public state: ClientState = ClientState.JOINING;\n public readyState: number = ReadyState.OPEN;\n public reconnectionToken: string;\n\n public _enqueuedMessages: any[] = [];\n public _afterNextPatchQueue;\n public _reconnectionToken: string;\n public _joinedAt: number;\n\n constructor(id: string, _ref: uWebSocketWrapper) {\n this.id = this.sessionId = id;\n this._ref = _ref;\n _ref.on('close', () => this.readyState = ReadyState.CLOSED);\n }\n\n get ref() { return this._ref; }\n set ref(_ref: uWebSocketWrapper) {\n this._ref = _ref;\n this.readyState = ReadyState.OPEN;\n }\n\n public sendBytes(type: string | number, bytes: Buffer | Uint8Array, options?: ISendOptions) {\n debugMessage(\"send bytes(to %s): '%s' -> %j\", this.sessionId, type, bytes);\n\n this.enqueueRaw(\n getMessageBytes.raw(Protocol.ROOM_DATA_BYTES, type, undefined, bytes),\n options,\n );\n }\n\n public send(messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions) {\n debugMessage(\"send(to %s): '%s' -> %O\", this.sessionId, messageOrType, messageOrOptions);\n\n this.enqueueRaw(\n getMessageBytes.raw(Protocol.ROOM_DATA, messageOrType, messageOrOptions),\n options,\n );\n }\n\n public enqueueRaw(data: Uint8Array | Buffer, options?: ISendOptions) {\n // use room's afterNextPatch queue\n if (options?.afterNextPatch) {\n this._afterNextPatchQueue.push([this, [data]]);\n return;\n }\n\n if (this.state === ClientState.JOINING) {\n // sending messages during `onJoin`.\n // - the client-side cannot register \"onMessage\" callbacks at this point.\n // - enqueue the messages to be send after JOIN_ROOM message has been sent\n // - create a new buffer for enqueued messages, as the underlying buffer might be modified\n this._enqueuedMessages.push(data);\n return;\n }\n\n this.raw(data, options);\n }\n\n public raw(data: Uint8Array | Buffer, options?: ISendOptions, cb?: (err?: Error) => void) {\n // skip if client not open\n if (this.readyState !== ReadyState.OPEN) {\n return;\n }\n\n this._ref.ws.send(data, true, false);\n }\n\n public error(code: number, message: string = '', cb?: (err?: Error) => void) {\n this.raw(getMessageBytes[Protocol.ERROR](code, message));\n\n if (cb) {\n // delay callback execution - uWS doesn't acknowledge when the message was sent\n // (same API as \"ws\" transport)\n setTimeout(cb, 1);\n }\n }\n\n public leave(code?: number, data?: string) {\n if (this.readyState !== ReadyState.OPEN) {\n // connection already closed. ignore.\n return;\n }\n\n this.readyState = ReadyState.CLOSING;\n\n if (code !== undefined) {\n this._ref.ws.end(code, data);\n\n } else {\n this._ref.ws.close();\n }\n }\n\n public close(code?: number, data?: string) {\n logger.warn('DEPRECATION WARNING: use client.leave() instead of client.close()');\n try {\n throw new Error();\n } catch (e: any) {\n logger.info(e.stack);\n }\n this.leave(code, data);\n }\n\n public toJSON() {\n return { sessionId: this.sessionId, readyState: this.readyState };\n }\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,OAAO,kBAAkB;AACzB,OAAwB;AAExB,SAAS,iBAAiB,UAA2C,aAAgC,QAAQ,oBAAoB;AAE1H,IAAM,oBAAN,cAAgC,aAAa;AAAA,EAElD,YAAY,IAAgC;AAC1C,UAAM;AACN,SAAK,KAAK;AAAA,EACZ;AACF;AAEO,IAAM,aAAa;AAAA,EACxB,YAAY;AAAA,EACZ,MAAM;AAAA,EACN,SAAS;AAAA,EACT,QAAQ;AACV;AAGO,IAAM,mBAAN,MAAwD;AAAA,EAgB7D,YAAY,IAAY,MAAyB;AATjD,SAAO,QAAqB,YAAY;AACxC,SAAO,aAAqB,WAAW;AAGvC,SAAO,oBAA2B,CAAC;AAMjC,SAAK,KAAK,KAAK,YAAY;AAC3B,SAAK,OAAO;AACZ,SAAK,GAAG,SAAS,MAAM,KAAK,aAAa,WAAW,MAAM;AAAA,EAC5D;AAAA,EAEA,IAAI,MAAM;AAAE,WAAO,KAAK;AAAA,EAAM;AAAA,EAC9B,IAAI,IAAI,MAAyB;AAC/B,SAAK,OAAO;AACZ,SAAK,aAAa,WAAW;AAAA,EAC/B;AAAA,EAEO,UAAU,MAAuB,OAA4B,SAAwB;AAC1F,iBAAa,iCAAiC,KAAK,WAAW,MAAM,KAAK;AAEzE,SAAK;AAAA,MACH,gBAAgB,IAAI,SAAS,iBAAiB,MAAM,QAAW,KAAK;AAAA,MACpE;AAAA,IACF;AAAA,EACF;AAAA,EAEO,KAAK,eAAoB,kBAAuC,SAAwB;AAC7F,iBAAa,2BAA2B,KAAK,WAAW,eAAe,gBAAgB;AAEvF,SAAK;AAAA,MACH,gBAAgB,IAAI,SAAS,WAAW,eAAe,gBAAgB;AAAA,MACvE;AAAA,IACF;AAAA,EACF;AAAA,EAEO,WAAW,MAA2B,SAAwB;AAEnE,QAAI,SAAS,gBAAgB;AAC3B,WAAK,qBAAqB,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC7C;AAAA,IACF;AAEA,QAAI,KAAK,UAAU,YAAY,SAAS;AAKtC,WAAK,kBAAkB,KAAK,IAAI;AAChC;AAAA,IACF;AAEA,SAAK,IAAI,MAAM,OAAO;AAAA,EACxB;AAAA,EAEO,IAAI,MAA2B,SAAwB,IAA4B;AAExF,QAAI,KAAK,eAAe,WAAW,MAAM;AACvC;AAAA,IACF;AAEA,SAAK,KAAK,GAAG,KAAK,MAAM,MAAM,KAAK;AAAA,EACrC;AAAA,EAEO,MAAM,MAAc,UAAkB,IAAI,IAA4B;AAC3E,SAAK,IAAI,gBAAgB,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC;AAEvD,QAAI,IAAI;AAGN,iBAAW,IAAI,CAAC;AAAA,IAClB;AAAA,EACF;AAAA,EAEO,MAAM,MAAe,MAAe;AACzC,QAAI,KAAK,eAAe,WAAW,MAAM;AAEvC;AAAA,IACF;AAEA,SAAK,aAAa,WAAW;AAE7B,QAAI,SAAS,QAAW;AACtB,WAAK,KAAK,GAAG,IAAI,MAAM,IAAI;AAAA,IAE7B,OAAO;AACL,WAAK,KAAK,GAAG,MAAM;AAAA,IACrB;AAAA,EACF;AAAA,EAEO,MAAM,MAAe,MAAe;AACzC,WAAO,KAAK,mEAAmE;AAC/E,QAAI;AACF,YAAM,IAAI,MAAM;AAAA,IAClB,SAAS,GAAQ;AACf,aAAO,KAAK,EAAE,KAAK;AAAA,IACrB;AACA,SAAK,MAAM,MAAM,IAAI;AAAA,EACvB;AAAA,EAEO,SAAS;AACd,WAAO,EAAE,WAAW,KAAK,WAAW,YAAY,KAAK,WAAW;AAAA,EAClE;AACF;",
|
|
6
|
+
"names": []
|
|
7
7
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -30,11 +31,11 @@ __export(uWebSocketsTransport_exports, {
|
|
|
30
31
|
uWebSocketsTransport: () => uWebSocketsTransport
|
|
31
32
|
});
|
|
32
33
|
module.exports = __toCommonJS(uWebSocketsTransport_exports);
|
|
33
|
-
var import_querystring = __toESM(require("querystring"));
|
|
34
|
-
var import_uWebSockets = __toESM(require("uWebSockets.js"));
|
|
35
|
-
var import_uwebsockets_express = __toESM(require("uwebsockets-express"));
|
|
34
|
+
var import_querystring = __toESM(require("querystring"), 1);
|
|
35
|
+
var import_uWebSockets = __toESM(require("uWebSockets.js"), 1);
|
|
36
|
+
var import_uwebsockets_express = __toESM(require("uwebsockets-express"), 1);
|
|
36
37
|
var import_core = require("@colyseus/core");
|
|
37
|
-
var import_uWebSocketClient = require("./uWebSocketClient.
|
|
38
|
+
var import_uWebSocketClient = require("./uWebSocketClient.ts");
|
|
38
39
|
class uWebSocketsTransport extends import_core.Transport {
|
|
39
40
|
constructor(options = {}, appOptions = {}) {
|
|
40
41
|
super();
|
|
@@ -42,7 +43,6 @@ class uWebSocketsTransport extends import_core.Transport {
|
|
|
42
43
|
this.clientWrappers = /* @__PURE__ */ new WeakMap();
|
|
43
44
|
this._originalRawSend = null;
|
|
44
45
|
this.app = appOptions.cert_file_name && appOptions.key_file_name ? import_uWebSockets.default.SSLApp(appOptions) : import_uWebSockets.default.App(appOptions);
|
|
45
|
-
this.expressApp = (0, import_uwebsockets_express.default)(this.app);
|
|
46
46
|
if (options.maxBackpressure === void 0) {
|
|
47
47
|
options.maxBackpressure = 1024 * 1024;
|
|
48
48
|
}
|
|
@@ -139,13 +139,21 @@ class uWebSocketsTransport extends import_core.Transport {
|
|
|
139
139
|
const sessionId = searchParams.sessionId;
|
|
140
140
|
const processAndRoomId = url.match(/\/[a-zA-Z0-9_\-]+\/([a-zA-Z0-9_\-]+)$/);
|
|
141
141
|
const roomId = processAndRoomId && processAndRoomId[1];
|
|
142
|
+
if (!sessionId && !roomId) {
|
|
143
|
+
const timeout = setTimeout(() => rawClient.close(), 1e3);
|
|
144
|
+
wrapper.on("message", (_) => rawClient.send(new Uint8Array([import_core.Protocol.PING]), true));
|
|
145
|
+
wrapper.on("close", () => clearTimeout(timeout));
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
142
148
|
const room = import_core.matchMaker.getLocalRoomById(roomId);
|
|
143
149
|
const client = new import_uWebSocketClient.uWebSocketClient(sessionId, wrapper);
|
|
150
|
+
const reconnectionToken = searchParams.reconnectionToken;
|
|
151
|
+
const skipHandshake = searchParams.skipHandshake !== void 0;
|
|
144
152
|
try {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
await (0, import_core.connectClientToRoom)(room, client, rawClient.context, {
|
|
154
|
+
reconnectionToken,
|
|
155
|
+
skipHandshake
|
|
156
|
+
});
|
|
149
157
|
} catch (e) {
|
|
150
158
|
(0, import_core.debugAndPrintError)(e);
|
|
151
159
|
client.error(e.code, e.message, () => client.leave());
|
|
@@ -154,14 +162,14 @@ class uWebSocketsTransport extends import_core.Transport {
|
|
|
154
162
|
registerMatchMakeRequest() {
|
|
155
163
|
const matchmakeRoute = "matchmake";
|
|
156
164
|
const allowedRoomNameChars = /([a-zA-Z_\-0-9]+)/gi;
|
|
157
|
-
const writeHeaders = (
|
|
165
|
+
const writeHeaders = (res, requestHeaders) => {
|
|
158
166
|
if (res.aborted) {
|
|
159
167
|
return;
|
|
160
168
|
}
|
|
161
169
|
const headers = Object.assign(
|
|
162
170
|
{},
|
|
163
171
|
import_core.matchMaker.controller.DEFAULT_CORS_HEADERS,
|
|
164
|
-
import_core.matchMaker.controller.getCorsHeaders
|
|
172
|
+
import_core.matchMaker.controller.getCorsHeaders(requestHeaders)
|
|
165
173
|
);
|
|
166
174
|
for (const header in headers) {
|
|
167
175
|
res.writeHeader(header, headers[header].toString());
|
|
@@ -182,7 +190,9 @@ class uWebSocketsTransport extends import_core.Transport {
|
|
|
182
190
|
};
|
|
183
191
|
this.app.options("/matchmake/*", (res, req) => {
|
|
184
192
|
res.onAborted(() => onAborted(res));
|
|
185
|
-
|
|
193
|
+
const reqHeaders = new Headers();
|
|
194
|
+
req.forEach((key, value) => reqHeaders.set(key, value));
|
|
195
|
+
if (writeHeaders(res, reqHeaders)) {
|
|
186
196
|
res.writeStatus("204 No Content");
|
|
187
197
|
res.end();
|
|
188
198
|
}
|
|
@@ -192,13 +202,13 @@ class uWebSocketsTransport extends import_core.Transport {
|
|
|
192
202
|
if (import_core.matchMaker.state === import_core.matchMaker.MatchMakerState.SHUTTING_DOWN) {
|
|
193
203
|
return res.close();
|
|
194
204
|
}
|
|
195
|
-
|
|
205
|
+
const headers = new Headers();
|
|
206
|
+
req.forEach((key, value) => headers.set(key, value));
|
|
207
|
+
writeHeaders(res, headers);
|
|
196
208
|
res.writeHeader("Content-Type", "application/json");
|
|
197
209
|
const url = req.getUrl();
|
|
198
210
|
const matchedParams = url.match(allowedRoomNameChars);
|
|
199
211
|
const matchmakeIndex = matchedParams.indexOf(matchmakeRoute);
|
|
200
|
-
const headers = {};
|
|
201
|
-
req.forEach((key, value) => headers[key] = value);
|
|
202
212
|
const token = (0, import_core.getBearerToken)(headers["authorization"]);
|
|
203
213
|
this.readJson(res, async (clientOptions) => {
|
|
204
214
|
try {
|
|
@@ -214,7 +224,7 @@ class uWebSocketsTransport extends import_core.Transport {
|
|
|
214
224
|
{
|
|
215
225
|
token,
|
|
216
226
|
headers,
|
|
217
|
-
ip: headers
|
|
227
|
+
ip: headers.get("x-real-ip") ?? headers.get("x-forwarded-for") ?? Buffer.from(res.getRemoteAddressAsText()).toString()
|
|
218
228
|
}
|
|
219
229
|
);
|
|
220
230
|
if (!res.aborted) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/uWebSocketsTransport.ts"],
|
|
4
|
-
"sourcesContent": ["import http, { IncomingHttpHeaders } from 'http';\nimport querystring, { ParsedUrlQuery } from 'querystring';\nimport uWebSockets from 'uWebSockets.js';\nimport expressify, { Application } from \"uwebsockets-express\";\n\nimport { AuthContext, HttpServerMock, ErrorCode, matchMaker, getBearerToken, Transport, debugAndPrintError, spliceOne } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.js';\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 public expressApp: Application;\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\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 this.expressApp = expressify(this.app);\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 // https://github.com/colyseus/colyseus/issues/458\n // Adding a mock object for Transport.server\n if(!this.server) {\n // @ts-ignore\n this.server = new HttpServerMock();\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: 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 'message' on wrapper\n this.clientWrappers.get(ws)?.emit('message', Buffer.from(message));\n },\n\n });\n\n this.registerMatchMakeRequest();\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 // @ts-ignore\n this.server.emit(\"listening\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n };\n\n if (typeof(port) === \"string\") {\n // @ts-ignore\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 // @ts-ignore\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 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 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 const room = matchMaker.getLocalRoomById(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.context);\n\n } catch (e) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => client.leave());\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.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 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.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\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 // cache all headers\n const headers: IncomingHttpHeaders = {};\n req.forEach((key, value) => headers[key] = value);\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['x-real-ip'] ?? headers['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) {\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": "
|
|
6
|
-
"names": ["uWebSockets", "
|
|
4
|
+
"sourcesContent": ["import type { IncomingHttpHeaders } from 'http';\nimport querystring, { type ParsedUrlQuery } from 'querystring';\nimport uWebSockets, { type WebSocket } from 'uWebSockets.js';\nimport expressify, { Application } from \"uwebsockets-express\";\n\nimport { type AuthContext, Transport, HttpServerMock, ErrorCode, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.ts';\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\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 // https://github.com/colyseus/colyseus/issues/458\n // Adding a mock object for Transport.server\n if(!this.server) {\n // @ts-ignore\n this.server = new HttpServerMock();\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 this.registerMatchMakeRequest();\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 // @ts-ignore\n this.server.emit(\"listening\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n };\n\n if (typeof(port) === \"string\") {\n // @ts-ignore\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 // @ts-ignore\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 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, () => client.leave());\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 = (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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",
|
|
6
|
+
"names": ["uWebSockets", "querystring"]
|
|
7
7
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// packages/transport/uwebsockets-transport/src/uWebSocketsTransport.ts
|
|
2
2
|
import querystring from "querystring";
|
|
3
3
|
import uWebSockets from "uWebSockets.js";
|
|
4
|
-
import
|
|
5
|
-
import { HttpServerMock, ErrorCode, matchMaker,
|
|
4
|
+
import "uwebsockets-express";
|
|
5
|
+
import { Transport, HttpServerMock, ErrorCode, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom } from "@colyseus/core";
|
|
6
6
|
import { uWebSocketClient, uWebSocketWrapper } from "./uWebSocketClient.mjs";
|
|
7
7
|
var uWebSocketsTransport = class extends Transport {
|
|
8
8
|
constructor(options = {}, appOptions = {}) {
|
|
@@ -11,7 +11,6 @@ var uWebSocketsTransport = class extends Transport {
|
|
|
11
11
|
this.clientWrappers = /* @__PURE__ */ new WeakMap();
|
|
12
12
|
this._originalRawSend = null;
|
|
13
13
|
this.app = appOptions.cert_file_name && appOptions.key_file_name ? uWebSockets.SSLApp(appOptions) : uWebSockets.App(appOptions);
|
|
14
|
-
this.expressApp = expressify(this.app);
|
|
15
14
|
if (options.maxBackpressure === void 0) {
|
|
16
15
|
options.maxBackpressure = 1024 * 1024;
|
|
17
16
|
}
|
|
@@ -108,13 +107,21 @@ var uWebSocketsTransport = class extends Transport {
|
|
|
108
107
|
const sessionId = searchParams.sessionId;
|
|
109
108
|
const processAndRoomId = url.match(/\/[a-zA-Z0-9_\-]+\/([a-zA-Z0-9_\-]+)$/);
|
|
110
109
|
const roomId = processAndRoomId && processAndRoomId[1];
|
|
110
|
+
if (!sessionId && !roomId) {
|
|
111
|
+
const timeout = setTimeout(() => rawClient.close(), 1e3);
|
|
112
|
+
wrapper.on("message", (_) => rawClient.send(new Uint8Array([Protocol.PING]), true));
|
|
113
|
+
wrapper.on("close", () => clearTimeout(timeout));
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
111
116
|
const room = matchMaker.getLocalRoomById(roomId);
|
|
112
117
|
const client = new uWebSocketClient(sessionId, wrapper);
|
|
118
|
+
const reconnectionToken = searchParams.reconnectionToken;
|
|
119
|
+
const skipHandshake = searchParams.skipHandshake !== void 0;
|
|
113
120
|
try {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
await connectClientToRoom(room, client, rawClient.context, {
|
|
122
|
+
reconnectionToken,
|
|
123
|
+
skipHandshake
|
|
124
|
+
});
|
|
118
125
|
} catch (e) {
|
|
119
126
|
debugAndPrintError(e);
|
|
120
127
|
client.error(e.code, e.message, () => client.leave());
|
|
@@ -123,14 +130,14 @@ var uWebSocketsTransport = class extends Transport {
|
|
|
123
130
|
registerMatchMakeRequest() {
|
|
124
131
|
const matchmakeRoute = "matchmake";
|
|
125
132
|
const allowedRoomNameChars = /([a-zA-Z_\-0-9]+)/gi;
|
|
126
|
-
const writeHeaders = (
|
|
133
|
+
const writeHeaders = (res, requestHeaders) => {
|
|
127
134
|
if (res.aborted) {
|
|
128
135
|
return;
|
|
129
136
|
}
|
|
130
137
|
const headers = Object.assign(
|
|
131
138
|
{},
|
|
132
139
|
matchMaker.controller.DEFAULT_CORS_HEADERS,
|
|
133
|
-
matchMaker.controller.getCorsHeaders
|
|
140
|
+
matchMaker.controller.getCorsHeaders(requestHeaders)
|
|
134
141
|
);
|
|
135
142
|
for (const header in headers) {
|
|
136
143
|
res.writeHeader(header, headers[header].toString());
|
|
@@ -151,7 +158,9 @@ var uWebSocketsTransport = class extends Transport {
|
|
|
151
158
|
};
|
|
152
159
|
this.app.options("/matchmake/*", (res, req) => {
|
|
153
160
|
res.onAborted(() => onAborted(res));
|
|
154
|
-
|
|
161
|
+
const reqHeaders = new Headers();
|
|
162
|
+
req.forEach((key, value) => reqHeaders.set(key, value));
|
|
163
|
+
if (writeHeaders(res, reqHeaders)) {
|
|
155
164
|
res.writeStatus("204 No Content");
|
|
156
165
|
res.end();
|
|
157
166
|
}
|
|
@@ -161,13 +170,13 @@ var uWebSocketsTransport = class extends Transport {
|
|
|
161
170
|
if (matchMaker.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {
|
|
162
171
|
return res.close();
|
|
163
172
|
}
|
|
164
|
-
|
|
173
|
+
const headers = new Headers();
|
|
174
|
+
req.forEach((key, value) => headers.set(key, value));
|
|
175
|
+
writeHeaders(res, headers);
|
|
165
176
|
res.writeHeader("Content-Type", "application/json");
|
|
166
177
|
const url = req.getUrl();
|
|
167
178
|
const matchedParams = url.match(allowedRoomNameChars);
|
|
168
179
|
const matchmakeIndex = matchedParams.indexOf(matchmakeRoute);
|
|
169
|
-
const headers = {};
|
|
170
|
-
req.forEach((key, value) => headers[key] = value);
|
|
171
180
|
const token = getBearerToken(headers["authorization"]);
|
|
172
181
|
this.readJson(res, async (clientOptions) => {
|
|
173
182
|
try {
|
|
@@ -183,7 +192,7 @@ var uWebSocketsTransport = class extends Transport {
|
|
|
183
192
|
{
|
|
184
193
|
token,
|
|
185
194
|
headers,
|
|
186
|
-
ip: headers
|
|
195
|
+
ip: headers.get("x-real-ip") ?? headers.get("x-forwarded-for") ?? Buffer.from(res.getRemoteAddressAsText()).toString()
|
|
187
196
|
}
|
|
188
197
|
);
|
|
189
198
|
if (!res.aborted) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/uWebSocketsTransport.ts"],
|
|
4
|
-
"sourcesContent": ["import http, { IncomingHttpHeaders } from 'http';\nimport querystring, { ParsedUrlQuery } from 'querystring';\nimport uWebSockets from 'uWebSockets.js';\nimport expressify, { Application } from \"uwebsockets-express\";\n\nimport { AuthContext, HttpServerMock, ErrorCode, matchMaker, getBearerToken, Transport, debugAndPrintError, spliceOne } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.js';\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 public expressApp: Application;\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\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 this.expressApp = expressify(this.app);\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 // https://github.com/colyseus/colyseus/issues/458\n // Adding a mock object for Transport.server\n if(!this.server) {\n // @ts-ignore\n this.server = new HttpServerMock();\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: 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 'message' on wrapper\n this.clientWrappers.get(ws)?.emit('message', Buffer.from(message));\n },\n\n });\n\n this.registerMatchMakeRequest();\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 // @ts-ignore\n this.server.emit(\"listening\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n };\n\n if (typeof(port) === \"string\") {\n // @ts-ignore\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 // @ts-ignore\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 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 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 const room = matchMaker.getLocalRoomById(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.context);\n\n } catch (e) {\n debugAndPrintError(e);\n\n // send error code to client then terminate\n client.error(e.code, e.message, () => client.leave());\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.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 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.state === matchMaker.MatchMakerState.SHUTTING_DOWN) {\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 // cache all headers\n const headers: IncomingHttpHeaders = {};\n req.forEach((key, value) => headers[key] = value);\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['x-real-ip'] ?? headers['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) {\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": ";AACA,OAAO,
|
|
4
|
+
"sourcesContent": ["import type { IncomingHttpHeaders } from 'http';\nimport querystring, { type ParsedUrlQuery } from 'querystring';\nimport uWebSockets, { type WebSocket } from 'uWebSockets.js';\nimport expressify, { Application } from \"uwebsockets-express\";\n\nimport { type AuthContext, Transport, HttpServerMock, ErrorCode, matchMaker, Protocol, getBearerToken, debugAndPrintError, spliceOne, connectClientToRoom } from '@colyseus/core';\nimport { uWebSocketClient, uWebSocketWrapper } from './uWebSocketClient.ts';\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\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 // https://github.com/colyseus/colyseus/issues/458\n // Adding a mock object for Transport.server\n if(!this.server) {\n // @ts-ignore\n this.server = new HttpServerMock();\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 this.registerMatchMakeRequest();\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 // @ts-ignore\n this.server.emit(\"listening\"); // Mocking Transport.server behaviour, https://github.com/colyseus/colyseus/issues/458\n };\n\n if (typeof(port) === \"string\") {\n // @ts-ignore\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 // @ts-ignore\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 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, () => client.leave());\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 = (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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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@colyseus/uwebsockets-transport",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.1",
|
|
4
|
+
"type": "module",
|
|
4
5
|
"input": "./src/index.ts",
|
|
5
6
|
"main": "./build/index.js",
|
|
6
|
-
"module": "./
|
|
7
|
+
"module": "./src/index.ts",
|
|
7
8
|
"typings": "./build/index.d.ts",
|
|
8
9
|
"exports": {
|
|
9
10
|
".": {
|
|
10
11
|
"types": "./build/index.d.ts",
|
|
11
|
-
"import": "./
|
|
12
|
+
"import": "./src/index.ts",
|
|
12
13
|
"require": "./build/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./*": {
|
|
16
|
+
"types": "./build/*.d.ts",
|
|
17
|
+
"import": "./src/*.ts",
|
|
18
|
+
"require": "./build/*.js"
|
|
13
19
|
}
|
|
14
20
|
},
|
|
15
21
|
"dependencies": {
|
|
@@ -17,10 +23,10 @@
|
|
|
17
23
|
"uwebsockets-express": "^1.3.8"
|
|
18
24
|
},
|
|
19
25
|
"devDependencies": {
|
|
20
|
-
"@colyseus/core": "^0.
|
|
26
|
+
"@colyseus/core": "^0.17.0"
|
|
21
27
|
},
|
|
22
28
|
"peerDependencies": {
|
|
23
|
-
"@colyseus/core": "0.
|
|
29
|
+
"@colyseus/core": "0.17.x"
|
|
24
30
|
},
|
|
25
31
|
"author": "Endel Dreyer",
|
|
26
32
|
"license": "MIT",
|
package/build/index.d.ts
DELETED