@geckos.io/server
Version:
Real-time client/server communication over UDP using WebRTC and Node.js
126 lines • 5.26 kB
JavaScript
import { createDataChannel } from './nodeDataChannel.js';
import { pause, promiseWithTimeout } from '../deps.js';
import CreateDataChannel from '../geckos/channel.js';
import WebRTCConnection from './webrtcConnection.js';
import { makeRandomId } from '../deps.js';
export default class ConnectionsManagerServer {
constructor(options) {
this.options = options;
this.connections = new Map();
}
createId() {
let id = makeRandomId(24);
while (this.connections.has(id))
id = makeRandomId(24);
return id;
}
getConnection(id) {
return this.connections.get(id);
}
getConnections() {
return this.connections;
}
async getUserData(authorization, request, response) {
var _a;
// check authorization and get userData
let userData = {};
if ((_a = this.options) === null || _a === void 0 ? void 0 : _a.authorization) {
if (typeof this.options.authorization !== 'function') {
console.log('[warning] Authorization is not a function!?');
return { _statusCode: 500 };
}
const res = await this.options.authorization(authorization, request, response);
if (typeof res === 'boolean' && res)
userData = {};
else if (typeof res === 'boolean' && !res)
return { _statusCode: 401 };
else if (typeof res === 'number' && res >= 100 && res < 600)
return { _statusCode: res };
else
userData = res;
}
return userData;
}
async createConnection(authorization, request, response) {
// get userData
const userData = await this.getUserData(authorization, request, response);
if (userData._statusCode)
return { userData, status: userData._statusCode };
const newId = this.createId();
const { ordered = false, label = 'geckos.io', iceServers = [], portRange, iceTransportPolicy = 'all', bindAddress = undefined, maxPacketLifeTime = undefined, maxRetransmits = 0, multiplex = true } = this.options;
// DataChannel configuration
const dc_config = {
maxPacketLifeTime,
maxRetransmits,
unordered: !ordered
};
// WebRTCConnection configuration
let rtc_config = {
// sdpSemantics: 'unified-plan',
iceTransportPolicy: iceTransportPolicy,
iceServers: iceServers.map((ice) => ice.urls),
bindAddress: bindAddress,
enableIceUdpMux: multiplex
};
// portRange is a nonstandard API
if ((portRange === null || portRange === void 0 ? void 0 : portRange.min) && (portRange === null || portRange === void 0 ? void 0 : portRange.max)) {
portRange.min = Math.max(portRange.min, 1025);
portRange.max = Math.min(portRange.max, 65535);
rtc_config = { ...rtc_config, portRangeBegin: portRange.min, portRangeEnd: portRange.max };
}
// create the webrtc connection
const connection = new WebRTCConnection(newId, rtc_config, this.connections, userData);
const pc = await connection.init();
if (!pc)
return { status: 500 };
pc.onStateChange(async (state) => {
// keep track of the maxMessageSize
if (state === 'connected')
connection.channel.maxMessageSize = +connection.channel.dataChannel.maxMessageSize();
if (state === 'disconnected' || state === 'failed' || state === 'closed') {
await this.deleteConnection(connection, state);
}
});
let gatheringState;
let localDescription;
const candidates = [];
pc.onDataChannel((dc) => {
// TODO(yandeu) This does not work :/
console.log('Peer1 Got DataChannel: ', dc.getLabel());
});
pc.onGatheringStateChange((state) => {
gatheringState = state;
});
pc.onLocalDescription((sdp, type) => {
localDescription = { sdp, type };
});
pc.onLocalCandidate((candidate, mid) => {
// @ts-ignore
connection.additionalCandidates.push({ candidate, sdpMid: mid });
candidates.push({ candidate, mid });
});
const dc = await promiseWithTimeout(createDataChannel(pc, label, dc_config), 2000); // pc.createDataChannel(label, dc_config)
connection.channel = new CreateDataChannel(connection, dc, this.options, userData);
let waitForLocalDescription = 0;
while (typeof localDescription === 'undefined' && waitForLocalDescription < 20) {
waitForLocalDescription++;
await pause(50);
}
const { id } = connection;
if (!id)
return { status: 500 };
this.connections.set(id, connection);
return {
connection: {
id,
localDescription
},
userData,
status: 200
};
}
async deleteConnection(connection, state) {
await connection.close(state);
}
}
//# sourceMappingURL=connectionsManager.js.map