begin work on the TCP.connect interface
authorRyan <ry@tinyclouds.org>
Mon, 2 Mar 2009 14:49:24 +0000 (15:49 +0100)
committerRyan <ry@tinyclouds.org>
Mon, 2 Mar 2009 14:49:24 +0000 (15:49 +0100)
Makefile
README
example.js
server.cc
tcp.cc [new file with mode: 0644]
tcp.h [new file with mode: 0644]
tcp_example.js [new file with mode: 0644]

index 8396309..6d7086c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -10,11 +10,14 @@ ifdef EVDIR
        LDFLAGS += -L$(EVDIR)/lib
 endif
 
-server: server.o oi_socket.o ebb_request_parser.o oi_buf.o
+server: server.o tcp.o oi_socket.o oi_async.o ebb_request_parser.o oi_buf.o
        g++ -o server $^ $(LDFLAGS) $(V8LIB) 
 
 server.o: server.cc 
        g++ $(CFLAGS) -c $<
+
+tcp.o: tcp.cc 
+       g++ $(CFLAGS) -c $<
        
 ebb_request_parser.o: ebb_request_parser.c deps/ebb/ebb_request_parser.h 
        g++ $(CFLAGS) -c $<
@@ -25,6 +28,9 @@ ebb_request_parser.c: deps/ebb/ebb_request_parser.rl
 oi_socket.o: deps/oi/oi_socket.c deps/oi/oi_socket.h 
        gcc $(CFLAGS) -c $<
 
+oi_async.o: deps/oi/oi_async.c deps/oi/oi_async.h 
+       gcc $(CFLAGS) -c $<
+
 oi_buf.o: deps/oi/oi_buf.c deps/oi/oi_buf.h 
        gcc $(CFLAGS) -c $<
 
diff --git a/README b/README
index b177f94..58c89a8 100644 (file)
--- a/README
+++ b/README
@@ -1,4 +1,16 @@
+WHEREAS, The usage of threads has complicated computer programming; and
 
-git submodule init
-git submodule update
-make
+WHEREAS, V8 javascript comes free of I/O and threads; and
+
+WHEREAS, Most operating systems do not provide asynchonous file system
+access.
+
+Now, therefore:
+
+This set server and client libraries were made to build simple but fast
+servers. They are provided free of charge under a permissive simple license. 
+
+Submitted by
+Ryah Dahl, Programmer
+Tim Becker, Programmer
+March 1, 2009 
index 4befa8e..515c598 100644 (file)
@@ -1,22 +1,26 @@
-  function encode(data) { 
-    var chunk = data.toString();
-    return chunk.length.toString(16) + "\r\n" + chunk + "\r\n";
-  }
+function encode(data) { 
+  var chunk = data.toString();
+  return chunk.length.toString(16) + "\r\n" + chunk + "\r\n";
+}
+
+function Process(request) {
+
+  log( "path: " + request.path );
+  log( "query string: " + request.query_string );
 
-  function Process(request) {
-    // onBody sends null on the last chunk.
-    request.onBody = function (chunk) {
-      if(chunk) { 
-        this.respond(encode(chunk));
-      } else {
-        this.respond(encode("\n"));
-        this.respond("0\r\n\r\n");
-        this.respond(null); // signals end-of-request
-      }
+  // onBody sends null on the last chunk.
+  request.onBody = function (chunk) {
+    if(chunk) { 
+      this.respond(encode(chunk));
+    } else {
+      this.respond(encode("\n"));
+      this.respond("0\r\n\r\n");
+      this.respond(null); // signals end-of-request
     }
-    request.respond("HTTP/1.0 200 OK\r\n");
-    request.respond("Content-Type: text-plain\r\n");
-    request.respond("Transfer-Encoding: chunked\r\n");
-    request.respond("\r\n");
   }
+  request.respond("HTTP/1.0 200 OK\r\n");
+  request.respond("Content-Type: text/plain\r\n");
+  request.respond("Transfer-Encoding: chunked\r\n");
+  request.respond("\r\n");
+}
 
index ef45738..def6287 100644 (file)
--- a/server.cc
+++ b/server.cc
@@ -1,6 +1,8 @@
 #include <oi.h>
 #include <ebb_request_parser.h>
 
+#include "tcp.h"
+
 #include <stdio.h>
 #include <assert.h>
 #include <string>
@@ -17,7 +19,7 @@ using namespace std;
 static oi_server server;
 static struct ev_loop *loop;
 
-static Persistent<Context> context_;
+static Persistent<Context> context;
 static Persistent<Function> process_;
 static Persistent<ObjectTemplate> request_template_;
 
@@ -320,7 +322,7 @@ static void on_headers_complete
 
   // Enter this processor's context so all the remaining operations
   // take place there
-  Context::Scope context_scope(context_);
+  Context::Scope context_scope(context);
 
   // Set up an exception handler before calling the Process function
   TryCatch try_catch;
@@ -329,7 +331,7 @@ static void on_headers_complete
   // and one argument, the request.
   const int argc = 1;
   Handle<Value> argv[argc] = { request->js_object };
-  Handle<Value> r = process_->Call(context_->Global(), argc, argv);
+  Handle<Value> r = process_->Call(context->Global(), argc, argv);
   if (r.IsEmpty()) {
     String::Utf8Value error(try_catch.Exception());
     printf("error: %s\n", *error);
@@ -495,9 +497,13 @@ static bool compile
   // Compile the script and check for errors.
   Handle<Script> compiled_script = Script::Compile(script);
   if (compiled_script.IsEmpty()) {
+
+    Handle<Message> message = try_catch.Message();
+
     String::Utf8Value error(try_catch.Exception());
-    printf("error: %s\n", *error);
-    // The script failed to compile; bail out.
+
+    printf("error: %s line %d\n", *error, message->GetLineNumber());
+
     return false;
   }
 
@@ -533,6 +539,9 @@ int main
   , char *argv[]
   ) 
 {
+  loop = ev_default_loop(0);
+
+
   map<string, string> options;
   string file;
   ParseOptions(argc, argv, options, &file);
@@ -547,15 +556,13 @@ int main
     return 1;
   }
 
-  Handle<ObjectTemplate> global = ObjectTemplate::New();
-  global->Set(String::New("log"), FunctionTemplate::New(LogCallback));
-
-  Handle<Context> context = Context::New(NULL, global);
-
-  context_ = Persistent<Context>::New(context);
-
+  context = Context::New(NULL, ObjectTemplate::New());
   Context::Scope context_scope(context);
 
+  Local<Object> g = Context::GetCurrent()->Global();
+  g->Set( String::New("log"), FunctionTemplate::New(LogCallback)->GetFunction());
+  g->Set( String::New("TCP"), tcp_initialize(loop));
+
   // Compile and run the script
   if (!compile(source))
     return false;
@@ -579,8 +586,6 @@ int main
   /////////////////////////////////////
   /////////////////////////////////////
   /////////////////////////////////////
-
-  loop = ev_default_loop(0);
   
   oi_server_init(&server, 1024);
   server.on_connection = new_connection;
@@ -604,7 +609,7 @@ int main
 
   ev_loop(loop, 0);
 
-  context_.Dispose();
+  context.Dispose();
   process_.Dispose();
 
   return 0;
diff --git a/tcp.cc b/tcp.cc
new file mode 100644 (file)
index 0000000..83fc818
--- /dev/null
+++ b/tcp.cc
@@ -0,0 +1,184 @@
+#include "tcp.h"
+
+#include <oi_socket.h>
+#include <oi_async.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+
+
+/*
+  Target API
+
+  TCP.connect({
+
+      host: "google.com",
+
+      port: 80, 
+
+      connect: function () {
+          this.write("GET /search?q=hello HTTP/1.0\r\n\r\n");
+      },
+
+      read: function (data) {
+
+          request.respond("<table> <td>" + data + "</td> </table>");
+
+      },
+
+      drain: function () {
+      }, 
+
+      error: function () {
+      } 
+  });
+
+*/
+
+static oi_async thread_pool;
+
+static struct addrinfo tcp_hints = 
+/* ai_flags      */ { AI_PASSIVE
+/* ai_family     */ , AF_UNSPEC
+/* ai_socktype   */ , SOCK_STREAM
+/* ai_protocol   */ , 0
+/* ai_addrlen    */ , 0
+/* ai_addr       */ , 0
+/* ai_canonname  */ , 0
+/* ai_next       */ , 0
+                    };
+
+static struct ev_loop *loop;
+
+class TCPClient {
+public:
+  oi_task resolve_task;
+  oi_socket socket;
+  struct addrinfo *address;
+  Persistent<Object> options;
+};
+
+static void on_connect
+  ( oi_socket *socket
+  )
+{
+  TCPClient *client = static_cast<TCPClient*> (socket->data);
+
+  HandleScope scope;
+
+  Handle<Value> connect_value = client->options->Get( String::NewSymbol("connect") );
+  if (!connect_value->IsFunction())
+    return; // error!
+  Handle<Function> connect_cb = Handle<Function>::Cast(connect_value);
+
+  TryCatch try_catch;
+  Handle<Value> r = connect_cb->Call(client->options, 0, NULL);
+  if (r.IsEmpty()) {
+    String::Utf8Value error(try_catch.Exception());
+    printf("connect error: %s\n", *error);
+  }
+}
+
+static void resolve_done
+  ( oi_task *resolve_task
+  , int result
+  )
+{
+  TCPClient *client = static_cast<TCPClient*> (resolve_task->data);
+
+  if(result != 0) {
+    printf("error. TODO make call options error callback\n");
+    client->options.Dispose();
+    delete client;
+    return;
+  }
+
+  // Got the address succesfully. Let's connect now.  
+
+  oi_socket_init(&client->socket, 30.0); // TODO adjustable timeout
+
+  client->socket.on_connect = on_connect;
+  client->socket.on_read    = NULL;
+  client->socket.on_drain   = NULL;
+  client->socket.on_error   = NULL;
+  client->socket.on_close   = NULL;
+  client->socket.on_timeout = NULL;
+  client->socket.data = client;
+
+  oi_socket_connect (&client->socket, client->address);
+  oi_socket_attach (&client->socket, loop);
+
+
+  freeaddrinfo(client->address);
+  client->address = NULL;
+}
+
+static Handle<Value> Connect
+  ( const Arguments& args
+  ) 
+{
+  if (args.Length() < 1)
+    return Undefined();
+
+  HandleScope scope;
+
+  Handle<Value> arg = args[0];
+  Handle<Object> options = arg->ToObject();
+
+  /* Make sure the user has provided at least host and port */   
+
+  Handle<Value> host_value = options->Get( String::NewSymbol("host") );
+
+  if(host_value->IsUndefined()) 
+    return False();    
+
+  Handle<Value> port_value = options->Get( String::NewSymbol("port") );
+
+  if(port_value->IsUndefined()) 
+    return False();    
+
+  Handle<String> host = host_value->ToString();
+  Handle<String> port = port_value->ToString();
+
+  char host_s[host->Length()+1];  // + 1 for \0
+  char port_s[port->Length()+1];
+
+  host->WriteAscii(host_s, 0, host->Length());
+  port->WriteAscii(port_s, 0, port->Length());
+
+  printf("resolving host: %s, port: %s\n", host_s, port_s);
+
+  TCPClient *client = new TCPClient;
+
+  oi_task_init_getaddrinfo ( &client->resolve_task
+                           , resolve_done
+                           , host_s
+                           , port_s
+                           , &tcp_hints
+                           , &client->address
+                           );
+  client->options = Persistent<Object>::New(options); 
+
+  oi_async_submit (&thread_pool, &client->resolve_task);
+}
+
+Handle<Object> tcp_initialize
+  ( struct ev_loop *_loop
+  )
+{
+  loop = _loop;
+
+  oi_async_init(&thread_pool);
+  oi_async_attach(loop, &thread_pool);
+
+  HandleScope scope;
+
+  Local<Object> t = Object::New();
+
+  t->Set(String::New("connect"), FunctionTemplate::New(Connect)->GetFunction());
+
+  return scope.Close(t);
+}
+
diff --git a/tcp.h b/tcp.h
new file mode 100644 (file)
index 0000000..d42b6c0
--- /dev/null
+++ b/tcp.h
@@ -0,0 +1,11 @@
+#ifndef tcp_h
+#define tcp_h
+
+#include <v8.h>
+#include <ev.h>
+
+using namespace v8;
+
+Handle<Object> tcp_initialize (struct ev_loop *);
+
+#endif
diff --git a/tcp_example.js b/tcp_example.js
new file mode 100644 (file)
index 0000000..8940b85
--- /dev/null
@@ -0,0 +1,9 @@
+
+TCP.connect ({
+  host: "google.com",
+  port: 80,
+  connected: function () {
+    log("connected to google.com");
+  }
+});
+