134 lines
3.5 KiB
JavaScript
134 lines
3.5 KiB
JavaScript
var EventEmitter = require('events').EventEmitter
|
|
var backoff = require('backoff')
|
|
|
|
module.exports =
|
|
function (createConnection) {
|
|
return function (opts, onConnect) {
|
|
onConnect = 'function' == typeof opts ? opts : onConnect
|
|
opts = 'object' == typeof opts ? opts : {initialDelay: 1e3, maxDelay: 30e3}
|
|
if(!onConnect)
|
|
onConnect = opts.onConnect
|
|
|
|
var emitter = new EventEmitter()
|
|
emitter.connected = false
|
|
emitter.reconnect = true
|
|
|
|
if(onConnect)
|
|
//use "connection" to match core (net) api.
|
|
emitter.on('connection', onConnect)
|
|
|
|
var backoffStrategy = opts.strategy || opts.type
|
|
var backoffMethod
|
|
if (typeof backoffStrategy == 'string')
|
|
backoffMethod = backoff[backoffStrategy](opts)
|
|
else
|
|
backoffMethod = backoffStrategy || backoff.fibonacci(opts)
|
|
|
|
if(opts.failAfter)
|
|
backoffMethod.failAfter(opts.failAfter);
|
|
|
|
backoffMethod.on('backoff', function (n, d, e) {
|
|
emitter.emit('backoff', n, d, e)
|
|
})
|
|
backoffMethod.on('fail', function (e) {
|
|
emitter.disconnect()
|
|
emitter.emit('fail', e)
|
|
})
|
|
|
|
var args
|
|
function attempt (n, delay) {
|
|
if(emitter.connected) return
|
|
if(!emitter.reconnect) return
|
|
|
|
emitter.emit('reconnect', n, delay)
|
|
var con = createConnection.apply(emitter, args)
|
|
emitter._connection = con
|
|
|
|
function onError (err) {
|
|
con.removeListener('error', onError)
|
|
emitter.emit('error', err)
|
|
onDisconnect(err)
|
|
}
|
|
|
|
function onDisconnect (err) {
|
|
emitter.connected = false
|
|
con.removeListener('close', onDisconnect)
|
|
con.removeListener('end' , onDisconnect)
|
|
|
|
//hack to make http not crash.
|
|
//HTTP IS THE WORST PROTOCOL.
|
|
if(con.constructor.name == 'Request')
|
|
con.on('error', function () {})
|
|
|
|
//emit disconnect before checking reconnect, so user has a chance to decide not to.
|
|
emitter.emit('disconnect', err)
|
|
|
|
if(!emitter.reconnect) return
|
|
try { backoffMethod.backoff() } catch (_) { }
|
|
}
|
|
|
|
con
|
|
.on('error', onError)
|
|
.on('close', onDisconnect)
|
|
.on('end' , onDisconnect)
|
|
|
|
if(opts.immediate || con.constructor.name == 'Request') {
|
|
emitter.connected = true
|
|
emitter.emit('connect', con)
|
|
emitter.emit('connection', con)
|
|
con.once('data', function () {
|
|
//this is the only way to know for sure that data is coming...
|
|
backoffMethod.reset()
|
|
})
|
|
} else {
|
|
con
|
|
.once('connect', function () {
|
|
backoffMethod.reset()
|
|
emitter.connected = true
|
|
if(onConnect)
|
|
con.removeListener('connect', onConnect)
|
|
emitter.emit('connect', con)
|
|
//also support net style 'connection' method.
|
|
emitter.emit('connection', con)
|
|
})
|
|
}
|
|
}
|
|
|
|
emitter.connect =
|
|
emitter.listen = function () {
|
|
this.reconnect = true
|
|
if(emitter.connected) return
|
|
backoffMethod.reset()
|
|
backoffMethod.on('ready', attempt)
|
|
if (!args) {
|
|
var len = arguments.length;
|
|
args = new Array(len);
|
|
for (var i = 0; i < len; i++) {
|
|
args[i] = arguments[i];
|
|
}
|
|
}
|
|
attempt(0, 0)
|
|
return emitter
|
|
}
|
|
|
|
//force reconnection
|
|
|
|
emitter.disconnect = function () {
|
|
this.reconnect = false
|
|
|
|
if(emitter._connection)
|
|
emitter._connection.end()
|
|
|
|
return emitter
|
|
}
|
|
|
|
emitter.reset = function () {
|
|
backoffMethod.reset()
|
|
attempt(0, 0)
|
|
}
|
|
|
|
return emitter
|
|
}
|
|
|
|
}
|