networking
Version:
Library for typed, event-based networking between a server and clients.
158 lines (157 loc) • 5.69 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const errors_1 = require("./errors");
class AuthQueue {
constructor(socket) {
this.socket = socket;
this.resolutions = [];
this.packets = [];
this.terminated = false;
this.resolutionPromises = [];
this.packetPromises = [];
this.terminatedPromises = [];
socket.on('@net/auth', (packet) => this.handleAuthPacket(packet));
socket.on('@net/auth/res', (success) => this.handleResolutionPacket(success));
socket.on('@net/auth/term', () => this.handleTerminatePacket());
}
handleAuthPacket(packet) {
if (this.resolutionPromises.length) {
if (this.packetPromises.length) {
this.packetPromises.shift().reject(new errors_1.AuthError(`Authentication out of sequence (expected resolution, got a step)`));
}
return;
}
if (this.packetPromises.length) {
let promise = this.packetPromises.shift();
if (promise.type !== packet.type) {
return promise.reject(new errors_1.AuthError(`Authentication out of sequence (expected ${promise.type}, got ${packet.type})`));
}
promise.resolve(packet.payload);
}
else {
this.packets.push(packet);
}
}
handleResolutionPacket(success) {
if (this.resolutionPromises.length) {
let resolution = this.resolutionPromises.shift();
resolution.resolve(success);
}
else {
this.resolutions.push(success);
}
}
handleTerminatePacket() {
// Set the terminated flag to true
this.terminated = true;
// Reject all resolution promises
if (this.resolutionPromises.length) {
this.resolutionPromises.forEach(promise => {
promise.reject(new errors_1.AuthError('Authentication out of sequence (terminated early)'));
});
}
// Reject all packet promises
if (this.packetPromises.length) {
this.packetPromises.forEach(promise => {
promise.reject(new errors_1.AuthError('Authentication out of sequence (terminated early)'));
});
}
// Resolve all termination promises
if (this.terminatedPromises.length) {
this.terminatedPromises.forEach(resolve => {
resolve();
});
}
// Clear
this.resolutions = [];
this.packets = [];
this.packetPromises = [];
this.resolutionPromises = [];
this.terminatedPromises = [];
// Remove listeners
this.socket.removeAllListeners('@net/auth');
this.socket.removeAllListeners('@net/auth/res');
this.socket.removeAllListeners('@net/auth/term');
}
/**
* Resolves with the next authentication packet of the specified type. Throws an `AuthError` if we receive one of
* a different type, or if the received authentication sequence doesn't match.
*
* @param type
*/
getPacket(type) {
return new Promise((resolve, reject) => {
if (this.resolutions.length) {
return reject(new errors_1.AuthError(`Authentication out of sequence (expected ${type}, got a resolution)`));
}
if (this.terminated) {
return reject(new errors_1.AuthError(`Authentication out of sequence (authentication was terminated by the remote)`));
}
if (this.packets.length) {
let packet = this.packets.shift();
if (packet.type !== type) {
return reject(new errors_1.AuthError(`Authentication out of sequence (expected ${type}, got ${packet.type})`));
}
resolve(packet.payload);
}
else {
this.packetPromises.push({
type, resolve, reject
});
}
});
}
/**
* Resolves with the next available resolution signal as a boolean.
*/
getResolution() {
return new Promise((resolve, reject) => {
if (this.terminated) {
return reject(new errors_1.AuthError(`Authentication out of sequence (authentication was terminated by the remote)`));
}
if (this.resolutions.length) {
let resolution = this.resolutions.shift();
resolve(resolution);
}
else {
this.resolutionPromises.push({
resolve, reject
});
}
});
}
getTermination() {
return new Promise(resolve => {
if (this.terminated) {
return resolve();
}
this.terminatedPromises.push(resolve);
});
}
/**
* Sends an authentication packet to the other side.
*
* @param type
* @param payload
*/
sendPacket(type, payload) {
if (this.terminated) {
throw new errors_1.AuthError(`Cannot send packet (authentication was terminated by the remote)`);
}
this.socket.emit('@net/auth', {
type, payload
});
}
/**
* Sends an authentication packet to the other side.
*
* @param success
*/
sendResolution(success) {
if (this.terminated) {
throw new errors_1.AuthError(`Cannot send resolution (authentication was terminated by the remote)`);
}
this.socket.emit('@net/auth/res', success);
}
}
exports.AuthQueue = AuthQueue;