Start on net2
authorRyan Dahl <ry@tinyclouds.org>
Mon, 14 Dec 2009 08:42:02 +0000 (09:42 +0100)
committerRyan Dahl <ry@tinyclouds.org>
Tue, 29 Dec 2009 20:12:30 +0000 (21:12 +0100)
src/node.cc
src/node_net2.cc [new file with mode: 0644]
src/node_net2.h [new file with mode: 0644]
wscript

index d150e9e..81a100a 100644 (file)
@@ -12,6 +12,7 @@
 
 #include <node_buffer.h>
 #include <node_io_watcher.h>
+#include <node_net2.h>
 #include <node_events.h>
 #include <node_dns.h>
 #include <node_net.h>
@@ -856,6 +857,8 @@ static Local<Object> Load(int argc, char *argv[]) {
   Stat::Initialize(process);                   // stat.cc
   SignalHandler::Initialize(process);          // signal_handler.cc
 
+  InitNet2(process);                           // net2.cc
+
   Stdio::Initialize(process);                  // stdio.cc
   ChildProcess::Initialize(process);           // child_process.cc
   DefineConstants(process);                    // constants.cc
diff --git a/src/node_net2.cc b/src/node_net2.cc
new file mode 100644 (file)
index 0000000..6ced4a2
--- /dev/null
@@ -0,0 +1,178 @@
+#include <node_net2.h>
+#include <v8.h>
+
+#include <node.h>
+
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <arpa/inet.h> /* inet_pton */
+
+#include <errno.h>
+
+
+
+namespace node {
+
+using namespace v8;
+
+static Persistent<String> errno_symbol;
+static Persistent<String> syscall_symbol;
+
+static inline Local<Value> ErrnoException(int errorno, const char *syscall, const char *msg = "") {
+  if (!msg[0]) msg = strerror(errorno);
+  Local<Value> e = Exception::Error(String::NewSymbol(msg));
+  Local<Object> obj = e->ToObject();
+  obj->Set(errno_symbol, Integer::New(errorno));
+  obj->Set(syscall_symbol, String::NewSymbol(syscall));
+  return e;
+}
+
+// Creates a new non-blocking socket fd
+// t.socket("TCP");
+// t.socket("UNIX");
+// t.socket("UDP");
+static Handle<Value> Socket(const Arguments& args) {
+  HandleScope scope;
+
+  // default to TCP
+  int domain = PF_INET6;
+  int type = SOCK_STREAM;
+
+  if (args[0]->IsString()) {
+    String::Utf8Value t(args[0]->ToString());
+    if (0 == strcasecmp(*t, "TCP")) {
+      domain = PF_INET6;
+      type = SOCK_STREAM;
+    } else if (0 == strcasecmp(*t, "UDP")) {
+      domain = PF_INET6;
+      type = SOCK_DGRAM;
+    } else if (0 == strcasecmp(*t, "UNIX")) {
+      domain = PF_UNIX;
+      type = SOCK_STREAM;
+    } else {
+      return ThrowException(Exception::Error(
+            String::New("Unknown socket type.")));
+    }
+  }
+
+  int fd = socket(domain, type, 0);
+
+  if (fd < 0) return ThrowException(ErrnoException(errno, "socket"));
+
+  int fcntl_errno;
+
+  int flags = fcntl(fd, F_GETFL, 0);
+  if (flags == -1) {
+    fcntl_errno = errno;
+    close(fd);
+    return ThrowException(ErrnoException(fcntl_errno, "fcntl"));
+  }
+
+  flags |= O_NONBLOCK;
+
+  if (fcntl(fd, F_SETFL, flags) == -1) {
+    fcntl_errno = errno;
+    close(fd);
+    return ThrowException(ErrnoException(fcntl_errno, "fcntl"));
+  }
+
+  return scope.Close(Integer::New(fd));
+}
+
+// 2 arguments means connect with unix
+//   t.connect(fd, "/tmp/socket")
+//
+// 3 arguments means connect with TCP or UDP
+//   t.connect(fd, "127.0.0.1", 80)
+static Handle<Value> Connect(const Arguments& args) {
+  HandleScope scope;
+
+  if (!args[0]->IsInt32()) {
+    return ThrowException(Exception::TypeError(
+          String::New("First argument should be file descriptor")));
+  }
+
+  if (args.Length() < 2) {
+    return ThrowException(Exception::TypeError(
+          String::New("Must have at least two args")));
+  }
+
+  int fd = args[0]->Int32Value();
+
+  struct sockaddr *addr;
+  socklen_t addrlen;
+
+  if (args.Length() == 2) {
+    // UNIX
+    String::Utf8Value path(args[1]->ToString());
+
+    struct sockaddr_un un = {0};
+
+    if (path.length() > sizeof un.sun_path) {
+      return ThrowException(Exception::Error(String::New("Socket path too long")));
+    }
+
+    un.sun_family = AF_UNIX;
+    strcpy(un.sun_path, *path);
+
+    addr = (struct sockaddr*)&un;
+    addrlen = path.length() + sizeof(un.sun_family);
+
+  } else {
+    // TCP or UDP
+    String::Utf8Value ip(args[1]->ToString());
+    int port = args[2]->Int32Value();
+   
+    struct sockaddr_in6 in6 = {0};
+
+    char ipv6[255] = "::FFFF:";
+
+    if (inet_pton(AF_INET, *ip, &(in6.sin6_addr)) > 0) {
+      // If this is an IPv4 address then we need to change it to the
+      // IPv4-mapped-on-IPv6 format which looks like 
+      //  ::FFFF:<IPv4  address>
+      // For more information see "Address Format" ipv6(7) and "BUGS" in
+      // inet_pton(3) 
+      strcat(ipv6, *ip);
+    } else {
+      strcpy(ipv6, *ip);
+    }
+
+    if (inet_pton(AF_INET6, ipv6, &(in6.sin6_addr)) <= 0) {
+      return ThrowException(
+        ErrnoException(errno, "inet_pton", "Invalid IP Address"));
+    }
+
+    in6.sin6_family = AF_INET6;
+    in6.sin6_port = htons(port);  
+
+    addr = (struct sockaddr*)&in6;
+    addrlen = sizeof in6;
+  }
+
+  int r = connect(fd, addr, addrlen);
+
+  if (r < 0 && errno != EINPROGRESS) {
+    return ThrowException(ErrnoException(errno, "connect"));
+  }
+
+  return Undefined();
+}
+
+
+void InitNet2(Handle<Object> target) {
+  HandleScope scope;
+
+  NODE_SET_METHOD(target, "socket", Socket);
+  NODE_SET_METHOD(target, "connect", Connect);
+  errno_symbol = NODE_PSYMBOL("errno");
+  syscall_symbol = NODE_PSYMBOL("syscall");
+}
+
+}  // namespace node
diff --git a/src/node_net2.h b/src/node_net2.h
new file mode 100644 (file)
index 0000000..cd3e9f0
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef NODE_NET2
+#define NODE_NET2
+
+#include <v8.h>
+
+namespace node {
+
+void InitNet2(v8::Handle<v8::Object> target);
+
+}
+
+#endif  // NODE_NET2
diff --git a/wscript b/wscript
index 773a17f..d2e13c9 100644 (file)
--- a/wscript
+++ b/wscript
@@ -323,6 +323,7 @@ def build(bld):
   node.source = """
     src/node.cc
     src/node_buffer.cc
+    src/node_net2.cc
     src/node_io_watcher.cc
     src/node_child_process.cc
     src/node_constants.cc