Skip to content

Instantly share code, notes, and snippets.

@ezekielchentnik
Last active March 26, 2020 19:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ezekielchentnik/e4dc1bddd336981734adaee65bce83d3 to your computer and use it in GitHub Desktop.
Save ezekielchentnik/e4dc1bddd336981734adaee65bce83d3 to your computer and use it in GitHub Desktop.
const app = require('express')();
const http = require('http').Server(app);
const io = require('./io.js')(http);
io.on('connection', socket =>{
console.log('someone connected');
socket.on('start', msg => {
io.emit('message', msg);
});
socket.on('disconnect', () => {
console.log('disconnected');
});
});
http.listen(3000, (){
console.log('listening on *:3000');
});
export default (url, options) => {
if (typeof url === "object") {
options = url || options;
url = options.url;
}
const parser = (options || {}).parser || JSON;
let connected = false;
const queue = [];
const listeners = {};
const parts = new URL(url || location.href);
const socket = new WebSocket(
[parts.protocol.replace("http", "ws"), "//", parts.host, "/io/"].join("")
);
const emit = (listeners, ...args) =>
(listeners || []).forEach(fn => fn.apply(io, args));
socket.onopen = () => {
io.emit("connect");
connected = true;
while (queue.length) {
socket.send(queue.shift());
}
};
socket.onmessage = event => {
let info = parser.parse(event.data);
let cbs = listeners[info.type];
if (info.type === "connect") {
io.id = info.data;
emit(cbs);
} else if (info.data) {
emit(cbs, info.data);
} else {
emit(cbs);
}
};
socket.onerror = error => {
emit(listeners.error, error);
};
socket.onclose = () => {
socket = null;
emit(listeners.close);
};
const io = {
close: () => {
if (socket) {
socket.close();
}
return io;
},
emit: (type, data) => {
let json = parser.stringify({
type: type,
data: data
});
if (connected) {
socket.send(json);
} else {
queue.push(json);
}
return io;
},
listeners: type => listeners[type] || [],
hasListeners: type => 0 < io.listeners(type).length,
on: (type, callback) => {
(listeners[type] || (listeners[type] = [])).push(callback);
return io;
},
once: (type, callback) => {
let current = listeners[type] || (listeners[type] = []);
current.push(function once() {
current.splice(current.indexOf(once), 1);
callback.apply(this, arguments);
});
return io;
},
off: (type, callback) => {
let current = listeners[type] || [];
let i = current.indexOf(callback);
if (-1 < i) current.splice(i, 1);
return io;
},
send: data => io.emit("message", data)
};
io.disconnect = io.close;
return io;
};
const http = require("http");
const path = require("path");
const uuid = require("uuid");
const EventEmitter = require("events").EventEmitter;
const WebSocket = require("ws");
const client = {
url: "/io/client.js",
js: require("fs").readFileSync(path.join(__dirname, "client.js"))
};
const responder = (req, res, next) => {
res.writeHead(200, "OK", {
"Content-Type": "application/javascript"
});
res.end(client.js);
if (next) next();
};
export default (app, options) => {
if (
arguments.length === 1 &&
Object.getPrototypeOf(app) === Object.prototype
) {
options = app;
app = options.server;
}
let server;
const parser = (options && options.parser) || JSON;
const format = (type, data) =>
parser.stringify({
type: type,
data: data
});
if (app instanceof http.Server) {
let request = app._events.request;
server = app;
app._events.request = req => {
return req.url === client.url
? responder.apply(this, arguments)
: request.apply(this, arguments);
};
} else {
server = http.Server(app);
app.get(client.url, responder);
Object.defineProperty(app, "listen", {
configurable: true,
value: function() {
server.listen.apply(server, arguments);
return app;
}
});
}
let emitters = new WeakMap();
let ws = new WebSocket.Server({ server: server });
let io = new EventEmitter();
let ioemit = io.emit.bind(io);
io.emit = (type, data) => {
ws.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(format(type, data));
}
});
};
ws.on("connection", socket => {
let emitter = new EventEmitter();
let emit = emitter.emit.bind(emitter);
socket.on("ping", data => emit("ping", data));
socket.on("pong", data => emit("pong", data));
socket.on("error", error => emit("error", error));
socket.on("close", () => emit("disconnect"));
socket.on("message", data => {
const info = parser.parse(data);
if (info.type === "connect") {
socket.send(format(info.type, (emitter.id = uuid.v4())));
}
emit(info.type, info.data);
});
emitter.emit = (type, data) => {
if (socket.readyState === WebSocket.OPEN) {
socket.send(format(type, data));
}
};
emitter.broadcast = {
emit: (type, data) =>
ws.clients.forEach(client => {
if (client !== socket && client.readyState === WebSocket.OPEN) {
client.send(data);
}
})
};
emitters.set(socket, emitter);
ioemit("connection", emitter);
});
ws.on("error", error => ioemit("error", error));
return io;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment