2016-03-23 16:23:10 +01:00
|
|
|
'use strict'
|
|
|
|
|
2016-04-25 02:21:32 +01:00
|
|
|
const debug = require('debug')
|
|
|
|
const log = debug('libp2p:websockets')
|
2016-06-19 06:35:31 +01:00
|
|
|
const SW = require('simple-websocket')
|
|
|
|
const isNode = require('detect-node')
|
|
|
|
let SWS
|
|
|
|
if (isNode) {
|
|
|
|
SWS = require('simple-websocket-server')
|
|
|
|
} else {
|
|
|
|
SWS = {}
|
|
|
|
}
|
2016-03-14 21:19:42 +00:00
|
|
|
const mafmt = require('mafmt')
|
2016-05-29 09:00:26 +01:00
|
|
|
const contains = require('lodash.contains')
|
2016-06-19 06:35:31 +01:00
|
|
|
const Connection = require('interface-connection').Connection
|
|
|
|
|
|
|
|
const CLOSE_TIMEOUT = 2000
|
|
|
|
// const IPFS_CODE = 421
|
2016-02-26 13:18:05 +00:00
|
|
|
|
2016-03-14 20:25:00 +00:00
|
|
|
exports = module.exports = WebSockets
|
2016-02-26 13:18:05 +00:00
|
|
|
|
2016-03-14 20:25:00 +00:00
|
|
|
function WebSockets () {
|
|
|
|
if (!(this instanceof WebSockets)) {
|
|
|
|
return new WebSockets()
|
|
|
|
}
|
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
this.dial = function (ma, options, callback) {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
callback = options
|
2016-03-14 20:25:00 +00:00
|
|
|
options = {}
|
|
|
|
}
|
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
if (!callback) {
|
|
|
|
callback = function noop () {}
|
2016-03-14 20:25:00 +00:00
|
|
|
}
|
2016-06-19 06:35:31 +01:00
|
|
|
|
|
|
|
const maOpts = ma.toOptions()
|
|
|
|
|
|
|
|
const socket = new SW('ws://' + maOpts.host + ':' + maOpts.port)
|
|
|
|
|
|
|
|
const conn = new Connection(socket)
|
|
|
|
|
|
|
|
socket.on('timeout', () => {
|
|
|
|
conn.emit('timeout')
|
|
|
|
})
|
|
|
|
|
|
|
|
socket.on('error', (err) => {
|
|
|
|
callback(err)
|
|
|
|
conn.emit('error', err)
|
|
|
|
})
|
|
|
|
|
|
|
|
socket.on('connect', () => {
|
|
|
|
callback(null, conn)
|
|
|
|
conn.emit('connect')
|
|
|
|
})
|
|
|
|
|
|
|
|
conn.getObservedAddrs = (cb) => {
|
|
|
|
return cb(null, [ma])
|
|
|
|
}
|
|
|
|
|
2016-03-14 20:25:00 +00:00
|
|
|
return conn
|
|
|
|
}
|
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
this.createListener = (options, handler) => {
|
2016-03-14 20:25:00 +00:00
|
|
|
if (typeof options === 'function') {
|
|
|
|
handler = options
|
|
|
|
options = {}
|
|
|
|
}
|
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
const listener = SWS.createServer((socket) => {
|
|
|
|
const conn = new Connection(socket)
|
2016-02-26 13:18:05 +00:00
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
conn.getObservedAddrs = (cb) => {
|
|
|
|
// TODO research if we can reuse the address in anyway
|
|
|
|
return cb(null, [])
|
|
|
|
}
|
|
|
|
handler(conn)
|
|
|
|
})
|
|
|
|
|
|
|
|
let listeningMultiaddr
|
2016-03-14 20:25:00 +00:00
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
listener._listen = listener.listen
|
|
|
|
listener.listen = (ma, callback) => {
|
|
|
|
if (!callback) {
|
|
|
|
callback = function noop () {}
|
2016-05-29 09:00:26 +01:00
|
|
|
}
|
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
listeningMultiaddr = ma
|
|
|
|
|
|
|
|
if (contains(ma.protoNames(), 'ipfs')) {
|
|
|
|
ma = ma.decapsulate('ipfs')
|
|
|
|
}
|
2016-03-14 20:25:00 +00:00
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
listener._listen(ma.toOptions(), callback)
|
|
|
|
}
|
|
|
|
|
|
|
|
listener._close = listener.close
|
|
|
|
listener.close = (options, callback) => {
|
|
|
|
if (typeof options === 'function') {
|
|
|
|
callback = options
|
|
|
|
options = { timeout: CLOSE_TIMEOUT }
|
|
|
|
}
|
|
|
|
if (!callback) { callback = function noop () {} }
|
|
|
|
if (!options) { options = { timeout: CLOSE_TIMEOUT } }
|
|
|
|
|
|
|
|
let closed = false
|
|
|
|
listener.once('close', () => {
|
|
|
|
closed = true
|
|
|
|
})
|
|
|
|
listener._close(callback)
|
|
|
|
setTimeout(() => {
|
|
|
|
if (closed) {
|
|
|
|
return
|
2016-03-14 20:25:00 +00:00
|
|
|
}
|
2016-06-19 06:35:31 +01:00
|
|
|
log('unable to close graciously, destroying conns')
|
|
|
|
Object.keys(listener.__connections).forEach((key) => {
|
|
|
|
log('destroying %s', key)
|
|
|
|
listener.__connections[key].destroy()
|
|
|
|
})
|
|
|
|
}, options.timeout || CLOSE_TIMEOUT)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Keep track of open connections to destroy in case of timeout
|
|
|
|
listener.__connections = {}
|
|
|
|
listener.on('connection', (socket) => {
|
|
|
|
const key = (~~(Math.random() * 1e9)).toString(36) + Date.now()
|
|
|
|
listener.__connections[key] = socket
|
|
|
|
|
|
|
|
socket.on('close', () => {
|
|
|
|
delete listener.__connections[key]
|
2016-03-14 20:25:00 +00:00
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
listener.getAddrs = (callback) => {
|
|
|
|
callback(null, [listeningMultiaddr])
|
2016-03-14 20:25:00 +00:00
|
|
|
}
|
2016-05-08 22:58:38 +02:00
|
|
|
|
2016-06-19 06:35:31 +01:00
|
|
|
return listener
|
2016-03-14 20:25:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
this.filter = (multiaddrs) => {
|
2016-05-29 09:00:26 +01:00
|
|
|
if (!Array.isArray(multiaddrs)) {
|
|
|
|
multiaddrs = [multiaddrs]
|
|
|
|
}
|
2016-03-14 20:25:00 +00:00
|
|
|
return multiaddrs.filter((ma) => {
|
2016-05-29 09:00:26 +01:00
|
|
|
if (contains(ma.protoNames(), 'ipfs')) {
|
|
|
|
ma = ma.decapsulate('ipfs')
|
|
|
|
}
|
2016-03-14 21:19:42 +00:00
|
|
|
return mafmt.WebSockets.matches(ma)
|
2016-03-14 20:25:00 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|