1 
  2 /**
  3  * Module dependencies.
  4  */
  5 
  6 var EventEmitter = require('events').EventEmitter
  7   , IOWatcher = process.binding('io_watcher').IOWatcher
  8   , zmq = require('../binding');
  9 
 10 /**
 11  * Expose bindings as the module.
 12  */
 13 
 14 exports = module.exports = zmq;
 15 
 16 /**
 17  * Map of socket types.
 18  */
 19 
 20 var types = exports.types = {
 21     pub: zmq.ZMQ_PUB
 22   , sub: zmq.ZMQ_SUB
 23   , req: zmq.ZMQ_REQ
 24   , xreq: zmq.ZMQ_XREQ
 25   , rep: zmq.ZMQ_REP
 26   , xrep: zmq.ZMQ_XREP
 27   , push: zmq.ZMQ_PUSH
 28   , pull: zmq.ZMQ_PULL
 29   , dealer: zmq.ZMQ_DEALER
 30   , router: zmq.ZMQ_ROUTER
 31   , pair: zmq.ZMQ_PAIR
 32 };
 33 
 34 /**
 35  * Map of socket options.
 36  */
 37 
 38 var opts = exports.options = {
 39     _fd: zmq.ZMQ_FD
 40   , _ioevents: zmq.ZMQ_EVENTS
 41   , _receiveMore: zmq.ZMQ_RCVMORE
 42   , _subscribe: zmq.ZMQ_SUBSCRIBE
 43   , _unsubscribe: zmq.ZMQ_UNSUBSCRIBE
 44   , affinity: zmq.ZMQ_AFFINITY
 45   , backlog: zmq.ZMQ_BACKLOG
 46   , hwm: zmq.ZMQ_HWM
 47   , identity: zmq.ZMQ_IDENTITY
 48   , linger: zmq.ZMQ_LINGER
 49   , mcast_loop: zmq.ZMQ_MCAST_LOOP
 50   , rate: zmq.ZMQ_RATE
 51   , rcvbuf: zmq.ZMQ_RCVBUF
 52   , reconnect_ivl: zmq.ZMQ_RECONNECT_IVL
 53   , recovery_ivl: zmq.ZMQ_RECOVERY_IVL
 54   , sndbuf: zmq.ZMQ_SNDBUF
 55   , swap: zmq.ZMQ_SWAP
 56 };
 57 
 58 // Context management happens here. We lazily initialize a default context,
 59 // and use that everywhere. Also cleans up on exit.
 60 var ctx;
 61 function defaultContext() {
 62   if (ctx) return ctx;
 63 
 64   var io_threads = 1;
 65   if (process.env.ZMQ_IO_THREADS) {
 66     io_threads = parseInt(process.env.ZMQ_IO_THREADS, 10);
 67     if (!io_threads || io_threads < 1) {
 68       console.warn('Invalid number in ZMQ_IO_THREADS, using 1 IO thread.');
 69       io_threads = 1;
 70     }
 71   }
 72 
 73  74   ctx = new zmq.Context(io_threads);
 75   process.on('exit', function() {
 76     // ctx.close();
 77     ctx = null;
 78   });
 79 
 80   return ctx;
 81 };
 82 
 83 /**
 84  * Create a new socket of the given `type`.
 85  *
 86  * @constructor
 87  * @param {String|Number} type
 88  * @api public
 89  */
 90 
 91 function Socket(type) {
 92   this.type = type;
 93   this._zmq = new zmq.Socket(defaultContext(), types[type]);
 94   this._outgoing = [];
 95   this._watcher = new IOWatcher;
 96   this._watcher.callback = this._flush.bind(this);
 97   this._watcher.set(this._fd, true, false);
 98   this._watcher.start();
 99 };
100 
101 /**
102  * Inherit from `EventEmitter.prototype`.
103  */
104 
105 Socket.prototype.__proto__ = EventEmitter.prototype;
106 
107 /**
108  * Set `opt` to `val`.
109  *
110  * @param {String|Number} opt
111  * @param {Mixed} val
112  * @return {Socket} for chaining
113  * @api public
114  */
115 
116 Socket.prototype.setsockopt = function(opt, val){
117   this._zmq.setsockopt(opts[opt] || opt, val);
118   return this;
119 };
120 
121 /**
122  * Get socket `opt`.
123  *
124  * @param {String|Number} opt
125  * @return {Mixed}
126  * @api public
127  */
128 
129 Socket.prototype.getsockopt = function(opt){
130   return this._zmq.getsockopt(opts[opt] || opt);
131 };
132 
133 /**
134  * Socket opt accessors allowing `sock.backlog = val`
135  * instead of `sock.setsockopt('backlog', val)`.
136  */
137 
138 Object.keys(opts).forEach(function(name){
139   Socket.prototype.__defineGetter__(name, function() {
140     return this._zmq.getsockopt(opts[name]);
141   });
142 
143   Socket.prototype.__defineSetter__(name, function(val) {
144     if ('string' == typeof val) val = new Buffer(val, 'utf8');
145     return this._zmq.setsockopt(opts[name], val);
146   });
147 });
148 
149 /**
150  * Async bind.
151  *
152  * Emits the "bind" event.
153  *
154  * @param {String} addr
155  * @param {Function} cb
156  * @return {Socket} for chaining
157  * @api public
158  */
159 
160 Socket.prototype.bind = function(addr, cb) {
161   var self = this;
162   self._watcher.stop();
163   self._zmq.bind(addr, function(err) {
164     self._watcher.start();
165     self.emit('bind');
166     cb && cb(err);
167   });
168   return this;
169 };
170 
171 /**
172  * Sync bind.
173  *
174  * @param {String} addr
175  * @return {Socket} for chaining
176  * @api public
177  */
178 
179 Socket.prototype.bindSync = function(addr) {
180   this._watcher.stop();
181   try {
182     this._zmq.bindSync(addr);
183   } catch (e) {
184     this._watcher.start();
185     throw e;
186   }
187   this._watcher.start();
188   return this;
189 };
190 
191 /**
192  * Connect to `addr`.
193  *
194  * @param {String} addr
195  * @return {Socket} for chaining
196  * @api public
197  */
198 
199 Socket.prototype.connect = function(addr) {
200   this._zmq.connect(addr);
201   return this;
202 };
203 
204 /**
205  * Subscribe with the given `filter`.
206  *
207  * @param {String} filter
208  * @return {Socket} for chaining
209  * @api public
210  */
211 
212 Socket.prototype.subscribe = function(filter) {
213   this._subscribe = filter;
214   return this;
215 };
216 
217 /**
218  * Unsubscribe with the given `filter`.
219  *
220 221  * @param {String} filter
222  * @return {Socket} for chaining
223  * @api public
224  */
225 
226 Socket.prototype.unsubscribe = function(filter) {
227   this._unsubscribe = filter;
228   return this;
229 };
230 
231 /**
232  * Send the given `msg`.
233  *
234  * @param {String|Buffer} msg
235  * @param {Number} flags
236  * @return {Socket} for chaining
237  * @api public
238  */
239 
240 Socket.prototype.send = function(msg, flags) {
241   // allow strings etc
242   if (!Buffer.isBuffer(msg)) {
243     msg = new Buffer(String(msg), 'utf8');
244   }
245 
246   this._outgoing.push([msg, flags || 0]);
247   this._flush();
248 
249   return this;
250 };
251 
252 // The workhorse that does actual send and receive operations.
253 // This helper is called from `send` above, and in response to
254 // the watcher noticing the signaller fd is readable.
255 Socket.prototype._flush = function() {
256   var args;
257 
258   // Don't allow recursive flush invocation as it can lead to stack
259   // exhaustion and write starvation
260   if (this._flushing) return;
261 
262   this._flushing = true;
263   try {
264     while (true) {
265       var emitArgs
266         , flags = this._ioevents;
267 
268       if (!this._outgoing.length) {
269         flags &= ~zmq.ZMQ_POLLOUT;
270       }
271 
272       if (!flags) break;
273     
274       if (flags & zmq.ZMQ_POLLIN) {
275           emitArgs = ['message'];
276 
277           do {
278             emitArgs.push(new Buffer(this._zmq.recv()));
279           } while (this._receiveMore);
280 
281           this.emit.apply(this, emitArgs);
282           if (this._zmq.state != zmq.STATE_READY) {
283             this._flushing = false;
284             return;
285           }
286       }
287 
288       // We send as much as possible in one burst so that we don't
289       // starve sends if we receive more than one message for each
290       // one sent.
291       while (flags & zmq.ZMQ_POLLOUT && this._outgoing.length) {
292         args = this._outgoing.shift();
293         this._zmq.send(args[0], args[1]);
294         flags = this._ioevents;
295       }
296     }
297   } catch (e) {
298     this.emit('error', e);
299   }
300 
301   this._flushing = false;
302 };
303 
304 /**
305  * Close the socket.
306  *
307  * @return {Socket} for chaining
308  * @api public
309  */
310 
311 Socket.prototype.close = function() {
312   this._watcher.stop();
313   this._watcher = null;
314   this._zmq.close();
315   return this;
316 };
317 
318 /**
319  * Create a `type` socket with the given `options`.
320  *
321  * @param {String} type
322  * @param {Object} options
323  * @return {Socket}
324  * @api public
325  */
326 
327 exports.socket =
328 exports.createSocket = function(type, options) {
329   var sock = new Socket(type);
330   for (var key in options) sock[key] = options[key];
331   return sock;
332 };
333