@epicgames-ps/lib-pixelstreamingsignalling-ue5.8 0.2.0 → 0.2.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.
Files changed (41) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/cjs/Logger.js +23 -9
  3. package/dist/cjs/Logger.js.map +1 -1
  4. package/dist/cjs/SFUConnection.js +5 -0
  5. package/dist/cjs/SFUConnection.js.map +1 -1
  6. package/dist/cjs/SignallingServer.js +11 -4
  7. package/dist/cjs/SignallingServer.js.map +1 -1
  8. package/dist/cjs/StreamerConnection.js +6 -1
  9. package/dist/cjs/StreamerConnection.js.map +1 -1
  10. package/dist/cjs/StreamerRegistry.js +6 -3
  11. package/dist/cjs/StreamerRegistry.js.map +1 -1
  12. package/dist/cjs/Utils.js +25 -0
  13. package/dist/cjs/Utils.js.map +1 -1
  14. package/dist/esm/Logger.js +24 -10
  15. package/dist/esm/Logger.js.map +1 -1
  16. package/dist/esm/SFUConnection.js +5 -0
  17. package/dist/esm/SFUConnection.js.map +1 -1
  18. package/dist/esm/SignallingServer.js +10 -3
  19. package/dist/esm/SignallingServer.js.map +1 -1
  20. package/dist/esm/StreamerConnection.js +6 -1
  21. package/dist/esm/StreamerConnection.js.map +1 -1
  22. package/dist/esm/StreamerRegistry.js +6 -3
  23. package/dist/esm/StreamerRegistry.js.map +1 -1
  24. package/dist/esm/Utils.js +22 -0
  25. package/dist/esm/Utils.js.map +1 -1
  26. package/dist/types/Logger.d.ts +2 -0
  27. package/dist/types/SignallingServer.d.ts +7 -0
  28. package/dist/types/Utils.d.ts +14 -0
  29. package/package.json +14 -14
  30. package/src/Logger.test.ts +109 -0
  31. package/src/Logger.ts +30 -9
  32. package/src/SFUConnection.test.ts +45 -0
  33. package/src/SFUConnection.ts +5 -0
  34. package/src/SignallingServer.maxPayload.test.ts +69 -0
  35. package/src/SignallingServer.ts +11 -0
  36. package/src/StreamerConnection.test.ts +48 -0
  37. package/src/StreamerConnection.ts +7 -1
  38. package/src/StreamerRegistry.test.ts +27 -0
  39. package/src/StreamerRegistry.ts +7 -3
  40. package/src/Utils.test.ts +31 -0
  41. package/src/Utils.ts +25 -0
@@ -121,7 +121,12 @@ export class StreamerConnection extends EventEmitter {
121
121
  }
122
122
  const player = this.server.playerRegistry.get(message.playerId);
123
123
  if (player) {
124
- player.protocol.disconnect(1011, message.reason);
124
+ // ws throws on a reason over 123 bytes or one that is not a string, which would crash the
125
+ // server. Such a reason is dropped; the player is still closed.
126
+ const reason = typeof message.reason === 'string' && Buffer.byteLength(message.reason) <= 123
127
+ ? message.reason
128
+ : undefined;
129
+ player.protocol.disconnect(1011, reason);
125
130
  }
126
131
  }
127
132
  }
@@ -1 +1 @@
1
- {"version":3,"file":"StreamerConnection.js","sourceRoot":"","sources":["../../src/StreamerConnection.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StreamerConnection.js","sourceRoot":"","sources":["../../src/StreamerConnection.ts"],"names":[],"mappings":"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"}
@@ -83,17 +83,20 @@ export class StreamerRegistry extends EventEmitter {
83
83
  }
84
84
  onEndpointId(streamer, message) {
85
85
  const oldId = streamer.streamerId;
86
+ // The id arrives as any JSON value. A non-string one is treated as no id, because sanitizing it
87
+ // would store a value that later lookups cannot handle.
88
+ const requestedId = typeof message.id === 'string' ? message.id : '';
86
89
  // id might conflict or be invalid so here we sanitize it
87
- const sanitizedId = this.sanitizeStreamerId(message.id);
90
+ const sanitizedId = this.sanitizeStreamerId(requestedId);
88
91
  let committedId = sanitizedId;
89
92
  if (this.authorizeStreamerId) {
90
93
  // Let the consumer accept the default id, override it (e.g. namespace per tenant), or
91
94
  // reject the registration entirely. This is where anti-squatting policy lives.
92
95
  committedId = this.authorizeStreamerId({
93
96
  streamer,
94
- requestedId: message.id,
97
+ requestedId,
95
98
  sanitizedId,
96
- collided: !!message.id && sanitizedId !== message.id
99
+ collided: !!requestedId && sanitizedId !== requestedId
97
100
  });
98
101
  if (committedId === null) {
99
102
  Logger.warn(`StreamerRegistry: streamer id '${message.id}' (was ${oldId}) rejected by authorizer. Disconnecting.`);
@@ -1 +1 @@
1
- {"version":3,"file":"StreamerRegistry.js","sourceRoot":"","sources":["../../src/StreamerRegistry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"StreamerRegistry.js","sourceRoot":"","sources":["../../src/StreamerRegistry.ts"],"names":[],"mappings":"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"}
package/dist/esm/Utils.js CHANGED
@@ -12,4 +12,26 @@ export function stringify(obj) {
12
12
  export function beautify(obj) {
13
13
  return jsonc.stringify(obj, undefined, '\t');
14
14
  }
15
+ /**
16
+ * The longest text, or protocol message as JSON, that the logger writes whole. A client chooses the
17
+ * size of what it sends, so anything longer is cut rather than written to the log in full.
18
+ */
19
+ export const MAX_LOGGED_LENGTH = 16 * 1024;
20
+ /**
21
+ * Returns the text, or if it is longer than MAX_LOGGED_LENGTH, its start and its full length.
22
+ */
23
+ export function limitLogText(text) {
24
+ if (text.length <= MAX_LOGGED_LENGTH) {
25
+ return text;
26
+ }
27
+ return `${text.slice(0, MAX_LOGGED_LENGTH)}... (${text.length} characters)`;
28
+ }
29
+ /**
30
+ * Returns a protocol message as the log file should hold it: the message itself while its JSON fits in
31
+ * MAX_LOGGED_LENGTH, otherwise its JSON text cut by limitLogText.
32
+ */
33
+ export function limitLoggedMessage(message) {
34
+ const text = JSON.stringify(message);
35
+ return text.length <= MAX_LOGGED_LENGTH ? message : limitLogText(text);
36
+ }
15
37
  //# sourceMappingURL=Utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Utils.js","sourceRoot":"","sources":["../../src/Utils.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAQ;IAC9B,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAQ;IAC7B,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"Utils.js","sourceRoot":"","sources":["../../src/Utils.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAE9B;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,GAAQ;IAC9B,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;AAChC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAQ;IAC7B,OAAO,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,CAAC;AAE3C;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,IAAY;IACrC,IAAI,IAAI,CAAC,MAAM,IAAI,iBAAiB,EAAE,CAAC;QACnC,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,QAAQ,IAAI,CAAC,MAAM,cAAc,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC,MAAM,IAAI,iBAAiB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;AAC3E,CAAC"}
@@ -11,6 +11,8 @@ export interface IConfig {
11
11
  logMessagesToConsole?: string;
12
12
  logLevelConsole?: string;
13
13
  logLevelFile?: string;
14
+ logMaxSize?: string;
15
+ logMaxFiles?: number;
14
16
  }
15
17
  /**
16
18
  * Call this as early as possible to setup the logging module with your
@@ -45,6 +45,13 @@ export interface IPeerOptionsRequest {
45
45
  * own. A provider that throws is treated as having no answer, and the static peerOptions are sent.
46
46
  */
47
47
  export type PeerOptionsProvider = (request: IPeerOptionsRequest) => unknown;
48
+ /**
49
+ * The largest websocket message, in bytes, accepted on the streamer, player and SFU ports. Signalling
50
+ * messages are a few kilobytes, and ws would otherwise accept up to 100 MiB in one message. A
51
+ * connection that sends more is closed with code 1009. Setting maxPayload in streamerWsOptions,
52
+ * playerWsOptions or sfuWsOptions overrides it for that port.
53
+ */
54
+ export declare const DEFAULT_MAX_PAYLOAD: number;
48
55
  /**
49
56
  * The main signalling server object.
50
57
  * Contains a streamer and player registry and handles setting up of websockets
@@ -6,3 +6,17 @@ export declare function stringify(obj: any): string;
6
6
  * Circular reference save version of JSON.stringify with extra formatting.
7
7
  */
8
8
  export declare function beautify(obj: any): string;
9
+ /**
10
+ * The longest text, or protocol message as JSON, that the logger writes whole. A client chooses the
11
+ * size of what it sends, so anything longer is cut rather than written to the log in full.
12
+ */
13
+ export declare const MAX_LOGGED_LENGTH: number;
14
+ /**
15
+ * Returns the text, or if it is longer than MAX_LOGGED_LENGTH, its start and its full length.
16
+ */
17
+ export declare function limitLogText(text: string): string;
18
+ /**
19
+ * Returns a protocol message as the log file should hold it: the message itself while its JSON fits in
20
+ * MAX_LOGGED_LENGTH, otherwise its JSON text cut by limitLogText.
21
+ */
22
+ export declare function limitLoggedMessage(message: object): object | string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epicgames-ps/lib-pixelstreamingsignalling-ue5.8",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Basic signalling library for developers wishing to build applications that signal a Pixel Streaming application.",
5
5
  "main": "dist/cjs/pixelstreamingsignalling.js",
6
6
  "module": "dist/esm/pixelstreamingsignalling.js",
@@ -19,31 +19,31 @@
19
19
  "author": "Epic Games",
20
20
  "license": "MIT",
21
21
  "devDependencies": {
22
- "@eslint/js": "^9.20.0",
22
+ "@eslint/js": "^9.39.5",
23
23
  "@types/cors": "^2.8.17",
24
24
  "@types/express": "^5.0.0",
25
25
  "@types/jest": "^29.5.14",
26
- "@types/node": "^22.14.0",
26
+ "@types/node": "^22.20.5",
27
27
  "@types/ws": "^8.5.14",
28
28
  "eslint": "^9.39.0",
29
29
  "eslint-config-prettier": "^10.0.1",
30
- "eslint-plugin-prettier": "^5.2.3",
31
- "eslint-plugin-tsdoc": "^0.4.0",
30
+ "eslint-plugin-prettier": "^5.5.7",
31
+ "eslint-plugin-tsdoc": "^0.5.4",
32
32
  "nodemon": "^3.1.9",
33
33
  "jest": "^29.7.0",
34
34
  "rimraf": "^6.0.1",
35
- "typedoc": "^0.28.0",
36
- "typedoc-plugin-markdown": "^4.4.2",
37
- "ts-jest": "^29.2.5",
35
+ "typedoc": "^0.28.20",
36
+ "typedoc-plugin-markdown": "^4.14.1",
37
+ "ts-jest": "^29.4.15",
38
38
  "typescript": "^5.7.3",
39
- "typescript-eslint": "8.57.2"
39
+ "typescript-eslint": "8.72.1"
40
40
  },
41
41
  "dependencies": {
42
- "body-parser": "^1.20.4",
42
+ "body-parser": "^2.4.0",
43
43
  "cors": "^2.8.5",
44
- "express": "^4.22.1",
45
- "express-rate-limit": "^8.5.1",
46
- "helmet": "^8.0.0",
44
+ "express": "^5.3.0",
45
+ "express-rate-limit": "^8.7.2",
46
+ "helmet": "^8.3.0",
47
47
  "hsts": "^2.2.0",
48
48
  "jsonc": "^2.0.0",
49
49
  "logform": "^2.7.0",
@@ -52,7 +52,7 @@
52
52
  "ws": "^8.18.0"
53
53
  },
54
54
  "peerDependencies": {
55
- "@epicgames-ps/lib-pixelstreamingcommon-ue5.8": "^0.1.0"
55
+ "@epicgames-ps/lib-pixelstreamingcommon-ue5.8": "^0.1.1"
56
56
  },
57
57
  "repository": {
58
58
  "type": "git",
@@ -0,0 +1,109 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+ import fs from 'fs';
3
+ import os from 'os';
4
+ import path from 'path';
5
+ import { IConfig, InitLogging, Logger } from './Logger';
6
+ import { MAX_LOGGED_LENGTH } from './Utils';
7
+
8
+ type Transport = (typeof Logger.transports)[number];
9
+
10
+ const LEVEL = Symbol.for('level');
11
+ const MESSAGE = Symbol.for('message');
12
+
13
+ // Runs a log call through a transport's format and returns the line it would write. Formatting does no
14
+ // I/O, so this reads exactly what the logger produces without waiting on the file.
15
+ function lineFor(transport: Transport, message: unknown): string {
16
+ const info = transport.format?.transform({ level: 'info', message, [LEVEL]: 'info' });
17
+ if (!info || info === true) {
18
+ throw new Error('the format dropped the line');
19
+ }
20
+ return info[MESSAGE] as string;
21
+ }
22
+
23
+ // InitLogging opens the day's file, so every logger a test creates is closed before its folder is removed.
24
+ async function withLogger(config: IConfig, check: (console: Transport, file: Transport) => void) {
25
+ InitLogging(config);
26
+ const [consoleTransport, fileTransport] = Logger.transports;
27
+ try {
28
+ check(consoleTransport, fileTransport);
29
+ } finally {
30
+ await new Promise((resolve) => {
31
+ fileTransport.once('finish', resolve);
32
+ fileTransport.close?.();
33
+ });
34
+ }
35
+ }
36
+
37
+ describe('InitLogging', () => {
38
+ let logDir: string;
39
+
40
+ beforeEach(() => {
41
+ logDir = fs.mkdtempSync(path.join(os.tmpdir(), 'signalling-log-'));
42
+ });
43
+
44
+ afterEach(() => {
45
+ fs.rmSync(logDir, { recursive: true, force: true });
46
+ });
47
+
48
+ it('refuses a file size that the file transport would take as no limit', () => {
49
+ expect(() => InitLogging({ logDir, logMaxSize: '100mb' })).toThrow('logMaxSize');
50
+ expect(() => InitLogging({ logDir, logMaxSize: '0m' })).toThrow('logMaxSize');
51
+ });
52
+
53
+ it('refuses a file count that the file transport would take as no limit', () => {
54
+ expect(() => InitLogging({ logDir, logMaxFiles: 0 })).toThrow('logMaxFiles');
55
+ expect(() => InitLogging({ logDir, logMaxFiles: NaN })).toThrow('logMaxFiles');
56
+ });
57
+
58
+ it('limits the size of each log file and how many are kept', async () => {
59
+ await withLogger({ logDir }, (_console, file) => {
60
+ const { options } = file as unknown as { options: { maxSize: unknown; maxFiles: unknown } };
61
+ expect(options.maxSize).toBe('100m');
62
+ expect(options.maxFiles).toBe(14);
63
+ });
64
+ await withLogger({ logDir, logMaxSize: '5m', logMaxFiles: 3 }, (_console, file) => {
65
+ const { options } = file as unknown as { options: { maxSize: unknown; maxFiles: unknown } };
66
+ expect(options.maxSize).toBe('5m');
67
+ expect(options.maxFiles).toBe(3);
68
+ });
69
+ });
70
+
71
+ it('cuts long text on the console and in the file', async () => {
72
+ const text = 'x'.repeat(100000);
73
+ await withLogger({ logDir }, (console, file) => {
74
+ const consoleLine = lineFor(console, text);
75
+ expect(consoleLine.endsWith('... (100000 characters)')).toBe(true);
76
+ expect(consoleLine.length).toBeLessThan(MAX_LOGGED_LENGTH + 100);
77
+
78
+ const fileLine = lineFor(file, text);
79
+ expect(
80
+ (JSON.parse(fileLine) as { message: string }).message.endsWith('... (100000 characters)')
81
+ ).toBe(true);
82
+ expect(fileLine.length).toBeLessThan(MAX_LOGGED_LENGTH + 200);
83
+ });
84
+ });
85
+
86
+ it('writes a protocol message to the file whole, or as cut JSON text when it is too long', async () => {
87
+ const logObj = (protoMessage: object) => ({
88
+ event: 'proto_message',
89
+ direction: 'incoming',
90
+ receiver: 'Player0',
91
+ protoMessage
92
+ });
93
+ await withLogger({ logDir }, (_console, file) => {
94
+ const short = JSON.parse(lineFor(file, logObj({ type: 'ping', time: 1 }))) as {
95
+ protoMessage: unknown;
96
+ };
97
+ expect(short.protoMessage).toEqual({ type: 'ping', time: 1 });
98
+
99
+ const long = { type: 'ping', pad: 'a'.repeat(100000) };
100
+ const longLine = lineFor(file, logObj(long));
101
+ const parsed = JSON.parse(longLine) as { protoMessage: string };
102
+ expect(parsed.protoMessage.startsWith('{"type":"ping"')).toBe(true);
103
+ expect(parsed.protoMessage.endsWith(`... (${JSON.stringify(long).length} characters)`)).toBe(
104
+ true
105
+ );
106
+ expect(longLine.length).toBeLessThan(MAX_LOGGED_LENGTH + 300);
107
+ });
108
+ });
109
+ });
package/src/Logger.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // Copyright Epic Games, Inc. All Rights Reserved.
2
- import { stringify, beautify } from './Utils';
2
+ import { stringify, beautify, limitLogText, limitLoggedMessage } from './Utils';
3
3
  import { IProtoLogObj } from './LoggingUtils';
4
4
  import path from 'path';
5
5
  import winston from 'winston';
@@ -30,6 +30,12 @@ export interface IConfig {
30
30
 
31
31
  // the minimum log level for file messages
32
32
  logLevelFile?: string;
33
+
34
+ // the size at which a log file is closed and a new one started, as a whole number followed by k, m or g
35
+ logMaxSize?: string;
36
+
37
+ // the number of log files to keep. The oldest is deleted when a new one is started.
38
+ logMaxFiles?: number;
33
39
  }
34
40
 
35
41
  /**
@@ -70,10 +76,23 @@ export function InitLogging(config: IConfig): void {
70
76
  const logDir = config.logDir || 'logs';
71
77
  const logLevelConsole = config.logLevelConsole || 'info';
72
78
  const logLevelFile = config.logLevelFile || 'info';
79
+ const logMaxSize = config.logMaxSize || '100m';
80
+ const logMaxFiles = config.logMaxFiles ?? 14;
73
81
  logMessagesToConsole = config.logMessagesToConsole || 'none';
74
82
 
83
+ // winston-daily-rotate-file takes a size it cannot parse, or a count of 0, as no limit at all.
84
+ if (!/^[1-9]\d*[kmg]$/i.test(logMaxSize)) {
85
+ throw new Error(`logMaxSize must be a whole number followed by k, m or g, not "${logMaxSize}".`);
86
+ }
87
+ if (!Number.isInteger(logMaxFiles) || logMaxFiles < 1) {
88
+ throw new Error(`logMaxFiles must be a whole number of at least 1, not "${logMaxFiles}".`);
89
+ }
90
+
75
91
  Logger = winston.createLogger({
76
- transports: [createConsoleTransport(logLevelConsole), createFileTransport(logDir, logLevelFile)]
92
+ transports: [
93
+ createConsoleTransport(logLevelConsole),
94
+ createFileTransport(logDir, logLevelFile, logMaxSize, logMaxFiles)
95
+ ]
77
96
  });
78
97
 
79
98
  const commonLogger = new CommonLogger(Logger);
@@ -118,7 +137,7 @@ function createConsoleFormat() {
118
137
  return printf((logObj: TransformableInfo) => {
119
138
  const prefix = `[${String(logObj['timestamp'])}] ${logObj.level}: `;
120
139
  if (typeof logObj.message === 'string') {
121
- return prefix + logObj.message;
140
+ return prefix + limitLogText(logObj.message);
122
141
  } else if (isLogObject(logObj.message)) {
123
142
  const { direction, receiver, sender, target, protoMessage } = logObj.message;
124
143
  switch (direction) {
@@ -139,9 +158,9 @@ function createConsoleFormat() {
139
158
  function formatMessageForConsole(message: BaseMessage) {
140
159
  switch (logMessagesToConsole) {
141
160
  case 'verbose':
142
- return stringify(message);
161
+ return limitLogText(stringify(message));
143
162
  case 'formatted':
144
- return beautify(message);
163
+ return limitLogText(beautify(message));
145
164
  default:
146
165
  return `[${message.type}]`;
147
166
  }
@@ -156,12 +175,13 @@ function createProtoMessageFilter() {
156
175
  });
157
176
  }
158
177
 
159
- function createFileTransport(logDirPath: string, logLevel: string) {
178
+ function createFileTransport(logDirPath: string, logLevel: string, maxSize: string, maxFiles: number) {
160
179
  return new winston.transports.DailyRotateFile({
161
180
  level: logLevel,
162
181
  filename: path.join(logDirPath, 'server-%DATE%.log'),
163
182
  datePattern: 'YYYY-MM-DD',
164
- maxFiles: '14d',
183
+ maxSize,
184
+ maxFiles,
165
185
  format: combine(timestamp({ format: 'YYYY-MM-DD HH:mm:ss.SSS' }), splat(), createFileFormat())
166
186
  });
167
187
  }
@@ -174,14 +194,15 @@ function createFileFormat() {
174
194
  timestamp,
175
195
  level,
176
196
  event: 'message',
177
- message
197
+ message: limitLogText(message)
178
198
  });
179
199
  } else if (isLogObject(logObj.message)) {
180
200
  const { timestamp, level, message } = logObj;
181
201
  return JSON.stringify({
182
202
  timestamp,
183
203
  level,
184
- ...message
204
+ ...message,
205
+ protoMessage: limitLoggedMessage(message.protoMessage)
185
206
  });
186
207
  }
187
208
  return '';
@@ -0,0 +1,45 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+ import type WebSocket from 'ws';
3
+ import { Messages } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.8';
4
+ import { SFUConnection } from './SFUConnection';
5
+ import { StreamerConnection } from './StreamerConnection';
6
+ import { StreamerRegistry } from './StreamerRegistry';
7
+ import { PlayerRegistry } from './PlayerRegistry';
8
+ import { SignallingServer } from './SignallingServer';
9
+
10
+ // A streamer the SFU can subscribe to, with no socket behind it.
11
+ function unconnectedStreamer(streamerId: string): StreamerConnection {
12
+ const streamer = new StreamerConnection({} as unknown as SignallingServer, {} as unknown as WebSocket);
13
+ streamer.streamerId = streamerId;
14
+ return streamer;
15
+ }
16
+
17
+ describe('SFUConnection subscribe', () => {
18
+ it('leaves the previous streamer when it subscribes to another', () => {
19
+ const a = unconnectedStreamer('A');
20
+ const b = unconnectedStreamer('B');
21
+ const streamerRegistry = new StreamerRegistry();
22
+ streamerRegistry.streamers.push(a, b);
23
+ const server = {
24
+ streamerRegistry,
25
+ playerRegistry: new PlayerRegistry()
26
+ } as unknown as SignallingServer;
27
+ const sfu = new SFUConnection(server, {} as unknown as WebSocket);
28
+ sfu.playerId = 'SFU1';
29
+
30
+ sfu.protocol.emit(Messages.subscribe.typeName, {
31
+ type: Messages.subscribe.typeName,
32
+ streamerId: 'A'
33
+ });
34
+ sfu.protocol.emit(Messages.subscribe.typeName, {
35
+ type: Messages.subscribe.typeName,
36
+ streamerId: 'B'
37
+ });
38
+
39
+ expect(a.subscribers.has('SFU1')).toBe(false);
40
+ expect(b.subscribers.has('SFU1')).toBe(true);
41
+ // A disconnect from the old streamer must not detach the SFU from the one it is on now.
42
+ a.emit('disconnect');
43
+ expect(sfu.subscribedStreamer).toBe(b);
44
+ });
45
+ });
@@ -184,6 +184,11 @@ export class SFUConnection extends EventEmitter implements IPlayer, IStreamer, L
184
184
  return;
185
185
  }
186
186
 
187
+ // Leave the previous streamer first, as PlayerConnection does, so it stops forwarding to this SFU
188
+ // and a later disconnect of it cannot detach the SFU from the streamer it is now on.
189
+ if (this.subscribedStreamer) {
190
+ this.unsubscribe();
191
+ }
187
192
  this.subscribedStreamer = streamer;
188
193
  // Register as a subscriber of the upstream streamer so it will forward signalling messages
189
194
  // back to this SFU (the streamer's forwardMessage only sends to subscribed players).
@@ -0,0 +1,69 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+ import * as wslib from 'ws';
3
+ import { SignallingServer, DEFAULT_MAX_PAYLOAD, IServerConfig } from './SignallingServer';
4
+
5
+ // The constructor binds real listening sockets and the class offers no way to close them, so the
6
+ // websocket servers are replaced here and only the options they are created with are checked.
7
+ jest.mock('ws', () => {
8
+ const actual = jest.requireActual<typeof wslib>('ws');
9
+ return {
10
+ ...actual,
11
+ WebSocketServer: jest.fn().mockImplementation(() => ({ on: jest.fn() }))
12
+ };
13
+ });
14
+
15
+ const STREAMER_PORT = 8888;
16
+ const PLAYER_PORT = 80;
17
+ const SFU_PORT = 8889;
18
+
19
+ function createdOptions(config: Partial<IServerConfig>): Map<number | undefined, wslib.ServerOptions> {
20
+ const constructor = jest.mocked(wslib.WebSocketServer);
21
+ constructor.mockClear();
22
+ new SignallingServer({
23
+ streamerPort: STREAMER_PORT,
24
+ playerPort: PLAYER_PORT,
25
+ sfuPort: SFU_PORT,
26
+ peerOptions: {},
27
+ ...config
28
+ });
29
+ const byPort = new Map<number | undefined, wslib.ServerOptions>();
30
+ for (const [options] of constructor.mock.calls) {
31
+ byPort.set(options?.port, options ?? {});
32
+ }
33
+ return byPort;
34
+ }
35
+
36
+ describe('SignallingServer websocket message size', () => {
37
+ it('limits every port to DEFAULT_MAX_PAYLOAD', () => {
38
+ const options = createdOptions({});
39
+
40
+ expect(options.size).toBe(3);
41
+ expect(options.get(STREAMER_PORT)?.maxPayload).toBe(DEFAULT_MAX_PAYLOAD);
42
+ expect(options.get(PLAYER_PORT)?.maxPayload).toBe(DEFAULT_MAX_PAYLOAD);
43
+ expect(options.get(SFU_PORT)?.maxPayload).toBe(DEFAULT_MAX_PAYLOAD);
44
+ });
45
+
46
+ it('is 1 MiB', () => {
47
+ expect(DEFAULT_MAX_PAYLOAD).toBe(1024 * 1024);
48
+ });
49
+
50
+ it('lets the websocket options of each port override the limit', () => {
51
+ const options = createdOptions({
52
+ streamerWsOptions: { maxPayload: 4 * 1024 * 1024 },
53
+ playerWsOptions: { maxPayload: 64 * 1024 },
54
+ sfuWsOptions: { maxPayload: 0 }
55
+ });
56
+
57
+ expect(options.get(STREAMER_PORT)?.maxPayload).toBe(4 * 1024 * 1024);
58
+ expect(options.get(PLAYER_PORT)?.maxPayload).toBe(64 * 1024);
59
+ expect(options.get(SFU_PORT)?.maxPayload).toBe(0);
60
+ });
61
+
62
+ it('keeps the other websocket options it is given', () => {
63
+ const verifyClient = jest.fn();
64
+ const options = createdOptions({ playerWsOptions: { verifyClient } });
65
+
66
+ expect(options.get(PLAYER_PORT)?.verifyClient).toBe(verifyClient);
67
+ expect(options.get(PLAYER_PORT)?.maxPayload).toBe(DEFAULT_MAX_PAYLOAD);
68
+ });
69
+ });
@@ -100,6 +100,14 @@ export interface IPeerOptionsRequest {
100
100
  */
101
101
  export type PeerOptionsProvider = (request: IPeerOptionsRequest) => unknown;
102
102
 
103
+ /**
104
+ * The largest websocket message, in bytes, accepted on the streamer, player and SFU ports. Signalling
105
+ * messages are a few kilobytes, and ws would otherwise accept up to 100 MiB in one message. A
106
+ * connection that sends more is closed with code 1009. Setting maxPayload in streamerWsOptions,
107
+ * playerWsOptions or sfuWsOptions overrides it for that port.
108
+ */
109
+ export const DEFAULT_MAX_PAYLOAD = 1024 * 1024;
110
+
103
111
  /**
104
112
  * The main signalling server object.
105
113
  * Contains a streamer and player registry and handles setting up of websockets
@@ -138,6 +146,7 @@ export class SignallingServer {
138
146
  const streamerServer = new wslib.WebSocketServer({
139
147
  port: config.streamerPort,
140
148
  backlog: 1,
149
+ maxPayload: DEFAULT_MAX_PAYLOAD,
141
150
  ...config.streamerWsOptions
142
151
  });
143
152
  streamerServer.on('connection', this.onStreamerConnected.bind(this));
@@ -148,6 +157,7 @@ export class SignallingServer {
148
157
  const playerServer = new wslib.WebSocketServer({
149
158
  server: server,
150
159
  port: server ? undefined : config.playerPort,
160
+ maxPayload: DEFAULT_MAX_PAYLOAD,
151
161
  ...config.playerWsOptions
152
162
  });
153
163
  playerServer.on('connection', this.onPlayerConnected.bind(this));
@@ -160,6 +170,7 @@ export class SignallingServer {
160
170
  const sfuServer = new wslib.WebSocketServer({
161
171
  port: config.sfuPort,
162
172
  backlog: 1,
173
+ maxPayload: DEFAULT_MAX_PAYLOAD,
163
174
  ...config.sfuWsOptions
164
175
  });
165
176
  sfuServer.on('connection', this.onSFUConnected.bind(this));
@@ -0,0 +1,48 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+ import type WebSocket from 'ws';
3
+ import { StreamerConnection } from './StreamerConnection';
4
+ import { SignallingServer } from './SignallingServer';
5
+
6
+ describe('StreamerConnection disconnectPlayer reason', () => {
7
+ // A streamer with one subscribed player whose protocol records the close it is asked for.
8
+ function streamerWithPlayer() {
9
+ const disconnect = jest.fn();
10
+ const player = { protocol: { disconnect } };
11
+ const server = { playerRegistry: { get: () => player } } as unknown as SignallingServer;
12
+ const streamer = new StreamerConnection(server, {} as unknown as WebSocket);
13
+ streamer.subscribers = new Set(['player-1']);
14
+ return { streamer, disconnect };
15
+ }
16
+
17
+ function disconnectPlayer(streamer: StreamerConnection, reason: unknown): void {
18
+ streamer.protocol.emit('disconnectPlayer', {
19
+ type: 'disconnectPlayer',
20
+ playerId: 'player-1',
21
+ reason
22
+ });
23
+ }
24
+
25
+ it('passes a short string reason through', () => {
26
+ const { streamer, disconnect } = streamerWithPlayer();
27
+ disconnectPlayer(streamer, 'bye');
28
+ expect(disconnect).toHaveBeenCalledWith(1011, 'bye');
29
+ });
30
+
31
+ it('closes without a reason when the reason is over 123 bytes', () => {
32
+ const { streamer, disconnect } = streamerWithPlayer();
33
+ disconnectPlayer(streamer, 'x'.repeat(124));
34
+ expect(disconnect).toHaveBeenCalledWith(1011, undefined);
35
+ });
36
+
37
+ it('counts the reason in bytes, not characters', () => {
38
+ const { streamer, disconnect } = streamerWithPlayer();
39
+ disconnectPlayer(streamer, String.fromCharCode(0xe9).repeat(62)); // 124 bytes in UTF-8
40
+ expect(disconnect).toHaveBeenCalledWith(1011, undefined);
41
+ });
42
+
43
+ it('closes without a reason when the reason is not a string', () => {
44
+ const { streamer, disconnect } = streamerWithPlayer();
45
+ disconnectPlayer(streamer, { length: 1 });
46
+ expect(disconnect).toHaveBeenCalledWith(1011, undefined);
47
+ });
48
+ });