var defineProperty_IE8 = false; if (Object.defineProperty) { try { Object.defineProperty({}, "x", {}); } catch (e) { defineProperty_IE8 = true; } } if (!Function.prototype.bind) { Function.prototype.bind = function (oThis) { if (typeof this !== 'function') { throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable'); } var aArgs = Array.prototype.slice.call(arguments, 1), fToBind = this, fNOP = function () { }, fBound = function () { return fToBind.apply(this instanceof fNOP && oThis ? this : oThis, aArgs.concat(Array.prototype.slice.call(arguments))); }; fNOP.prototype = this.prototype; fBound.prototype = new fNOP(); return fBound; }; } var EventEmitter = require('events').EventEmitter; var inherits = require('inherits'); var packers = require('./packers'); var Mapper = require('./Mapper'); var BASE_TIMEOUT = 5000; function unifyResponseMethods(responseMethods) { if (!responseMethods) return {}; for (var key in responseMethods) { var value = responseMethods[key]; if (typeof value == 'string') responseMethods[key] = { response: value }; } ; return responseMethods; } ; function unifyTransport(transport) { if (!transport) return; if (transport instanceof Function) return { send: transport }; if (transport.send instanceof Function) return transport; if (transport.postMessage instanceof Function) { transport.send = transport.postMessage; return transport; } if (transport.write instanceof Function) { transport.send = transport.write; return transport; } if (transport.onmessage !== undefined) return; if (transport.pause instanceof Function) return; throw new SyntaxError("Transport is not a function nor a valid object"); } ; function RpcNotification(method, params) { if (defineProperty_IE8) { this.method = method; this.params = params; } else { Object.defineProperty(this, 'method', { value: method, enumerable: true }); Object.defineProperty(this, 'params', { value: params, enumerable: true }); } } ; function RpcBuilder(packer, options, transport, onRequest) { var self = this; if (!packer) throw new SyntaxError('Packer is not defined'); if (!packer.pack || !packer.unpack) throw new SyntaxError('Packer is invalid'); var responseMethods = unifyResponseMethods(packer.responseMethods); if (options instanceof Function) { if (transport != undefined) throw new SyntaxError("There can't be parameters after onRequest"); onRequest = options; transport = undefined; options = undefined; } ; if (options && options.send instanceof Function) { if (transport && !(transport instanceof Function)) throw new SyntaxError("Only a function can be after transport"); onRequest = transport; transport = options; options = undefined; } ; if (transport instanceof Function) { if (onRequest != undefined) throw new SyntaxError("There can't be parameters after onRequest"); onRequest = transport; transport = undefined; } ; if (transport && transport.send instanceof Function) if (onRequest && !(onRequest instanceof Function)) throw new SyntaxError("Only a function can be after transport"); options = options || {}; EventEmitter.call(this); if (onRequest) this.on('request', onRequest); if (defineProperty_IE8) this.peerID = options.peerID; else Object.defineProperty(this, 'peerID', { value: options.peerID }); var max_retries = options.max_retries || 0; function transportMessage(event) { self.decode(event.data || event); } ; this.getTransport = function () { return transport; }; this.setTransport = function (value) { if (transport) { if (transport.removeEventListener) transport.removeEventListener('message', transportMessage); else if (transport.removeListener) transport.removeListener('data', transportMessage); } ; if (value) { if (value.addEventListener) value.addEventListener('message', transportMessage); else if (value.addListener) value.addListener('data', transportMessage); } ; transport = unifyTransport(value); }; if (!defineProperty_IE8) Object.defineProperty(this, 'transport', { get: this.getTransport.bind(this), set: this.setTransport.bind(this) }); this.setTransport(transport); var request_timeout = options.request_timeout || BASE_TIMEOUT; var ping_request_timeout = options.ping_request_timeout || request_timeout; var response_timeout = options.response_timeout || BASE_TIMEOUT; var duplicates_timeout = options.duplicates_timeout || BASE_TIMEOUT; var requestID = 0; var requests = new Mapper(); var responses = new Mapper(); var processedResponses = new Mapper(); var message2Key = {}; function storeResponse(message, id, dest) { var response = { message: message, timeout: setTimeout(function () { responses.remove(id, dest); }, response_timeout) }; responses.set(response, id, dest); } ; function storeProcessedResponse(ack, from) { var timeout = setTimeout(function () { processedResponses.remove(ack, from); }, duplicates_timeout); processedResponses.set(timeout, ack, from); } ; function RpcRequest(method, params, id, from, transport) { RpcNotification.call(this, method, params); this.getTransport = function () { return transport; }; this.setTransport = function (value) { transport = unifyTransport(value); }; if (!defineProperty_IE8) Object.defineProperty(this, 'transport', { get: this.getTransport.bind(this), set: this.setTransport.bind(this) }); var response = responses.get(id, from); if (!(transport || self.getTransport())) { if (defineProperty_IE8) this.duplicated = Boolean(response); else Object.defineProperty(this, 'duplicated', { value: Boolean(response) }); } var responseMethod = responseMethods[method]; this.pack = packer.pack.bind(packer, this, id); this.reply = function (error, result, transport) { if (error instanceof Function || error && error.send instanceof Function) { if (result != undefined) throw new SyntaxError("There can't be parameters after callback"); transport = error; result = null; error = undefined; } else if (result instanceof Function || result && result.send instanceof Function) { if (transport != undefined) throw new SyntaxError("There can't be parameters after callback"); transport = result; result = null; } ; transport = unifyTransport(transport); if (response) clearTimeout(response.timeout); if (from != undefined) { if (error) error.dest = from; if (result) result.dest = from; } ; var message; if (error || result != undefined) { if (self.peerID != undefined) { if (error) error.from = self.peerID; else result.from = self.peerID; } if (responseMethod) { if (responseMethod.error == undefined && error) message = { error: error }; else { var method = error ? responseMethod.error : responseMethod.response; message = { method: method, params: error || result }; } } else message = { error: error, result: result }; message = packer.pack(message, id); } else if (response) message = response.message; else message = packer.pack({ result: null }, id); storeResponse(message, id, from); transport = transport || this.getTransport() || self.getTransport(); if (transport) return transport.send(message); return message; }; } ; inherits(RpcRequest, RpcNotification); function cancel(message) { var key = message2Key[message]; if (!key) return; delete message2Key[message]; var request = requests.pop(key.id, key.dest); if (!request) return; clearTimeout(request.timeout); storeProcessedResponse(key.id, key.dest); } ; this.cancel = function (message) { if (message) return cancel(message); for (var message in message2Key) cancel(message); }; this.close = function () { var transport = this.getTransport(); if (transport && transport.close) transport.close(); this.cancel(); processedResponses.forEach(clearTimeout); responses.forEach(function (response) { clearTimeout(response.timeout); }); }; this.encode = function (method, params, dest, transport, callback) { if (params instanceof Function) { if (dest != undefined) throw new SyntaxError("There can't be parameters after callback"); callback = params; transport = undefined; dest = undefined; params = undefined; } else if (dest instanceof Function) { if (transport != undefined) throw new SyntaxError("There can't be parameters after callback"); callback = dest; transport = undefined; dest = undefined; } else if (transport instanceof Function) { if (callback != undefined) throw new SyntaxError("There can't be parameters after callback"); callback = transport; transport = undefined; } ; if (self.peerID != undefined) { params = params || {}; params.from = self.peerID; } ; if (dest != undefined) { params = params || {}; params.dest = dest; } ; var message = { method: method, params: params }; if (callback) { var id = requestID++; var retried = 0; message = packer.pack(message, id); function dispatchCallback(error, result) { self.cancel(message); callback(error, result); } ; var request = { message: message, callback: dispatchCallback, responseMethods: responseMethods[method] || {} }; var encode_transport = unifyTransport(transport); function sendRequest(transport) { var rt = (method === 'ping' ? ping_request_timeout : request_timeout); request.timeout = setTimeout(timeout, rt * Math.pow(2, retried++)); message2Key[message] = { id: id, dest: dest }; requests.set(request, id, dest); transport = transport || encode_transport || self.getTransport(); if (transport) return transport.send(message); return message; } ; function retry(transport) { transport = unifyTransport(transport); console.warn(retried + ' retry for request message:', message); var timeout = processedResponses.pop(id, dest); clearTimeout(timeout); return sendRequest(transport); } ; function timeout() { if (retried < max_retries) return retry(transport); var error = new Error('Request has timed out'); error.request = message; error.retry = retry; dispatchCallback(error); } ; return sendRequest(transport); } ; message = packer.pack(message); transport = transport || this.getTransport(); if (transport) return transport.send(message); return message; }; this.decode = function (message, transport) { if (!message) throw new TypeError("Message is not defined"); try { message = packer.unpack(message); } catch (e) { return console.debug(e, message); } ; var id = message.id; var ack = message.ack; var method = message.method; var params = message.params || {}; var from = params.from; var dest = params.dest; if (self.peerID != undefined && from == self.peerID) return; if (id == undefined && ack == undefined) { var notification = new RpcNotification(method, params); if (self.emit('request', notification)) return; return notification; } ; function processRequest() { transport = unifyTransport(transport) || self.getTransport(); if (transport) { var response = responses.get(id, from); if (response) return transport.send(response.message); } ; var idAck = (id != undefined) ? id : ack; var request = new RpcRequest(method, params, idAck, from, transport); if (self.emit('request', request)) return; return request; } ; function processResponse(request, error, result) { request.callback(error, result); } ; function duplicatedResponse(timeout) { console.warn("Response already processed", message); clearTimeout(timeout); storeProcessedResponse(ack, from); } ; if (method) { if (dest == undefined || dest == self.peerID) { var request = requests.get(ack, from); if (request) { var responseMethods = request.responseMethods; if (method == responseMethods.error) return processResponse(request, params); if (method == responseMethods.response) return processResponse(request, null, params); return processRequest(); } var processed = processedResponses.get(ack, from); if (processed) return duplicatedResponse(processed); } return processRequest(); } ; var error = message.error; var result = message.result; if (error && error.dest && error.dest != self.peerID) return; if (result && result.dest && result.dest != self.peerID) return; var request = requests.get(ack, from); if (!request) { var processed = processedResponses.get(ack, from); if (processed) return duplicatedResponse(processed); return console.warn("No callback was defined for this message", message); } ; processResponse(request, error, result); }; } ; inherits(RpcBuilder, EventEmitter); RpcBuilder.RpcNotification = RpcNotification; module.exports = RpcBuilder; var clients = require('./clients'); var transports = require('./clients/transports'); RpcBuilder.clients = clients; RpcBuilder.clients.transports = transports; RpcBuilder.packers = packers; //# sourceMappingURL=index.js.map