@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
@@ -179,7 +179,13 @@ export class StreamerConnection extends EventEmitter implements IStreamer, LogUt
179
179
  }
180
180
  const player = this.server.playerRegistry.get(message.playerId);
181
181
  if (player) {
182
- player.protocol.disconnect(1011, message.reason);
182
+ // ws throws on a reason over 123 bytes or one that is not a string, which would crash the
183
+ // server. Such a reason is dropped; the player is still closed.
184
+ const reason =
185
+ typeof message.reason === 'string' && Buffer.byteLength(message.reason) <= 123
186
+ ? message.reason
187
+ : undefined;
188
+ player.protocol.disconnect(1011, reason);
183
189
  }
184
190
  }
185
191
  }
@@ -0,0 +1,27 @@
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 { StreamerConnection } from './StreamerConnection';
5
+ import { StreamerRegistry } from './StreamerRegistry';
6
+ import { SignallingServer } from './SignallingServer';
7
+
8
+ // A streamer the registry can add and talk to, with no socket behind it.
9
+ function unconnectedStreamer(): StreamerConnection {
10
+ return new StreamerConnection({} as unknown as SignallingServer, {} as unknown as WebSocket);
11
+ }
12
+
13
+ describe('StreamerRegistry endpointId handling', () => {
14
+ it('treats a non-string id as no id, so a later registration still works', () => {
15
+ const registry = new StreamerRegistry();
16
+ const first = unconnectedStreamer();
17
+ registry.add(first);
18
+
19
+ // The id arrives as any JSON value; a number here used to be stored as the streamer id.
20
+ first.protocol.emit(Messages.endpointId.typeName, { type: Messages.endpointId.typeName, id: 5 });
21
+ expect(typeof first.streamerId).toBe('string');
22
+
23
+ const second = unconnectedStreamer();
24
+ expect(() => registry.add(second)).not.toThrow();
25
+ expect(second.streamerId).not.toBe(first.streamerId);
26
+ });
27
+ });
@@ -176,8 +176,12 @@ export class StreamerRegistry extends EventEmitter {
176
176
  private onEndpointId(streamer: IStreamer, message: Messages.endpointId): void {
177
177
  const oldId = streamer.streamerId;
178
178
 
179
+ // The id arrives as any JSON value. A non-string one is treated as no id, because sanitizing it
180
+ // would store a value that later lookups cannot handle.
181
+ const requestedId = typeof message.id === 'string' ? message.id : '';
182
+
179
183
  // id might conflict or be invalid so here we sanitize it
180
- const sanitizedId = this.sanitizeStreamerId(message.id);
184
+ const sanitizedId = this.sanitizeStreamerId(requestedId);
181
185
 
182
186
  let committedId: string | null = sanitizedId;
183
187
  if (this.authorizeStreamerId) {
@@ -185,9 +189,9 @@ export class StreamerRegistry extends EventEmitter {
185
189
  // reject the registration entirely. This is where anti-squatting policy lives.
186
190
  committedId = this.authorizeStreamerId({
187
191
  streamer,
188
- requestedId: message.id,
192
+ requestedId,
189
193
  sanitizedId,
190
- collided: !!message.id && sanitizedId !== message.id
194
+ collided: !!requestedId && sanitizedId !== requestedId
191
195
  });
192
196
 
193
197
  if (committedId === null) {
@@ -0,0 +1,31 @@
1
+ // Copyright Epic Games, Inc. All Rights Reserved.
2
+ import { MAX_LOGGED_LENGTH, limitLogText, limitLoggedMessage } from './Utils';
3
+
4
+ describe('limitLogText', () => {
5
+ it('returns text up to the limit unchanged', () => {
6
+ const text = 'a'.repeat(MAX_LOGGED_LENGTH);
7
+ expect(limitLogText(text)).toBe(text);
8
+ });
9
+
10
+ it('cuts longer text to the limit and gives its full length', () => {
11
+ const text = 'a'.repeat(MAX_LOGGED_LENGTH + 1);
12
+ expect(limitLogText(text)).toBe(
13
+ `${'a'.repeat(MAX_LOGGED_LENGTH)}... (${MAX_LOGGED_LENGTH + 1} characters)`
14
+ );
15
+ });
16
+ });
17
+
18
+ describe('limitLoggedMessage', () => {
19
+ it('returns a message whose JSON fits as the message itself', () => {
20
+ const message = { type: 'ping', time: 1 };
21
+ expect(limitLoggedMessage(message)).toBe(message);
22
+ });
23
+
24
+ it('returns a message whose JSON is too long as its JSON text, cut', () => {
25
+ const message = { type: 'ping', pad: 'a'.repeat(MAX_LOGGED_LENGTH) };
26
+ const json = JSON.stringify(message);
27
+ expect(limitLoggedMessage(message)).toBe(
28
+ `${json.slice(0, MAX_LOGGED_LENGTH)}... (${json.length} characters)`
29
+ );
30
+ });
31
+ });
package/src/Utils.ts CHANGED
@@ -14,3 +14,28 @@ export function stringify(obj: any): string {
14
14
  export function beautify(obj: any): string {
15
15
  return jsonc.stringify(obj, undefined, '\t');
16
16
  }
17
+
18
+ /**
19
+ * The longest text, or protocol message as JSON, that the logger writes whole. A client chooses the
20
+ * size of what it sends, so anything longer is cut rather than written to the log in full.
21
+ */
22
+ export const MAX_LOGGED_LENGTH = 16 * 1024;
23
+
24
+ /**
25
+ * Returns the text, or if it is longer than MAX_LOGGED_LENGTH, its start and its full length.
26
+ */
27
+ export function limitLogText(text: string): string {
28
+ if (text.length <= MAX_LOGGED_LENGTH) {
29
+ return text;
30
+ }
31
+ return `${text.slice(0, MAX_LOGGED_LENGTH)}... (${text.length} characters)`;
32
+ }
33
+
34
+ /**
35
+ * Returns a protocol message as the log file should hold it: the message itself while its JSON fits in
36
+ * MAX_LOGGED_LENGTH, otherwise its JSON text cut by limitLogText.
37
+ */
38
+ export function limitLoggedMessage(message: object): object | string {
39
+ const text = JSON.stringify(message);
40
+ return text.length <= MAX_LOGGED_LENGTH ? message : limitLogText(text);
41
+ }