This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'); | |
}); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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