From 911cbd0cef02c93f821c0c80a3d5dbad2b692c42 Mon Sep 17 00:00:00 2001 From: Daniel Ennis Date: Wed, 16 Mar 2011 23:12:16 -0400 Subject: [PATCH] Add support for file descriptor type detection. setImplementationMethods checks the type of a socket and defines different behavior based on the type, so auto detect it if type not implicitly specified. --- lib/net.js | 36 ++++++++++++++-- src/node_net.cc | 72 ++++++++++++++++++++++++++++++++ test/simple/test-gets-auto-fd.js | 24 +++++++++++ 3 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 test/simple/test-gets-auto-fd.js diff --git a/lib/net.js b/lib/net.js index 39010bf8b1..90a96b8d59 100644 --- a/lib/net.js +++ b/lib/net.js @@ -226,7 +226,7 @@ function Socket(options) { this.allowHalfOpen = options.allowHalfOpen || false; } else if (typeof options == 'number') { this.fd = arguments[0]; - this.type = arguments[1]; + this.type = arguments[1] || null; } if (parseInt(this.fd, 10) >= 0) { @@ -235,6 +235,7 @@ function Socket(options) { setImplmentationMethods(this); } } + util.inherits(Socket, stream.Stream); exports.Socket = Socket; @@ -250,7 +251,7 @@ Socket.prototype.open = function(fd, type) { initSocket(this); this.fd = fd; - this.type = type || null; + this.type = type || getFdType(fd) this.readable = true; setImplmentationMethods(this); @@ -1142,16 +1143,43 @@ Server.prototype.close = function() { } }; +function getFdType(fd) { + var type = 'file'; + var family; + try { + type = binding.getsocktype(fd); + family = binding.getsockfamily(fd); + if (family == "AF_UNIX") { + type = "unix"; + } else if (type == "SOCK_STREAM" && family == "AF_INET") { + type = "tcp"; + } else if (type == "SOCK_STREAM" && family == "AF_INET6") { + type = "tcp6"; + } else if (type == "SOCK_DGRAM" && family == "AF_INET") { + type = "udp"; + } else if (type == "SOCK_DGRAM" && family == "AF_INET6") { + type = "udp6"; + } + } catch (e) { + if (e.code == 'ENOTSOCK') { + type = 'file'; + } else { + throw e; + } + } + return type; +} +exports.getFdType = getFdType; var dummyFD = null; var lastEMFILEWarning = 0; -// Ensures to have at least on free file-descriptor free. +// Ensures to have at least one free file-descriptor free. // callback should only use 1 file descriptor and close it before end of call function rescueEMFILE(callback) { // Output a warning, but only at most every 5 seconds. var now = new Date(); if (now - lastEMFILEWarning > 5000) { - console.error('(node) Hit max file limit. Increase "ulimit - n"'); + console.error('(node) Hit max file limit. Increase "ulimit -n"'); lastEMFILEWarning = now; } diff --git a/src/node_net.cc b/src/node_net.cc index a0bda02035..d2017d5cad 100644 --- a/src/node_net.cc +++ b/src/node_net.cc @@ -591,6 +591,42 @@ static Handle GetSockName(const Arguments& args) { return scope.Close(info); } +static Handle GetSockFamily(const Arguments& args) { + HandleScope scope; + + FD_ARG(args[0]) + + Local result; + + struct sockaddr_storage address_storage; + socklen_t len = sizeof(struct sockaddr_storage); + +#ifdef __POSIX__ + if (0 > getsockname(fd, (struct sockaddr *) &address_storage, &len)) { + return ThrowException(ErrnoException(errno, "getsockname")); + } + +#else // __MINGW32__ + if (SOCKET_ERROR == getsockname(_get_osfhandle(fd), + (struct sockaddr *) &address_storage, &len)) { + return ThrowException(ErrnoException(WSAGetLastError(), "getsockname")); + } +#endif // __MINGW32__ + switch ((address_storage).ss_family) { + case AF_INET6: + result = String::New("AF_INET6"); + break; + case AF_INET: + result = String::New("AF_INET"); + break; + case AF_UNIX: + result = String::New("AF_UNIX"); + break; + default: + result = Integer::New((address_storage).ss_family); + } + scope.Close(result); +} static Handle GetPeerName(const Arguments& args) { HandleScope scope; @@ -720,6 +756,40 @@ static Handle SocketError(const Arguments& args) { return scope.Close(Integer::New(error)); } +static Handle GetSockType(const Arguments& args) { + HandleScope scope; + + FD_ARG(args[0]) + + int type; + socklen_t len = sizeof(int); + +#ifdef __POSIX__ + int r = getsockopt(fd, SOL_SOCKET, SO_TYPE, &type, &len); + + if (r < 0) { + return ThrowException(ErrnoException(errno, "getsockopt")); + } +#else // __MINGW32__ + int r = getsockopt(_get_osfhandle(fd), SOL_SOCKET, SO_TYPE, (char*)&type, &len); + + if (r < 0) { + return ThrowException(ErrnoException(WSAGetLastError(), "getsockopt")); + } +#endif + Local result; + switch (type) { + case SOCK_STREAM: + result = String::New("SOCK_STREAM"); + break; + case SOCK_DGRAM: + result = String::New("SOCK_DGRAM"); + break; + default: + result = Integer::New(type); + } + return scope.Close(result); +} // var bytesRead = t.read(fd, buffer, offset, length); // returns null on EAGAIN or EINTR, raises an exception on all other errors @@ -1760,6 +1830,8 @@ void InitNet(Handle target) { NODE_SET_METHOD(target, "getaddrinfo", GetAddrInfo); NODE_SET_METHOD(target, "isIP", IsIP); NODE_SET_METHOD(target, "errnoException", CreateErrnoException); + NODE_SET_METHOD(target, "getsocktype", GetSockType); + NODE_SET_METHOD(target, "getsockfamily", GetSockFamily); errno_symbol = NODE_PSYMBOL("errno"); syscall_symbol = NODE_PSYMBOL("syscall"); diff --git a/test/simple/test-gets-auto-fd.js b/test/simple/test-gets-auto-fd.js new file mode 100644 index 0000000000..bd0f6627e2 --- /dev/null +++ b/test/simple/test-gets-auto-fd.js @@ -0,0 +1,24 @@ +var assert = require('assert'); +var net = require('net'); +var common = require('../common'); + +var server = net.createServer(); + +//test unix sockets +var fds = process.binding('net').socketpair(); +var unixsocket = new net.Socket(fds[0]); +assert.ok(unixsocket.type == 'unix', 'Should be UNIX'); + +//test that stdin is default file +assert.ok(process.stdin.type == 'file', 'Should be File'); + +//test tcp sockets. +server.listen(function() { + var client = net.createConnection(this.address().port); + client.on('connect', function() { + var newStream = new net.Socket(client.fd); + assert.ok(newStream.type == 'tcp', 'Should be TCP'); + client.destroy(); + server.close(); + }); +});