UNPKG

enet

Version:

The ENet Networking Library cross compiled to javascript

932 lines (861 loc) 36.6 kB
// ========================================================================== // sockets. Note that the implementation assumes all sockets are always // nonblocking // ========================================================================== mergeInto(LibraryManager.library, { $NodeSockets__postset: 'Module["getStreamSocket"]=NodeSockets.getStreamSocket;'+ 'Module["createStreamFromSocket"]=NodeSockets.createStreamFromSocket;', $NodeSockets__deps: ['__setErrNo', '$ERRNO_CODES'], $NodeSockets: { getStreamSocket: function(fd){ var stream = FS.getStream(fd); return stream ? stream.socket : undefined; }, createStreamFromSocket: function(socket){ var stream = FS.createStream({ addrlen: {{{ C_STRUCTS.sockaddr_in.__size__ }}}, connected: false, stream: false, dgram: true, socket: socket, bound: false, inQueue: [] }); stream.skipBind = true; return stream.fd; }, inet_aton_raw: function(str) { var b = str.split("."); return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0; }, inet_ntoa_raw: function(addr) { return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) }, DGRAM:function(){ #if CHROME_SOCKETS if((typeof window !== 'undefined') && window["chrome"] && window["chrome"]["socket"]) return NodeSockets.ChromeDgram(); #endif if(typeof require !== 'undefined') return require("dgram");//node or browserified assert(false,"no dgram sockets backend found!"); }, NET:function(){ #if CHROME_SOCKETS if((typeof window !== 'undefined') && window["chrome"] && window["chrome"]["socket"]) return NodeSockets.ChromeNet(); #endif if(typeof require !== 'undefined') return require("net");//node or browserified assert(false, "no tcp socket backend found!"); }, buffer2uint8array: function(buffer){ var arraybuffer = new ArrayBuffer(buffer.length); var uint8Array = new Uint8Array(arraybuffer); for(var i = 0; i < buffer.length; i++) { uint8Array[i] = buffer["readUInt8"](i);//cannot index Buffer with [] if browserified.. } return uint8Array; }, #if CHROME_SOCKETS ChromeNet: undefined, /* use https://github.com/GoogleChrome/net-chromeify.git ? (Apache license)*/ ChromeDgram: function(){ /* * node dgram API from chrome.socket API - using Uint8Array() instead of Buffer() * Copyright (C) 2013 Mokhtar Naamani * license: MIT */ var exports = {}; exports["createSocket"] = function (type, message_event_callback){ assert( type === 'udp4', "only supporting udp4 sockets in chrome"); return new UDPSocket(message_event_callback); } function UDPSocket(msg_evt_cb){ var self = this; self._is_chrome_socket = true; self._event_listeners = {}; self["on"]("listening",function(){ //send pending datagrams.. self.__pending.forEach(function(job){ job.socket_id = self.__socket_id; send_datagram(job); }); delete self.__pending; //start polling socket for incoming datagrams self.__poll_interval = setInterval(do_recv,30); #if SOCKET_DEBUG console.log("chrome socket bound to:",JSON.stringify(self.address())); #endif }); if(msg_evt_cb) self["on"]("message",msg_evt_cb); function do_recv(){ if(!self.__socket_id) return; window["chrome"]["socket"]["recvFrom"](self.__socket_id, undefined, function(info){ var buff; //todo - set correct address family //todo - error detection. if(info["resultCode"] > 0){ buff = new Uint8Array(info["data"]); self["emit"]("message",buff,{"address":info["address"],"port":info["port"],"size":info["data"]["byteLength"],"family":"IPv4"}); } }); } self.__pending = [];//queued datagrams to send (if app tried to send before socket is ready) } UDPSocket.prototype["on"] = function(evt,callback){ //used to register callbacks //store event name e in this._events this._event_listeners[evt] ? this._event_listeners[evt].push(callback) : this._event_listeners[evt]=[callback]; }; UDPSocket.prototype["emit"] = function(e){ //used internally to fire events //'apply' event handler function to 'this' channel pass eventname 'e' and arguemnts.slice(1) var self = this; var args = Array.prototype.slice.call(arguments); if(this._event_listeners && this._event_listeners[e]){ this._event_listeners[e].forEach(function(cb){ cb.apply(self,args.length>1?args.slice(1):[undefined]); }); } }; UDPSocket.prototype["close"] = function(){ //Close the underlying socket and stop listening for data on it. if(!self.__socket_id) return; window["chrome"]["socket"]["destroy"](self.__socket_id); clearInterval(self.__poll_interval); delete self.__poll_interval; }; UDPSocket.prototype["bind"] = function(port,address){ var self = this; address = address || "0.0.0.0"; port = port || 0; if(self.__socket_id || self.__bound ) return;//only bind once! self.__bound = true; window["chrome"]["socket"]["create"]('udp',{},function(socketInfo){ self.__socket_id = socketInfo["socketId"]; window["chrome"]["socket"]["bind"](self.__socket_id,address,port,function(result){ window["chrome"]["socket"]["getInfo"](self.__socket_id,function(info){ self.__local_address = info["localAddress"]; self.__local_port = info["localPort"]; self["emit"]("listening"); }); }); }); }; UDPSocket.prototype["address"] = function(){ return({"address":this.__local_address,"port":this.__local_port}); }; UDPSocket.prototype["setBroadcast"] = function(flag){ //do chrome udp sockets support broadcast? #if SOCKET_DEBUG console.log("setting broadcast on chrome socket to:",flag); #endif }; UDPSocket.prototype["send"] = function(buff, offset, length, port, address, callback){ var self = this; var job = { socket_id:self.__socket_id, buff:buff, offset:offset, length:length, port:port, address:address, callback:callback }; if(!self.__socket_id){ if(!self.__bound) self.bind(); self.__pending.push(job); }else{ send_datagram(job); } }; function send_datagram(job){ var data; var buff; var i; if(job.offset == 0 && job.length == job.buff.length){ buff = job.buff; }else{ buff = job.buff.subarray(job.offset,job.offset+job.length); } data = buff.buffer; window["chrome"]["socket"]["sendTo"](job.socket_id,data,job.address,job.port,function(result){ var err; if(result["bytesWritten"] < data.byteLength ) err = 'truncation-error'; if(result["bytesWritten"] < 0 ) err = 'send-error'; if(job.callback) job.callback(err,result["bytesWritten"]); }); } return exports; }, #endif }, close__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], close: function(fildes) { // int close(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/close.html var stream = FS.getStream(fildes); if(stream) { if(stream.socket){ if(stream.interval) clearInterval(stream.interval); if(typeof stream.socket["close"] == "function"){ if(stream._dgram_on_message) stream.socket["removeListener"]("message",stream._dgram_on_message); if(!stream.skipBind) stream.socket["close"]();//udp sockets, tcp listening sockets } if(typeof stream.socket["end"] == "function") stream.socket["end"]();//tcp connections FS.closeStream(stream.fd); return 0; } else { try { if (stream.stream_ops.close) { stream.stream_ops.close(stream); } } catch (e) { throw e; // why are we throwing it again? } finally { FS.closeStream(stream.fd); } return 0; } } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, socket__deps: ['$NodeSockets', '__setErrNo', '$ERRNO_CODES'], socket: function(family, type, protocol) { var fd; if(!(family == {{{ cDefine('AF_INET') }}} || family == {{{ cDefine('AF_INET6') }}})) { ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); return -1; } var v6 = (family == {{{ cDefine('AF_INET6')}}}) var stream = type === {{{ cDefine('SOCK_STREAM') }}}; var dgram = type === {{{ cDefine('SOCK_DGRAM') }}}; if (protocol) { assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if stream, must be tcp assert(dgram == (protocol == {{{ cDefine('IPPROTO_UDP') }}})); // if dgram, must be udp } try{ if(stream){ fd = FS.createStream({ addrlen : v6 ? {{{ C_STRUCTS.sockaddr_in6.__size__ }}} : {{{ C_STRUCTS.sockaddr_in.__size__ }}} , connected: false, stream: true, socket: true, //real socket will be created when bind() or connect() is called //to choose between server and connection sockets inQueue: [] }).fd; }else if(dgram){ fd = FS.createStream({ addrlen : v6 ? {{{ C_STRUCTS.sockaddr_in6.__size__ }}} : {{{ C_STRUCTS.sockaddr_in.__size__ }}} , connected: false, stream: false, dgram: true, socket: NodeSockets.DGRAM()["createSocket"](v6?'udp6':'udp4'), bound: false, inQueue: [] }).fd; }else{ ___setErrNo(ERRNO_CODES.EPROTOTYPE); return -1; } #if SOCKET_DEBUG console.log("created socket fd:",fd); #endif return fd; }catch(e){ ___setErrNo(ERRNO_CODES.EACCES); #if SOCKET_DEBUG console.log(e); #end if return -1; } }, /* * http://pubs.opengroup.org/onlinepubs/009695399/functions/connect.html */ connect__deps: ['$NodeSockets', 'htons', '__setErrNo', '$ERRNO_CODES','inet_ntop6_raw'], connect: function(fd, addr, addrlen) { if(typeof fd == 'number' && (fd > 64 || fd < 1) ){ ___setErrNo(ERRNO_CODES.EBADF); return -1; } var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } if(info.dgram && !info.bound) _bind(fd); if(info.stream && info.CONNECTING){ ___setErrNo(ERRNO_CODES.EALREADY); return -1; } if(info.stream && info.ESTABLISHED){ ___setErrNo(ERRNO_CODES.EISCONN); return -1; } if(info.stream && info.CLOSED){ //only do a successful connect once per socket ___setErrNo(ERRNO_CODES.ECONNRESET); return -1; } if(info.stream && info.socket.server){ //listening tcp socket cannot connect ___setErrNo(ERRNO_CODES.EOPNOTSUPP); return -1; } info.connected = true; assert( info.addrlen === addrlen ); switch(addrlen){ case {{{ C_STRUCTS.sockaddr_in.__size__ }}}: info.addr = {{{ makeGetValue('addr', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'i32') }}}; info.port = _htons( {{{ makeGetValue('addr', C_STRUCTS.sockaddr_in.sin_port, 'i16') }}}); info.host = NodeSockets.inet_ntoa_raw(info.addr); break; case {{{ C_STRUCTS.sockaddr_in6.__size__ }}}: info.port = _htons( {{{ makeGetValue('addr', C_STRUCTS.sockaddr_in6.sin6_port, 'i16')}}} ); info.host = _inet_ntop6_raw(addr + {{{ C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr }}} ); break; } if(!info.stream) return 0; (function(info){ var intervalling = false, interval; var outQueue = []; info.hasData = function() { return info.inQueue.length > 0 } info.CONNECTING = true; function onEnded(){ info.ESTABLISHED = false; info.CLOSED = true; } info.sender = function(buf){ outQueue.push(buf); trySend(); }; function send(data) { var buff = new Buffer(data); if(!info.socket["write"](buff)) info.paused = true; } function trySend() { if (!info.ESTABLISHED) { if (!intervalling) { intervalling = true; info.interval = setInterval(trySend, 100); } return; } for (var i = 0; i < outQueue.length; i++) { send(outQueue[i]); } outQueue.length = 0; if (intervalling) { intervalling = false; if(info.interval) clearInterval(info.interval); } } try{ info.socket = new NodeSockets.NET()["connect"]({host:info.host,port:info.port,localAddress:info.local_host},function(){ info.CONNECTING = false; info.ESTABLISHED = true; }); }catch(e){ return -1; } info.socket["on"]('drain',function(){ info.paused = false; }); info.socket["on"]('data',function(buf){ info.inQueue.push(new Uint8Array(buf)); }); info.socket["on"]('close',onEnded); info.socket["on"]('error',onEnded); info.socket["on"]('end',onEnded); info.socket["on"]('timeout',function(){ info.socket["end"](); onEnded(); }); })(info); //for tcp we always return and do an async connect irrespective of socket option O_NONBLOCK ___setErrNo(ERRNO_CODES.EINPROGRESS); return -1; }, recv__deps: ['$NodeSockets','recvfrom'], recv: function(fd, buf, len, flags) { var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } return _recvfrom(fd,buf,len,flags,0,0); }, send__deps: ['$NodeSockets'], send: function(fd, buf, len, flags) { var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } if(info.dgram && !info.bound) _bind(fd); info.sender(HEAPU8.subarray(buf, buf+len)); return len; }, sendmsg__deps: ['$NodeSockets', 'connect','inet_ntop6_raw'], sendmsg: function(fd, msg, flags) { var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } // if we are not connected, use the address info in the message var name = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_name, '*') }}}; var namelen = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_namelen, 'i32') }}}; if (!info.connected) { assert(name, 'sendmsg on non-connected socket, and no name/address in the message'); if(info.stream) _connect(fd, name, namelen); } var iov = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iov, 'i8*') }}}; var num = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; #if SOCKET_DEBUG Module.print('sendmsg vecs: ' + num); #endif var totalSize = 0; for (var i = 0; i < num; i++) { totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; } var buffer = new Uint8Array(totalSize); var ret = 0; for (var i = 0; i < num; i++) { var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; #if SOCKET_DEBUG Module.print('sendmsg curr size: ' + currNum); #endif if (!currNum) continue; var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; buffer.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret); ret += currNum; } assert( info.addrlen === namelen ); var addr,port,host; switch(namelen){ case {{{ C_STRUCTS.sockaddr_in.__size__ }}}: addr = getValue(name + {{{ C_STRUCTS.sockaddr_in.sin_addr.s_addr }}}, 'i32'); port = _htons(getValue(name + {{{ C_STRUCTS.sockaddr_in.sin_port }}}, 'i16')); host = NodeSockets.inet_ntoa_raw(addr); break; case {{{ C_STRUCTS.sockaddr_in6.__size__ }}}: port = _htons( {{{ makeGetValue('name', C_STRUCTS.sockaddr_in6.sin6_port, 'i16') }}}); host = _inet_ntop6_raw(name + {{{ C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr }}}); break; } if(info.dgram && !info.bound) _bind(fd); info.sender(buffer,host,port); // send all the iovs as a single message return ret; }, recvmsg__deps: ['$NodeSockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES', 'htons', 'inet_pton6_raw'], recvmsg: function(fd, msg, flags) { var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } if (!info.hasData()) { ___setErrNo(ERRNO_CODES.EWOULDBLOCK); return -1; } var buffer = info.inQueue.shift(); var bytes = buffer.length; #if SOCKET_DEBUG Module.print('recvmsg bytes: ' + bytes); #endif var name = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_name, '*') }}}; var namelen = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_namelen, 'i32') }}}; assert( info.addrlen === namelen ); // write source - assuming a dgram.. switch(namelen){ case {{{ C_STRUCTS.sockaddr_in.__size__ }}}: if(info.connected){ {{{ makeSetValue('name', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'info.addr', 'i32') }}}; {{{ makeSetValue('name', C_STRUCTS.sockaddr_in.sin_port, '_htons(info.port)', 'i16') }}}; }else{ {{{ makeSetValue('name', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'NodeSockets.inet_aton_raw(buffer.from.host)', 'i32') }}}; {{{ makeSetValue('name', C_STRUCTS.sockaddr_in.sin_port, '_htons(buffer.from.port)', 'i16') }}}; } break; case {{{ C_STRUCTS.sockaddr_in6.__size__ }}}: if(info.connected){ _inet_pton6_raw(info.host,name + {{{ C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr }}}); {{{ makeSetValue('name', C_STRUCTS.sockaddr_in6.sin6_port, '_htons(info.port)', 'i16') }}}; }else{ _inet_pton6_raw(buffer.from.host,name + {{{ C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr }}}); {{{ makeSetValue('name', C_STRUCTS.sockaddr_in6.sin6_port, '_htons(buffer.from.port)', 'i16') }}}; } break; } // write data var ret = bytes; var iov = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iov, 'i8*') }}}; var num = {{{ makeGetValue('msg', C_STRUCTS.msghdr.msg_iovlen, 'i32') }}}; var bufferPos = 0; for (var i = 0; i < num && bytes > 0; i++) { var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; #if SOCKET_DEBUG Module.print('recvmsg loop ' + [i, num, bytes, currNum]); #endif if (!currNum) continue; currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..? bytes -= currNum; var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; #if SOCKET_DEBUG Module.print('recvmsg call recv ' + currNum); #endif HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf); bufferPos += currNum; } if (info.stream) { // This is tcp (reliable), so if not all was read, keep it if (bufferPos < bytes) { info.inQueue.unshift(buffer.subarray(bufferPos)); #if SOCKET_DEBUG Module.print('recvmsg: put back: ' + (bytes - bufferPos)); #endif } } return ret; }, recvfrom__deps: ['$NodeSockets','inet_pton6_raw','__setErrNo', '$ERRNO_CODES'], recvfrom: function(fd, buf, len, flags, addr, addrlen) { var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } if (!info.hasData()) { //todo: should return 0 if info.stream && info.CLOSED ? ___setErrNo(ERRNO_CODES.EAGAIN); // no data, and all sockets are nonblocking, so this is the right behavior return -1; } var buffer = info.inQueue.shift(); if(addr){ assert( info.addrlen === addrlen ); switch(addrlen){ case {{{ C_STRUCTS.sockaddr_in.__size__ }}}: {{{ makeSetValue('addr', C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'NodeSockets.inet_aton_raw(buffer.from.host)', 'i32') }}}; {{{ makeSetValue('addr', C_STRUCTS.sockaddr_in.sin_port, '_htons(buffer.from.port)', 'i16') }}}; break; case {{{ C_STRUCTS.sockaddr_in6.__size__ }}}: _inet_pton6_raw(buffer.from.host,addr+ {{{ C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr }}}); {{{ makeSetValue('addr', C_STRUCTS.sockaddr_in6.sin6_port, '_htons(buffer.from.port)', 'i16') }}}; break; } } #if SOCKET_DEBUG Module.print('recv: ' + [Array.prototype.slice.call(buffer)]); #endif if (len < buffer.length) { if (info.stream) { // This is tcp (reliable), so if not all was read, keep it info.inQueue.unshift(buffer.subarray(len)); #if SOCKET_DEBUG Module.print('recv: put back: ' + (len - buffer.length)); #endif } buffer = buffer.subarray(0, len); } HEAPU8.set(buffer, buf); return buffer.length; }, shutdown: function(fd, how) { var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } //todo: if how = 0 disable sending info.sender=function(){return -1;} // = 1 disable receiving (delete info.inQueue?) if(info.interval) clearInterval(info.interval); if(info.socket && fd > 63){ info.socket["close"] && info.socket["close"](); info.socket["end"] && info.socket["end"](); } if(info.socket) _close(fd); return 0; }, ioctl: function(fd, request, varargs) { var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } var bytes = 0; if (info.hasData()) { bytes = info.inQueue[0].length; } var dest = {{{ makeGetValue('varargs', '0', 'i32') }}}; {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}}; return 0; }, setsockopt: function(d, level, optname, optval, optlen) { #if SOCKET_DEBUG console.log('ignoring setsockopt command'); #endif return 0; }, bind__deps: ['connect','inet_ntop6_raw'], bind: function(fd, addr, addrlen) { if(typeof fd == 'number' && (fd > 64 || fd < 1) ){ ___setErrNo(ERRNO_CODES.EBADF); return -1; } var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } if(info.dgram && info.bound){ ___setErrNo(ERRNO_CODES.EINVAL); return -1; } if(info.connected){ ___setErrNo(ERRNO_CODES.EISCONN); return -1; } try{ if(addr){ assert(info.addrlen === addrlen); switch(addrlen){ case {{{ C_STRUCTS.sockaddr_in.__size__ }}}: info.local_addr = {{{ makeGetValue('addr',C_STRUCTS.sockaddr_in.sin_addr.s_addr, 'i32') }}}; info.local_port = _htons( {{{ makeGetValue('addr', C_STRUCTS.sockaddr_in.sin_port, 'i16') }}} ); info.local_host = NodeSockets.inet_ntoa_raw(info.local_addr); break; case {{{ C_STRUCTS.sockaddr_in6.__size__ }}}: info.local_port = _htons( {{{makeGetValue('addr', C_STRUCTS.sockaddr_in6.sin6_port, 'i16') }}} ); info.local_host = _inet_ntop6_raw(addr + {{{ C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr }}} ); break; } } if(info.stream){ //we dont actually bind a tcp node socket yet only store the local address to to use //when connect or listen are called. return 0; } if(info.dgram){ info.bound = true; info.hasData = function(){return info.inQueue.length>0} if(!info.skipBind) info.socket["bind"](info.local_port||0,info.local_host||undefined); info._dgram_on_message = function(msg,rinfo){ if(info.host && info.connected){ //connected dgram socket will only accept packets from info.host:info.port if(info.host !== rinfo.address || info.port !== rinfo.port) return; } var buf; if(info.socket._is_chrome_socket) { buf = msg; }else{ buf = msg instanceof ArrayBuffer ? new Uint8Array(msg) : NodeSockets.buffer2uint8array(msg); } buf.from= { host: rinfo["address"], port: rinfo["port"] } info.inQueue.push(buf); }; info.socket["on"]('message',info._dgram_on_message); info.sender = function(buf,ip,port){ var buffer; if(info.socket._is_chrome_socket) { buffer = buf; }else{ buffer = new Buffer(buf); } info.socket["send"](buffer,0,buffer.length,port,ip); } } }catch(e){ #if SOCKET_DEBUG console.log(e); #endif return -1; } return 0; }, listen: function(fd, backlog) { if(typeof fd == 'number' && (fd > 64 || fd < 1) ){ ___setErrNo(ERRNO_CODES.EBADF); return -1; } var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } assert(info.stream); info.socket = NodeSockets.NET()["createServer"](); info.server = info.socket;//mark it as a listening socket info.connQueue = []; info.socket["listen"](info.local_port||0,info.local_host,backlog,function(){ #if SOCKET_DEBUG console.log('listening on',info.local_port||0,info.local_host); #endif }); info.socket["on"]("connection",function(socket){ info.connQueue.push(socket); }); return 0; }, accept__deps: ['$NodeSockets','inet_pton6_raw','__setErrNo', '$ERRNO_CODES'], accept: function(fd, addr, addrlen) { if(typeof fd == 'number' && (fd > 64 || fd < 1) ){ ___setErrNo(ERRNO_CODES.EBADF); return -1; } var info = FS.streams[fd]; if (!info || !info.socket) { ___setErrNo(ERRNO_CODES.ENOTSOCK); return -1; } if(!info.server){ //not a listening socket ___setErrNo(ERRNO_CODES.EINVAL); return -1; } if(info.connQueue.length == 0) { ___setErrNo(ERRNO_CODES.EAGAIN); return -1; } var newfd = FS.createStream({ socket:false, //newfd will be > 63 inQueue:[] }).fd; if(newfd == -1){ ___setErrNo(ERRNO_CODES.ENFILE); return -1; } var conn = FS.streams[newfd]; conn.socket = info.connQueue.shift(); conn.port = _htons(conn.socket["remotePort"]); conn.host = conn.socket["remoteAddress"]; if (addr) { switch(info.addrlen){ case {{{ C_STRUCTS.sockaddr_in.__size__ }}}: setValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_addr.s_addr }}}, NodeSockets.inet_aton_raw(conn.host), 'i32'); setValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_port }}}, conn.port, 'i16'); setValue(addr + {{{ C_STRUCTS.sockaddr_in.sin_family }}}, {{{ cDefine('AF_INET') }}}, 'i16'); setValue(addrlen, {{{ C_STRUCTS.sockaddr_in.__size__ }}}, 'i32'); break; case {{{ C_STRUCTS.sockaddr_in6.__size__ }}}: _inet_pton6_raw(conn.host,addr + {{{ C_STRUCTS.sockaddr_in6.sin6_addr.__in6_union.__s6_addr }}}); setValue(addr + {{{ C_STRUCTS.sockaddr_in6.sin6_port }}}, conn.port, 'i16'); setValue(addr + {{{ C_STRUCTS.sockaddr_in6.sin6_family }}}, {{{ cDefine('AF_INET6') }}}, 'i16'); setValue(addrlen, {{{ C_STRUCTS.sockaddr_in6.__size__ }}}, 'i32'); break; } } (function(info){ var intervalling = false, interval; var outQueue = []; info.hasData = function() { return info.inQueue.length > 0 } info.ESTABLISHED = true; function onEnded(){ info.ESTABLISHED = false; info.CLOSED = true; } info.sender = function(buf){ outQueue.push(buf); trySend(); }; function send(data) { var buff = new Buffer(data); if(!info.socket["write"](buff)) info.paused = true; } function trySend() { if (!info.ESTABLISHED) { if (!intervalling) { intervalling = true; info.interval = setInterval(trySend, 100); } return; } for (var i = 0; i < outQueue.length; i++) { send(outQueue[i]); } outQueue.length = 0; if (intervalling) { intervalling = false; if(info.interval) clearInterval(info.interval); } } info.socket["on"]('drain',function(){ info.paused = false; }); info.socket["on"]('data',function(buf){ info.inQueue.push(new Uint8Array(buf)); }); info.socket["on"]('close',onEnded); info.socket["on"]('error',onEnded); info.socket["on"]('end',onEnded); info.socket["on"]('timeout',function(){ info.socket["end"](); onEnded(); }); })(conn); return newfd; }, /* * http://pubs.opengroup.org/onlinepubs/009695399/functions/select.html */ select: function(nfds, readfds, writefds, exceptfds, timeout) { // readfds are supported, // writefds checks socket open status // exceptfds not supported // timeout is always 0 - fully async assert(!exceptfds); var errorCondition = 0; function canRead(info) { // make sure hasData exists. // we do create it when the socket is connected, // but other implementations may create it lazily if(info.stream){ if ((info.socket["_readableState"]["ended"] || info.socket["errorEmitted"] ) && info.inQueue.length == 0) { errorCondition = -1; return false; } return info.hasData && info.hasData(); }else{ if(info.socket["_receiving"] || info.socket["_bound"]) return (info.hasData && info.hasData()); errorCondition = -1; return false; } } function canWrite(info) { // make sure socket exists. // we do create it when the socket is connected, // but other implementations may create it lazily if(info.stream){ if (info.socket["_writableState"]["ended"] || info.socket["_writableState"]["ending"] || info.socket["errorEmitted"]) { errorCondition = -1; return false; } return info.socket && info.socket["writable"] }else{ if(info.socket["_receiving"] || info.socket["_bound"]) return (info.hasData && info.hasData()); errorCondition = -1; return false; } } function checkfds(nfds, fds, can) { if (!fds) return 0; var bitsSet = 0; var dstLow = 0; var dstHigh = 0; var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}}; var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}}; nfds = Math.min(64, nfds); // fd sets have 64 bits for (var fd = 0; fd < nfds; fd++) { var mask = 1 << (fd % 32), int = fd < 32 ? srcLow : srcHigh; if (int & mask) { // index is in the set, check if it is ready for read var info = FS.streams[fd]; if (info && can(info)) { // set bit fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask); bitsSet++; } } } {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}}; {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}}; return bitsSet; } var totalHandles = checkfds(nfds, readfds, canRead) + checkfds(nfds, writefds, canWrite); if (errorCondition) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else { return totalHandles; } }, socketpair__deps: ['__setErrNo', '$ERRNO_CODES'], socketpair: function(domain, type, protocol, sv) { // int socketpair(int domain, int type, int protocol, int sv[2]); // http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html ___setErrNo(ERRNO_CODES.EOPNOTSUPP); return -1; }, //http://pubs.opengroup.org/onlinepubs/009695399/functions/getsockname.html getsockname__deps: ['__setErrNo', '$ERRNO_CODES', '$NodeSockets'], getsockname: function(fd, addr, len) { return -1; }, });