From be6b3acf0e72808b4acc06ee775e0fb8476c0afd Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 3 May 2009 21:06:20 +0200 Subject: [PATCH] extract headers, status_code, path, http version from http messages. still a work in progress. --- src/http.cc | 176 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/http.h | 15 ++++- src/http.js | 25 ++++++++ src/main.js | 1 - src/node.cc | 8 +-- wscript | 2 +- 6 files changed, 211 insertions(+), 16 deletions(-) create mode 100644 src/http.js diff --git a/src/http.cc b/src/http.cc index 6c699b3fb9..943adec2a2 100644 --- a/src/http.cc +++ b/src/http.cc @@ -5,16 +5,73 @@ #include #include +#define ON_MESSAGE_SYMBOL String::NewSymbol("onMessage") +#define MESSAGE_HANDLER_SYMBOL String::NewSymbol("messageHandler") +#define ON_HEADERS_COMPLETE_SYMBOL String::NewSymbol("onHeadersComplete") + +#define PATH_SYMBOL String::NewSymbol("path") +#define STATUS_CODE_SYMBOL String::NewSymbol("status_code") +#define HTTP_VERSION_SYMBOL String::NewSymbol("http_version") + using namespace v8; using namespace node; using namespace std; + +// Helper Functions + +static Persistent _fill_field; +static Persistent _append_header_field; +static Persistent _append_header_value; + +#define CATCH_NATIVE_HTTP_FUNCTION(variable, jsname) \ +do { \ + if (variable.IsEmpty()) { \ + Local __g = Context::GetCurrent()->Global(); \ + Local __node_v = __g->Get(String::NewSymbol("node")); \ + Local __node = __node_v->ToObject(); \ + Local __http_v = __node->Get(String::NewSymbol("http")); \ + Local __http = __http_v->ToObject(); \ + Local __value = __http->Get(String::NewSymbol(jsname)); \ + Handle __function_handle = Handle::Cast(__value); \ + variable = Persistent::New(__function_handle); \ + } \ +} while(0) + +void +fillField (Handle message, Handle field, Handle value) +{ + HandleScope scope; + CATCH_NATIVE_HTTP_FUNCTION(_fill_field, "fillField"); + Handle argv[] = { message, field, value }; + _fill_field->Call(message->ToObject(), 3, argv); +} + +void +appendHeaderField (Handle message, Handle d) +{ + HandleScope scope; + CATCH_NATIVE_HTTP_FUNCTION(_append_header_field, "appendHeaderField"); + Handle argv[] = { message, d }; + _append_header_field->Call(message->ToObject(), 2, argv); +} + +void +appendHeaderValue (Handle message, Handle d) +{ + HandleScope scope; + CATCH_NATIVE_HTTP_FUNCTION(_append_header_value, "appendHeaderValue"); + Handle argv[] = { message, d }; + _append_header_value->Call(message->ToObject(), 2, argv); +} + + void -HTTPClient::Initialize (Handle target) +HTTPConnection::Initialize (Handle target) { HandleScope scope; - Local t = FunctionTemplate::New(HTTPClient::v8New); + Local t = FunctionTemplate::New(HTTPConnection::v8New); t->InstanceTemplate()->SetInternalFieldCount(1); target->Set(String::NewSymbol("HTTPClient"), t->GetFunction()); @@ -25,7 +82,7 @@ HTTPClient::Initialize (Handle target) } Handle -HTTPClient::v8New (const Arguments& args) +HTTPConnection::v8New (const Arguments& args) { HandleScope scope; @@ -44,18 +101,121 @@ HTTPClient::v8New (const Arguments& args) Local protocol_instance = protocol->NewInstance(argc, argv); - new HTTPClient(args.This(), protocol_instance); + // changeme the type should come from javascript + new HTTPConnection(args.This(), protocol_instance, HTTP_RESPONSE); return args.This(); } void -HTTPClient::OnReceive (const void *buf, size_t len) +HTTPConnection::OnReceive (const void *buf, size_t len) +{ + http_parser_execute(&parser_, static_cast(buf), len); + + if (http_parser_has_error(&parser_)) { + // do something. + Close(); + return; + } +} + +int +HTTPConnection::on_message_begin (http_parser *parser) +{ + HTTPConnection *connection = static_cast (parser->data); + HandleScope scope; + + Local protocol = connection->GetProtocol(); + Local on_message_v = protocol->Get(ON_MESSAGE_SYMBOL); + if (!on_message_v->IsFunction()) return -1; + Handle on_message = Handle::Cast(on_message_v); + Local message_handler = on_message->NewInstance(); + connection->handle_->SetHiddenValue(MESSAGE_HANDLER_SYMBOL, message_handler); + return 0; +} + +int +HTTPConnection::on_path (http_parser *parser, const char *buf, size_t len) +{ + HTTPConnection *connection = static_cast (parser->data); + + HandleScope scope; + + Local message_handler_v = + connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL); + fillField(message_handler_v, PATH_SYMBOL, String::New(buf, len)); + + return 0; +} + +int +HTTPConnection::on_header_field (http_parser *parser, const char *buf, size_t len) +{ + HTTPConnection *connection = static_cast (parser->data); + HandleScope scope; + Local message_handler_v = + connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL); + appendHeaderField(message_handler_v, String::New(buf, len)); + return 0; +} + +int +HTTPConnection::on_header_value (http_parser *parser, const char *buf, size_t len) +{ + HTTPConnection *connection = static_cast (parser->data); + HandleScope scope; + Local message_handler_v = + connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL); + appendHeaderValue(message_handler_v, String::New(buf, len)); + return 0; +} + +int +HTTPConnection::on_headers_complete (http_parser *parser) { - printf("http client got data!\n"); + HTTPConnection *connection = static_cast (parser->data); + + Local message_handler_v = + connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL); + Local message_handler = message_handler_v->ToObject(); + + // STATUS + message_handler->Set(STATUS_CODE_SYMBOL, + Integer::New(connection->parser_.status_code)); + + // VERSION + char version[10]; + snprintf(version, 10, "%d.%d", connection->parser_.version_major, connection->parser_.version_minor); + message_handler->Set(HTTP_VERSION_SYMBOL, String::New(version)); + + + + + Local on_headers_complete_v = message_handler->Get(ON_HEADERS_COMPLETE_SYMBOL); + if (on_headers_complete_v->IsFunction() == false) return 0; + + Handle on_headers_complete = Handle::Cast(on_headers_complete_v); + + + on_headers_complete->Call(message_handler, 0, NULL); + + return 0; } -HTTPClient::HTTPClient (Handle handle, Handle protocol) +HTTPConnection::HTTPConnection (Handle handle, Handle protocol, enum http_parser_type type) : Connection(handle, protocol) { -} + http_parser_init (&parser_, type); + parser_.on_message_begin = on_message_begin; + parser_.on_path = on_path; + parser_.on_query_string = NULL; + parser_.on_uri = NULL; + parser_.on_fragment = NULL; + parser_.on_header_field = on_header_field; + parser_.on_header_value = on_header_value; + parser_.on_headers_complete = on_headers_complete; + parser_.on_body = NULL; + parser_.on_message_complete = NULL; + parser_.data = this; +} + diff --git a/src/http.h b/src/http.h index db9a6a992b..63f856460b 100644 --- a/src/http.h +++ b/src/http.h @@ -3,19 +3,30 @@ #include #include "net.h" +#include namespace node { -class HTTPClient : public node::Connection { +class HTTPConnection : public Connection { public: static void Initialize (v8::Handle target); - HTTPClient (v8::Handle handle, v8::Handle protocol); + HTTPConnection (v8::Handle handle, + v8::Handle protocol, + enum http_parser_type type); static v8::Handle v8New (const v8::Arguments& args); protected: void OnReceive (const void *buf, size_t len); + static int on_message_begin (http_parser *parser); + static int on_path (http_parser *parser, const char *at, size_t length); + static int on_header_field (http_parser *parser, const char *buf, size_t len); + static int on_header_value (http_parser *parser, const char *buf, size_t len); + static int on_headers_complete (http_parser *parser); + + + http_parser parser_; }; } // namespace node diff --git a/src/http.js b/src/http.js new file mode 100644 index 0000000000..28128ee4a3 --- /dev/null +++ b/src/http.js @@ -0,0 +1,25 @@ +node.http = { + fillField : function (msg, field, data) { + msg[field] = (msg[field] || "") + data; + }, + + appendHeaderField : function (msg, data) { + if (msg.hasOwnProperty("headers")) { + var last_pair = msg.headers[msg.headers.length-1]; + if (last_pair.length == 1) + last_pair[0] += data; + else + msg.headers.push([data]); + } else { + msg.headers = [[data]]; + } + }, + + appendHeaderValue : function (msg, data) { + var last_pair = msg.headers[msg.headers.length-1]; + if (last_pair.length == 1) + last_pair[1] = data; + else + last_pair[1] += data; + } +}; diff --git a/src/main.js b/src/main.js index bfaf10549a..3b0961fd3e 100644 --- a/src/main.js +++ b/src/main.js @@ -178,4 +178,3 @@ clearInterval = clearTimeout; loadScript(ARGV[1], this); })(); - diff --git a/src/node.cc b/src/node.cc index 081ce0e6da..527cafcd3a 100644 --- a/src/node.cc +++ b/src/node.cc @@ -56,9 +56,6 @@ ObjectWrap::MakeWeak (Persistent _, void *data) delete w; } - - - // Extracts a C string from a V8 Utf8Value. const char* ToCString(const v8::String::Utf8Value& value) @@ -252,11 +249,14 @@ main (int argc, char *argv[]) Connection::Initialize(g); node::Init_timer(g); node::Init_file(g); - HTTPClient::Initialize(g); + HTTPConnection::Initialize(g); // NATIVE JAVASCRIPT MODULES TryCatch try_catch; + ExecuteString(String::New(native_http), String::New("http.js")); + if (try_catch.HasCaught()) goto native_js_error; + ExecuteString(String::New(native_file), String::New("file.js")); if (try_catch.HasCaught()) goto native_js_error; diff --git a/wscript b/wscript index f1ec621aa8..baf7fc18b6 100644 --- a/wscript +++ b/wscript @@ -137,7 +137,7 @@ def build(bld): js2c.JS2C(source, targets) native_cc = bld.new_task_gen( - source="src/file.js src/main.js", + source="src/http.js src/file.js src/main.js", target="src/natives.h", rule=javascript_in_c, before="cxx"