Browse Source

Add beginnings of the setTimeout interface

v0.7.4-release
Ryan 16 years ago
parent
commit
a890b67406
  1. 5
      Makefile
  2. 12
      node.cc
  3. 376
      node_http.cc
  4. 190
      node_timer.cc
  5. 10
      node_timer.h

5
Makefile

@ -10,7 +10,7 @@ ifdef EVDIR
LDFLAGS += -L$(EVDIR)/lib
endif
node: node.o node_tcp.o node_http.o oi_socket.o oi_async.o oi_buf.o ebb_request_parser.o
node: node.o node_tcp.o node_http.o node_timer.o oi_socket.o oi_async.o oi_buf.o ebb_request_parser.o
g++ -o node $^ $(LDFLAGS) $(V8LIB)
node.o: node.cc
@ -21,6 +21,9 @@ node_tcp.o: node_tcp.cc
node_http.o: node_http.cc
g++ $(CFLAGS) -c $<
node_timer.o: node_timer.cc
g++ $(CFLAGS) -c $<
oi_socket.o: deps/oi/oi_socket.c deps/oi/oi_socket.h
gcc $(CFLAGS) -c $<

12
node.cc

@ -2,6 +2,7 @@
#include "node_tcp.h"
#include "node_http.h"
#include "node_timer.h"
#include <stdio.h>
#include <assert.h>
@ -113,10 +114,8 @@ static Handle<Value> LogCallback
return v8::Undefined();
}
int main
( int argc
, char *argv[]
)
int
main (int argc, char *argv[])
{
loop = ev_default_loop(0);
@ -142,9 +141,12 @@ int main
g->Set( String::New("TCP"), node_tcp_initialize(loop));
g->Set( String::New("HTTP"), node_http_initialize(loop));
node_timer_initialize(g, loop);
// Compile and run the script
if (!compile(source))
return false;
return 1;
ev_loop(loop, 0);

376
node_http.cc

@ -93,53 +93,12 @@ class HttpRequest {
Connection &connection;
ebb_request parser_info;
private:
Persistent<Object> js_object;
};
static HttpRequest* UnwrapRequest
( Handle<Object> obj
)
{
Handle<External> field = Handle<External>::Cast(obj->GetInternalField(0));
void* ptr = field->Value();
return static_cast<HttpRequest*>(ptr);
}
void
HttpRequest::MakeBodyCallback (const char *base, size_t length)
{
HandleScope handle_scope;
//
// XXX don't always allocate onBody strings
//
Handle<Value> onBody_val = js_object->Get(on_body_str);
if (!onBody_val->IsFunction()) return;
Handle<Function> onBody = Handle<Function>::Cast(onBody_val);
TryCatch try_catch;
const int argc = 1;
Handle<Value> argv[argc];
if(length) {
// TODO use array for binary data
Handle<String> chunk = String::New(base, length);
argv[0] = chunk;
} else {
argv[0] = Null();
}
Handle<Value> result = onBody->Call(js_object, argc, argv);
if (result.IsEmpty()) {
String::Utf8Value error(try_catch.Exception());
printf("error: %s\n", *error);
}
}
static Handle<Value> GetMethodString
( int method
)
static Handle<Value>
GetMethodString (int method)
{
switch(method) {
case EBB_COPY: return copy_str;
@ -160,12 +119,15 @@ static Handle<Value> GetMethodString
return Null();
}
static Handle<Value> RespondCallback
( const Arguments& args
)
static Handle<Value>
RespondCallback (const Arguments& args)
{
HttpRequest* request = UnwrapRequest(args.Holder());
HandleScope scope;
Handle<External> field = Handle<External>::Cast(args.Holder()->GetInternalField(0));
HttpRequest* request = static_cast<HttpRequest*>(field->Value());
Handle<Value> arg = args[0];
// TODO Make sure that we write reponses in the correct order. With
@ -195,146 +157,36 @@ static Handle<Value> RespondCallback
return Undefined();
}
HttpRequest::~HttpRequest ()
{
//printf("request is being destructed\n");
connection.socket.on_drain = oi_socket_close;
HandleScope scope;
// delete a reference to the respond method
js_object->Delete(respond_str);
js_object.Dispose();
}
HttpRequest::HttpRequest (Connection &c) : connection(c)
{
ebb_request_init(&parser_info);
}
Local<Object>
HttpRequest::CreateJSObject ()
{
HandleScope scope;
if (request_template.IsEmpty()) {
Handle<ObjectTemplate> raw_template = ObjectTemplate::New();
raw_template->SetInternalFieldCount(1);
raw_template->Set(respond_str, FunctionTemplate::New(RespondCallback));
request_template = Persistent<ObjectTemplate>::New(raw_template);
}
// Create an empty http request wrapper.
Handle<Object> result = request_template->NewInstance();
// Wrap the raw C++ pointer in an External so it can be referenced
// from within JavaScript.
Handle<External> request_ptr = External::New(this);
// Store the request pointer in the JavaScript wrapper.
result->SetInternalField(0, request_ptr);
result->Set ( path_str
, String::New(path.c_str(), path.length())
);
result->Set ( uri_str
, String::New(uri.c_str(), uri.length())
);
result->Set ( query_string_str
, String::New(query_string.c_str(), query_string.length())
);
result->Set ( fragment_str
, String::New(fragment.c_str(), fragment.length())
);
result->Set ( method_str
, GetMethodString(parser_info.method)
);
char version[10];
snprintf ( version
, 10 // big enough? :)
, "%d.%d"
, parser_info.version_major
, parser_info.version_minor
);
result->Set ( http_version_str
, String::New(version)
);
Handle<Object> headers = Object::New();
list<string>::iterator field_iterator = header_fields.begin();
list<string>::iterator value_iterator = header_values.begin();
while( value_iterator != header_values.end() ) {
string &f = *field_iterator;
string &v = *value_iterator;
headers->Set( String::New(f.c_str(), f.length() )
, String::New(v.c_str(), v.length() )
);
field_iterator++;
value_iterator++;
}
result->Set(String::NewSymbol("headers"), headers);
js_object = Persistent<Object>::New(result);
return scope.Close(result);
}
static void on_path
( ebb_request *req
, const char *buf
, size_t len
)
static void
on_path (ebb_request *req, const char *buf, size_t len)
{
HttpRequest *request = static_cast<HttpRequest*> (req->data);
request->path.append(buf, len);
}
static void on_uri
( ebb_request *req
, const char *buf
, size_t len
)
static void
on_uri (ebb_request *req, const char *buf, size_t len)
{
HttpRequest *request = static_cast<HttpRequest*> (req->data);
request->uri.append(buf, len);
}
static void on_query_string
( ebb_request *req
, const char *buf
, size_t len
)
static void
on_query_string (ebb_request *req, const char *buf, size_t len)
{
HttpRequest *request = static_cast<HttpRequest*> (req->data);
request->query_string.append(buf, len);
}
static void on_fragment
( ebb_request *req
, const char *buf
, size_t len
)
static void
on_fragment (ebb_request *req, const char *buf, size_t len)
{
HttpRequest *request = static_cast<HttpRequest*> (req->data);
request->fragment.append(buf, len);
}
static void on_header_field
( ebb_request *req
, const char *buf
, size_t len
, int header_index
)
static void
on_header_field (ebb_request *req, const char *buf, size_t len, int header_index)
{
HttpRequest *request = static_cast<HttpRequest*> (req->data);
@ -345,12 +197,8 @@ static void on_header_field
}
}
static void on_header_value
( ebb_request *req
, const char *buf
, size_t len
, int header_index
)
static void
on_header_value (ebb_request *req, const char *buf, size_t len, int header_index)
{
HttpRequest *request = static_cast<HttpRequest*> (req->data);
@ -361,9 +209,8 @@ static void on_header_value
}
}
static void on_headers_complete
( ebb_request *req
)
static void
on_headers_complete (ebb_request *req)
{
HttpRequest *request = static_cast<HttpRequest*> (req->data);
@ -385,19 +232,15 @@ static void on_headers_complete
}
}
static void on_request_complete
( ebb_request *req
)
static void
on_request_complete (ebb_request *req)
{
HttpRequest *request = static_cast<HttpRequest*> (req->data);
request->MakeBodyCallback(NULL, 0); // EOF
}
static void on_body
( ebb_request *req
, const char *base
, size_t length
)
static void
on_body (ebb_request *req, const char *base, size_t length)
{
HttpRequest *request = static_cast<HttpRequest*> (req->data);
@ -413,17 +256,6 @@ static ebb_request * on_request
HttpRequest *request = new HttpRequest(*connection);
request->parser_info.on_path = on_path;
request->parser_info.on_query_string = on_query_string;
request->parser_info.on_uri = on_uri;
request->parser_info.on_fragment = on_fragment;
request->parser_info.on_header_field = on_header_field;
request->parser_info.on_header_value = on_header_value;
request->parser_info.on_headers_complete = on_headers_complete;
request->parser_info.on_body = on_body;
request->parser_info.on_complete = on_request_complete;
request->parser_info.data = request;
return &request->parser_info;
}
@ -462,11 +294,144 @@ static void on_drain
//oi_socket_close(&connection->socket);
}
static oi_socket* on_connection
( oi_server *_server
, struct sockaddr *addr
, socklen_t len
)
HttpRequest::~HttpRequest ()
{
//printf("request is being destructed\n");
connection.socket.on_drain = oi_socket_close;
HandleScope scope;
// delete a reference to the respond method
js_object->Delete(respond_str);
js_object.Dispose();
}
HttpRequest::HttpRequest (Connection &c) : connection(c)
{
ebb_request_init(&parser_info);
parser_info.on_path = on_path;
parser_info.on_query_string = on_query_string;
parser_info.on_uri = on_uri;
parser_info.on_fragment = on_fragment;
parser_info.on_header_field = on_header_field;
parser_info.on_header_value = on_header_value;
parser_info.on_headers_complete = on_headers_complete;
parser_info.on_body = on_body;
parser_info.on_complete = on_request_complete;
parser_info.data = this;
}
void
HttpRequest::MakeBodyCallback (const char *base, size_t length)
{
HandleScope handle_scope;
//
// XXX don't always allocate onBody strings
//
Handle<Value> onBody_val = js_object->Get(on_body_str);
if (!onBody_val->IsFunction()) return;
Handle<Function> onBody = Handle<Function>::Cast(onBody_val);
TryCatch try_catch;
const int argc = 1;
Handle<Value> argv[argc];
if(length) {
// TODO use array for binary data
Handle<String> chunk = String::New(base, length);
argv[0] = chunk;
} else {
argv[0] = Null();
}
Handle<Value> result = onBody->Call(js_object, argc, argv);
if (result.IsEmpty()) {
String::Utf8Value error(try_catch.Exception());
printf("error: %s\n", *error);
}
}
Local<Object>
HttpRequest::CreateJSObject ()
{
HandleScope scope;
if (request_template.IsEmpty()) {
Handle<ObjectTemplate> raw_template = ObjectTemplate::New();
raw_template->SetInternalFieldCount(1);
raw_template->Set(respond_str, FunctionTemplate::New(RespondCallback));
request_template = Persistent<ObjectTemplate>::New(raw_template);
}
// Create an empty http request wrapper.
Handle<Object> result = request_template->NewInstance();
// Wrap the raw C++ pointer in an External so it can be referenced
// from within JavaScript.
Handle<External> request_ptr = External::New(this);
// Store the request pointer in the JavaScript wrapper.
result->SetInternalField(0, request_ptr);
result->Set ( path_str
, String::New(path.c_str(), path.length())
);
result->Set ( uri_str
, String::New(uri.c_str(), uri.length())
);
result->Set ( query_string_str
, String::New(query_string.c_str(), query_string.length())
);
result->Set ( fragment_str
, String::New(fragment.c_str(), fragment.length())
);
result->Set ( method_str
, GetMethodString(parser_info.method)
);
char version[10];
snprintf ( version
, 10 // big enough? :)
, "%d.%d"
, parser_info.version_major
, parser_info.version_minor
);
result->Set ( http_version_str
, String::New(version)
);
Handle<Object> headers = Object::New();
list<string>::iterator field_iterator = header_fields.begin();
list<string>::iterator value_iterator = header_values.begin();
while( value_iterator != header_values.end() ) {
string &f = *field_iterator;
string &v = *value_iterator;
headers->Set( String::New(f.c_str(), f.length() )
, String::New(v.c_str(), v.length() )
);
field_iterator++;
value_iterator++;
}
result->Set(String::NewSymbol("headers"), headers);
js_object = Persistent<Object>::New(result);
return scope.Close(result);
}
static oi_socket*
on_connection (oi_server *_server, struct sockaddr *addr, socklen_t len)
{
HandleScope scope;
@ -494,10 +459,8 @@ static oi_socket* on_connection
return &connection->socket;
}
static void server_destroy
( Persistent<Value> _
, void *data
)
static void
server_destroy (Persistent<Value> _, void *data)
{
Server *server = static_cast<Server *> (data);
delete server;
@ -583,7 +546,8 @@ server_constructor (const Arguments& args)
return args.This();
}
Handle<Object> node_http_initialize (struct ev_loop *_loop)
Handle<Object>
node_http_initialize (struct ev_loop *_loop)
{
HandleScope scope;

190
node_timer.cc

@ -0,0 +1,190 @@
#include <ev.h>
#include <v8.h>
using namespace v8;
static struct ev_loop *loop;
class Timer {
public:
Timer(Handle<Function> callback, int argc, Handle<Value> argv[], ev_tstamp after, ev_tstamp repeat);
~Timer();
Local<External> CreateTimeoutID ();
void CallCallback ();
private:
ev_timer watcher;
Persistent<External> timeoutID;
Persistent<Function> callback;
int argc;
Persistent<Value> argv[];
};
static void
onTimeout (struct ev_loop *loop, ev_timer *watcher, int revents)
{
Timer *timer = static_cast<Timer*>(watcher->data);
timer->CallCallback();
// use ev_is_active instead?
if(watcher->repeat == 0.)
delete timer;
}
Timer::Timer (Handle<Function> _callback, int _argc, Handle<Value> _argv[], ev_tstamp after, ev_tstamp repeat)
{
HandleScope scope;
callback = Persistent<Function>::New(_callback);
argc = _argc;
ev_timer_init (&watcher, onTimeout, after, repeat);
watcher.data = this;
ev_timer_start (loop, &watcher);
}
Timer::~Timer ()
{
ev_timer_stop (loop, &watcher);
callback.Dispose();
timeoutID.Dispose();
timeoutID.Clear();
}
void
Timer::CallCallback ()
{
HandleScope scope;
TryCatch try_catch;
Handle<Value> r = callback->Call (Context::GetCurrent()->Global(), argc, argv);
if (r.IsEmpty()) {
String::Utf8Value error(try_catch.Exception());
printf("timer callback error: %s\n", *error);
}
}
Local<External>
Timer::CreateTimeoutID ()
{
HandleScope scope;
Local<External> timeoutID_local = External::New(this);
timeoutID = Persistent<External>::New(timeoutID_local);
return scope.Close(timeoutID_local);
}
static Timer *
UnwrapTimeoutID (Handle<External> timeoutID)
{
HandleScope scope;
Timer *timer = static_cast<Timer*>(timeoutID->Value());
return timer;
}
// timeoutID = setTimeout(func, delay, [param1, param2, ...]);
// timeoutID = setTimeout(code, delay);
//
// * timeoutID is the ID of the timeout, which can be used with
// clearTimeout.
//
// * func is the function you want to execute after delay milliseconds.
//
// * code in the alternate syntax, is a string of code you want to execute
// after delay milliseconds. (not recommended)
//
// * delay is the number of milliseconds (thousandths of a second) that the
// function call should be delayed by.
static Handle<Value>
setTimeout(const Arguments& args)
{
if (args.Length() < 2)
return Undefined();
HandleScope scope;
Local<Function> callback = Local<Function>::Cast(args[0]);
uint32_t delay = args[1]->Uint32Value();
ev_tstamp after = (double)delay / 1000.0;
int argc = 0;
Handle<Value> argv[] = {};
/*
int argc = args.Length() - 2;
Handle<Value> argv[] = new Handle<Value>[argc];
// the rest of the arguments, if any arg parameters for the callback
for(int i = 2; i < args.Length(); i++)
argv[i - 2] = args[i];
*/
Timer *timer = new Timer(callback, argc, argv, after, 0.0);
Local<External> timeoutID = timer->CreateTimeoutID();
return scope.Close(timeoutID);
}
// clearTimeout(timeoutID)
static Handle<Value> clearTimeout
( const Arguments& args
)
{
if (args.Length() < 1)
return Undefined();
Handle<External> timeoutID = Handle<External>::Cast(args[0]);
Timer *timer = UnwrapTimeoutID(timeoutID);
delete timer;
return Undefined();
}
// intervalID = setInterval(func, delay[, param1, param2, ...]);
// intervalID = setInterval(code, delay);
//
// where
//
// * intervalID is a unique interval ID you can pass to clearInterval().
//
// * func is the function you want to be called repeatedly.
//
// * code in the alternate syntax, is a string of code you want to be executed
// repeatedly.
//
// * delay is the number of milliseconds (thousandths of a second) that the
// setInterval() function should wait before each call to func.
static Handle<Value> setInterval
( const Arguments& args
)
{
}
void
node_timer_initialize (Handle<Object> target, struct ev_loop *_loop)
{
loop = _loop;
HandleScope scope;
target->Set ( String::New("setTimeout")
, FunctionTemplate::New(setTimeout)->GetFunction()
);
target->Set ( String::New("clearTimeout")
, FunctionTemplate::New(clearTimeout)->GetFunction()
);
target->Set ( String::New("setInterval")
, FunctionTemplate::New(setInterval)->GetFunction()
);
target->Set ( String::New("clearInterval")
, FunctionTemplate::New(clearTimeout)->GetFunction()
);
}

10
node_timer.h

@ -0,0 +1,10 @@
#ifndef node_timer_h
#define node_timer_h
#include <ev.h>
#include <v8.h>
using namespace v8;
void node_timer_initialize (Handle<Object> target, struct ev_loop *_loop);
#endif // node_timer_h
Loading…
Cancel
Save