Skip to content

Commit

Permalink
begin work on the TCP.connect interface
Browse files Browse the repository at this point in the history
  • Loading branch information
ry committed Mar 2, 2009
1 parent bd136fa commit 1afe6d2
Show file tree
Hide file tree
Showing 7 changed files with 268 additions and 37 deletions.
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 $<
Expand All @@ -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 $<

Expand Down
18 changes: 15 additions & 3 deletions README
Original file line number Diff line number Diff line change
@@ -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
40 changes: 22 additions & 18 deletions example.js
Original file line number Diff line number Diff line change
@@ -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");
}

35 changes: 20 additions & 15 deletions server.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <oi.h>
#include <ebb_request_parser.h>

#include "tcp.h"

#include <stdio.h>
#include <assert.h>
#include <string>
Expand All @@ -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_;

Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -533,6 +539,9 @@ int main
, char *argv[]
)
{
loop = ev_default_loop(0);


map<string, string> options;
string file;
ParseOptions(argc, argv, options, &file);
Expand All @@ -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;
Expand All @@ -579,8 +586,6 @@ int main
/////////////////////////////////////
/////////////////////////////////////
/////////////////////////////////////

loop = ev_default_loop(0);

oi_server_init(&server, 1024);
server.on_connection = new_connection;
Expand All @@ -604,7 +609,7 @@ int main

ev_loop(loop, 0);

context_.Dispose();
context.Dispose();
process_.Dispose();

return 0;
Expand Down
184 changes: 184 additions & 0 deletions tcp.cc
Original file line number Diff line number Diff line change
@@ -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);
}

11 changes: 11 additions & 0 deletions tcp.h
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions tcp_example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

TCP.connect ({
host: "google.com",
port: 80,
connected: function () {
log("connected to google.com");
}
});

0 comments on commit 1afe6d2

Please sign in to comment.