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 } }