zerorpc
Version:
A port of ZeroRPC to node.js
163 lines (136 loc) • 5.13 kB
JavaScript
// Open Source Initiative OSI - The MIT License (MIT):Licensing
//
// The MIT License (MIT)
// Copyright (c) 2015 François-Xavier Bourlet (bombela+zerorpc@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
var nodeUtil = require("util"),
zmq = require("zeromq"),
nodeEvents = require("events"),
events = require("./events"),
util = require("./util"),
channel = require("./channel");
//The default channel capacity
var CHANNEL_CAPACITY = 100;
//Creates a new socket
//zmqSocket : Object
// The underlying ZeroMQ socket to use
function Socket(zmqSocket) {
var self = this;
self._zmqSocket = zmqSocket;
util.eventProxy(self._zmqSocket, self, "error");
var error = function(message) {
self.emit("error", message);
};
self._zmqSocket.on("message", function() {
//Deserialize the object and perform some sanity checks
if(arguments[arguments.length - 2].length != 0) {
return error("Expected second to last argument to be an empty buffer, but it is not");
}
var envelope = Array.prototype.slice.call(arguments, 0, arguments.length - 2);
try {
var event = events.deserialize(envelope, arguments[arguments.length - 1]);
} catch(e) {
return error("Invalid event: " + e);
}
//Emit the event
self.emit("socket/receive", event);
});
}
nodeUtil.inherits(Socket, nodeEvents.EventEmitter);
//Sends a message on the socket
//event : Object
// The ZeroRPC event to send
Socket.prototype.send = function(event) {
var message = events.serialize(event);
this._zmqSocket.send.call(this._zmqSocket, message);
};
//Binds to a ZeroMQ endpoint
//endpoint : String
// The ZeroMQ endpoint
Socket.prototype.bind = function(endpoint) {
this._zmqSocket.bindSync(endpoint);
}
//Connects to a ZeroMQ endpoint
//endpoint : String
// The ZeroMQ endpoint
Socket.prototype.connect = function(endpoint) {
this._zmqSocket.connect(endpoint);
}
//Creates a new multiplexing socket
//zmqSocket : Object
// The underlying ZeroMQ socket to use
function MultiplexingSocket(zmqSocket, heartbeat) {
Socket.call(this, zmqSocket);
var self = this;
//Map of open channel IDs => channel objects
self.channels = {};
self._heartbeatInterval = heartbeat;
self._closed = false;
//Route events to a channel if possible; otherwise emit the event
self.on("socket/receive", function(event) {
var ch = self.channels[event.header.response_to || ""];
if(ch) {
ch.invoke(event);
} else {
self.emit("multiplexing-socket/receive", event);
}
});
}
nodeUtil.inherits(MultiplexingSocket, Socket);
//Opens a new channel
//srcEvent : Object or null
// The ZeroRPC event that caused the channel to be opened, or null if
// this is a locally opened channel.
MultiplexingSocket.prototype.openChannel = function(srcEvent) {
if(srcEvent) {
var ch = new channel.ServerChannel(srcEvent, this, CHANNEL_CAPACITY, this._heartbeatInterval);
} else {
var ch = new channel.ClientChannel(this, CHANNEL_CAPACITY, this._heartbeatInterval);
}
this.channels[ch.id] = ch;
return ch;
};
//Closes the socket
MultiplexingSocket.prototype.close = function(linger) {
if (this.closed()) {
throw Error("Socket already closed");
}
if (linger !== undefined) {
this._zmqSocket.setsockopt(zmq.options.linger, linger);
}
this._zmqSocket.close();
for(var id in this.channels) this.channels[id].destroy();
this._closed = true;
};
MultiplexingSocket.prototype.closed = function() {
return this._closed;
};
MultiplexingSocket.prototype.setTimeout = function(timeout) {
};
//Creates a new multiplexing socket server
function server(heartbeat) {
return new MultiplexingSocket(zmq.socket("xrep"), heartbeat);
}
//Creates a new multiplexing socket client
function client(heartbeat) {
return new MultiplexingSocket(zmq.socket("xreq"), heartbeat);
}
exports.server = server;
exports.client = client;