Skip to content

Commit 1afe6d2

Browse files
committed
begin work on the TCP.connect interface
1 parent bd136fa commit 1afe6d2

File tree

7 files changed

+268
-37
lines changed

7 files changed

+268
-37
lines changed

Makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,14 @@ ifdef EVDIR
1010
LDFLAGS += -L$(EVDIR)/lib
1111
endif
1212

13-
server: server.o oi_socket.o ebb_request_parser.o oi_buf.o
13+
server: server.o tcp.o oi_socket.o oi_async.o ebb_request_parser.o oi_buf.o
1414
g++ -o server $^ $(LDFLAGS) $(V8LIB)
1515

1616
server.o: server.cc
1717
g++ $(CFLAGS) -c $<
18+
19+
tcp.o: tcp.cc
20+
g++ $(CFLAGS) -c $<
1821

1922
ebb_request_parser.o: ebb_request_parser.c deps/ebb/ebb_request_parser.h
2023
g++ $(CFLAGS) -c $<
@@ -25,6 +28,9 @@ ebb_request_parser.c: deps/ebb/ebb_request_parser.rl
2528
oi_socket.o: deps/oi/oi_socket.c deps/oi/oi_socket.h
2629
gcc $(CFLAGS) -c $<
2730

31+
oi_async.o: deps/oi/oi_async.c deps/oi/oi_async.h
32+
gcc $(CFLAGS) -c $<
33+
2834
oi_buf.o: deps/oi/oi_buf.c deps/oi/oi_buf.h
2935
gcc $(CFLAGS) -c $<
3036

README

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,16 @@
1+
WHEREAS, The usage of threads has complicated computer programming; and
12

2-
git submodule init
3-
git submodule update
4-
make
3+
WHEREAS, V8 javascript comes free of I/O and threads; and
4+
5+
WHEREAS, Most operating systems do not provide asynchonous file system
6+
access.
7+
8+
Now, therefore:
9+
10+
This set server and client libraries were made to build simple but fast
11+
servers. They are provided free of charge under a permissive simple license.
12+
13+
Submitted by
14+
Ryah Dahl, Programmer
15+
Tim Becker, Programmer
16+
March 1, 2009

example.js

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
1-
function encode(data) {
2-
var chunk = data.toString();
3-
return chunk.length.toString(16) + "\r\n" + chunk + "\r\n";
4-
}
1+
function encode(data) {
2+
var chunk = data.toString();
3+
return chunk.length.toString(16) + "\r\n" + chunk + "\r\n";
4+
}
5+
6+
function Process(request) {
7+
8+
log( "path: " + request.path );
9+
log( "query string: " + request.query_string );
510

6-
function Process(request) {
7-
// onBody sends null on the last chunk.
8-
request.onBody = function (chunk) {
9-
if(chunk) {
10-
this.respond(encode(chunk));
11-
} else {
12-
this.respond(encode("\n"));
13-
this.respond("0\r\n\r\n");
14-
this.respond(null); // signals end-of-request
15-
}
11+
// onBody sends null on the last chunk.
12+
request.onBody = function (chunk) {
13+
if(chunk) {
14+
this.respond(encode(chunk));
15+
} else {
16+
this.respond(encode("\n"));
17+
this.respond("0\r\n\r\n");
18+
this.respond(null); // signals end-of-request
1619
}
17-
request.respond("HTTP/1.0 200 OK\r\n");
18-
request.respond("Content-Type: text-plain\r\n");
19-
request.respond("Transfer-Encoding: chunked\r\n");
20-
request.respond("\r\n");
2120
}
21+
request.respond("HTTP/1.0 200 OK\r\n");
22+
request.respond("Content-Type: text/plain\r\n");
23+
request.respond("Transfer-Encoding: chunked\r\n");
24+
request.respond("\r\n");
25+
}
2226

server.cc

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#include <oi.h>
22
#include <ebb_request_parser.h>
33

4+
#include "tcp.h"
5+
46
#include <stdio.h>
57
#include <assert.h>
68
#include <string>
@@ -17,7 +19,7 @@ using namespace std;
1719
static oi_server server;
1820
static struct ev_loop *loop;
1921

20-
static Persistent<Context> context_;
22+
static Persistent<Context> context;
2123
static Persistent<Function> process_;
2224
static Persistent<ObjectTemplate> request_template_;
2325

@@ -320,7 +322,7 @@ static void on_headers_complete
320322

321323
// Enter this processor's context so all the remaining operations
322324
// take place there
323-
Context::Scope context_scope(context_);
325+
Context::Scope context_scope(context);
324326

325327
// Set up an exception handler before calling the Process function
326328
TryCatch try_catch;
@@ -329,7 +331,7 @@ static void on_headers_complete
329331
// and one argument, the request.
330332
const int argc = 1;
331333
Handle<Value> argv[argc] = { request->js_object };
332-
Handle<Value> r = process_->Call(context_->Global(), argc, argv);
334+
Handle<Value> r = process_->Call(context->Global(), argc, argv);
333335
if (r.IsEmpty()) {
334336
String::Utf8Value error(try_catch.Exception());
335337
printf("error: %s\n", *error);
@@ -495,9 +497,13 @@ static bool compile
495497
// Compile the script and check for errors.
496498
Handle<Script> compiled_script = Script::Compile(script);
497499
if (compiled_script.IsEmpty()) {
500+
501+
Handle<Message> message = try_catch.Message();
502+
498503
String::Utf8Value error(try_catch.Exception());
499-
printf("error: %s\n", *error);
500-
// The script failed to compile; bail out.
504+
505+
printf("error: %s line %d\n", *error, message->GetLineNumber());
506+
501507
return false;
502508
}
503509

@@ -533,6 +539,9 @@ int main
533539
, char *argv[]
534540
)
535541
{
542+
loop = ev_default_loop(0);
543+
544+
536545
map<string, string> options;
537546
string file;
538547
ParseOptions(argc, argv, options, &file);
@@ -547,15 +556,13 @@ int main
547556
return 1;
548557
}
549558

550-
Handle<ObjectTemplate> global = ObjectTemplate::New();
551-
global->Set(String::New("log"), FunctionTemplate::New(LogCallback));
552-
553-
Handle<Context> context = Context::New(NULL, global);
554-
555-
context_ = Persistent<Context>::New(context);
556-
559+
context = Context::New(NULL, ObjectTemplate::New());
557560
Context::Scope context_scope(context);
558561

562+
Local<Object> g = Context::GetCurrent()->Global();
563+
g->Set( String::New("log"), FunctionTemplate::New(LogCallback)->GetFunction());
564+
g->Set( String::New("TCP"), tcp_initialize(loop));
565+
559566
// Compile and run the script
560567
if (!compile(source))
561568
return false;
@@ -579,8 +586,6 @@ int main
579586
/////////////////////////////////////
580587
/////////////////////////////////////
581588
/////////////////////////////////////
582-
583-
loop = ev_default_loop(0);
584589

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

605610
ev_loop(loop, 0);
606611

607-
context_.Dispose();
612+
context.Dispose();
608613
process_.Dispose();
609614

610615
return 0;

tcp.cc

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
#include "tcp.h"
2+
3+
#include <oi_socket.h>
4+
#include <oi_async.h>
5+
6+
#include <sys/types.h>
7+
#include <sys/socket.h>
8+
#include <netdb.h>
9+
10+
11+
12+
/*
13+
Target API
14+
15+
TCP.connect({
16+
17+
host: "google.com",
18+
19+
port: 80,
20+
21+
connect: function () {
22+
this.write("GET /search?q=hello HTTP/1.0\r\n\r\n");
23+
},
24+
25+
read: function (data) {
26+
27+
request.respond("<table> <td>" + data + "</td> </table>");
28+
29+
},
30+
31+
drain: function () {
32+
},
33+
34+
error: function () {
35+
}
36+
});
37+
38+
*/
39+
40+
static oi_async thread_pool;
41+
42+
static struct addrinfo tcp_hints =
43+
/* ai_flags */ { AI_PASSIVE
44+
/* ai_family */ , AF_UNSPEC
45+
/* ai_socktype */ , SOCK_STREAM
46+
/* ai_protocol */ , 0
47+
/* ai_addrlen */ , 0
48+
/* ai_addr */ , 0
49+
/* ai_canonname */ , 0
50+
/* ai_next */ , 0
51+
};
52+
53+
static struct ev_loop *loop;
54+
55+
class TCPClient {
56+
public:
57+
oi_task resolve_task;
58+
oi_socket socket;
59+
struct addrinfo *address;
60+
Persistent<Object> options;
61+
};
62+
63+
static void on_connect
64+
( oi_socket *socket
65+
)
66+
{
67+
TCPClient *client = static_cast<TCPClient*> (socket->data);
68+
69+
HandleScope scope;
70+
71+
Handle<Value> connect_value = client->options->Get( String::NewSymbol("connect") );
72+
if (!connect_value->IsFunction())
73+
return; // error!
74+
Handle<Function> connect_cb = Handle<Function>::Cast(connect_value);
75+
76+
TryCatch try_catch;
77+
Handle<Value> r = connect_cb->Call(client->options, 0, NULL);
78+
if (r.IsEmpty()) {
79+
String::Utf8Value error(try_catch.Exception());
80+
printf("connect error: %s\n", *error);
81+
}
82+
}
83+
84+
static void resolve_done
85+
( oi_task *resolve_task
86+
, int result
87+
)
88+
{
89+
TCPClient *client = static_cast<TCPClient*> (resolve_task->data);
90+
91+
if(result != 0) {
92+
printf("error. TODO make call options error callback\n");
93+
client->options.Dispose();
94+
delete client;
95+
return;
96+
}
97+
98+
// Got the address succesfully. Let's connect now.
99+
100+
oi_socket_init(&client->socket, 30.0); // TODO adjustable timeout
101+
102+
client->socket.on_connect = on_connect;
103+
client->socket.on_read = NULL;
104+
client->socket.on_drain = NULL;
105+
client->socket.on_error = NULL;
106+
client->socket.on_close = NULL;
107+
client->socket.on_timeout = NULL;
108+
client->socket.data = client;
109+
110+
oi_socket_connect (&client->socket, client->address);
111+
oi_socket_attach (&client->socket, loop);
112+
113+
114+
freeaddrinfo(client->address);
115+
client->address = NULL;
116+
}
117+
118+
static Handle<Value> Connect
119+
( const Arguments& args
120+
)
121+
{
122+
if (args.Length() < 1)
123+
return Undefined();
124+
125+
HandleScope scope;
126+
127+
Handle<Value> arg = args[0];
128+
Handle<Object> options = arg->ToObject();
129+
130+
/* Make sure the user has provided at least host and port */
131+
132+
Handle<Value> host_value = options->Get( String::NewSymbol("host") );
133+
134+
if(host_value->IsUndefined())
135+
return False();
136+
137+
Handle<Value> port_value = options->Get( String::NewSymbol("port") );
138+
139+
if(port_value->IsUndefined())
140+
return False();
141+
142+
Handle<String> host = host_value->ToString();
143+
Handle<String> port = port_value->ToString();
144+
145+
char host_s[host->Length()+1]; // + 1 for \0
146+
char port_s[port->Length()+1];
147+
148+
host->WriteAscii(host_s, 0, host->Length());
149+
port->WriteAscii(port_s, 0, port->Length());
150+
151+
printf("resolving host: %s, port: %s\n", host_s, port_s);
152+
153+
TCPClient *client = new TCPClient;
154+
155+
oi_task_init_getaddrinfo ( &client->resolve_task
156+
, resolve_done
157+
, host_s
158+
, port_s
159+
, &tcp_hints
160+
, &client->address
161+
);
162+
client->options = Persistent<Object>::New(options);
163+
164+
oi_async_submit (&thread_pool, &client->resolve_task);
165+
}
166+
167+
Handle<Object> tcp_initialize
168+
( struct ev_loop *_loop
169+
)
170+
{
171+
loop = _loop;
172+
173+
oi_async_init(&thread_pool);
174+
oi_async_attach(loop, &thread_pool);
175+
176+
HandleScope scope;
177+
178+
Local<Object> t = Object::New();
179+
180+
t->Set(String::New("connect"), FunctionTemplate::New(Connect)->GetFunction());
181+
182+
return scope.Close(t);
183+
}
184+

tcp.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef tcp_h
2+
#define tcp_h
3+
4+
#include <v8.h>
5+
#include <ev.h>
6+
7+
using namespace v8;
8+
9+
Handle<Object> tcp_initialize (struct ev_loop *);
10+
11+
#endif

tcp_example.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
2+
TCP.connect ({
3+
host: "google.com",
4+
port: 80,
5+
connected: function () {
6+
log("connected to google.com");
7+
}
8+
});
9+

0 commit comments

Comments
 (0)