#include "App.h" #include "Utilities.h" #include using namespace v8; struct HttpResponseWrapper { template static inline uWS::HttpResponse *getHttpResponse(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); 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.", NewStringType::kNormal).ToLocalChecked())); } 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) { Isolate *isolate = args.GetIsolate(); 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), isolate](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)}; CallJS(isolate, Local::New(isolate, p), 2, argv); NeuterArrayBuffer(dataArrayBuffer); }); args.GetReturnValue().Set(args.Holder()); } } /* Takes nothing, returns nothing. Cb wants nothing returned. */ template static void res_onAborted(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); 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), isolate]() { HandleScope hs(isolate); /* Mark this resObject invalid */ Local::New(isolate, resObject)->SetAlignedPointerInInternalField(0, nullptr); CallJS(isolate, Local::New(isolate, p), 0, nullptr); }); args.GetReturnValue().Set(args.Holder()); } } /* Takes nothing, returns arraybuffer */ template static void res_getRemoteAddress(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); 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) { Isolate *isolate = args.GetIsolate(); 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) { Isolate *isolate = args.GetIsolate(); 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), isolate](int offset) -> bool { HandleScope hs(isolate); Local argv[] = {Integer::NewFromUnsigned(isolate, offset)}; /* We should check if this is really here! */ MaybeLocal maybeBoolean = CallJS(isolate, Local::New(isolate, p), 1, argv); if (maybeBoolean.IsEmpty()) { std::cerr << "ERROR! onWritable must return a boolean value according to documentation!" << std::endl; exit(-1); } return BooleanValue(isolate, maybeBoolean.ToLocalChecked()); /* 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) { Isolate *isolate = args.GetIsolate(); 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(isolate->GetCurrentContext()).ToChecked(); } 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(isolate->GetCurrentContext(), 0, Boolean::New(isolate, ok)).ToChecked(); array->Set(isolate->GetCurrentContext(), 1, Boolean::New(isolate, hasResponded)).ToChecked(); args.GetReturnValue().Set(array); } } /* Takes data, returns true for success, false for backpressure */ template static void res_write(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); 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) { Isolate *isolate = args.GetIsolate(); 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 */ template static void res_cork(const FunctionCallbackInfo &args) { Isolate *isolate = args.GetIsolate(); auto *res = getHttpResponse(args); if (res) { res->cork([cb = Local::Cast(args[0]), isolate]() { /* This one is called from JS so we don't need CallJS */ cb->Call(isolate->GetCurrentContext(), isolate->GetCurrentContext()->Global(), 0, nullptr).IsEmpty(); }); args.GetReturnValue().Set(args.Holder()); } } template static Local init(Isolate *isolate) { Local resTemplateLocal = FunctionTemplate::New(isolate); if (SSL) { resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.SSLHttpResponse", NewStringType::kNormal).ToLocalChecked()); } else { resTemplateLocal->SetClassName(String::NewFromUtf8(isolate, "uWS.HttpResponse", NewStringType::kNormal).ToLocalChecked()); } resTemplateLocal->InstanceTemplate()->SetInternalFieldCount(1); /* Register our functions */ resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeStatus", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_writeStatus)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "end", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_end)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "tryEnd", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_tryEnd)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "write", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_write)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "writeHeader", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_writeHeader)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "close", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_close)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getWriteOffset", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getWriteOffset)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onWritable", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_onWritable)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onAborted", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_onAborted)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "onData", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_onData)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "getRemoteAddress", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_getRemoteAddress)); resTemplateLocal->PrototypeTemplate()->Set(String::NewFromUtf8(isolate, "cork", NewStringType::kNormal).ToLocalChecked(), FunctionTemplate::New(isolate, res_cork)); /* Create our template */ Local resObjectLocal = resTemplateLocal->GetFunction(isolate->GetCurrentContext()).ToLocalChecked()->NewInstance(isolate->GetCurrentContext()).ToLocalChecked(); return resObjectLocal; } };