iojs-nanomsg
Version:
nanomsg streams for iøjs
349 lines (293 loc) • 9.24 kB
JavaScript
/*
* free and unencumbered software released into the public domain.
*/
var nn = require('bindings')('nanomsg.node') //should be .iojs
var dup = require('stream').Duplex
var sock = {
pub : [nn.NN_PUB, nn.NN_SUB],
sub : [nn.NN_SUB, nn.NN_PUB],
bus : [nn.NN_BUS, nn.NN_BUS],
pair : [nn.NN_PAIR, nn.NN_PAIR],
surv : [nn.NN_SURVEYOR, nn.NN_RESPONDENT],
surveyor : [nn.NN_SURVEYOR, nn.NN_RESPONDENT],
resp : [nn.NN_RESPONDENT, nn.NN_SURVEYOR],
respondent : [nn.NN_RESPONDENT, nn.NN_SURVEYOR],
req : [nn.NN_REQ, nn.NN_REP],
rep : [nn.NN_REP, nn.NN_REQ],
pull : [nn.NN_PULL, nn.NN_PUSH],
push : [nn.NN_PUSH, nn.NN_PULL],
}
var af = {
af_sp : nn.AF_SP,
AF_SP : nn.AF_SP,
af_sp_raw : nn.AF_SP_RAW,
raw : nn.AF_SP_RAW,
af : nn.AF_SP
}
var sol = {
linger : nn.NN_LINGER,
sndbuf : nn.NN_SNDBUF,
rcvbuf : nn.NN_RCVBUF,
sndtimeo : nn.NN_SNDTIMEO,
rcvtimeo : nn.NN_RCVTIMEO,
reconn : nn.NN_RECONNECT_IVL,
maxreconn : nn.NN_RECONNECT_IVL_MAX,
sndprio : nn.NN_SNDPRIO,
tcpnodelay : nn.NN_TCP_NODELAY
}
if (nn.NN_VERSION > 3) sol.rcvprio = nn.NN_RCVPRIO
module.exports = {
version: nn.NN_VERSION,
versionstr: '0.'+nn.NN_VERSION+'-beta',
socket: function ( type, opts ) {
//preflight check
if(typeof opts == 'string') opts = { fam: opts }
opts = opts || { fam: 'af' }
if(!opts.hasOwnProperty('fam')) opts.fam = 'af'
//ensure first nanomsg socket is not zero, else disregard it
var nns = nn.Socket(af[opts.fam], sock[type][0])
if(nns == 0) return new self(nn.Socket(af[opts.fam],sock[type][0]), type, opts)
return new self( nns, type, opts )
}
}
require('util').inherits( self, dup)
function self (s, t, o) {
//error handle
if(s < 0) throw new Error(nn.Err() + ': ' + t + ' creating socket'+'\n')
var ctx = this
this.socket = s
this.fam = o.fam
this.type = t
this.how = {}
this.bind = bind
this.connect = connect
this.close = close
this.shutdown = shutdown
this.unhook = unhook
this.setsockopt = setsockopt
this.getsockopt = getsockopt
this.linger = linger
this.sndbuf = sndbuf
this.rcvbuf = rcvbuf
this.sndtimeo = sndtimeo
this.rcvtimeo = rcvtimeo
this.reconn = reconn
this.maxreconn = maxreconn
this.sndprio = sndprio
this.rcvprio = rcvprio
this.tcpnodelay = tcpnodelay
this.sleep = uvsleeper( function (t, d) { setTimeout(d, t) } )
this.send = function(msg) { nn.Send( s, msg ) }
this.recv = recv
this.destroyed = false
this.destroy = destroy
for(var sokopt in sol) if(o.hasOwnProperty(sokopt)) this[sokopt](o[sokopt])
switch(t){
case 'pub':
case 'push':
break
case 'surv':
case 'surveyor':
case 'rep':
case 'pull':
case 'sub':
case 'pair':
case 'bus':
case 'req':
case 'resp':
case 'respondent': nn.Recv( s, recv )
break
}
function recv (msg){
//msgs that are -1 is a req/surveyor issue so break the loop
if (msg == -1) return
ctx.push( msg )
if (!ctx.destroyed) nn.Recv( s, recv )
}
this._read = function(n){ }
this._write = function(buf, _, next){ nn.Send(s,buf); next() }
dup.call(this)
}
function close(fn) {
if (!fn) fn = function(){}
this.destroy()
switch(this.type){
case 'pub':
case 'push':
nn.Close( this.socket )
return fn('closing a pub or a push')
case 'pair':
return fn(this.unhook(Math.floor(Math.random()*100000000)))
default:
return fn(this.unhook())
}
}
function shutdown(addr) {
var confirm = this.how[addr][1] + ' endpoint '+ addr + ' shutdown'
if(nn.Shutdown(this.socket, this.how[addr][0]) < 0)
throw new Error(nn.Err() +': '+this.type+' shutdown@' + addr+'\n')
delete this.how[addr]; return confirm
}
function bind (addr) {
var eid = nn.Bind( this.socket, addr )
if(eid < 0) throw new Error(nn.Err() +': '+this.type+' bind@' + addr+'\n')
this.how[addr] = [eid,'bind']; return this
}
function connect (addr) {
var eid = nn.Connect( this.socket, addr )
if(eid < 0) throw new Error(nn.Err() +': '+this.type+' connect@' + addr+'\n')
this.how[addr] = [eid,'connect']; return this
}
function setsockopt(level, option, value){
return nn.Setsockopt(this.socket, nn[level], nn[option], value)
}
function getsockopt(level, option){
return nn.Getsockopt(this.socket, nn[level], nn[option])
}
function linger(number){
if(number){
if(setsol(this.socket, 'linger', number) > -1)
return 'linger set to ' + number + 'ms'
throw new Error(nn.Err() + ': '+this.type+' linger@'+number+'\n')
} else {
return getsol(this.socket, 'linger')
}
}
function sndbuf(number){
if(number){
if(setsol(this.socket, 'sndbuf', number) > -1)
return 'sndbuf set to ' + number + ' bytes'
throw new Error(nn.Err() + ': '+this.type+' sndbuf@'+number+'\n')
} else {
return getsol(this.socket, 'sndbuf')
}
}
function rcvbuf(number){
if(number){
if(setsol(this.socket, 'rcvbuf', number) > -1)
return 'rcvbuf set to ' + number + ' bytes'
throw new Error(nn.Err() + ': '+this.type+' rcvbuf@'+number+'\n')
} else {
return getsol(this.socket, 'rcvbuf')
}
}
function sndtimeo(number){
if(number){
if(setsol(this.socket, 'sndtimeo', number) > -1)
return 'sndtimeo set to ' + number + 'ms'
throw new Error(nn.Err() + ': '+this.type+' reconn@'+number+'\n')
} else {
return getsol(this.socket, 'sndtimeo')
}
}
function rcvtimeo(number){
if(number){
if(setsol(this.socket, 'rcvtimeo', number) > -1)
return 'rcvtimeo set to ' + number + 'ms'
throw new Error(nn.Err() + ': '+this.type+' reconn@'+number+'\n')
} else {
return getsol(this.socket, 'rcvtimeo')
}
}
function reconn(number){
if(number){
if(setsol(this.socket, 'reconn', number) > -1)
return 'reconn set to ' + number + 'ms'
throw new Error(nn.Err() + ': '+this.type+' reconn@'+number+'\n')
} else {
return getsol(this.socket, 'reconn')
}
}
function maxreconn(number){
if(number){
if(setsol(this.socket, 'maxreconn', number) > -1)
return 'maxreconn set to ' + number + 'ms'
throw new Error(nn.Err() + ': '+this.type+' maxreconn@'+number+'\n')
} else {
return getsol(this.socket, 'maxreconn')
}
}
function sndprio(number){
if(number){
if(setsol(this.socket, 'sndprio', number) > -1)
return 'sndprio set to ' + number
throw new Error(nn.Err() + ': '+this.type+' sndprio@'+number+'\n')
} else {
return getsol(this.socket, 'sndprio')
}
}
function rcvprio(number){
if(nn.NN_VERSION > 3){
if(number){
if(setsol(this.socket, 'rcvprio', number) > -1)
return 'rcvprio set to ' + number
throw new Error(nn.Err() + ': '+this.type+' rcvprio@'+number+'\n')
} else {
return getsol(this.socket, 'rcvprio')
}
} else {
var version = 'current lib version: '+ nn.versionstr + '\n'
var err = 'rcvprio: available in nanomsg beta-0.4 and higher.'
throw new Error(version + err + ':' +this.type+' rcvprio@'+number+'\n')
}
}
function tcpnodelay(bool){
if(arguments.length){
if(bool){
if(nn.Setsockopt(this.socket, nn.NN_TCP, nn.NN_TCP_NODELAY, 1) > -1)
return 'tcp nodelay: on'
throw new Error(nn.Err() + ': '+this.type+' nodelay@'+'activing'+'\n')
} else {
if(nn.Setsockopt(this.socket, nn.NN_TCP, nn.NN_TCP_NODELAY, 0) > -1)
return 'tcp nodelay: off'
throw new Error(nn.Err() + ': '+this.type+' nodelay@'+'deactiving'+'\n')
}
} else {
switch(nn.Getsockopt(this.socket, nn.NN_TCP, nn.NN_TCP_NODELAY)){
case 1: return 'tcp nodelay: on'
case 0: return 'tcp nodelay: off'
default:
throw new Error(nn.Err() + ': '+this.type+' nodelay@'+'getsockopt'+'\n')
}
}
}
function setsol(socket, option, value){
return nn.Setsockopt(socket, nn.NN_SOL_SOCKET, sol[option], value)
}
function getsol(socket, option){
return nn.Getsockopt(socket, nn.NN_SOL_SOCKET, sol[option])
}
function destroy(){
if (this.destroyed) return;
this.destroyed = true
}
function unhook(random){
var unsocket = nn.Socket(af[this.fam], sock[this.type][1])
var addr = 'inproc://unhook' + unsocket
if (random) addr += random
nn.Bind( unsocket, addr )
nn.Connect( this.socket, addr )
this.sleep(10)
switch (this.type){
case 'surv':
case 'surveyor':
case 'req':
nn.Send( this.socket, 'swimming like a fish' )
nn.Rcv(unsocket)
}
return nn.Send( unsocket, 'unhook and close' )
}
function uvsleeper(fn) {
return function () {
var done = false, err, res
fn.apply(this, Array.prototype.slice.apply(arguments).concat(cb))
while (!done) nn.Sleeper()
if (err) throw err
return res
function cb (e, r) {
err = e
res = r
done = true
}
}
}