Browse Source

begin work on the TCP.connect interface

v0.7.4-release
Ryan 16 years ago
parent
commit
1afe6d26db
  1. 8
      Makefile
  2. 18
      README
  3. 6
      example.js
  4. 35
      server.cc
  5. 184
      tcp.cc
  6. 11
      tcp.h
  7. 9
      tcp_example.js

8
Makefile

@ -10,12 +10,15 @@ ifdef EVDIR
LDFLAGS += -L$(EVDIR)/lib LDFLAGS += -L$(EVDIR)/lib
endif 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) g++ -o server $^ $(LDFLAGS) $(V8LIB)
server.o: server.cc server.o: server.cc
g++ $(CFLAGS) -c $< g++ $(CFLAGS) -c $<
tcp.o: tcp.cc
g++ $(CFLAGS) -c $<
ebb_request_parser.o: ebb_request_parser.c deps/ebb/ebb_request_parser.h ebb_request_parser.o: ebb_request_parser.c deps/ebb/ebb_request_parser.h
g++ $(CFLAGS) -c $< 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 oi_socket.o: deps/oi/oi_socket.c deps/oi/oi_socket.h
gcc $(CFLAGS) -c $< 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 oi_buf.o: deps/oi/oi_buf.c deps/oi/oi_buf.h
gcc $(CFLAGS) -c $< gcc $(CFLAGS) -c $<

18
README

@ -1,4 +1,16 @@
WHEREAS, The usage of threads has complicated computer programming; and
git submodule init WHEREAS, V8 javascript comes free of I/O and threads; and
git submodule update
make 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

6
example.js

@ -4,6 +4,10 @@
} }
function Process(request) { function Process(request) {
log( "path: " + request.path );
log( "query string: " + request.query_string );
// onBody sends null on the last chunk. // onBody sends null on the last chunk.
request.onBody = function (chunk) { request.onBody = function (chunk) {
if(chunk) { if(chunk) {
@ -15,7 +19,7 @@
} }
} }
request.respond("HTTP/1.0 200 OK\r\n"); request.respond("HTTP/1.0 200 OK\r\n");
request.respond("Content-Type: text-plain\r\n"); request.respond("Content-Type: text/plain\r\n");
request.respond("Transfer-Encoding: chunked\r\n"); request.respond("Transfer-Encoding: chunked\r\n");
request.respond("\r\n"); request.respond("\r\n");
} }

35
server.cc

@ -1,6 +1,8 @@
#include <oi.h> #include <oi.h>
#include <ebb_request_parser.h> #include <ebb_request_parser.h>
#include "tcp.h"
#include <stdio.h> #include <stdio.h>
#include <assert.h> #include <assert.h>
#include <string> #include <string>
@ -17,7 +19,7 @@ using namespace std;
static oi_server server; static oi_server server;
static struct ev_loop *loop; static struct ev_loop *loop;
static Persistent<Context> context_; static Persistent<Context> context;
static Persistent<Function> process_; static Persistent<Function> process_;
static Persistent<ObjectTemplate> request_template_; static Persistent<ObjectTemplate> request_template_;
@ -320,7 +322,7 @@ static void on_headers_complete
// Enter this processor's context so all the remaining operations // Enter this processor's context so all the remaining operations
// take place there // take place there
Context::Scope context_scope(context_); Context::Scope context_scope(context);
// Set up an exception handler before calling the Process function // Set up an exception handler before calling the Process function
TryCatch try_catch; TryCatch try_catch;
@ -329,7 +331,7 @@ static void on_headers_complete
// and one argument, the request. // and one argument, the request.
const int argc = 1; const int argc = 1;
Handle<Value> argv[argc] = { request->js_object }; 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()) { if (r.IsEmpty()) {
String::Utf8Value error(try_catch.Exception()); String::Utf8Value error(try_catch.Exception());
printf("error: %s\n", *error); printf("error: %s\n", *error);
@ -495,9 +497,13 @@ static bool compile
// Compile the script and check for errors. // Compile the script and check for errors.
Handle<Script> compiled_script = Script::Compile(script); Handle<Script> compiled_script = Script::Compile(script);
if (compiled_script.IsEmpty()) { if (compiled_script.IsEmpty()) {
Handle<Message> message = try_catch.Message();
String::Utf8Value error(try_catch.Exception()); 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; return false;
} }
@ -533,6 +539,9 @@ int main
, char *argv[] , char *argv[]
) )
{ {
loop = ev_default_loop(0);
map<string, string> options; map<string, string> options;
string file; string file;
ParseOptions(argc, argv, options, &file); ParseOptions(argc, argv, options, &file);
@ -547,15 +556,13 @@ int main
return 1; return 1;
} }
Handle<ObjectTemplate> global = ObjectTemplate::New(); context = Context::New(NULL, ObjectTemplate::New());
global->Set(String::New("log"), FunctionTemplate::New(LogCallback));
Handle<Context> context = Context::New(NULL, global);
context_ = Persistent<Context>::New(context);
Context::Scope context_scope(context); 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 // Compile and run the script
if (!compile(source)) if (!compile(source))
return false; return false;
@ -580,8 +587,6 @@ int main
///////////////////////////////////// /////////////////////////////////////
///////////////////////////////////// /////////////////////////////////////
loop = ev_default_loop(0);
oi_server_init(&server, 1024); oi_server_init(&server, 1024);
server.on_connection = new_connection; server.on_connection = new_connection;
@ -604,7 +609,7 @@ int main
ev_loop(loop, 0); ev_loop(loop, 0);
context_.Dispose(); context.Dispose();
process_.Dispose(); process_.Dispose();
return 0; return 0;

184
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);
}

11
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

9
tcp_example.js

@ -0,0 +1,9 @@
TCP.connect ({
host: "google.com",
port: 80,
connected: function () {
log("connected to google.com");
}
});
Loading…
Cancel
Save