#include "App.h" #include #include "Utilities.h" using namespace v8; struct HttpResponseWrapper { static Persistent resTemplate[2]; template static inline uWS::HttpResponse *getHttpResponse(const FunctionCallbackInfo &args) { auto *res = (uWS::HttpResponse *) args.Holder()->GetAlignedPointerFromInternalField(0); if (!res) { args.GetReturnValue().Set(isolate->ThrowException(String::NewFromUtf8(isolate, "Invalid access of discarded (invalid, deleted) uWS.HttpResponse/SSLHttpResponse."))); } return res; } /* Marks this JS object invalid */ static inline void invalidateResObject(const FunctionCallbackInfo &args) { args.Holder()->SetAlignedPointerInInternalField(0, nullptr); } /* Takes nothing, kills the connection */ template static void res_close(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { invalidateResObject(args); res->close(); args.GetReturnValue().Set(args.Holder()); } } /* Takes function of data and isLast. Expects nothing from callback, returns this */ template static void res_onData(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { /* This thing perfectly fits in with unique_function, and will Reset on destructor */ UniquePersistent p(isolate, Local::Cast(args[0])); res->onData([p = std::move(p)](std::string_view data, bool last) { HandleScope hs(isolate); Local dataArrayBuffer = ArrayBuffer::New(isolate, (void *) data.data(), data.length()); Local argv[] = {dataArrayBuffer, Boolean::New(isolate, last)}; Local::New(isolate, p)->Call(isolate->GetCurrentContext()->Global(), 2, argv); dataArrayBuffer->Neuter(); }); args.GetReturnValue().Set(args.Holder()); } } /* Takes nothing, returns nothing. Cb wants nothing returned. */ template static void res_onAborted(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { /* This thing perfectly fits in with unique_function, and will Reset on destructor */ UniquePersistent p(isolate, Local::Cast(args[0])); /* This is how we capture res (C++ this in invocation of this function) */ UniquePersistent resObject(isolate, args.Holder()); res->onAborted([p = std::move(p), resObject = std::move(resObject)]() { HandleScope hs(isolate); /* Mark this resObject invalid */ Local::New(isolate, resObject)->SetAlignedPointerInInternalField(0, nullptr); Local::New(isolate, p)->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); }); args.GetReturnValue().Set(args.Holder()); } } /* Takes nothing, returns arraybuffer */ template static void res_getRemoteAddress(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { std::string_view ip = res->getRemoteAddress(); /* Todo: we need to pass a copy here */ args.GetReturnValue().Set(ArrayBuffer::New(isolate, (void *) ip.data(), ip.length()/*, ArrayBufferCreationMode::kInternalized*/)); } } /* Returns the current write offset */ template static void res_getWriteOffset(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { args.GetReturnValue().Set(Integer::New(isolate, getHttpResponse(args)->getWriteOffset())); } } /* Takes function of bool(int), returns this */ template static void res_onWritable(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { /* This thing perfectly fits in with unique_function, and will Reset on destructor */ UniquePersistent p(isolate, Local::Cast(args[0])); res->onWritable([p = std::move(p)](int offset) { HandleScope hs(isolate); Local argv[] = {Integer::NewFromUnsigned(isolate, offset)}; return Local::New(isolate, p)->Call(isolate->GetCurrentContext()->Global(), 1, argv)->BooleanValue(); /* How important is this return? */ }); args.GetReturnValue().Set(args.Holder()); } } /* Takes string or arraybuffer, returns this */ template static void res_writeStatus(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { NativeString data(args.GetIsolate(), args[0]); if (data.isInvalid(args)) { return; } res->writeStatus(data.getString()); args.GetReturnValue().Set(args.Holder()); } } /* Takes string or arraybuffer, returns this */ template static void res_end(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { NativeString data(args.GetIsolate(), args[0]); if (data.isInvalid(args)) { return; } invalidateResObject(args); res->end(data.getString()); args.GetReturnValue().Set(args.Holder()); } } /* Takes data and optionally totalLength, returns true for success, false for backpressure */ template static void res_tryEnd(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { NativeString data(args.GetIsolate(), args[0]); if (data.isInvalid(args)) { return; } int totalSize = 0; if (args.Length() > 1) { totalSize = args[1]->Uint32Value(); } auto [ok, hasResponded] = res->tryEnd(data.getString(), totalSize); /* Invalidate this object if we responded completely */ if (hasResponded) { invalidateResObject(args); } /* This is a quick fix, it will need updating in µWS later on */ Local array = Array::New(isolate, 2); array->Set(0, Boolean::New(isolate, ok)); array->Set(1, Boolean::New(isolate, hasResponded)); args.GetReturnValue().Set(array); } } /* Takes data, returns true for success, false for backpressure */ template static void res_write(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { NativeString data(args.GetIsolate(), args[0]); if (data.isInvalid(args)) { return; } bool ok = res->write(data.getString()); args.GetReturnValue().Set(Boolean::New(isolate, ok)); } } /* Takes key, value. Returns this */ template static void res_writeHeader(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { NativeString header(args.GetIsolate(), args[0]); if (header.isInvalid(args)) { return; } NativeString value(args.GetIsolate(), args[1]); if (value.isInvalid(args)) { return; } res->writeHeader(header.getString(),value.getString()); args.GetReturnValue().Set(args.Holder()); } } /* Takes function, returns this (EXPERIMENTAL) */ template static void res_cork(const FunctionCallbackInfo &args) { auto *res = getHttpResponse(args); if (res) { res->cork([cb = Local::Cast(args[0])]() { cb->Call(isolate->GetCurrentContext()->Global(), 0, nullptr); }); args.GetReturnValue().Set(args.Holder()); } } template static void initResTemplate() { Local resTemplateLocal = FunctionTemplate::New(isolate); if (SSL) { resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLHttpResponse")); } else { resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.HttpResponse")); } resTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); /* Register our functions */ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeStatus"), FunctionTemplate::New(isolate, res_writeStatus)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end"), FunctionTemplate::New(isolate, res_end)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "tryEnd"), FunctionTemplate::New(isolate, res_tryEnd)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "write"), FunctionTemplate::New(isolate, res_write)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeHeader"), FunctionTemplate::New(isolate, res_writeHeader)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "close"), FunctionTemplate::New(isolate, res_close)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getWriteOffset"), FunctionTemplate::New(isolate, res_getWriteOffset)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onWritable"), FunctionTemplate::New(isolate, res_onWritable)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onAborted"), FunctionTemplate::New(isolate, res_onAborted)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onData"), FunctionTemplate::New(isolate, res_onData)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddress"), FunctionTemplate::New(isolate, res_getRemoteAddress)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "experimental_cork"), FunctionTemplate::New(isolate, res_cork)); /* Create our template */ Local resObjectLocal = resTemplateLocal->GetFunction()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); resTemplate[SSL].Reset(isolate, resObjectLocal); } template static Local getResInstance() { return Local::New(isolate, resTemplate[std::is_same::value])->Clone(); } }; Persistent HttpResponseWrapper::resTemplate[2];