@nmtjs/ws-transport 0.7.7 → 0.8.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/dist/server.js +21 -10
- package/dist/server.js.map +1 -1
- package/package.json +12 -7
- package/src/server.ts +25 -13
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { App, SSLApp } from "uWebSockets.js";
|
|
1
|
+
import { App, SSLApp, us_socket_local_port } from "uWebSockets.js";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
3
|
import { createPromise } from "@nmtjs/common";
|
|
4
4
|
import { ClientMessageType, decodeNumber } from "@nmtjs/protocol/common";
|
|
@@ -96,15 +96,26 @@ export class WsTransportServer {
|
|
|
96
96
|
}
|
|
97
97
|
async start() {
|
|
98
98
|
return new Promise((resolve, reject) => {
|
|
99
|
-
const hostname =
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
const { hostname = "127.0.0.1", port = 0, unix } = this.options;
|
|
100
|
+
if (unix) {
|
|
101
|
+
this.server.listen_unix((socket) => {
|
|
102
|
+
if (socket) {
|
|
103
|
+
this.logger.info("Server started on unix://%s", unix);
|
|
104
|
+
resolve();
|
|
105
|
+
} else {
|
|
106
|
+
reject(new Error("Failed to start WebSockets server"));
|
|
107
|
+
}
|
|
108
|
+
}, unix);
|
|
109
|
+
} else {
|
|
110
|
+
this.server.listen(hostname, port, (socket) => {
|
|
111
|
+
if (socket) {
|
|
112
|
+
this.logger.info("WebSocket Server started on %s:%s", hostname, us_socket_local_port(socket));
|
|
113
|
+
resolve();
|
|
114
|
+
} else {
|
|
115
|
+
reject(new Error("Failed to start WebSockets server"));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
}
|
|
108
119
|
});
|
|
109
120
|
}
|
|
110
121
|
async stop() {
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"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","names":["context: TransportPluginContext","options: WsTransportOptions","data: WsUserData","ws: WsTransportSocket","error: any","connection: Connection<WsConnectionData>","messageType: ServerMessageType","buffer: ArrayBuffer","cause: Error","res: HttpResponse","req: HttpRequest"],"sources":["src/server.ts"],"sourcesContent":["import {\n App,\n type HttpRequest,\n type HttpResponse,\n SSLApp,\n type TemplatedApp,\n} from 'uWebSockets.js'\nimport { randomUUID } from 'node:crypto'\nimport { createPromise } from '@nmtjs/common'\nimport {\n ClientMessageType,\n decodeNumber,\n type ServerMessageType,\n} from '@nmtjs/protocol/common'\nimport {\n type Connection,\n ProtocolInjectables,\n type Transport,\n type TransportPluginContext,\n} from '@nmtjs/protocol/server'\nimport { WsTransportInjectables } from './injectables.ts'\nimport type {\n WsTransportOptions,\n WsTransportSocket,\n WsUserData,\n} from './types.ts'\nimport { getRequestData, send } from './utils.ts'\n\nexport type WsConnectionData = {}\n\nexport class WsTransportServer implements Transport<WsConnectionData> {\n protected server!: TemplatedApp\n protected clients: Map<string, WsTransportSocket> = new Map()\n\n constructor(\n protected readonly context: TransportPluginContext,\n protected readonly options: WsTransportOptions,\n ) {\n this.server = this.options.tls ? SSLApp(options.tls!) : App()\n\n this.server\n .options('/*', (res, req) => {\n this.applyCors(res, req)\n res.writeStatus('200 OK')\n res.endWithoutBody()\n })\n .get('/healthy', (res, req) => {\n this.applyCors(res, req)\n res.writeHeader('Content-Type', 'text/plain')\n res.end('OK')\n })\n .ws<WsUserData>('/api', {\n sendPingsAutomatically: true,\n maxPayloadLength: this.options.maxPayloadLength,\n upgrade: (res, req, context) => {\n const requestData = getRequestData(req)\n\n const contentType =\n requestData.headers.get('content-type') ||\n requestData.query.get('content-type')\n\n const acceptType =\n requestData.headers.get('accept') || requestData.query.get('accept')\n\n const data: WsUserData = {\n id: randomUUID(),\n request: {\n query: requestData.query,\n headers: requestData.headers,\n proxiedRemoteAddress: Buffer.from(\n res.getProxiedRemoteAddressAsText(),\n ).toString(),\n remoteAddress: Buffer.from(\n res.getRemoteAddressAsText(),\n ).toString(),\n contentType,\n acceptType,\n },\n opening: createPromise(),\n backpressure: null,\n context: {} as any,\n }\n\n res.upgrade(\n data,\n req.getHeader('sec-websocket-key'),\n req.getHeader('sec-websocket-protocol'),\n req.getHeader('sec-websocket-extensions'),\n context,\n )\n },\n open: async (ws: WsTransportSocket) => {\n const { id, context, request, opening } = ws.getUserData()\n this.clients.set(id, ws)\n this.logger.debug('Connection %s opened', id)\n try {\n const { context: _context, connection } =\n await this.context.protocol.connections.add(\n this,\n { id, data: {} },\n {\n acceptType: request.acceptType,\n contentType: request.contentType,\n },\n )\n Object.assign(context, _context)\n context.container.provide(\n ProtocolInjectables.connection,\n connection,\n )\n context.container.provide(\n WsTransportInjectables.connectionData,\n request,\n )\n opening.resolve()\n } catch (error) {\n opening.reject(error)\n }\n },\n message: async (ws: WsTransportSocket, buffer) => {\n const { opening } = ws.getUserData()\n const messageType = decodeNumber(buffer, 'Uint8')\n if (messageType in this === false) {\n ws.end(1011, 'Unknown message type')\n } else {\n try {\n await opening.promise\n await this[messageType](\n ws,\n buffer.slice(Uint8Array.BYTES_PER_ELEMENT),\n )\n } catch (error: any) {\n this.logError(error, 'Error while processing message')\n }\n }\n },\n drain: (ws: WsTransportSocket) => {\n const data = ws.getUserData()\n data.backpressure?.resolve()\n data.backpressure = null\n },\n close: async (ws: WsTransportSocket, code, message) => {\n const { id } = ws.getUserData()\n\n this.logger.debug(\n 'Connection %s closed with code %s: %s',\n id,\n code,\n Buffer.from(message).toString(),\n )\n this.clients.delete(id)\n await this.protocol.connections.remove(id)\n },\n })\n }\n\n send(\n connection: Connection<WsConnectionData>,\n messageType: ServerMessageType,\n buffer: ArrayBuffer,\n ) {\n const ws = this.clients.get(connection.id)\n if (ws) send(ws, messageType, buffer)\n }\n\n async start() {\n return new Promise<void>((resolve, reject) => {\n const hostname = this.options.hostname ?? '127.0.0.1'\n this.server.listen(hostname, this.options.port!, (socket) => {\n if (socket) {\n this.logger.info(\n 'Server started on %s:%s',\n hostname,\n this.options.port!,\n )\n resolve()\n } else {\n reject(new Error('Failed to start server'))\n }\n })\n })\n }\n\n async stop() {\n this.server.close()\n }\n\n protected get protocol() {\n return this.context.protocol\n }\n\n protected get logger() {\n return this.context.logger\n }\n\n protected async logError(\n cause: Error,\n message = 'Unknown error while processing request',\n ) {\n this.logger.error(new Error(message, { cause }))\n }\n\n protected applyCors(res: HttpResponse, req: HttpRequest) {\n // TODO: this should be configurable\n const origin = req.getHeader('origin')\n if (!origin) return\n res.writeHeader('Access-Control-Allow-Origin', origin)\n res.writeHeader('Access-Control-Allow-Headers', 'Content-Type')\n res.writeHeader('Access-Control-Allow-Methods', 'GET, POST')\n res.writeHeader('Access-Control-Allow-Credentials', 'true')\n }\n\n protected [ClientMessageType.Rpc](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n this.protocol.rpcRaw(id, buffer)\n }\n\n protected [ClientMessageType.RpcAbort](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n this.protocol.rpcAbortRaw(id, buffer)\n }\n\n protected [ClientMessageType.RpcStreamAbort](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n this.protocol.rpcStreamAbortRaw(id, buffer)\n }\n\n protected [ClientMessageType.ClientStreamPush](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.clientStreams.push(\n id,\n streamId,\n buffer.slice(Uint32Array.BYTES_PER_ELEMENT),\n )\n }\n\n protected [ClientMessageType.ClientStreamEnd](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.clientStreams.end(id, streamId)\n }\n\n protected [ClientMessageType.ClientStreamAbort](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.clientStreams.abort(id, streamId)\n }\n\n protected [ClientMessageType.ServerStreamPull](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.serverStreams.pull(id, streamId)\n }\n\n protected [ClientMessageType.ServerStreamAbort](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.serverStreams.abort(id, streamId)\n }\n}\n"],"version":3}
|
|
1
|
+
{"mappings":"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","names":["context: TransportPluginContext","options: WsTransportOptions","data: WsUserData","ws: WsTransportSocket","error: any","connection: Connection<WsConnectionData>","messageType: ServerMessageType","buffer: ArrayBuffer","cause: Error","res: HttpResponse","req: HttpRequest"],"sources":["src/server.ts"],"sourcesContent":["import {\n App,\n type HttpRequest,\n type HttpResponse,\n SSLApp,\n type TemplatedApp,\n us_socket_local_port,\n} from 'uWebSockets.js'\nimport { randomUUID } from 'node:crypto'\nimport { createPromise } from '@nmtjs/common'\nimport {\n ClientMessageType,\n decodeNumber,\n type ServerMessageType,\n} from '@nmtjs/protocol/common'\nimport {\n type Connection,\n ProtocolInjectables,\n type Transport,\n type TransportPluginContext,\n} from '@nmtjs/protocol/server'\nimport { WsTransportInjectables } from './injectables.ts'\nimport type {\n WsTransportOptions,\n WsTransportSocket,\n WsUserData,\n} from './types.ts'\nimport { getRequestData, send } from './utils.ts'\n\nexport type WsConnectionData = {}\n\nexport class WsTransportServer implements Transport<WsConnectionData> {\n protected server!: TemplatedApp\n protected clients: Map<string, WsTransportSocket> = new Map()\n\n constructor(\n protected readonly context: TransportPluginContext,\n protected readonly options: WsTransportOptions,\n ) {\n this.server = this.options.tls ? SSLApp(options.tls!) : App()\n\n this.server\n .options('/*', (res, req) => {\n this.applyCors(res, req)\n res.writeStatus('200 OK')\n res.endWithoutBody()\n })\n .get('/healthy', (res, req) => {\n this.applyCors(res, req)\n res.writeHeader('Content-Type', 'text/plain')\n res.end('OK')\n })\n .ws<WsUserData>('/api', {\n sendPingsAutomatically: true,\n maxPayloadLength: this.options.maxPayloadLength,\n upgrade: (res, req, context) => {\n const requestData = getRequestData(req)\n\n const contentType =\n requestData.headers.get('content-type') ||\n requestData.query.get('content-type')\n\n const acceptType =\n requestData.headers.get('accept') || requestData.query.get('accept')\n\n const data: WsUserData = {\n id: randomUUID(),\n request: {\n query: requestData.query,\n headers: requestData.headers,\n proxiedRemoteAddress: Buffer.from(\n res.getProxiedRemoteAddressAsText(),\n ).toString(),\n remoteAddress: Buffer.from(\n res.getRemoteAddressAsText(),\n ).toString(),\n contentType,\n acceptType,\n },\n opening: createPromise(),\n backpressure: null,\n context: {} as any,\n }\n\n res.upgrade(\n data,\n req.getHeader('sec-websocket-key'),\n req.getHeader('sec-websocket-protocol'),\n req.getHeader('sec-websocket-extensions'),\n context,\n )\n },\n open: async (ws: WsTransportSocket) => {\n const { id, context, request, opening } = ws.getUserData()\n this.clients.set(id, ws)\n this.logger.debug('Connection %s opened', id)\n try {\n const { context: _context, connection } =\n await this.context.protocol.connections.add(\n this,\n { id, data: {} },\n {\n acceptType: request.acceptType,\n contentType: request.contentType,\n },\n )\n Object.assign(context, _context)\n context.container.provide(\n ProtocolInjectables.connection,\n connection,\n )\n context.container.provide(\n WsTransportInjectables.connectionData,\n request,\n )\n opening.resolve()\n } catch (error) {\n opening.reject(error)\n }\n },\n message: async (ws: WsTransportSocket, buffer) => {\n const { opening } = ws.getUserData()\n const messageType = decodeNumber(buffer, 'Uint8')\n if (messageType in this === false) {\n ws.end(1011, 'Unknown message type')\n } else {\n try {\n await opening.promise\n await this[messageType](\n ws,\n buffer.slice(Uint8Array.BYTES_PER_ELEMENT),\n )\n } catch (error: any) {\n this.logError(error, 'Error while processing message')\n }\n }\n },\n drain: (ws: WsTransportSocket) => {\n const data = ws.getUserData()\n data.backpressure?.resolve()\n data.backpressure = null\n },\n close: async (ws: WsTransportSocket, code, message) => {\n const { id } = ws.getUserData()\n\n this.logger.debug(\n 'Connection %s closed with code %s: %s',\n id,\n code,\n Buffer.from(message).toString(),\n )\n this.clients.delete(id)\n await this.protocol.connections.remove(id)\n },\n })\n }\n\n send(\n connection: Connection<WsConnectionData>,\n messageType: ServerMessageType,\n buffer: ArrayBuffer,\n ) {\n const ws = this.clients.get(connection.id)\n if (ws) send(ws, messageType, buffer)\n }\n\n async start() {\n return new Promise<void>((resolve, reject) => {\n const { hostname = '127.0.0.1', port = 0, unix } = this.options\n if (unix) {\n this.server.listen_unix((socket) => {\n if (socket) {\n this.logger.info('Server started on unix://%s', unix)\n resolve()\n } else {\n reject(new Error('Failed to start WebSockets server'))\n }\n }, unix)\n } else {\n this.server.listen(hostname, port, (socket) => {\n if (socket) {\n this.logger.info(\n 'WebSocket Server started on %s:%s',\n hostname,\n us_socket_local_port(socket),\n )\n resolve()\n } else {\n reject(new Error('Failed to start WebSockets server'))\n }\n })\n }\n })\n }\n\n async stop() {\n this.server.close()\n }\n\n protected get protocol() {\n return this.context.protocol\n }\n\n protected get logger() {\n return this.context.logger\n }\n\n protected async logError(\n cause: Error,\n message = 'Unknown error while processing request',\n ) {\n this.logger.error(new Error(message, { cause }))\n }\n\n protected applyCors(res: HttpResponse, req: HttpRequest) {\n // TODO: this should be configurable\n const origin = req.getHeader('origin')\n if (!origin) return\n res.writeHeader('Access-Control-Allow-Origin', origin)\n res.writeHeader('Access-Control-Allow-Headers', 'Content-Type')\n res.writeHeader('Access-Control-Allow-Methods', 'GET, POST')\n res.writeHeader('Access-Control-Allow-Credentials', 'true')\n }\n\n protected [ClientMessageType.Rpc](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n this.protocol.rpcRaw(id, buffer)\n }\n\n protected [ClientMessageType.RpcAbort](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n this.protocol.rpcAbortRaw(id, buffer)\n }\n\n protected [ClientMessageType.RpcStreamAbort](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n this.protocol.rpcStreamAbortRaw(id, buffer)\n }\n\n protected [ClientMessageType.ClientStreamPush](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.clientStreams.push(\n id,\n streamId,\n buffer.slice(Uint32Array.BYTES_PER_ELEMENT),\n )\n }\n\n protected [ClientMessageType.ClientStreamEnd](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.clientStreams.end(id, streamId)\n }\n\n protected [ClientMessageType.ClientStreamAbort](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.clientStreams.abort(id, streamId)\n }\n\n protected [ClientMessageType.ServerStreamPull](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.serverStreams.pull(id, streamId)\n }\n\n protected [ClientMessageType.ServerStreamAbort](\n ws: WsTransportSocket,\n buffer: ArrayBuffer,\n ) {\n const { id } = ws.getUserData()\n const streamId = decodeNumber(buffer, 'Uint32')\n this.protocol.serverStreams.abort(id, streamId)\n }\n}\n"],"version":3}
|
package/package.json
CHANGED
|
@@ -8,16 +8,21 @@
|
|
|
8
8
|
}
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.51.0"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"@nmtjs/
|
|
11
|
+
"uWebSockets.js": "github:uNetworking/uWebSockets.js#v20.51.0"
|
|
12
|
+
},
|
|
13
|
+
"peerDependencies": {
|
|
14
|
+
"@nmtjs/common": "0.8.0",
|
|
15
|
+
"@nmtjs/core": "0.8.0",
|
|
16
|
+
"@nmtjs/protocol": "0.8.0"
|
|
15
17
|
},
|
|
16
18
|
"devDependencies": {
|
|
17
|
-
"@types/node": "^18
|
|
19
|
+
"@types/node": "^18",
|
|
20
|
+
"@nmtjs/client": "0.8.0",
|
|
21
|
+
"@nmtjs/common": "0.8.0",
|
|
22
|
+
"@nmtjs/protocol": "0.8.0"
|
|
18
23
|
},
|
|
19
24
|
"engines": {
|
|
20
|
-
"node": ">=18
|
|
25
|
+
"node": ">=18"
|
|
21
26
|
},
|
|
22
27
|
"files": [
|
|
23
28
|
"src",
|
|
@@ -25,7 +30,7 @@
|
|
|
25
30
|
"LICENSE.md",
|
|
26
31
|
"README.md"
|
|
27
32
|
],
|
|
28
|
-
"version": "0.
|
|
33
|
+
"version": "0.8.0",
|
|
29
34
|
"scripts": {
|
|
30
35
|
"build": "neemata-build --root ./src './**/*.ts'",
|
|
31
36
|
"type-check": "tsc --noEmit"
|
package/src/server.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
type HttpResponse,
|
|
5
5
|
SSLApp,
|
|
6
6
|
type TemplatedApp,
|
|
7
|
+
us_socket_local_port,
|
|
7
8
|
} from 'uWebSockets.js'
|
|
8
9
|
import { randomUUID } from 'node:crypto'
|
|
9
10
|
import { createPromise } from '@nmtjs/common'
|
|
@@ -165,19 +166,30 @@ export class WsTransportServer implements Transport<WsConnectionData> {
|
|
|
165
166
|
|
|
166
167
|
async start() {
|
|
167
168
|
return new Promise<void>((resolve, reject) => {
|
|
168
|
-
const hostname =
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
'Server started on
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
169
|
+
const { hostname = '127.0.0.1', port = 0, unix } = this.options
|
|
170
|
+
if (unix) {
|
|
171
|
+
this.server.listen_unix((socket) => {
|
|
172
|
+
if (socket) {
|
|
173
|
+
this.logger.info('Server started on unix://%s', unix)
|
|
174
|
+
resolve()
|
|
175
|
+
} else {
|
|
176
|
+
reject(new Error('Failed to start WebSockets server'))
|
|
177
|
+
}
|
|
178
|
+
}, unix)
|
|
179
|
+
} else {
|
|
180
|
+
this.server.listen(hostname, port, (socket) => {
|
|
181
|
+
if (socket) {
|
|
182
|
+
this.logger.info(
|
|
183
|
+
'WebSocket Server started on %s:%s',
|
|
184
|
+
hostname,
|
|
185
|
+
us_socket_local_port(socket),
|
|
186
|
+
)
|
|
187
|
+
resolve()
|
|
188
|
+
} else {
|
|
189
|
+
reject(new Error('Failed to start WebSockets server'))
|
|
190
|
+
}
|
|
191
|
+
})
|
|
192
|
+
}
|
|
181
193
|
})
|
|
182
194
|
}
|
|
183
195
|
|