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 $<
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 $<
+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
- 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");
+}
#include <oi.h>
#include <ebb_request_parser.h>
+#include "tcp.h"
+
#include <stdio.h>
#include <assert.h>
#include <string>
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_;
// 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;
// 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);
// 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;
}
, char *argv[]
)
{
+ loop = ev_default_loop(0);
+
+
map<string, string> options;
string file;
ParseOptions(argc, argv, options, &file);
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;
/////////////////////////////////////
/////////////////////////////////////
/////////////////////////////////////
-
- loop = ev_default_loop(0);
oi_server_init(&server, 1024);
server.on_connection = new_connection;
ev_loop(loop, 0);
- context_.Dispose();
+ context.Dispose();
process_.Dispose();
return 0;
--- /dev/null
+#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);
+}
+
--- /dev/null
+#ifndef tcp_h
+#define tcp_h
+
+#include <v8.h>
+#include <ev.h>
+
+using namespace v8;
+
+Handle<Object> tcp_initialize (struct ev_loop *);
+
+#endif
--- /dev/null
+
+TCP.connect ({
+ host: "google.com",
+ port: 80,
+ connected: function () {
+ log("connected to google.com");
+ }
+});
+