#include "node.h" #include "http.h" #include #include #include #include #define ENCODING_SYMBOL String::NewSymbol("encoding") #define MESSAGE_HANDLER_SYMBOL String::NewSymbol("messageHandler") #define ON_MESSAGE_SYMBOL String::NewSymbol("onMessage") #define ON_PATH_SYMBOL String::NewSymbol("onPath") #define ON_QUERY_STRING_SYMBOL String::NewSymbol("onQueryString") #define ON_URI_SYMBOL String::NewSymbol("onURI") #define ON_FRAGMENT_SYMBOL String::NewSymbol("onFragment") #define ON_HEADER_FIELD_SYMBOL String::NewSymbol("onHeaderField") #define ON_HEADER_VALUE_SYMBOL String::NewSymbol("onHeaderValue") #define ON_HEADERS_COMPLETE_SYMBOL String::NewSymbol("onHeadersComplete") #define ON_BODY_SYMBOL String::NewSymbol("onBody") #define ON_MESSAGE_COMPLETE_SYMBOL String::NewSymbol("onMessageComplete") #define STATUS_CODE_SYMBOL String::NewSymbol("status_code") #define HTTP_VERSION_SYMBOL String::NewSymbol("http_version") #define SHOULD_KEEP_ALIVE_SYMBOL String::NewSymbol("should_keep_alive") using namespace v8; using namespace node; using namespace std; Persistent HTTPConnection::client_constructor_template; Persistent HTTPConnection::server_constructor_template; static Persistent http_module; void HTTPConnection::Initialize (Handle target) { HandleScope scope; Local t = FunctionTemplate::New(v8NewClient); client_constructor_template = Persistent::New(t); client_constructor_template->Inherit(Connection::constructor_template); client_constructor_template->InstanceTemplate()->SetInternalFieldCount(1); target->Set(String::NewSymbol("LowLevelClient"), client_constructor_template->GetFunction()); t = FunctionTemplate::New(v8NewServer); server_constructor_template = Persistent::New(t); server_constructor_template->Inherit(Connection::constructor_template); server_constructor_template->InstanceTemplate()->SetInternalFieldCount(1); target->Set(String::NewSymbol("ServerSideSocket"), server_constructor_template->GetFunction()); } Handle HTTPConnection::v8NewClient (const Arguments& args) { HandleScope scope; HTTPConnection *connection = new HTTPConnection(args.This(), HTTP_RESPONSE); ObjectWrap::InformV8ofAllocation(connection); return args.This(); } Handle HTTPConnection::v8NewServer (const Arguments& args) { HandleScope scope; HTTPConnection *connection = new HTTPConnection(args.This(), HTTP_REQUEST); ObjectWrap::InformV8ofAllocation(connection); return args.This(); } void HTTPConnection::OnReceive (const void *buf, size_t len) { http_parser_execute(&parser_, static_cast(buf), len); if (http_parser_has_error(&parser_)) ForceClose(); } int HTTPConnection::on_message_begin (http_parser *parser) { HTTPConnection *connection = static_cast (parser->data); HandleScope scope; Local on_message_v = connection->handle_->Get(ON_MESSAGE_SYMBOL); if (!on_message_v->IsFunction()) return -1; Handle on_message = Handle::Cast(on_message_v); TryCatch try_catch; Local message_handler = on_message->NewInstance(); if (try_catch.HasCaught()) { fatal_exception(try_catch); return -1; } connection->handle_->SetHiddenValue(MESSAGE_HANDLER_SYMBOL, message_handler); return 0; } #define DEFINE_PARSER_CALLBACK(name, symbol) \ int \ HTTPConnection::name (http_parser *parser, const char *buf, size_t len) \ { \ HandleScope scope; \ HTTPConnection *connection = static_cast (parser->data); \ Local message_handler_v = \ connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL); \ if (message_handler_v->IsObject() == false) \ return -1; \ Local message_handler = message_handler_v->ToObject(); \ Local callback_v = message_handler->Get(symbol); \ if (callback_v->IsFunction() == false) \ return 0; \ Local callback = Local::Cast(callback_v); \ TryCatch try_catch; \ Local argv[1] = { String::New(buf, len) }; \ Local ret = callback->Call(message_handler, 1, argv); \ if (ret.IsEmpty()) { \ fatal_exception(try_catch); \ return -2; \ } \ if (ret->IsFalse()) return -3; \ return 0; \ } DEFINE_PARSER_CALLBACK(on_uri, ON_URI_SYMBOL) DEFINE_PARSER_CALLBACK(on_header_field, ON_HEADER_FIELD_SYMBOL) DEFINE_PARSER_CALLBACK(on_header_value, ON_HEADER_VALUE_SYMBOL) int HTTPConnection::on_headers_complete (http_parser *parser) { HTTPConnection *connection = static_cast (parser->data); HandleScope scope; 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)); // SHOULD KEEP ALIVE message_handler->Set( SHOULD_KEEP_ALIVE_SYMBOL , http_parser_should_keep_alive(&connection->parser_) ? True() : False() ); 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); TryCatch try_catch; Local ret = on_headers_complete->Call(message_handler, 0, NULL); if (ret.IsEmpty()) { fatal_exception(try_catch); return -2; } if (ret->IsFalse()) return -3; return 0; } int HTTPConnection::on_body (http_parser *parser, const char *buf, size_t len) { assert(len != 0); HTTPConnection *connection = static_cast (parser->data); HandleScope scope; Local message_handler_v = connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL); Local message_handler = message_handler_v->ToObject(); Local on_body_v = message_handler->Get(ON_BODY_SYMBOL); if (on_body_v->IsFunction() == false) return 0; Handle on_body = Handle::Cast(on_body_v); /* Look at the value of message_handler.encoding to decide how to * send the body chunk. This is rather sloppy and unnecesary. FIXME */ enum encoding encoding = RAW; Local encoding_v = message_handler->Get(ENCODING_SYMBOL); if (encoding_v->IsString()) { Local encoding_string = encoding_v->ToString(); char buf[5]; // need enough room for "utf8" or "raw" encoding_string->WriteAscii(buf, 0, 4); buf[4] = '\0'; if(strcasecmp(buf, "utf8") == 0) encoding = UTF8; } Handle argv[1]; // TODO each message should have their encoding. // don't look at the conneciton for encoding if(encoding == UTF8) { // utf8 encoding Handle chunk = String::New((const char*)buf, len); argv[0] = chunk; } else { // raw encoding Local array = Array::New(len); for (size_t i = 0; i < len; i++) { char val = static_cast(buf)[i]; array->Set(Integer::New(i), Integer::New(val)); } argv[0] = array; } TryCatch try_catch; Local ret = on_body->Call(message_handler, 1, argv); if (ret.IsEmpty()) { fatal_exception(try_catch); return -2; } if (ret->IsFalse()) return -3; return 0; } int HTTPConnection::on_message_complete (http_parser *parser) { HTTPConnection *connection = static_cast (parser->data); HandleScope scope; Local message_handler_v = connection->handle_->GetHiddenValue(MESSAGE_HANDLER_SYMBOL); Local message_handler = message_handler_v->ToObject(); connection->handle_->DeleteHiddenValue(MESSAGE_HANDLER_SYMBOL); Local on_msg_complete_v = message_handler->Get(ON_MESSAGE_COMPLETE_SYMBOL); if (on_msg_complete_v->IsFunction() == false) return 0; Handle on_msg_complete = Handle::Cast(on_msg_complete_v); TryCatch try_catch; Local ret = on_msg_complete->Call(message_handler, 0, NULL); if (ret.IsEmpty()) { fatal_exception(try_catch); return -2; } if (ret->IsFalse()) return -3; return 0; } HTTPConnection::HTTPConnection (Handle handle, enum http_parser_type type) : Connection(handle) { http_parser_init (&parser_, type); parser_.on_message_begin = on_message_begin; parser_.on_uri = on_uri; parser_.on_header_field = on_header_field; parser_.on_header_value = on_header_value; parser_.on_headers_complete = on_headers_complete; parser_.on_body = on_body; parser_.on_message_complete = on_message_complete; parser_.data = this; } HTTPConnection::~HTTPConnection ( ) { V8::AdjustAmountOfExternalAllocatedMemory(-sizeof(HTTPConnection)); } Persistent HTTPServer::constructor_template; void HTTPServer::Initialize (Handle target) { HandleScope scope; Local t = FunctionTemplate::New(v8New); constructor_template = Persistent::New(t); constructor_template->Inherit(Acceptor::constructor_template); constructor_template->InstanceTemplate()->SetInternalFieldCount(1); target->Set(String::NewSymbol("LowLevelServer"), constructor_template->GetFunction()); } Handle HTTPServer::v8New (const Arguments& args) { HandleScope scope; if (args.Length() < 1 || args[0]->IsFunction() == false) return ThrowException(String::New("Must at give connection handler as the first argument")); Local protocol_class = Local::Cast(args[0]); Local options; if (args.Length() > 1 && args[1]->IsObject()) { options = args[1]->ToObject(); } else { options = Object::New(); } HTTPServer *s = new HTTPServer(args.This(), protocol_class, options); ObjectWrap::InformV8ofAllocation(s); return args.This(); } Connection* HTTPServer::OnConnection (struct sockaddr *addr, socklen_t len) { HandleScope scope; Local connection_handler = GetConnectionHandler (); if (connection_handler.IsEmpty()) { Close(); return NULL; } TryCatch try_catch; Local connection_handle = HTTPConnection::server_constructor_template->GetFunction()->NewInstance(0, NULL); if (connection_handle.IsEmpty()) { fatal_exception(try_catch); return NULL; } HTTPConnection *connection = NODE_UNWRAP(HTTPConnection, connection_handle); if (!connection) return NULL; connection->SetAcceptor(handle_); Handle argv[1] = { connection_handle }; Local ret = connection_handler->Call(handle_, 1, argv); if (ret.IsEmpty()) fatal_exception(try_catch); return connection; }