redweb 0.8.0 → 0.9.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.
- package/CHANGELOG.md +11 -0
- package/README.md +458 -307
- package/client.d.ts +42 -0
- package/client.js +55 -0
- package/docs/MULTIPLAYER_OPERATIONS.md +50 -0
- package/docs/PRODUCTION_READINESS.md +68 -0
- package/docs/VERIFICATION_EVIDENCE.md +20 -0
- package/index.d.ts +320 -114
- package/index.js +27 -12
- package/package.json +28 -15
- package/src/htmx/HtmxRenderer.js +13 -13
- package/src/http/BaseHttpServer.js +112 -112
- package/src/http/HttpServer.js +18 -18
- package/src/http/HttpsServer.js +20 -20
- package/src/serverLifecycle.js +46 -46
- package/src/ws/AdmissionPolicy.js +145 -0
- package/src/ws/BaseHandler.js +40 -40
- package/src/ws/BaseSocketServer.js +195 -100
- package/src/ws/DefaultHandler.js +5 -5
- package/src/ws/DefaultRoute.js +8 -8
- package/src/ws/DistributionBridge.js +271 -0
- package/src/ws/FixedStepService.js +74 -0
- package/src/ws/HeartbeatMonitor.js +75 -0
- package/src/ws/Metrics.js +34 -0
- package/src/ws/ProtocolPolicy.js +130 -0
- package/src/ws/RoomRegistry.js +117 -0
- package/src/ws/RouteRuntime.js +146 -0
- package/src/ws/SecureSocketServer.js +9 -9
- package/src/ws/SessionRegistry.js +135 -0
- package/src/ws/SocketRoute.js +523 -254
- package/src/ws/SocketServer.js +8 -8
- package/src/ws/TaskQueue.js +64 -0
- package/src/ws/TokenBucket.js +31 -0
- package/src/ws/TransportPolicy.js +68 -0
- package/src/ws/index.js +7 -2
- package/src/ws/protocol-schema.json +13 -0
- package/src/ws/protocol-validation.js +21 -0
- package/src/ws/shutdown.js +33 -33
- package/src/ws/util.js +38 -30
package/src/ws/BaseHandler.js
CHANGED
|
@@ -18,47 +18,47 @@ class BaseHandler {
|
|
|
18
18
|
* @param {WebSocket & {sendJson: (message: Object) => void, broadcast: (message: Object) => void}} socket - The WebSocket connection that sent the message.
|
|
19
19
|
* @param {any} message - The incoming message in parsed JSON.
|
|
20
20
|
*/
|
|
21
|
-
async handleMessage(socket, message) {
|
|
22
|
-
const validationResult = await this.validateMessage(message, socket);
|
|
23
|
-
if (validationResult === false) {
|
|
24
|
-
throw new Error('Invalid message');
|
|
25
|
-
}
|
|
26
|
-
return this.onMessage(socket, message);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
validateMessage() {
|
|
30
|
-
return true;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Handles an incoming binary message.
|
|
35
|
-
* @param {WebSocket & {sendJson: (message: Object) => void}} socket - The WebSocket connection that sent the message.
|
|
36
|
-
* @param {Buffer} buffer - The incoming binary message.
|
|
37
|
-
*/
|
|
38
|
-
async handleBinaryMessage(socket, buffer) {
|
|
39
|
-
return this.onBinaryMessage(socket, buffer);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Method to be overriden to process messages.
|
|
44
|
-
* @param {WebSocket} socket - The WebSocket connection that sent the message.
|
|
45
|
-
* @param {any} message - The incoming message in parsed JSON.
|
|
21
|
+
async handleMessage(socket, message) {
|
|
22
|
+
const validationResult = await this.validateMessage(message, socket);
|
|
23
|
+
if (validationResult === false) {
|
|
24
|
+
throw new Error('Invalid message');
|
|
25
|
+
}
|
|
26
|
+
return this.onMessage(socket, message);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
validateMessage() {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Handles an incoming binary message.
|
|
35
|
+
* @param {WebSocket & {sendJson: (message: Object) => void}} socket - The WebSocket connection that sent the message.
|
|
36
|
+
* @param {Buffer} buffer - The incoming binary message.
|
|
46
37
|
*/
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Method to be overriden to process
|
|
53
|
-
* @param {WebSocket
|
|
54
|
-
* @param {
|
|
55
|
-
*/
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
38
|
+
async handleBinaryMessage(socket, buffer) {
|
|
39
|
+
return this.onBinaryMessage(socket, buffer);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Method to be overriden to process messages.
|
|
44
|
+
* @param {WebSocket} socket - The WebSocket connection that sent the message.
|
|
45
|
+
* @param {any} message - The incoming message in parsed JSON.
|
|
46
|
+
*/
|
|
47
|
+
onMessage(socket, message) {
|
|
48
|
+
throw new Error('onMessage must be implemented by the handler.');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Method to be overriden to process binary messages.
|
|
53
|
+
* @param {WebSocket & {sendJson: (message: Object) => void}} socket - The WebSocket connection that sent the message.
|
|
54
|
+
* @param {Buffer} buffer - The incoming binary message.
|
|
55
|
+
*/
|
|
56
|
+
onBinaryMessage(socket, buffer) {
|
|
57
|
+
socket.sendJson({ error: 'Binary messages are not supported by this handler' });
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
onInitialContact(socket) {
|
|
61
|
+
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
@@ -1,31 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {Object} SocketServerOptions
|
|
3
|
-
* @property {import('http').Server} [server] HTTP server to bind to
|
|
4
|
-
* @property {number} [port=3000] Port to listen on
|
|
5
|
-
* @property {boolean} [listen=true] Whether owned servers should automatically start listening
|
|
6
|
-
* @property {Array<new () => import('./SocketRoute').SocketRoute>} [routes]
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const DefaultRoute = require('./DefaultRoute');
|
|
10
|
-
const {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
3
|
+
* @property {import('http').Server} [server] HTTP server to bind to
|
|
4
|
+
* @property {number} [port=3000] Port to listen on
|
|
5
|
+
* @property {boolean} [listen=true] Whether owned servers should automatically start listening
|
|
6
|
+
* @property {Array<new () => import('./SocketRoute').SocketRoute>} [routes]
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const DefaultRoute = require('./DefaultRoute');
|
|
10
|
+
const { PLACEMENT_REDIRECT, ADMISSION_SETTLEMENT } = require('./AdmissionPolicy');
|
|
11
|
+
const { PROTOCOL_REJECTION } = require('./ProtocolPolicy');
|
|
12
|
+
const {
|
|
13
|
+
listenServer,
|
|
14
|
+
closeServer,
|
|
15
|
+
settleTasks,
|
|
16
|
+
throwCleanupErrors,
|
|
17
|
+
validateListenerOptions,
|
|
18
|
+
} = require('../serverLifecycle');
|
|
19
|
+
|
|
20
|
+
const SOCKET_OPTIONS = {
|
|
21
|
+
port: 3000,
|
|
22
|
+
bind: '0.0.0.0',
|
|
23
|
+
ssl: null,
|
|
24
|
+
listen: true,
|
|
25
|
+
routes: [],
|
|
26
|
+
fallbackToRoot: false,
|
|
27
|
+
closeServerOnShutdown: undefined,
|
|
28
|
+
logger: console,
|
|
29
|
+
listenCallback: undefined,
|
|
30
|
+
};
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
33
|
* Base WebSocket server
|
|
@@ -35,54 +37,62 @@ class BaseSocketServer {
|
|
|
35
37
|
* @param {import('http').Server} server
|
|
36
38
|
* @param {SocketServerOptions} [options]
|
|
37
39
|
*/
|
|
38
|
-
constructor(server, options = {}, ownsServer = false, name = 'SocketServer') {
|
|
39
|
-
if (!server || typeof server.on !== 'function') throw new TypeError('A Node HTTP(S) server is required.');
|
|
40
|
-
Object.assign(this, { ...SOCKET_OPTIONS, ...options });
|
|
41
|
-
validateListenerOptions(this);
|
|
42
|
-
if (!Array.isArray(this.routes)) throw new TypeError('`routes` must be an array.');
|
|
43
|
-
this.server = server;
|
|
44
|
-
this.ownsServer = ownsServer;
|
|
45
|
-
this.closeServerOnShutdown = options.closeServerOnShutdown ?? ownsServer;
|
|
40
|
+
constructor(server, options = {}, ownsServer = false, name = 'SocketServer') {
|
|
41
|
+
if (!server || typeof server.on !== 'function') throw new TypeError('A Node HTTP(S) server is required.');
|
|
42
|
+
Object.assign(this, { ...SOCKET_OPTIONS, ...options });
|
|
43
|
+
validateListenerOptions(this);
|
|
44
|
+
if (!Array.isArray(this.routes)) throw new TypeError('`routes` must be an array.');
|
|
45
|
+
this.server = server;
|
|
46
|
+
this.ownsServer = ownsServer;
|
|
47
|
+
this.closeServerOnShutdown = options.closeServerOnShutdown ?? ownsServer;
|
|
48
|
+
this.draining = false;
|
|
49
|
+
this.pendingUpgrades = new Map();
|
|
50
|
+
this.rawConnections = this.closeServerOnShutdown ? new Set() : null;
|
|
51
|
+
this._connectionHandler = this.rawConnections ? socket => {
|
|
52
|
+
this.rawConnections.add(socket);
|
|
53
|
+
socket.once('close', () => this.rawConnections.delete(socket));
|
|
54
|
+
} : null;
|
|
55
|
+
if (this._connectionHandler) this.server.on('connection', this._connectionHandler);
|
|
46
56
|
|
|
47
57
|
/* ─── ROUTE INITIALISATION ─────────────────────────── */
|
|
48
|
-
const RouteClasses = options.routes?.length ? [...options.routes] : [DefaultRoute];
|
|
49
|
-
this.routes = [];
|
|
50
|
-
try {
|
|
51
|
-
for (const RouteClass of RouteClasses) {
|
|
52
|
-
const route = new RouteClass(server, { logger: this.logger });
|
|
53
|
-
if (this.routes.some(existing => existing.path === route.path)) {
|
|
54
|
-
this.disposeRoutes([route]);
|
|
55
|
-
throw new Error('WebSocket route paths must be unique.');
|
|
56
|
-
}
|
|
57
|
-
this.routes.push(route);
|
|
58
|
-
}
|
|
59
|
-
} catch (error) {
|
|
60
|
-
this.disposeRoutes(this.routes);
|
|
61
|
-
throw error;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
this._upgradeHandler = this.handleUpgrade.bind(this);
|
|
65
|
-
this.server.on('upgrade', this._upgradeHandler);
|
|
66
|
-
|
|
67
|
-
const shouldListen = (ownsServer && this.listen !== false) || (!ownsServer && options.listen === true);
|
|
68
|
-
if (shouldListen) {
|
|
69
|
-
listenServer(this.server, {
|
|
70
|
-
port: this.port,
|
|
71
|
-
bind: this.bind,
|
|
72
|
-
callback: this.listenCallback,
|
|
73
|
-
logger: this.logger,
|
|
74
|
-
name,
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
disposeRoutes(routes) {
|
|
80
|
-
routes.forEach(route => {
|
|
81
|
-
Promise.resolve()
|
|
82
|
-
.then(() => route.shutdown?.())
|
|
83
|
-
.catch(error => this.logger?.error?.('Error shutting down route:', error));
|
|
84
|
-
});
|
|
85
|
-
}
|
|
58
|
+
const RouteClasses = options.routes?.length ? [...options.routes] : [DefaultRoute];
|
|
59
|
+
this.routes = [];
|
|
60
|
+
try {
|
|
61
|
+
for (const RouteClass of RouteClasses) {
|
|
62
|
+
const route = new RouteClass(server, { logger: this.logger });
|
|
63
|
+
if (this.routes.some(existing => existing.path === route.path)) {
|
|
64
|
+
this.disposeRoutes([route]);
|
|
65
|
+
throw new Error('WebSocket route paths must be unique.');
|
|
66
|
+
}
|
|
67
|
+
this.routes.push(route);
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
this.disposeRoutes(this.routes);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
this._upgradeHandler = this.handleUpgrade.bind(this);
|
|
75
|
+
this.server.on('upgrade', this._upgradeHandler);
|
|
76
|
+
|
|
77
|
+
const shouldListen = (ownsServer && this.listen !== false) || (!ownsServer && options.listen === true);
|
|
78
|
+
if (shouldListen) {
|
|
79
|
+
listenServer(this.server, {
|
|
80
|
+
port: this.port,
|
|
81
|
+
bind: this.bind,
|
|
82
|
+
callback: this.listenCallback,
|
|
83
|
+
logger: this.logger,
|
|
84
|
+
name,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
disposeRoutes(routes) {
|
|
90
|
+
routes.forEach(route => {
|
|
91
|
+
Promise.resolve()
|
|
92
|
+
.then(() => route.shutdown?.())
|
|
93
|
+
.catch(error => this.logger?.error?.('Error shutting down route:', error));
|
|
94
|
+
});
|
|
95
|
+
}
|
|
86
96
|
|
|
87
97
|
handleUpgrade(req, sock, head) {
|
|
88
98
|
// Some websocket clients (e.g., certain UE plugins) are finicky about the
|
|
@@ -97,50 +107,135 @@ class BaseSocketServer {
|
|
|
97
107
|
})();
|
|
98
108
|
|
|
99
109
|
const route =
|
|
100
|
-
this.routes.find(r => r.path === path) ||
|
|
101
|
-
(this.fallbackToRoot ? this.routes.find(r => r.path === '/') : undefined);
|
|
110
|
+
this.routes.find(r => r.path === path) ||
|
|
111
|
+
(this.fallbackToRoot ? this.routes.find(r => r.path === '/') : undefined);
|
|
102
112
|
|
|
103
113
|
if (!route) return sock.destroy();
|
|
114
|
+
if (this.draining) return this.rejectUpgrade(sock, 503, 'Service Unavailable');
|
|
115
|
+
if (route.isReady?.() === false) return this.rejectUpgrade(sock, 503, 'Service Unavailable');
|
|
116
|
+
|
|
117
|
+
if (route.admissionPolicy || route.protocolPolicy || route.transportPolicy && route.transportPolicy.maxConnections !== Infinity) {
|
|
118
|
+
let reservation;
|
|
119
|
+
try {
|
|
120
|
+
reservation = route.reserveUpgrade(req);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
this.logger?.error?.('WebSocket admission reservation failed:', error);
|
|
123
|
+
}
|
|
124
|
+
if (!reservation) return this.rejectUpgrade(sock, 503, 'Service Unavailable');
|
|
125
|
+
const controller = new AbortController();
|
|
126
|
+
this.pendingUpgrades.set(sock, { controller, route, reservation });
|
|
127
|
+
void Promise.resolve()
|
|
128
|
+
.then(() => route.authorizeUpgrade(req, sock, controller.signal))
|
|
129
|
+
.then(accepted => {
|
|
130
|
+
if (sock.destroyed) return;
|
|
131
|
+
if (this.draining) return this.rejectUpgrade(sock, 503, 'Service Unavailable');
|
|
132
|
+
if (!accepted) {
|
|
133
|
+
const redirect = req[PLACEMENT_REDIRECT];
|
|
134
|
+
if (redirect) return this.rejectUpgrade(sock, 307, 'Temporary Redirect', { Location: redirect });
|
|
135
|
+
const rejection = req[PROTOCOL_REJECTION];
|
|
136
|
+
return rejection
|
|
137
|
+
? this.rejectUpgrade(sock, rejection.statusCode, rejection.statusText, rejection.headers)
|
|
138
|
+
: this.rejectUpgrade(sock, 401, 'Unauthorized');
|
|
139
|
+
}
|
|
140
|
+
this.completeUpgrade(route, req, sock, head);
|
|
141
|
+
})
|
|
142
|
+
.catch(error => {
|
|
143
|
+
this.logger?.error?.('WebSocket admission failed:', error);
|
|
144
|
+
if (!sock.destroyed) this.rejectUpgrade(sock, 401, 'Unauthorized');
|
|
145
|
+
})
|
|
146
|
+
.finally(async () => {
|
|
147
|
+
await req[ADMISSION_SETTLEMENT];
|
|
148
|
+
this.pendingUpgrades.delete(sock);
|
|
149
|
+
route.releaseUpgrade(reservation);
|
|
150
|
+
});
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
this.completeUpgrade(route, req, sock, head);
|
|
155
|
+
}
|
|
104
156
|
|
|
157
|
+
completeUpgrade(route, req, sock, head) {
|
|
105
158
|
route.server.handleUpgrade(req, sock, head, (s, r) =>
|
|
106
159
|
route.server.emit('connection', s, r)
|
|
107
160
|
);
|
|
108
161
|
}
|
|
109
162
|
|
|
163
|
+
rejectUpgrade(socket, statusCode, statusText, headers = {}) {
|
|
164
|
+
try {
|
|
165
|
+
const extraHeaders = Object.entries(headers).map(([name, value]) => `${name}: ${value}\r\n`).join('');
|
|
166
|
+
socket.end?.(
|
|
167
|
+
`HTTP/1.1 ${statusCode} ${statusText}\r\n` +
|
|
168
|
+
extraHeaders +
|
|
169
|
+
'Connection: close\r\n' +
|
|
170
|
+
'Content-Length: 0\r\n\r\n'
|
|
171
|
+
);
|
|
172
|
+
} catch {
|
|
173
|
+
socket.destroy?.();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
110
177
|
/**
|
|
111
178
|
* Dynamically attach a new route at runtime
|
|
112
179
|
* @param {new () => import('./SocketRoute').SocketRoute} RouteClass
|
|
113
180
|
*/
|
|
114
|
-
addRoute(RouteClass) {
|
|
115
|
-
const route = new RouteClass(this.server, { logger: this.logger });
|
|
116
|
-
if (this.routes.some(existing => existing.path === route.path)) {
|
|
117
|
-
this.disposeRoutes([route]);
|
|
118
|
-
throw new Error(`A WebSocket route already exists at ${route.path}.`);
|
|
119
|
-
}
|
|
120
|
-
this.routes.push(route);
|
|
121
|
-
return route;
|
|
181
|
+
addRoute(RouteClass) {
|
|
182
|
+
const route = new RouteClass(this.server, { logger: this.logger });
|
|
183
|
+
if (this.routes.some(existing => existing.path === route.path)) {
|
|
184
|
+
this.disposeRoutes([route]);
|
|
185
|
+
throw new Error(`A WebSocket route already exists at ${route.path}.`);
|
|
186
|
+
}
|
|
187
|
+
this.routes.push(route);
|
|
188
|
+
return route;
|
|
122
189
|
}
|
|
123
190
|
|
|
124
191
|
/**
|
|
125
192
|
* Gracefully tear down all routes (and their services)
|
|
126
193
|
*/
|
|
127
|
-
shutdown() {
|
|
128
|
-
if (!this._shutdownPromise) this._shutdownPromise = this.performShutdown();
|
|
129
|
-
return this._shutdownPromise;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
this.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
194
|
+
shutdown() {
|
|
195
|
+
if (!this._shutdownPromise) this._shutdownPromise = this.performShutdown();
|
|
196
|
+
return this._shutdownPromise;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
isReady() {
|
|
200
|
+
return !this.draining && this.routes.every(route => route.isReady?.() !== false);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
beginDrain() {
|
|
204
|
+
if (this.draining) return false;
|
|
205
|
+
this.draining = true;
|
|
206
|
+
this.pendingUpgrades.forEach(({ controller }) => controller.abort());
|
|
207
|
+
this.routes.forEach(route => route.beginDrain?.());
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
async performShutdown() {
|
|
212
|
+
this.beginDrain();
|
|
213
|
+
this.server.off?.('upgrade', this._upgradeHandler);
|
|
214
|
+
const deadline = Date.now() + Math.max(0, ...this.routes.map(route => route.shutdownTimeoutMs));
|
|
215
|
+
const errors = await settleTasks(this.routes.map(route => () => route.shutdown?.()));
|
|
216
|
+
if (this.closeServerOnShutdown && this.server.listening) {
|
|
217
|
+
try {
|
|
218
|
+
await this.closeOwnedServer(Math.max(0, deadline - Date.now()));
|
|
219
|
+
} catch (error) {
|
|
220
|
+
errors.push(error);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (this._connectionHandler) this.server.off?.('connection', this._connectionHandler);
|
|
224
|
+
throwCleanupErrors(errors, 'One or more WebSocket server cleanup operations failed.');
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
closeOwnedServer(timeoutMs) {
|
|
228
|
+
let timer;
|
|
229
|
+
const closing = closeServer(this.server);
|
|
230
|
+
const timeout = new Promise(resolve => {
|
|
231
|
+
timer = setTimeout(() => {
|
|
232
|
+
this.rawConnections?.forEach(socket => socket.destroy?.());
|
|
233
|
+
resolve();
|
|
234
|
+
}, timeoutMs);
|
|
235
|
+
timer.unref?.();
|
|
236
|
+
});
|
|
237
|
+
return Promise.race([closing, timeout]).finally(() => clearTimeout(timer));
|
|
238
|
+
}
|
|
239
|
+
}
|
|
145
240
|
|
|
146
241
|
module.exports = { BaseSocketServer, SOCKET_OPTIONS };
|
package/src/ws/DefaultHandler.js
CHANGED
|
@@ -5,9 +5,9 @@ class DefaultHandler extends BaseHandler {
|
|
|
5
5
|
super("DefaultHandler");
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
onMessage(socket, message) {
|
|
9
|
-
socket.sendJson({ message: `I got your message of ${JSON.stringify(message)}` });
|
|
10
|
-
}
|
|
11
|
-
}
|
|
8
|
+
onMessage(socket, message) {
|
|
9
|
+
socket.sendJson({ message: `I got your message of ${JSON.stringify(message)}` });
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
12
|
|
|
13
|
-
module.exports = DefaultHandler;
|
|
13
|
+
module.exports = DefaultHandler;
|
package/src/ws/DefaultRoute.js
CHANGED
|
@@ -2,14 +2,14 @@ const SocketRoute = require("./SocketRoute");
|
|
|
2
2
|
const DefaultHandler = require('./DefaultHandler');
|
|
3
3
|
|
|
4
4
|
class DefaultRoute extends SocketRoute {
|
|
5
|
-
constructor(server, options = {}) {
|
|
6
|
-
super({
|
|
7
|
-
server,
|
|
8
|
-
path: "/",
|
|
9
|
-
handlers: [DefaultHandler],
|
|
10
|
-
logger: options.logger,
|
|
11
|
-
})
|
|
5
|
+
constructor(server, options = {}) {
|
|
6
|
+
super({
|
|
7
|
+
server,
|
|
8
|
+
path: "/",
|
|
9
|
+
handlers: [DefaultHandler],
|
|
10
|
+
logger: options.logger,
|
|
11
|
+
})
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
module.exports = DefaultRoute;
|
|
15
|
+
module.exports = DefaultRoute;
|