Skip to content

Instantly share code, notes, and snippets.

@justgook
Created January 24, 2020 09:46
Show Gist options
  • Save justgook/c6694d16f383fbc6e34c22e2f386d035 to your computer and use it in GitHub Desktop.
Save justgook/c6694d16f383fbc6e34c22e2f386d035 to your computer and use it in GitHub Desktop.
/* Non-SSL is simply App() */
const rooms = new Map();
const { TextEncoder } = require("util");
const NewUser = 10;
const UserLeave = 11;
const RoomData = 12;
const YourAreMaster = 14;
const YourAreSlave = 15;
const uWebSockets = require('uWebSockets.js')
const app = uWebSockets.App({
/* There are tons of SSL options */
// key_file_name: 'misc/key.pem',
// cert_file_name: 'misc/cert.pem',
}).ws('/*', {
compression: 0,
maxPayloadLength: 16 * 1024 * 1024,
idleTimeout: 10 * 60 * 60, //Maximum amount of seconds that may pass without sending or getting a message.
open: (ws, req) => open(ws, new URL(`ws://placeholder${req.getUrl()}?${req.getQuery()}`)),
message: message,
drain: (ws) => {
console.log("drain?");
},
close: onclose
}).any('/*', (res, req) => {
res.end('Nothing to see here!');
}).listen(9001, (listenSocket) => {
if (listenSocket) {
console.log('Listening to port 9001');
}
});
function onclose(ws, code, message) {
const roomSet = rooms.get(ws.room);
const masterIterator = roomSet.values();
console.log("User leave", ws.room);
if (masterIterator.next().value === ws) {
const candidate = masterIterator.next().value;
if (candidate) {
console.log(`New master for room ${ws.room}`);
candidate.send(intStringString(YourAreMaster, ws.session, ws.room), true);
}
}
roomSet.delete(ws)
if (roomSet.size === 0) {
rooms.delete(ws.room);
console.log(`Room ${ws.room} destroyed`);
} else {
for (let value of roomSet.values()) {
value.send(intString(UserLeave, ws.session), true);
}
}
}
function open(socket, url) {
let roomUUID = url.pathname.replace("/", "");
let sessionId = url.searchParams.get("sessionId");
if (roomUUID && rooms.has(roomUUID)) {
socket.session = validateSessionId(sessionId, rooms.get(roomUUID).values());
socket.send(intStringString(YourAreSlave, socket.session, roomUUID), true);
console.log("New user in room", roomUUID);
} else {
roomUUID = roomUUID ? roomUUID : create_UUID();
rooms.set(roomUUID, new Set());
socket.session = validateSessionId(sessionId, new Set().values());
socket.send(intStringString(YourAreMaster, socket.session, roomUUID), true);
console.log("Room created", roomUUID);
}
socket.room = roomUUID;
const roomSet = rooms.get(roomUUID);
for (let client of roomSet.values()) {
client.send(intString(NewUser, socket.session), true);
}
roomSet.add(socket);
}
function validateSessionId(sessionId, room) {
let candidate = { done: false };
while (!candidate.done) {
candidate = room.next();
if (sessionId === candidate.session) {
sessionId = create_UUID();
candidate.done = true;
}
}
return sessionId || create_UUID();
}
function message(socket, message) {
console.log("brodcastOrSendToMaster", message);
const room = rooms.get(socket.room).values();
const master = room.next().value;
if (master == socket) {
for (let value of room) {
value.send(message, true);
}
} else {
master.send(message, true);
}
}
const intStringString =
(code, session, room) =>
concatBuffer(intString(code, session), sizedString(room));
const intString =
(code, session) =>
concatBuffer(unsignedInt32BE(code), sizedString(session));
//TODO Encoders - maybe find better way?
function unsignedInt32BE(data) {
const buf = Buffer.alloc(4);
buf.writeUInt32BE(data, 0);
return buf;
}
function sizedString(string) {
const encoder = new TextEncoder("utf-8");
const bytes = encoder.encode(string);
return concatBuffer(unsignedInt32BE(bytes.length), bytes);
}
//TODO HELPER FUNCTIONS - NEED TO FIND BETTER SOLUTION!!
function concatBuffer(buffer1, buffer2) {
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp.buffer;
};
// UUID generator from: https://gist.github.com/LeverOne/1308368
const create_UUID = function (a, b) {
for (b = a = ''; a++ < 36; b += a * 51 & 52 ? (a ^ 15 ? 8 ^ Math.random() * (a ^ 20 ? 16 : 4) : 4).toString(16) : '-') ;
return b
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment