#include Persistent reqTemplate, resTemplate; Persistent httpPersistent; uWS::HttpRequest *currentReq = nullptr; struct HttpServer { struct Request { static void on(const FunctionCallbackInfo &args) { NativeString eventName(args[0]); if (std::string(eventName.getData(), eventName.getLength()) == "data") { args.Holder()->SetInternalField(1, args[1]); } else if (std::string(eventName.getData(), eventName.getLength()) == "end") { args.Holder()->SetInternalField(2, args[1]); } else { std::cout << "Warning: req.on(" << std::string(eventName.getData(), eventName.getLength()) << ") is not implemented!" << std::endl; } args.GetReturnValue().Set(args.Holder()); } static void headers(Local property, const PropertyCallbackInfo &args) { uWS::HttpRequest *req = currentReq; if (!req) { std::cerr << "Warning: req.headers usage past request handler is not supported!" << std::endl; } else { NativeString nativeString(property); uWS::Header header = req->getHeader(nativeString.getData(), nativeString.getLength()); if (header) { args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) header.value, String::kNormalString, header.valueLength)); } } } static void url(Local property, const PropertyCallbackInfo &args) { args.GetReturnValue().Set(args.This()->GetInternalField(4)); } static void method(Local property, const PropertyCallbackInfo &args) { //std::cout << "method" << std::endl; long methodId = ((long) args.This()->GetAlignedPointerFromInternalField(3)) >> 1; switch (methodId) { case uWS::HttpMethod::METHOD_GET: args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "GET", String::kNormalString, 3)); break; case uWS::HttpMethod::METHOD_PUT: args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "PUT", String::kNormalString, 3)); break; case uWS::HttpMethod::METHOD_POST: args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "POST", String::kNormalString, 4)); break; case uWS::HttpMethod::METHOD_HEAD: args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "HEAD", String::kNormalString, 4)); break; case uWS::HttpMethod::METHOD_PATCH: args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "PATCH", String::kNormalString, 5)); break; case uWS::HttpMethod::METHOD_TRACE: args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "TRACE", String::kNormalString, 5)); break; case uWS::HttpMethod::METHOD_DELETE: args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "DELETE", String::kNormalString, 6)); break; case uWS::HttpMethod::METHOD_OPTIONS: args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "OPTIONS", String::kNormalString, 7)); break; case uWS::HttpMethod::METHOD_CONNECT: args.GetReturnValue().Set(String::NewFromOneByte(args.GetIsolate(), (uint8_t *) "CONNECT", String::kNormalString, 7)); break; } } // placeholders static void unpipe(const FunctionCallbackInfo &args) { //std::cout << "req.unpipe called" << std::endl; } static void resume(const FunctionCallbackInfo &args) { //std::cout << "req.resume called" << std::endl; } static void socket(const FunctionCallbackInfo &args) { // return new empty object args.GetReturnValue().Set(Object::New(args.GetIsolate())); } static Local getTemplateObject(Isolate *isolate) { Local reqTemplateLocal = FunctionTemplate::New(isolate); reqTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uws.Request")); reqTemplateLocal->InstanceTemplate()->SetInternalFieldCount(5); reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "url"), Request::url); reqTemplateLocal->PrototypeTemplate()->SetAccessor(String::NewFromUtf8(isolate, "method"), Request::method); reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "on"), FunctionTemplate::New(isolate, Request::on)); reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "unpipe"), FunctionTemplate::New(isolate, Request::unpipe)); reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "resume"), FunctionTemplate::New(isolate, Request::resume)); reqTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "socket"), FunctionTemplate::New(isolate, Request::socket)); Local reqObjectLocal = reqTemplateLocal->GetFunction()->NewInstance(); Local headersTemplate = ObjectTemplate::New(isolate); headersTemplate->SetNamedPropertyHandler(Request::headers); reqObjectLocal->Set(String::NewFromUtf8(isolate, "headers"), headersTemplate->NewInstance()); return reqObjectLocal; } }; struct Response { static void on(const FunctionCallbackInfo &args) { NativeString eventName(args[0]); if (std::string(eventName.getData(), eventName.getLength()) == "close") { args.Holder()->SetInternalField(1, args[1]); } else { std::cout << "Warning: res.on(" << std::string(eventName.getData(), eventName.getLength()) << ") is not implemented!" << std::endl; } args.GetReturnValue().Set(args.Holder()); } static void end(const FunctionCallbackInfo &args) { uWS::HttpResponse *res = (uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0); if (res) { NativeString nativeString(args[0]); ((Persistent *) &res->userData)->Reset(); ((Persistent *) &res->userData)->~Persistent(); ((Persistent *) &res->extraUserData)->Reset(); ((Persistent *) &res->extraUserData)->~Persistent(); res->end(nativeString.getData(), nativeString.getLength()); } } // todo: this is slow static void writeHead(const FunctionCallbackInfo &args) { uWS::HttpResponse *res = (uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0); if (res) { std::string head = "HTTP/1.1 " + std::to_string(args[0]->IntegerValue()) + " "; if (args.Length() > 1 && args[1]->IsString()) { NativeString statusMessage(args[1]); head.append(statusMessage.getData(), statusMessage.getLength()); } else { head += "OK"; } if (args[args.Length() - 1]->IsObject()) { Local headersObject = args[args.Length() - 1]->ToObject(); Local headers = headersObject->GetOwnPropertyNames(); for (int i = 0; i < headers->Length(); i++) { Local key = headers->Get(i); Local value = headersObject->Get(key); NativeString nativeKey(key); NativeString nativeValue(value); head += "\r\n"; head.append(nativeKey.getData(), nativeKey.getLength()); head += ": "; head.append(nativeValue.getData(), nativeValue.getLength()); } } head += "\r\n\r\n"; res->write(head.data(), head.length()); } } // todo: if not writeHead called before then should write implicit headers static void write(const FunctionCallbackInfo &args) { uWS::HttpResponse *res = (uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0); if (res) { NativeString nativeString(args[0]); res->write(nativeString.getData(), nativeString.getLength()); } } static void setHeader(const FunctionCallbackInfo &args) { //std::cout << "res.setHeader called" << std::endl; } static void getHeader(const FunctionCallbackInfo &args) { //std::cout << "res.getHeader called" << std::endl; } static Local getTemplateObject(Isolate *isolate) { Local resTemplateLocal = FunctionTemplate::New(isolate); resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uws.Response")); resTemplateLocal->InstanceTemplate()->SetInternalFieldCount(5); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end"), FunctionTemplate::New(isolate, Response::end)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeHead"), FunctionTemplate::New(isolate, Response::writeHead)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "write"), FunctionTemplate::New(isolate, Response::write)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "on"), FunctionTemplate::New(isolate, Response::on)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "setHeader"), FunctionTemplate::New(isolate, Response::setHeader)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getHeader"), FunctionTemplate::New(isolate, Response::getHeader)); return resTemplateLocal->GetFunction()->NewInstance(); } }; // todo: wrap everything up - most important function to get correct static void createServer(const FunctionCallbackInfo &args) { // todo: delete this on destructor uWS::Group *group = hub.createGroup(); group->setUserData(new GroupData); GroupData *groupData = (GroupData *) group->getUserData(); Isolate *isolate = args.GetIsolate(); Persistent *httpRequestCallback = &groupData->httpRequestHandler; httpRequestCallback->Reset(isolate, Local::Cast(args[0])); group->onHttpRequest([isolate, httpRequestCallback](uWS::HttpResponse *res, uWS::HttpRequest req, char *data, size_t length, size_t remainingBytes) { HandleScope hs(isolate); currentReq = &req; Local reqObject = Local::New(isolate, reqTemplate)->Clone(); reqObject->SetAlignedPointerInInternalField(0, &req); new (&res->extraUserData) Persistent(isolate, reqObject); Local resObject = Local::New(isolate, resTemplate)->Clone(); resObject->SetAlignedPointerInInternalField(0, res); new (&res->userData) Persistent(isolate, resObject); // store url & method (needed by Koa and Express) long methodId = req.getMethod() << 1; reqObject->SetAlignedPointerInInternalField(3, (void *) methodId); reqObject->SetInternalField(4, String::NewFromOneByte(isolate, (uint8_t *) req.getUrl().value, String::kNormalString, req.getUrl().valueLength)); Local argv[] = {reqObject, resObject}; Local::New(isolate, *httpRequestCallback)->Call(isolate->GetCurrentContext()->Global(), 2, argv); if (length) { Local dataCallback = reqObject->GetInternalField(1); if (!dataCallback->IsUndefined()) { Local argv[] = {ArrayBuffer::New(isolate, data, length)}; Local::Cast(dataCallback)->Call(isolate->GetCurrentContext()->Global(), 1, argv); } if (!remainingBytes) { Local endCallback = reqObject->GetInternalField(2); if (!endCallback->IsUndefined()) { Local::Cast(endCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); } } } currentReq = nullptr; reqObject->SetAlignedPointerInInternalField(0, nullptr); }); group->onCancelledHttpRequest([isolate](uWS::HttpResponse *res) { HandleScope hs(isolate); // mark res as invalid Local resObject = Local::New(isolate, *(Persistent *) &res->userData); resObject->SetAlignedPointerInInternalField(0, nullptr); // mark req as invalid Local reqObject = Local::New(isolate, *(Persistent *) &res->extraUserData); reqObject->SetAlignedPointerInInternalField(0, nullptr); // emit res 'close' on aborted response Local closeCallback = resObject->GetInternalField(1); if (!closeCallback->IsUndefined()) { Local::Cast(closeCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); } ((Persistent *) &res->userData)->Reset(); ((Persistent *) &res->userData)->~Persistent(); ((Persistent *) &res->extraUserData)->Reset(); ((Persistent *) &res->extraUserData)->~Persistent(); }); group->onHttpData([isolate](uWS::HttpResponse *res, char *data, size_t length, size_t remainingBytes) { Local reqObject = Local::New(isolate, *(Persistent *) res->extraUserData); Local dataCallback = reqObject->GetInternalField(1); if (!dataCallback->IsUndefined()) { Local argv[] = {ArrayBuffer::New(isolate, data, length)}; Local::Cast(dataCallback)->Call(isolate->GetCurrentContext()->Global(), 1, argv); } if (!remainingBytes) { Local endCallback = reqObject->GetInternalField(2); if (!endCallback->IsUndefined()) { Local::Cast(endCallback)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); } } }); Local newInstance; if (!args.IsConstructCall()) { args.GetReturnValue().Set(newInstance = Local::New(args.GetIsolate(), httpPersistent)->NewInstance()); } else { args.GetReturnValue().Set(newInstance = args.This()); } newInstance->SetAlignedPointerInInternalField(0, group); } static void on(const FunctionCallbackInfo &args) { NativeString eventName(args[0]); std::cout << "Warning: server.on(" << std::string(eventName.getData(), eventName.getLength()) << ") is not implemented!" << std::endl; } static void listen(const FunctionCallbackInfo &args) { uWS::Group *group = (uWS::Group *) args.Holder()->GetAlignedPointerFromInternalField(0); std::cout << "listen: " << hub.listen(args[0]->IntegerValue(), nullptr, 0, group) << std::endl; if (args[args.Length() - 1]->IsFunction()) { Local::Cast(args[args.Length() - 1])->Call(args.GetIsolate()->GetCurrentContext()->Global(), 0, nullptr); } } // var app = getExpressApp(express) static void getExpressApp(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); if (args[0]->IsFunction()) { Local express = Local::Cast(args[0]); express->Get(String::NewFromUtf8(isolate, "request"))->ToObject()->SetPrototype(Local::New(args.GetIsolate(), reqTemplate)->GetPrototype()); express->Get(String::NewFromUtf8(isolate, "response"))->ToObject()->SetPrototype(Local::New(args.GetIsolate(), resTemplate)->GetPrototype()); // also change app.listen? // change prototypes back? args.GetReturnValue().Set(express->NewInstance()); } } static void getResponsePrototype(const FunctionCallbackInfo &args) { args.GetReturnValue().Set(Local::New(args.GetIsolate(), resTemplate)->GetPrototype()); } static void getRequestPrototype(const FunctionCallbackInfo &args) { args.GetReturnValue().Set(Local::New(args.GetIsolate(), reqTemplate)->GetPrototype()); } static Local getHttpServer(Isolate *isolate) { Local httpServer = FunctionTemplate::New(isolate, HttpServer::createServer); httpServer->InstanceTemplate()->SetInternalFieldCount(1); httpServer->Set(String::NewFromUtf8(isolate, "createServer"), FunctionTemplate::New(isolate, HttpServer::createServer)); httpServer->Set(String::NewFromUtf8(isolate, "getExpressApp"), FunctionTemplate::New(isolate, HttpServer::getExpressApp)); httpServer->Set(String::NewFromUtf8(isolate, "getResponsePrototype"), FunctionTemplate::New(isolate, HttpServer::getResponsePrototype)); httpServer->Set(String::NewFromUtf8(isolate, "getRequestPrototype"), FunctionTemplate::New(isolate, HttpServer::getRequestPrototype)); httpServer->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "listen"), FunctionTemplate::New(isolate, HttpServer::listen)); httpServer->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "on"), FunctionTemplate::New(isolate, HttpServer::on)); reqTemplate.Reset(isolate, Request::getTemplateObject(isolate)); resTemplate.Reset(isolate, Response::getTemplateObject(isolate)); Local httpServerLocal = httpServer->GetFunction(); httpPersistent.Reset(isolate, httpServerLocal); return httpServerLocal; } };