From a890b674061ff9e923a9f6bc106eadd4dab28649 Mon Sep 17 00:00:00 2001 From: Ryan Date: Tue, 3 Mar 2009 19:17:33 +0100 Subject: [PATCH] Add beginnings of the setTimeout interface --- Makefile | 5 +- node.cc | 12 +- node_http.cc | 376 +++++++++++++++++++++++--------------------------- node_timer.cc | 190 +++++++++++++++++++++++++ node_timer.h | 10 ++ 5 files changed, 381 insertions(+), 212 deletions(-) create mode 100644 node_timer.cc create mode 100644 node_timer.h diff --git a/Makefile b/Makefile index 5b6082f995..b1e812e070 100644 --- a/Makefile +++ b/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 $< diff --git a/node.cc b/node.cc index 13d40ca214..402643206b 100644 --- a/node.cc +++ b/node.cc @@ -2,6 +2,7 @@ #include "node_tcp.h" #include "node_http.h" +#include "node_timer.h" #include #include @@ -113,10 +114,8 @@ static Handle 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); diff --git a/node_http.cc b/node_http.cc index df97f42ef8..a95fb48984 100644 --- a/node_http.cc +++ b/node_http.cc @@ -93,53 +93,12 @@ class HttpRequest { Connection &connection; ebb_request parser_info; - + private: Persistent js_object; }; -static HttpRequest* UnwrapRequest - ( Handle obj - ) -{ - Handle field = Handle::Cast(obj->GetInternalField(0)); - void* ptr = field->Value(); - return static_cast(ptr); -} - -void -HttpRequest::MakeBodyCallback (const char *base, size_t length) -{ - HandleScope handle_scope; - // - // XXX don't always allocate onBody strings - // - Handle onBody_val = js_object->Get(on_body_str); - if (!onBody_val->IsFunction()) return; - Handle onBody = Handle::Cast(onBody_val); - - TryCatch try_catch; - const int argc = 1; - Handle argv[argc]; - - if(length) { - // TODO use array for binary data - Handle chunk = String::New(base, length); - argv[0] = chunk; - } else { - argv[0] = Null(); - } - - Handle result = onBody->Call(js_object, argc, argv); - - if (result.IsEmpty()) { - String::Utf8Value error(try_catch.Exception()); - printf("error: %s\n", *error); - } -} - -static Handle GetMethodString - ( int method - ) +static Handle +GetMethodString (int method) { switch(method) { case EBB_COPY: return copy_str; @@ -160,12 +119,15 @@ static Handle GetMethodString return Null(); } -static Handle RespondCallback - ( const Arguments& args - ) +static Handle +RespondCallback (const Arguments& args) { - HttpRequest* request = UnwrapRequest(args.Holder()); HandleScope scope; + + Handle field = Handle::Cast(args.Holder()->GetInternalField(0)); + + HttpRequest* request = static_cast(field->Value()); + Handle arg = args[0]; // TODO Make sure that we write reponses in the correct order. With @@ -195,146 +157,36 @@ static Handle 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 -HttpRequest::CreateJSObject () -{ - HandleScope scope; - - if (request_template.IsEmpty()) { - Handle raw_template = ObjectTemplate::New(); - raw_template->SetInternalFieldCount(1); - raw_template->Set(respond_str, FunctionTemplate::New(RespondCallback)); - - request_template = Persistent::New(raw_template); - } - - // Create an empty http request wrapper. - Handle result = request_template->NewInstance(); - - // Wrap the raw C++ pointer in an External so it can be referenced - // from within JavaScript. - Handle 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 headers = Object::New(); - list::iterator field_iterator = header_fields.begin(); - list::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::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 (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 (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 (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 (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 (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 (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 (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 (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 (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 onBody_val = js_object->Get(on_body_str); + if (!onBody_val->IsFunction()) return; + Handle onBody = Handle::Cast(onBody_val); + + TryCatch try_catch; + const int argc = 1; + Handle argv[argc]; + + if(length) { + // TODO use array for binary data + Handle chunk = String::New(base, length); + argv[0] = chunk; + } else { + argv[0] = Null(); + } + + Handle result = onBody->Call(js_object, argc, argv); + + if (result.IsEmpty()) { + String::Utf8Value error(try_catch.Exception()); + printf("error: %s\n", *error); + } +} + +Local +HttpRequest::CreateJSObject () +{ + HandleScope scope; + + if (request_template.IsEmpty()) { + Handle raw_template = ObjectTemplate::New(); + raw_template->SetInternalFieldCount(1); + raw_template->Set(respond_str, FunctionTemplate::New(RespondCallback)); + + request_template = Persistent::New(raw_template); + } + + // Create an empty http request wrapper. + Handle result = request_template->NewInstance(); + + // Wrap the raw C++ pointer in an External so it can be referenced + // from within JavaScript. + Handle 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 headers = Object::New(); + list::iterator field_iterator = header_fields.begin(); + list::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::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 _ - , void *data - ) +static void +server_destroy (Persistent _, void *data) { Server *server = static_cast (data); delete server; @@ -583,7 +546,8 @@ server_constructor (const Arguments& args) return args.This(); } -Handle node_http_initialize (struct ev_loop *_loop) +Handle +node_http_initialize (struct ev_loop *_loop) { HandleScope scope; diff --git a/node_timer.cc b/node_timer.cc new file mode 100644 index 0000000000..d4b4775f84 --- /dev/null +++ b/node_timer.cc @@ -0,0 +1,190 @@ +#include +#include +using namespace v8; +static struct ev_loop *loop; + +class Timer { + public: + Timer(Handle callback, int argc, Handle argv[], ev_tstamp after, ev_tstamp repeat); + ~Timer(); + Local CreateTimeoutID (); + void CallCallback (); + private: + ev_timer watcher; + Persistent timeoutID; + Persistent callback; + int argc; + Persistent argv[]; +}; + +static void +onTimeout (struct ev_loop *loop, ev_timer *watcher, int revents) +{ + Timer *timer = static_cast(watcher->data); + + timer->CallCallback(); + + // use ev_is_active instead? + if(watcher->repeat == 0.) + delete timer; +} + +Timer::Timer (Handle _callback, int _argc, Handle _argv[], ev_tstamp after, ev_tstamp repeat) +{ + HandleScope scope; + callback = Persistent::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 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 +Timer::CreateTimeoutID () +{ + HandleScope scope; + + Local timeoutID_local = External::New(this); + + timeoutID = Persistent::New(timeoutID_local); + + return scope.Close(timeoutID_local); +} + +static Timer * +UnwrapTimeoutID (Handle timeoutID) +{ + HandleScope scope; + + Timer *timer = static_cast(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 +setTimeout(const Arguments& args) +{ + if (args.Length() < 2) + return Undefined(); + + HandleScope scope; + + Local callback = Local::Cast(args[0]); + uint32_t delay = args[1]->Uint32Value(); + + ev_tstamp after = (double)delay / 1000.0; + + int argc = 0; + Handle argv[] = {}; + /* + int argc = args.Length() - 2; + Handle argv[] = new Handle[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 timeoutID = timer->CreateTimeoutID(); + + return scope.Close(timeoutID); +} + +// clearTimeout(timeoutID) +static Handle clearTimeout + ( const Arguments& args + ) +{ + if (args.Length() < 1) + return Undefined(); + + Handle timeoutID = Handle::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 setInterval + ( const Arguments& args + ) +{ +} + +void +node_timer_initialize (Handle 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() + ); +} diff --git a/node_timer.h b/node_timer.h new file mode 100644 index 0000000000..7c73bdd6b7 --- /dev/null +++ b/node_timer.h @@ -0,0 +1,10 @@ +#ifndef node_timer_h +#define node_timer_h + +#include +#include +using namespace v8; + +void node_timer_initialize (Handle target, struct ev_loop *_loop); + +#endif // node_timer_h