@epicgames-ps/lib-pixelstreamingsignalling-ue5.8 0.1.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/.lintstagedrc.mjs +7 -0
- package/.prettierignore +0 -0
- package/CHANGELOG.md +21 -0
- package/README.md +32 -0
- package/dist/cjs/Logger.js +148 -0
- package/dist/cjs/Logger.js.map +1 -0
- package/dist/cjs/LoggingUtils.js +64 -0
- package/dist/cjs/LoggingUtils.js.map +1 -0
- package/dist/cjs/PlayerConnection.js +220 -0
- package/dist/cjs/PlayerConnection.js.map +1 -0
- package/dist/cjs/PlayerRegistry.js +77 -0
- package/dist/cjs/PlayerRegistry.js.map +1 -0
- package/dist/cjs/SFUConnection.js +264 -0
- package/dist/cjs/SFUConnection.js.map +1 -0
- package/dist/cjs/SignallingServer.js +149 -0
- package/dist/cjs/SignallingServer.js.map +1 -0
- package/dist/cjs/StreamerConnection.js +172 -0
- package/dist/cjs/StreamerConnection.js.map +1 -0
- package/dist/cjs/StreamerRegistry.js +149 -0
- package/dist/cjs/StreamerRegistry.js.map +1 -0
- package/dist/cjs/Utils.js +19 -0
- package/dist/cjs/Utils.js.map +1 -0
- package/dist/cjs/WebServer.js +124 -0
- package/dist/cjs/WebServer.js.map +1 -0
- package/dist/cjs/pixelstreamingsignalling.js +28 -0
- package/dist/cjs/pixelstreamingsignalling.js.map +1 -0
- package/dist/esm/Logger.js +141 -0
- package/dist/esm/Logger.js.map +1 -0
- package/dist/esm/LoggingUtils.js +58 -0
- package/dist/esm/LoggingUtils.js.map +1 -0
- package/dist/esm/PlayerConnection.js +183 -0
- package/dist/esm/PlayerConnection.js.map +1 -0
- package/dist/esm/PlayerRegistry.js +73 -0
- package/dist/esm/PlayerRegistry.js.map +1 -0
- package/dist/esm/SFUConnection.js +227 -0
- package/dist/esm/SFUConnection.js.map +1 -0
- package/dist/esm/SignallingServer.js +112 -0
- package/dist/esm/SignallingServer.js.map +1 -0
- package/dist/esm/StreamerConnection.js +135 -0
- package/dist/esm/StreamerConnection.js.map +1 -0
- package/dist/esm/StreamerRegistry.js +145 -0
- package/dist/esm/StreamerRegistry.js.map +1 -0
- package/dist/esm/Utils.js +15 -0
- package/dist/esm/Utils.js.map +1 -0
- package/dist/esm/WebServer.js +117 -0
- package/dist/esm/WebServer.js.map +1 -0
- package/dist/esm/pixelstreamingsignalling.js +12 -0
- package/dist/esm/pixelstreamingsignalling.js.map +1 -0
- package/dist/types/Logger.d.ts +20 -0
- package/dist/types/LoggingUtils.d.ts +44 -0
- package/dist/types/PlayerConnection.d.ts +66 -0
- package/dist/types/PlayerRegistry.d.ts +66 -0
- package/dist/types/SFUConnection.d.ts +85 -0
- package/dist/types/SignallingServer.d.ts +48 -0
- package/dist/types/StreamerConnection.d.ts +61 -0
- package/dist/types/StreamerRegistry.d.ts +96 -0
- package/dist/types/Utils.d.ts +8 -0
- package/dist/types/WebServer.d.ts +42 -0
- package/dist/types/pixelstreamingsignalling.d.ts +9 -0
- package/docs/Logger/README.md +19 -0
- package/docs/Logger/functions/InitLogging.md +26 -0
- package/docs/Logger/interfaces/IConfig.md +41 -0
- package/docs/Logger/variables/Logger.md +15 -0
- package/docs/LoggingUtils/README.md +19 -0
- package/docs/LoggingUtils/functions/createHandlerListener.md +42 -0
- package/docs/LoggingUtils/functions/logForward.md +36 -0
- package/docs/LoggingUtils/functions/logIncoming.md +30 -0
- package/docs/LoggingUtils/functions/logOutgoing.md +30 -0
- package/docs/LoggingUtils/interfaces/IMessageLogger.md +29 -0
- package/docs/LoggingUtils/interfaces/IProtoLogObj.md +57 -0
- package/docs/PlayerConnection/README.md +11 -0
- package/docs/PlayerConnection/classes/PlayerConnection.md +177 -0
- package/docs/PlayerRegistry/README.md +16 -0
- package/docs/PlayerRegistry/classes/PlayerRegistry.md +423 -0
- package/docs/PlayerRegistry/interfaces/IPlayer.md +86 -0
- package/docs/PlayerRegistry/interfaces/IPlayerInfo.md +43 -0
- package/docs/README.md +19 -0
- package/docs/SFUConnection/README.md +11 -0
- package/docs/SFUConnection/classes/SFUConnection.md +554 -0
- package/docs/SignallingServer/README.md +19 -0
- package/docs/SignallingServer/classes/SignallingServer.md +76 -0
- package/docs/SignallingServer/interfaces/IServerConfig.md +92 -0
- package/docs/SignallingServer/type-aliases/ProtocolConfig.md +15 -0
- package/docs/StreamerConnection/README.md +11 -0
- package/docs/StreamerConnection/classes/StreamerConnection.md +507 -0
- package/docs/StreamerRegistry/README.md +16 -0
- package/docs/StreamerRegistry/classes/StreamerRegistry.md +431 -0
- package/docs/StreamerRegistry/interfaces/IStreamer.md +375 -0
- package/docs/StreamerRegistry/interfaces/IStreamerInfo.md +51 -0
- package/docs/Utils/README.md +12 -0
- package/docs/Utils/functions/beautify.md +23 -0
- package/docs/Utils/functions/stringify.md +23 -0
- package/docs/WebServer/README.md +15 -0
- package/docs/WebServer/classes/WebServer.md +50 -0
- package/docs/WebServer/interfaces/IWebServerConfig.md +76 -0
- package/docs/pixelstreamingsignalling/README.md +145 -0
- package/eslint.config.mjs +39 -0
- package/package.json +58 -0
- package/src/Logger.ts +189 -0
- package/src/LoggingUtils.ts +80 -0
- package/src/PlayerConnection.ts +257 -0
- package/src/PlayerRegistry.ts +116 -0
- package/src/SFUConnection.ts +329 -0
- package/src/SignallingServer.ts +214 -0
- package/src/StreamerConnection.ts +194 -0
- package/src/StreamerRegistry.ts +252 -0
- package/src/Utils.ts +16 -0
- package/src/WebServer.ts +213 -0
- package/src/pixelstreamingsignalling.ts +11 -0
- package/tsconfig.cjs.json +8 -0
- package/tsconfig.esm.json +8 -0
- package/tsconfig.json +29 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { SignallingProtocol, WebSocketTransportNJS, Messages, EventEmitter, MessageHelpers } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.8';
|
|
2
|
+
import { stringify } from './Utils';
|
|
3
|
+
import { Logger } from './Logger';
|
|
4
|
+
import * as LogUtils from './LoggingUtils';
|
|
5
|
+
/**
|
|
6
|
+
* A connection between the signalling server and a streamer connection.
|
|
7
|
+
* This is where messages expected to be handled by the streamer come in
|
|
8
|
+
* and where messages are sent to the streamer.
|
|
9
|
+
*
|
|
10
|
+
* Interesting internals:
|
|
11
|
+
* streamerId: The unique id string of this streamer.
|
|
12
|
+
* transport: The ITransport where transport events can be subscribed to
|
|
13
|
+
* protocol: The SignallingProtocol where signalling messages can be
|
|
14
|
+
* subscribed to.
|
|
15
|
+
* streaming: True when the streamer is ready to accept subscriptions.
|
|
16
|
+
*/
|
|
17
|
+
export class StreamerConnection extends EventEmitter {
|
|
18
|
+
/**
|
|
19
|
+
* Initializes a new connection with given and sane values. Adds listeners for the
|
|
20
|
+
* websocket close and error and will emit a disconnected event when disconneted.
|
|
21
|
+
* @param server - The signalling server object that spawned this streamer.
|
|
22
|
+
* @param ws - The websocket coupled to this streamer connection.
|
|
23
|
+
* @param remoteAddress - The remote address of this connection. Only used as display.
|
|
24
|
+
* @param request - The HTTP upgrade request that opened this connection, if available.
|
|
25
|
+
*/
|
|
26
|
+
constructor(server, ws, remoteAddress, request) {
|
|
27
|
+
super();
|
|
28
|
+
this.server = server;
|
|
29
|
+
this.streamerId = '';
|
|
30
|
+
this.transport = new WebSocketTransportNJS(ws);
|
|
31
|
+
this.protocol = new SignallingProtocol(this.transport);
|
|
32
|
+
this.streaming = false;
|
|
33
|
+
this.remoteAddress = remoteAddress;
|
|
34
|
+
this.request = request;
|
|
35
|
+
this.maxSubscribers = 0;
|
|
36
|
+
this.subscribers = new Set();
|
|
37
|
+
this.transport.on('error', this.onTransportError.bind(this));
|
|
38
|
+
this.transport.on('close', this.onTransportClose.bind(this));
|
|
39
|
+
this.registerMessageHandlers();
|
|
40
|
+
this.protocol.on('unhandled', (message) => {
|
|
41
|
+
Logger.warn(`Unhandled streamer protocol message: ${JSON.stringify(message)}`);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Returns an identifier that is displayed in logs.
|
|
46
|
+
* @returns A string describing this connection.
|
|
47
|
+
*/
|
|
48
|
+
getReadableIdentifier() {
|
|
49
|
+
return this.streamerId;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Sends a signalling message to the player.
|
|
53
|
+
* @param message - The message to send.
|
|
54
|
+
*/
|
|
55
|
+
sendMessage(message) {
|
|
56
|
+
LogUtils.logOutgoing(this, message);
|
|
57
|
+
this.protocol.sendMessage(message);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Returns a descriptive object for the REST API inspection operations.
|
|
61
|
+
* @returns An IStreamerInfo object containing viewable information about this connection.
|
|
62
|
+
*/
|
|
63
|
+
getStreamerInfo() {
|
|
64
|
+
return {
|
|
65
|
+
streamerId: this.streamerId,
|
|
66
|
+
type: 'Streamer',
|
|
67
|
+
streaming: this.streaming,
|
|
68
|
+
remoteAddress: this.remoteAddress,
|
|
69
|
+
subscribers: this.server.playerRegistry
|
|
70
|
+
.listPlayers()
|
|
71
|
+
.filter((player) => player.subscribedStreamer == this)
|
|
72
|
+
.map((player) => player.getPlayerInfo())
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
registerMessageHandlers() {
|
|
76
|
+
/* eslint-disable @typescript-eslint/unbound-method */
|
|
77
|
+
this.protocol.on(Messages.endpointId.typeName, LogUtils.createHandlerListener(this, this.onEndpointId));
|
|
78
|
+
this.protocol.on(Messages.disconnectPlayer.typeName, LogUtils.createHandlerListener(this, this.onDisconnectPlayerRequest));
|
|
79
|
+
this.protocol.on(Messages.layerPreference.typeName, LogUtils.createHandlerListener(this, this.onLayerPreference));
|
|
80
|
+
this.protocol.on(Messages.ping.typeName, LogUtils.createHandlerListener(this, this.onPingMessage));
|
|
81
|
+
/* eslint-enable @typescript-eslint/unbound-method */
|
|
82
|
+
this.protocol.on(Messages.offer.typeName, this.forwardMessage.bind(this));
|
|
83
|
+
this.protocol.on(Messages.answer.typeName, this.forwardMessage.bind(this));
|
|
84
|
+
this.protocol.on(Messages.iceCandidate.typeName, this.forwardMessage.bind(this));
|
|
85
|
+
}
|
|
86
|
+
forwardMessage(message) {
|
|
87
|
+
if (!message.playerId) {
|
|
88
|
+
Logger.warn(`No playerId specified, cannot forward message: ${stringify(message)}`);
|
|
89
|
+
}
|
|
90
|
+
else if (!this.subscribers.has(message.playerId)) {
|
|
91
|
+
// Only forward to players that are actually subscribed to this streamer. Without this
|
|
92
|
+
// check a streamer could target any player on the server (resolved via the global player
|
|
93
|
+
// registry), allowing cross-tenant signalling on a shared signalling server.
|
|
94
|
+
Logger.warn(`Streamer ${this.streamerId} tried to forward a message to player ${message.playerId} which is not subscribed to it. Ignoring.`);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const player = this.server.playerRegistry.get(message.playerId);
|
|
98
|
+
if (player) {
|
|
99
|
+
LogUtils.logForward(this, player, message);
|
|
100
|
+
player.protocol.sendMessage(message);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
onTransportError(error) {
|
|
105
|
+
Logger.error(`Streamer (${this.streamerId}) transport error ${error.message}`);
|
|
106
|
+
}
|
|
107
|
+
onTransportClose() {
|
|
108
|
+
Logger.info(`Streamer '${this.streamerId}' - websocket disconnected.`);
|
|
109
|
+
this.emit('disconnect');
|
|
110
|
+
}
|
|
111
|
+
onEndpointId(_message) {
|
|
112
|
+
this.streaming = true; // we're ready to stream when we id ourselves
|
|
113
|
+
}
|
|
114
|
+
onDisconnectPlayerRequest(message) {
|
|
115
|
+
if (message.playerId) {
|
|
116
|
+
if (!this.subscribers.has(message.playerId)) {
|
|
117
|
+
// A streamer may only disconnect its own subscribed players. Otherwise any streamer
|
|
118
|
+
// could forcibly close arbitrary player connections on a shared signalling server.
|
|
119
|
+
Logger.warn(`Streamer ${this.streamerId} tried to disconnect player ${message.playerId} which is not subscribed to it. Ignoring.`);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const player = this.server.playerRegistry.get(message.playerId);
|
|
123
|
+
if (player) {
|
|
124
|
+
player.protocol.disconnect(1011, message.reason);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
onLayerPreference(message) {
|
|
129
|
+
this.emit('layer_preference', message);
|
|
130
|
+
}
|
|
131
|
+
onPingMessage(message) {
|
|
132
|
+
this.sendMessage(MessageHelpers.createMessage(Messages.pong, { time: message.time }));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=StreamerConnection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamerConnection.js","sourceRoot":"","sources":["../../src/StreamerConnection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
import { Messages, MessageHelpers, EventEmitter } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.8';
|
|
3
|
+
import { Logger } from './Logger';
|
|
4
|
+
/**
|
|
5
|
+
* Handles all the streamer connections of a signalling server and
|
|
6
|
+
* can be used to lookup connections by id etc.
|
|
7
|
+
* Fires events when streamers are added or removed.
|
|
8
|
+
* Events:
|
|
9
|
+
* 'added': (playerId: string) Player was added.
|
|
10
|
+
* 'removed': (playerId: string) Player was removed.
|
|
11
|
+
*/
|
|
12
|
+
export class StreamerRegistry extends EventEmitter {
|
|
13
|
+
constructor(authorizeStreamerId) {
|
|
14
|
+
super();
|
|
15
|
+
this.defaultStreamerIdPrefix = 'UnknownStreamer';
|
|
16
|
+
this.streamers = [];
|
|
17
|
+
this.authorizeStreamerId = authorizeStreamerId;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Adds a streamer to the registry. If the streamer already has an id
|
|
21
|
+
* it will be sanitized (checked against existing ids and altered if
|
|
22
|
+
* there are collisions), or if it has no id it will be assigned a
|
|
23
|
+
* default unique id.
|
|
24
|
+
* @returns True if the add was successful.
|
|
25
|
+
*/
|
|
26
|
+
add(streamer) {
|
|
27
|
+
streamer.streamerId = this.sanitizeStreamerId(streamer.streamerId);
|
|
28
|
+
if (this.find(streamer.streamerId)) {
|
|
29
|
+
Logger.error(`StreamerRegistry: Tried to register streamer ${streamer.streamerId} but that id already exists.`);
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
this.streamers.push(streamer);
|
|
33
|
+
// request that the new streamer id itself.
|
|
34
|
+
streamer.protocol.on(Messages.endpointId.typeName, this.onEndpointId.bind(this, streamer));
|
|
35
|
+
streamer.sendMessage(MessageHelpers.createMessage(Messages.identify));
|
|
36
|
+
this.emit('added', streamer.streamerId);
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Removes a streamer from the registry. If the streamer isn't found
|
|
41
|
+
* it does nothing.
|
|
42
|
+
* @returns True if the streamer was removed.
|
|
43
|
+
*/
|
|
44
|
+
remove(streamer) {
|
|
45
|
+
const index = this.streamers.indexOf(streamer);
|
|
46
|
+
if (index === -1) {
|
|
47
|
+
Logger.debug(`StreamerRegistry: Tried to remove streamer ${streamer.streamerId} but it doesn't exist`);
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
this.streamers.splice(index, 1);
|
|
51
|
+
this.emit('removed', streamer.streamerId);
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Attempts to find the given streamer id in the registry.
|
|
56
|
+
*/
|
|
57
|
+
find(streamerId) {
|
|
58
|
+
return this.streamers.find((streamer) => streamer.streamerId === streamerId);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Used by players who haven't subscribed but try to send a message.
|
|
62
|
+
* This is to cover legacy connections that do not know how to subscribe.
|
|
63
|
+
* The player will be assigned the first streamer in the list.
|
|
64
|
+
* @returns The first streamerId in the registry or null if there are none.
|
|
65
|
+
*/
|
|
66
|
+
getFirstStreamerId() {
|
|
67
|
+
if (this.empty()) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return this.streamers[0].streamerId;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Returns true when the registry is empty.
|
|
74
|
+
*/
|
|
75
|
+
empty() {
|
|
76
|
+
return this.streamers.length === 0;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns the total number of connected streamers.
|
|
80
|
+
*/
|
|
81
|
+
count() {
|
|
82
|
+
return this.streamers.length;
|
|
83
|
+
}
|
|
84
|
+
onEndpointId(streamer, message) {
|
|
85
|
+
const oldId = streamer.streamerId;
|
|
86
|
+
// id might conflict or be invalid so here we sanitize it
|
|
87
|
+
const sanitizedId = this.sanitizeStreamerId(message.id);
|
|
88
|
+
let committedId = sanitizedId;
|
|
89
|
+
if (this.authorizeStreamerId) {
|
|
90
|
+
// Let the consumer accept the default id, override it (e.g. namespace per tenant), or
|
|
91
|
+
// reject the registration entirely. This is where anti-squatting policy lives.
|
|
92
|
+
committedId = this.authorizeStreamerId({
|
|
93
|
+
streamer,
|
|
94
|
+
requestedId: message.id,
|
|
95
|
+
sanitizedId,
|
|
96
|
+
collided: !!message.id && sanitizedId !== message.id
|
|
97
|
+
});
|
|
98
|
+
if (committedId === null) {
|
|
99
|
+
Logger.warn(`StreamerRegistry: streamer id '${message.id}' (was ${oldId}) rejected by authorizer. Disconnecting.`);
|
|
100
|
+
this.remove(streamer);
|
|
101
|
+
streamer.protocol.disconnect(1008, 'streamer id not authorized');
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// An override must remain unique or it would corrupt id-based lookups. If the consumer
|
|
105
|
+
// hands back an id already held by a different streamer we reject rather than clobber.
|
|
106
|
+
const existing = this.find(committedId);
|
|
107
|
+
if (existing && existing !== streamer) {
|
|
108
|
+
Logger.error(`StreamerRegistry: authorizer returned id '${committedId}' which is already in use. Disconnecting.`);
|
|
109
|
+
this.remove(streamer);
|
|
110
|
+
streamer.protocol.disconnect(1008, 'streamer id not authorized');
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
streamer.streamerId = committedId;
|
|
115
|
+
Logger.debug(`StreamerRegistry: Streamer id change. ${oldId} -> ${streamer.streamerId}`);
|
|
116
|
+
streamer.emit('id_changed', streamer.streamerId);
|
|
117
|
+
// because we might have sanitized the id, we confirm the id back to the streamer
|
|
118
|
+
streamer.sendMessage(MessageHelpers.createMessage(Messages.endpointIdConfirm, { committedId: streamer.streamerId }));
|
|
119
|
+
}
|
|
120
|
+
sanitizeStreamerId(id) {
|
|
121
|
+
// create a default id if none supplied
|
|
122
|
+
if (!id) {
|
|
123
|
+
id = this.defaultStreamerIdPrefix;
|
|
124
|
+
}
|
|
125
|
+
// search for existing streamerId and optionally append a numeric value
|
|
126
|
+
let maxPostfix = -1;
|
|
127
|
+
for (const streamer of this.streamers) {
|
|
128
|
+
const idMatchRegex = /^(.*?)(\d*)$/;
|
|
129
|
+
const [, baseId, postfix] = streamer.streamerId.match(idMatchRegex);
|
|
130
|
+
// if the id is numeric then base id will be empty and we need to compare with the postfix
|
|
131
|
+
if ((baseId !== '' && baseId !== id) || (baseId === '' && postfix !== id)) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const numPostfix = Number(postfix);
|
|
135
|
+
if (numPostfix > maxPostfix) {
|
|
136
|
+
maxPostfix = numPostfix;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (maxPostfix >= 0) {
|
|
140
|
+
return id + (maxPostfix + 1);
|
|
141
|
+
}
|
|
142
|
+
return id;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=StreamerRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"StreamerRegistry.js","sourceRoot":"","sources":["../../src/StreamerRegistry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
import { jsonc } from 'jsonc';
|
|
3
|
+
/**
|
|
4
|
+
* Cirular reference safe version of JSON.stringify
|
|
5
|
+
*/
|
|
6
|
+
export function stringify(obj) {
|
|
7
|
+
return jsonc.stringify(obj);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Circular reference save version of JSON.stringify with extra formatting.
|
|
11
|
+
*/
|
|
12
|
+
export function beautify(obj) {
|
|
13
|
+
return jsonc.stringify(obj, undefined, '\t');
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=Utils.js.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import http from 'http';
|
|
6
|
+
import https from 'https';
|
|
7
|
+
import helmet from 'helmet';
|
|
8
|
+
import cors from 'cors';
|
|
9
|
+
import { Logger } from './Logger';
|
|
10
|
+
import RateLimit from 'express-rate-limit';
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
12
|
+
const hsts = require('hsts');
|
|
13
|
+
/**
|
|
14
|
+
* An object to manage the initialization of a web server. Used to serve the
|
|
15
|
+
* pixel streaming frontend.
|
|
16
|
+
*/
|
|
17
|
+
export class WebServer {
|
|
18
|
+
constructor(app, config) {
|
|
19
|
+
var _a;
|
|
20
|
+
Logger.debug('Starting WebServer with config: %s', config);
|
|
21
|
+
// only listen on the http port if we're not using https or if we want to redirect
|
|
22
|
+
if (!config.httpsPort || config.https_redirect) {
|
|
23
|
+
this.httpServer = http.createServer(app);
|
|
24
|
+
this.httpServer.listen(config.httpPort, () => {
|
|
25
|
+
Logger.info(`Http server listening on port ${config.httpPort}`);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/* eslint-disable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access */
|
|
29
|
+
// if using https listen on the given ports and setup some details
|
|
30
|
+
if (config.httpsPort) {
|
|
31
|
+
const options = { key: config.ssl_key, cert: config.ssl_cert };
|
|
32
|
+
this.httpsServer = https.createServer(options, app);
|
|
33
|
+
this.httpsServer.listen(config.httpsPort, () => {
|
|
34
|
+
Logger.info(`Https server listening on port ${config.httpsPort}`);
|
|
35
|
+
});
|
|
36
|
+
app.use(helmet({
|
|
37
|
+
contentSecurityPolicy: {
|
|
38
|
+
directives: {
|
|
39
|
+
'connect-src': ['*', "'self'"]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}));
|
|
43
|
+
app.use(hsts({
|
|
44
|
+
maxAge: 15552000 // 180 days in seconds
|
|
45
|
+
}));
|
|
46
|
+
// Setup http -> https redirect if requested
|
|
47
|
+
if (config.https_redirect) {
|
|
48
|
+
app.use((req, res, next) => {
|
|
49
|
+
if (!req.secure) {
|
|
50
|
+
if (req.get('Host')) {
|
|
51
|
+
const hostAddressParts = req.get('Host').split(':');
|
|
52
|
+
let hostAddress = hostAddressParts[0];
|
|
53
|
+
if (config.httpsPort != 443) {
|
|
54
|
+
hostAddress = `${hostAddress}:${config.httpsPort}`;
|
|
55
|
+
}
|
|
56
|
+
return res.redirect(['https://', hostAddress, req.originalUrl].join(''));
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
Logger.error(`Unable to get host name from header. Requestor ${req.ip}, url path: '${req.originalUrl}', available headers ${JSON.stringify(req.headers)}`);
|
|
60
|
+
return res.status(400).send('Bad Request');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
next();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if ((_a = config.cors) === null || _a === void 0 ? void 0 : _a.enabled) {
|
|
68
|
+
const corsOptions = {};
|
|
69
|
+
if (config.cors.allowedOrigins && config.cors.allowedOrigins.length > 0) {
|
|
70
|
+
corsOptions.origin = config.cors.allowedOrigins;
|
|
71
|
+
}
|
|
72
|
+
if (config.cors.allowedMethods && config.cors.allowedMethods.length > 0) {
|
|
73
|
+
corsOptions.methods = config.cors.allowedMethods;
|
|
74
|
+
}
|
|
75
|
+
if (config.cors.allowedHeaders && config.cors.allowedHeaders.length > 0) {
|
|
76
|
+
corsOptions.allowedHeaders = config.cors.allowedHeaders;
|
|
77
|
+
}
|
|
78
|
+
if (config.cors.credentials) {
|
|
79
|
+
corsOptions.credentials = true;
|
|
80
|
+
}
|
|
81
|
+
// Register cors before the rate limiter so that preflight (OPTIONS)
|
|
82
|
+
// requests are answered with CORS headers even when an origin is
|
|
83
|
+
// close to the rate limit, and before any route handlers so the
|
|
84
|
+
// homepage, static files, and the REST API all respond uniformly.
|
|
85
|
+
app.use(cors(corsOptions));
|
|
86
|
+
Logger.info(`CORS enabled. Allowed origins: ${corsOptions.origin ? JSON.stringify(corsOptions.origin) : '*'}`);
|
|
87
|
+
}
|
|
88
|
+
const limiter = RateLimit({
|
|
89
|
+
windowMs: 60 * 1000, // 1 minute
|
|
90
|
+
max: config.perMinuteRateLimit ? config.perMinuteRateLimit : 3000
|
|
91
|
+
});
|
|
92
|
+
// apply rate limiter to all requests. Registered before any route
|
|
93
|
+
// handler so that static files, the homepage route, and any routes
|
|
94
|
+
// registered on `app` by downstream code are all subject to it.
|
|
95
|
+
app.use(limiter);
|
|
96
|
+
if (config.serveStatic) {
|
|
97
|
+
app.use(express.static(config.root));
|
|
98
|
+
// Request has been sent to site root, send the homepage file
|
|
99
|
+
app.get('/', function (req, res) {
|
|
100
|
+
// Try a few paths, see if any resolve to a homepage file the user has set
|
|
101
|
+
const p = path.resolve(path.join(config.root, config.homepageFile));
|
|
102
|
+
if (fs.existsSync(p)) {
|
|
103
|
+
// Send the file for browser to display it
|
|
104
|
+
res.sendFile(p);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Catch file doesn't exist, and send back 404 if not
|
|
108
|
+
const error = 'Unable to locate file ' + config.homepageFile;
|
|
109
|
+
Logger.error(error);
|
|
110
|
+
res.status(404).send(error);
|
|
111
|
+
return;
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
/* eslint-enable @typescript-eslint/no-unsafe-call,@typescript-eslint/no-unsafe-member-access */
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=WebServer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"WebServer.js","sourceRoot":"","sources":["../../src/WebServer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
2
|
+
export * from './Logger';
|
|
3
|
+
export * from './LoggingUtils';
|
|
4
|
+
export * from './PlayerConnection';
|
|
5
|
+
export * from './PlayerRegistry';
|
|
6
|
+
export * from './SFUConnection';
|
|
7
|
+
export * from './SignallingServer';
|
|
8
|
+
export * from './StreamerConnection';
|
|
9
|
+
export * from './StreamerRegistry';
|
|
10
|
+
export * from './WebServer';
|
|
11
|
+
// keep Utils internal for now
|
|
12
|
+
//# sourceMappingURL=pixelstreamingsignalling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixelstreamingsignalling.js","sourceRoot":"","sources":["../../src/pixelstreamingsignalling.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,cAAc,UAAU,CAAC;AACzB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC;AAChC,cAAc,oBAAoB,CAAC;AACnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,8BAA8B"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import winston from 'winston';
|
|
2
|
+
import 'winston-daily-rotate-file';
|
|
3
|
+
/**
|
|
4
|
+
* The actual logger object. This is just a winston logger.
|
|
5
|
+
* You can use InitLogging to get a decent result, or you can
|
|
6
|
+
* completely create your own winston logger and assign it.
|
|
7
|
+
*/
|
|
8
|
+
export declare let Logger: winston.Logger;
|
|
9
|
+
export interface IConfig {
|
|
10
|
+
logDir?: string;
|
|
11
|
+
logMessagesToConsole?: string;
|
|
12
|
+
logLevelConsole?: string;
|
|
13
|
+
logLevelFile?: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Call this as early as possible to setup the logging module with your
|
|
17
|
+
* preferred settings.
|
|
18
|
+
* @param config - The settings to init the logger with. See IConfig interface
|
|
19
|
+
*/
|
|
20
|
+
export declare function InitLogging(config: IConfig): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BaseMessage } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.8';
|
|
2
|
+
export interface IProtoLogObj {
|
|
3
|
+
event: string;
|
|
4
|
+
direction: string;
|
|
5
|
+
sender?: string;
|
|
6
|
+
receiver?: string;
|
|
7
|
+
target?: string;
|
|
8
|
+
protoMessage: BaseMessage;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Most methods in here rely on connections implementing this interface so we can identify
|
|
12
|
+
* who is sending or receiving etc.
|
|
13
|
+
*/
|
|
14
|
+
export interface IMessageLogger {
|
|
15
|
+
getReadableIdentifier(): string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Call to log messages received on a connection that we will handle here at the server.
|
|
19
|
+
* Do not call this for messages being forwarded to another connection.
|
|
20
|
+
* @param recvr - IMessageLogger The connection the message was received on.
|
|
21
|
+
*/
|
|
22
|
+
export declare function logIncoming(recvr: IMessageLogger, message: BaseMessage): void;
|
|
23
|
+
/**
|
|
24
|
+
* Call to log messages created here at the server and being sent to the connection.
|
|
25
|
+
* Do not call this for messages being forwarded to this connection.
|
|
26
|
+
* @param sender - IMessageLogger The connection the message is being sent to.
|
|
27
|
+
*/
|
|
28
|
+
export declare function logOutgoing(sender: IMessageLogger, message: BaseMessage): void;
|
|
29
|
+
/**
|
|
30
|
+
* Call this for messages being forwarded to this connection. That is messages received on
|
|
31
|
+
* one connection and being sent to another with only minor changes being made.
|
|
32
|
+
* @param recvr - The connection the message was received on.
|
|
33
|
+
* @param target - The connection the message is being sent to.
|
|
34
|
+
*/
|
|
35
|
+
export declare function logForward(recvr: IMessageLogger, target: IMessageLogger, message: BaseMessage): void;
|
|
36
|
+
/**
|
|
37
|
+
* We don't want to log every incoming and outgoing messages. This is because some messages are simply
|
|
38
|
+
* forwarded to other connections. This results in duplicated spam. So we only want to log incoming
|
|
39
|
+
* messages that we handle internally, and any messages that we forward we only log once for the recv
|
|
40
|
+
* and send events.
|
|
41
|
+
* This creation method allows a simple way to enforce this. Any events we handle directly will
|
|
42
|
+
* be preceded by the logging call.
|
|
43
|
+
*/
|
|
44
|
+
export declare function createHandlerListener(obj: IMessageLogger, handler: (message: any) => void): (message: BaseMessage) => void;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { IncomingMessage } from 'http';
|
|
2
|
+
import WebSocket from 'ws';
|
|
3
|
+
import { ITransport, SignallingProtocol, BaseMessage } from '@epicgames-ps/lib-pixelstreamingcommon-ue5.8';
|
|
4
|
+
import { IPlayer, IPlayerInfo } from './PlayerRegistry';
|
|
5
|
+
import { IStreamer } from './StreamerRegistry';
|
|
6
|
+
import * as LogUtils from './LoggingUtils';
|
|
7
|
+
import { SignallingServer } from './SignallingServer';
|
|
8
|
+
/**
|
|
9
|
+
* A connection between the signalling server and a player connection.
|
|
10
|
+
* This is where messages expected to be handled by the player come in
|
|
11
|
+
* and where messages are sent to the player.
|
|
12
|
+
*
|
|
13
|
+
* Interesting internals:
|
|
14
|
+
* playerId: The unique id string of this player.
|
|
15
|
+
* transport: The ITransport where transport events can be subscribed to
|
|
16
|
+
* protocol: The SignallingProtocol where signalling messages can be
|
|
17
|
+
* subscribed to.
|
|
18
|
+
*/
|
|
19
|
+
export declare class PlayerConnection implements IPlayer, LogUtils.IMessageLogger {
|
|
20
|
+
playerId: string;
|
|
21
|
+
transport: ITransport;
|
|
22
|
+
protocol: SignallingProtocol;
|
|
23
|
+
subscribedStreamer: IStreamer | null;
|
|
24
|
+
remoteAddress?: string;
|
|
25
|
+
request?: IncomingMessage;
|
|
26
|
+
private server;
|
|
27
|
+
private streamerIdChangeListener;
|
|
28
|
+
private streamerDisconnectedListener;
|
|
29
|
+
/**
|
|
30
|
+
* Initializes a new connection with given and sane values. Adds listeners for the
|
|
31
|
+
* websocket close and error so it can react by unsubscribing and resetting itself.
|
|
32
|
+
* @param server - The signalling server object that spawned this player.
|
|
33
|
+
* @param ws - The websocket coupled to this player connection.
|
|
34
|
+
* @param remoteAddress - The remote address of this connection. Only used as display.
|
|
35
|
+
* @param request - The HTTP upgrade request that opened this connection, if available.
|
|
36
|
+
*/
|
|
37
|
+
constructor(server: SignallingServer, ws: WebSocket, remoteAddress?: string, request?: IncomingMessage);
|
|
38
|
+
/**
|
|
39
|
+
* Returns an identifier that is displayed in logs.
|
|
40
|
+
* @returns A string describing this connection.
|
|
41
|
+
*/
|
|
42
|
+
getReadableIdentifier(): string;
|
|
43
|
+
/**
|
|
44
|
+
* Sends a signalling message to the player.
|
|
45
|
+
* @param message - The message to send.
|
|
46
|
+
*/
|
|
47
|
+
sendMessage(message: BaseMessage): void;
|
|
48
|
+
/**
|
|
49
|
+
* Returns a descriptive object for the REST API inspection operations.
|
|
50
|
+
* @returns An IPlayerInfo object containing viewable information about this connection.
|
|
51
|
+
*/
|
|
52
|
+
getPlayerInfo(): IPlayerInfo;
|
|
53
|
+
private registerMessageHandlers;
|
|
54
|
+
private sendToStreamer;
|
|
55
|
+
private subscribe;
|
|
56
|
+
private unsubscribe;
|
|
57
|
+
private disconnect;
|
|
58
|
+
private onStreamerDisconnected;
|
|
59
|
+
private onTransportError;
|
|
60
|
+
private onTransportClose;
|
|
61
|
+
private onSubscribeMessage;
|
|
62
|
+
private onUnsubscribeMessage;
|
|
63
|
+
private onListStreamers;
|
|
64
|
+
private onStreamerIdChanged;
|
|
65
|
+
private onPingMessage;
|
|
66
|
+
}
|