mirror of
https://github.com/nodejs/node-v0.x-archive.git
synced 2026-04-28 03:01:10 -04:00
begin work on the TCP.connect interface
This commit is contained in:
8
Makefile
8
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 $<
|
||||
|
||||
|
||||
18
README
18
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
|
||||
|
||||
40
example.js
40
example.js
@@ -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) {
|
||||
// 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
|
||||
}
|
||||
function Process(request) {
|
||||
|
||||
log( "path: " + request.path );
|
||||
log( "query string: " + request.query_string );
|
||||
|
||||
// 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
server.cc
35
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;
|
||||
|
||||
184
tcp.cc
Normal file
184
tcp.cc
Normal file
@@ -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
Normal file
11
tcp.h
Normal file
@@ -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
Normal file
9
tcp_example.js
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
TCP.connect ({
|
||||
host: "google.com",
|
||||
port: 80,
|
||||
connected: function () {
|
||||
log("connected to google.com");
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user