#include #include #include #include #include #include #include using namespace v8; using namespace std; #define PORT "1981" static oi_server server; static struct ev_loop *loop; static Persistent context_; static Persistent process_; static Persistent request_template_; class Connection { public: Connection ( void) { oi_socket_init (&socket, 30.0); ebb_request_parser_init (&parser); } ebb_request_parser parser; oi_socket socket; }; class HttpRequest { public: HttpRequest (Connection &c); ~HttpRequest() { } string path; Connection &connection; ebb_request parser_info; Persistent js_object; }; HttpRequest* UnwrapRequest ( Handle obj ) { Handle field = Handle::Cast(obj->GetInternalField(0)); void* ptr = field->Value(); return static_cast(ptr); } Handle GetPath ( Local name , const AccessorInfo& info ) { HttpRequest* request = UnwrapRequest(info.Holder()); return String::New(request->path.c_str(), request->path.length()); } Handle GetMethod ( Local name , const AccessorInfo& info ) { HttpRequest* request = UnwrapRequest(info.Holder()); // TODO allocate these strings only once. reference global switch(request->parser_info.method) { case EBB_COPY: return String::New("COPY"); case EBB_DELETE: return String::New("DELETE"); case EBB_GET: return String::New("GET"); case EBB_HEAD: return String::New("HEAD"); case EBB_LOCK: return String::New("LOCK"); case EBB_MKCOL: return String::New("MKCOL"); case EBB_MOVE: return String::New("MOVE"); case EBB_OPTIONS: return String::New("OPTIONS"); case EBB_POST: return String::New("POST"); case EBB_PROPFIND: return String::New("PROPFIND"); case EBB_PROPPATCH: return String::New("PROPPATCH"); case EBB_PUT: return String::New("PUT"); case EBB_TRACE: return String::New("TRACE"); case EBB_UNLOCK: return String::New("UNLOCK"); default: return Null(); } } Handle RespondCallback ( const Arguments& args ) { HttpRequest* request = UnwrapRequest(args.Holder()); HandleScope scope; Handle arg = args[0]; // TODO Make sure that we write reponses in the correct order. With // keep-alive it's possible that one response can return before the last // one has been sent!!! if(arg == Null()) { request->connection.socket.on_drain = oi_socket_close; } else { Local s = arg->ToString(); oi_buf *buf = oi_buf_new2(s->Length()); s->WriteAscii(buf->base); oi_socket_write(&request->connection.socket, buf); } return Undefined(); } HttpRequest::HttpRequest (Connection &c) : connection(c) { ebb_request_init(&parser_info); HandleScope handle_scope; if (request_template_.IsEmpty()) { Handle raw_template = ObjectTemplate::New(); raw_template->SetInternalFieldCount(1); // Add accessors for each of the fields of the request. raw_template->SetAccessor(String::NewSymbol("path"), GetPath); raw_template->SetAccessor(String::NewSymbol("method"), GetMethod); raw_template->Set(String::New("respond"), FunctionTemplate::New(RespondCallback)); request_template_ = Persistent::New(raw_template); } Handle templ = request_template_; // Create an empty http request wrapper. Handle result = templ->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); js_object = Persistent::New(result); } 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_headers_complete ( ebb_request *req ) { HttpRequest *request = static_cast (req->data); // Create a handle scope to keep the temporary object references. HandleScope handle_scope; // Enter this processor's context so all the remaining operations // take place there Context::Scope context_scope(context_); // Set up an exception handler before calling the Process function TryCatch try_catch; // Invoke the process function, giving the global object as 'this' // and one argument, the request. const int argc = 1; Handle argv[argc] = { request->js_object }; Handle result = process_->Call(context_->Global(), argc, argv); if (result.IsEmpty()) { String::Utf8Value error(try_catch.Exception()); printf("error: %s\n", *error); } } static void on_request_complete ( ebb_request *req ) { HttpRequest *request = static_cast (req->data); } static void on_body ( ebb_request *req , const char *chunk , size_t length ) { HttpRequest *request = static_cast (req->data); } ebb_request * on_request ( void *data ) { Connection *connection = static_cast (data); HttpRequest *request = new HttpRequest(*connection); request->parser_info.on_path = on_path; request->parser_info.on_query_string = NULL; request->parser_info.on_uri = NULL; request->parser_info.on_fragment = NULL; request->parser_info.on_header_field = NULL; request->parser_info.on_header_value = NULL; 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; } static void on_read ( oi_socket *socket , const void *buf , size_t count ) { Connection *connection = static_cast (socket->data); ebb_request_parser_execute ( &connection->parser , static_cast (buf) // FIXME change ebb to use void* , count ); /* TODO check for errors */ } static void on_close ( oi_socket *socket ) { Connection *connection = static_cast (socket->data); /* TODO free requests */ free(connection); } static void on_drain ( oi_socket *socket ) { Connection *connection = static_cast (socket->data); //oi_socket_close(&connection->socket); } static oi_socket* new_connection ( oi_server *server , struct sockaddr *addr , socklen_t len ) { Connection *connection = new Connection(); connection->socket.on_read = on_read; connection->socket.on_error = NULL; connection->socket.on_close = on_close; connection->socket.on_timeout = NULL; connection->socket.on_drain = on_drain; connection->socket.data = connection; connection->parser.new_request = on_request; connection->parser.data = connection; return &connection->socket; } // Reads a file into a v8 string. Handle ReadFile ( const string& name ) { FILE* file = fopen(name.c_str(), "rb"); if (file == NULL) return Handle(); fseek(file, 0, SEEK_END); int size = ftell(file); rewind(file); char* chars = new char[size + 1]; chars[size] = '\0'; for (int i = 0; i < size;) { int read = fread(&chars[i], 1, size - i, file); i += read; } fclose(file); Handle result = String::New(chars, size); delete[] chars; return result; } void ParseOptions ( int argc , char* argv[] , map& options , string* file ) { for (int i = 1; i < argc; i++) { string arg = argv[i]; int index = arg.find('=', 0); if (index == string::npos) { *file = arg; } else { string key = arg.substr(0, index); string value = arg.substr(index+1); options[key] = value; } } } bool ExecuteScript ( Handle script ) { HandleScope handle_scope; // We're just about to compile the script; set up an error handler to // catch any exceptions the script might throw. TryCatch try_catch; // Compile the script and check for errors. Handle