#include "CrossLang.hpp" #include #include #include #include #include #include #include #include #include #include using namespace Tesses::Framework::Streams; using namespace Tesses::Framework::Http; using namespace Tesses::Framework::Mail; namespace Tesses::CrossLang { static std::shared_ptr TObjectToSMTPBody(GCList &ls, std::string mimeType, TObject obj) { std::shared_ptr body; std::string text; TByteArray *ba; std::shared_ptr sho; if (GetObject(obj, text)) { body = std::make_shared(text, mimeType); } else if (GetObjectHeap(obj, ba)) { std::shared_ptr ms = std::make_shared(true); ms->WriteBlock(ba->data.data(), ba->data.size()); ms->Seek(0L, SeekOrigin::Begin); body = std::make_shared(mimeType, ms); } else if (GetObject(obj, sho)) { ls.Add(sho); body = std::make_shared(mimeType, sho); } return body; } class TNativeObjectThatReturnsHttpDictionary : public TNativeObject { public: virtual bool IsAvailable() = 0; }; class DummyTNativeObjectThatReturnsHttpDictionary : public TNativeObjectThatReturnsHttpDictionary { bool isAvail; public: DummyTNativeObjectThatReturnsHttpDictionary() { this->isAvail = true; } bool IsAvailable() { return this->isAvail; } void Close() { this->isAvail = false; } std::string TypeName() { return "Dummy"; } TObject CallMethod(GCList &ls, std::string key, std::vector args) { return Undefined(); } }; class THttpDictionary : public TNativeObject { TNativeObjectThatReturnsHttpDictionary *ctx; HttpDictionary *dict; public: THttpDictionary(HttpDictionary *dict, TNativeObjectThatReturnsHttpDictionary *ctx) { this->dict = dict; this->ctx = ctx; } std::string TypeName() { return "Net.Http.HttpDictionary"; } void Mark() { if (this->marked) return; this->marked = true; ctx->Mark(); } TObject CallMethod(GCList &ls, std::string key, std::vector args) { if (!ctx->IsAvailable()) return Undefined(); else if (key == "AddValue") { std::string key; std::string value; int64_t i64; double d64; std::shared_ptr da; if (GetArgument(args, 0, key)) { if (GetArgument(args, 1, value)) this->dict->AddValue(key, value); else if (GetArgument(args, 1, i64)) this->dict->AddValue(key, i64); else if (GetArgument(args, 1, d64)) this->dict->AddValue(key, d64); else if (GetArgument(args, 1, da)) this->dict->AddValue(key, *da); } } else if (key == "SetValue") { std::string key; std::string value; int64_t i64; double d64; std::shared_ptr da; if (GetArgument(args, 0, key)) { if (GetArgument(args, 1, value)) this->dict->SetValue(key, value); else if (GetArgument(args, 1, i64)) this->dict->SetValue(key, i64); else if (GetArgument(args, 1, d64)) this->dict->SetValue(key, d64); else if (GetArgument(args, 1, da)) this->dict->SetValue(key, *da); } } else if (key == "Clear") { std::string key; bool existsAfter; if (GetArgument(args, 0, key) && GetArgument(args, 0, existsAfter)) dict->Clear(key, existsAfter); else dict->Clear(); } else if (key == "GetFirstBoolean") { std::string key; if (GetArgument(args, 0, key)) { return dict->GetFirstBoolean(key); } return false; } else if (key == "TryGetFirst") { std::string key; std::string value; if (GetArgument(args, 0, key) && dict->TryGetFirst(key, value)) { return value; } return nullptr; } else if (key == "TryGetFirstDouble") { std::string key; double value; if (GetArgument(args, 0, key) && dict->TryGetFirstDouble(key, value)) { return value; } return nullptr; } else if (key == "TryGetFirstInt") { std::string key; int64_t value; if (GetArgument(args, 0, key) && dict->TryGetFirstInt(key, value)) { return value; } return nullptr; } else if (key == "TryGetFirstDate") { std::string key; Tesses::Framework::Date::DateTime value; if (GetArgument(args, 0, key) && dict->TryGetFirstDate(key, value)) { return std::make_shared( value); } return nullptr; } else if (key == "TryGetOnlyOne") { std::string key; std::string value; if (GetArgument(args, 0, key) && dict->TryGetOnlyOne(key, value)) { return value; } return nullptr; } else if (key == "TryGetOnlyOneBoolean") { std::string key; bool value; if (GetArgument(args, 0, key) && dict->TryGetOnlyOneBoolean(key, value)) { return value; } return nullptr; } else if (key == "TryGetOnlyOneDouble") { std::string key; double value; if (GetArgument(args, 0, key) && dict->TryGetOnlyOneDouble(key, value)) { return value; } return nullptr; } else if (key == "TryGetOnlyOneInt") { std::string key; int64_t value; if (GetArgument(args, 0, key) && dict->TryGetOnlyOneInt(key, value)) { return value; } return nullptr; } else if (key == "TryGetOnlyOneDate") { std::string key; Tesses::Framework::Date::DateTime value; if (GetArgument(args, 0, key) && dict->TryGetOnlyOneDate(key, value)) { return std::make_shared( value); } return nullptr; } else if (key == "ToList") { TList *_ls = TList::Create(ls); for (auto item : dict->kvp) { for (auto i : item.second) { auto d = TDictionary::Create(ls); ls.GetGC()->BarrierBegin(); d->SetValue("Key", item.first); d->SetValue("Value", i); _ls->Add(d); ls.GetGC()->BarrierEnd(); } } return _ls; } else if (key == "SetContentDisposition") { std::string type; std::string filename; std::string fieldName; if (GetArgument(args, 0, filename) && GetArgument(args, 1, type)) { ContentDisposition dispo; dispo.filename = filename; dispo.type = type; if (GetArgument(args, 2, fieldName)) dispo.fieldName = fieldName; this->dict->SetValue("Content-Disposition", dispo.ToString()); } return nullptr; } else if (key == "TryGetContentDisposition") { std::string cdStr; ContentDisposition cd; if (this->dict->TryGetFirst("Content-Disposition", cdStr) && ContentDisposition::TryParse(cdStr, cd)) { return TDictionary::Create(ls, {TDItem("Type", cd.type), TDItem("FileName", cd.filename), TDItem("FieldName", cd.fieldName)}); } return nullptr; } return Undefined(); } }; class TServerContext : public TNativeObjectThatReturnsHttpDictionary { ServerContext *ctx; public: TServerContext(ServerContext *ctx) { this->ctx = ctx; } std::string TypeName() { return "Net.Http.ServerContext"; } bool IsAvailable() { return ctx != nullptr; } ServerContext *GetContext() { return this->ctx; } TObject CallMethod(GCList &ls, std::string key, std::vector args) { if (this->ctx == nullptr) return Undefined(); else if (key == "getEncrypted") return ctx->encrypted; else if (key == "getMethod") return ctx->method; else if (key == "getIP") return ctx->ip; else if (key == "getPort") return (int64_t)ctx->port; else if (key == "getOriginalPath") return ctx->originalPath; else if (key == "getPath") return ctx->path; else if (key == "setPath") { std::string str; if (GetArgument(args, 0, str)) { ctx->path = str; return str; } } else if (key == "getVersion") return ctx->version; else if (key == "setStatusCode") { int64_t sc; if (GetArgument(args, 0, sc)) { ctx->statusCode = (StatusCode)sc; return sc; } } else if (key == "getStatusCode") return (int64_t)ctx->statusCode; else if (key == "getQueryParams") return TNativeObject::Create(ls, &ctx->queryParams, this); else if (key == "getBodyParams") return TNativeObject::Create(ls, &ctx->bodyParams, this); else if (key == "getRequestHeaders") return TNativeObject::Create( ls, &ctx->requestHeaders, this); else if (key == "getResponseHeaders") return TNativeObject::Create( ls, &ctx->responseHeaders, this); else if (key == "getPathArguments") return TNativeObject::Create( ls, &ctx->pathArguments, this); else if (key == "GetStream") return ctx->GetStream(); else if (key == "OpenRequestStream") return ctx->OpenRequestStream(); else if (key == "OpenResponseStream") return ctx->OpenResponseStream(); else if (key == "ParseFormData") { TCallable *callable; std::shared_ptr vfs; if (GetArgumentHeap(args, 0, callable)) { ctx->ParseFormData( [callable, &ls](std::string a, std::string b, std::string c) -> std::shared_ptr { auto res = callable->Call(ls, {a, b, c}); std::shared_ptr strm; if (GetObject(res, strm)) { return strm; } return nullptr; }); } else if (GetArgument(args, 0, vfs)) { int i = 1; std::vector response; ctx->ParseFormData( [vfs, &response, &ls, &i](std::string mime, std::string filename, std::string name) -> std::shared_ptr { std::string realFileName = "/" + std::to_string(i) + ".bin"; i++; response.push_back(TDictionary::Create( ls, {TDItem("RealFileName", realFileName), TDItem("FileName", filename), TDItem("Mime", mime), TDItem("Name", name)})); auto strm = vfs->OpenFile(realFileName, "wb"); return strm; }); return TList::Create(ls, response.begin(), response.end()); } } else if (key == "getNeedToParseFormData") return ctx->NeedToParseFormData(); else if (key == "ReadString") return ctx->ReadString(); else if (key == "ReadStream") { std::shared_ptr strm; if (GetArgument(args, 0, strm)) { ctx->ReadStream(strm); } } else if (key == "ReadJson") { return Json_Decode(ls, ctx->ReadString()); } else if (key == "SendJson") { if (args.size() > 0) { ctx->WithMimeType("application/json") .SendText(Json_Encode(args[0])); } } else if (key == "SendText") { std::string text; if (GetArgument(args, 0, text)) ctx->SendText(text); } else if (key == "WithMimeType") { std::string text; if (GetArgument(args, 0, text)) ctx->WithMimeType(text); return this; } else if (key == "WithLastModified") { std::shared_ptr da; if (GetArgument(args, 0, da)) ctx->WithLastModified(*da); return this; } else if (key == "WithDebug") { bool debug = true; GetArgument(args, 0, debug); ctx->WithDebug(debug); return this; } else if (key == "getDebug") { return ctx->Debug(); } else if (key == "SendServerSentEvents") { if (!args.empty() && std::holds_alternative< std::shared_ptr>( args[0])) { ctx->SendServerSentEvents( std::get>(args[0])); } } else if (key == "WithHeaderIntercepter") { TCallable *callable; if (GetArgumentHeap(args, 0, callable)) { auto marked = CreateMarkedTObject(ls.GetGC(), callable); ctx->WithHeaderIntercepter( [marked](ServerContext &ctx) -> bool { auto obj = marked->GetObject(); TCallable *callable; if (GetObjectHeap(obj, callable)) { GCList ls(marked->GetGC()); auto ptr = TNativeObject::Create(ls, &ctx); auto res = callable->Call(ls, {ptr}); ptr->Finish(); bool r0; if (GetObject(res, r0)) return r0; } return false; }); } return this; } else if (key == "WithContentDisposition") { std::string filename; bool isInline; if (GetArgument(args, 0, filename) && GetArgument(args, 1, isInline)) ctx->WithContentDisposition(filename, isInline); return this; } else if (key == "WithHeader") { std::string key; std::string value; if (GetArgument(args, 0, key) && GetArgument(args, 1, value)) ctx->WithHeader(key, value); return this; } else if (key == "WithSingleHeader") { std::string key; std::string value; if (GetArgument(args, 0, key) && GetArgument(args, 1, value)) ctx->WithSingleHeader(key, value); return this; } else if (key == "SendStream") { std::shared_ptr strm; if (GetArgument(args, 0, strm)) ctx->SendStream(strm); } else if (key == "SendBytes") { TByteArray *ba; if (GetArgumentHeap(args, 0, ba)) ctx->SendBytes(ba->data); } else if (key == "GetUrlWithQuery") { return ctx->GetUrlWithQuery(); } else if (key == "GetOriginalPathWithQuery") { return ctx->GetOriginalPathWithQuery(); } else if (key == "MakeAbsolute") { std::string path; if (GetArgument(args, 0, path)) { return ctx->MakeAbsolute(path); } return nullptr; } else if (key == "WithLocationHeader") { std::string path; if (GetArgument(args, 0, path)) { int64_t sc; if (GetArgument(args, 1, sc)) { ctx->WithLocationHeader(path, (StatusCode)sc); return nullptr; } ctx->WithLocationHeader(path); } return this; } else if (key == "GetServerRoot") { return ctx->GetServerRoot(); } else if (key == "SendRedirect") { std::string path; if (GetArgument(args, 0, path)) { int64_t sc; if (GetArgument(args, 1, sc)) { ctx->SendRedirect(path, (StatusCode)sc); return nullptr; } ctx->SendRedirect(path); } return nullptr; } else if (key == "WriteHeaders") ctx->WriteHeaders(); else if (key == "StartWebSocketSession") { TDictionary *dict; TClassObject *cls; if (GetArgumentHeap(args, 0, dict)) { ctx->StartWebSocketSession( [dict, &ls](std::function sendMessage, std::function ping, std::function close) -> void { GCList ls2(ls.GetGC()); dict->CallMethod( ls2, "Open", {TExternalMethod::Create( ls2, "Send a message", {"messageTextOrByteArray"}, [sendMessage]( GCList &ls, std::vector args) -> TObject { std::string str; TByteArray *bArray; if (GetArgument(args, 0, str)) { WebSocketMessage msg(str); sendMessage(msg); } else if (GetArgumentHeap(args, 0, bArray)) { WebSocketMessage msg(bArray->data); sendMessage(msg); } return nullptr; }), TExternalMethod::Create( ls2, "Ping client", {}, [ping](GCList &ls, std::vector args) -> TObject { ping(); return nullptr; }), TExternalMethod::Create( ls2, "Close client", {}, [close](GCList &ls, std::vector args) -> TObject { close(); return nullptr; })}); }, [dict, &ls](WebSocketMessage &msg) -> void { GCList ls2(ls.GetGC()); TObject v; if (msg.isBinary) { auto r = TByteArray::Create(ls2); r->data = msg.data; v = r; } else { v = msg.ToString(); } dict->CallMethod(ls2, "Receive", {v}); }, [dict, &ls](bool close) -> void { GCList ls2(ls.GetGC()); dict->CallMethod(ls2, "Close", {close}); }); } else if (GetArgumentHeap(args, 0, cls)) { ctx->StartWebSocketSession( [cls, &ls](std::function sendMessage, std::function ping, std::function close) -> void { GCList ls2(ls.GetGC()); cls->CallMethod( ls2, "", "Open", {TExternalMethod::Create( ls2, "Send a message", {"messageTextOrByteArray"}, [sendMessage]( GCList &ls, std::vector args) -> TObject { std::string str; TByteArray *bArray; if (GetArgument(args, 0, str)) { WebSocketMessage msg(str); sendMessage(msg); } else if (GetArgumentHeap(args, 0, bArray)) { WebSocketMessage msg(bArray->data); sendMessage(msg); } return nullptr; }), TExternalMethod::Create( ls2, "Ping client", {}, [ping](GCList &ls, std::vector args) -> TObject { ping(); return nullptr; }), TExternalMethod::Create( ls2, "Close client", {}, [close](GCList &ls, std::vector args) -> TObject { close(); return nullptr; })}); }, [cls, &ls](WebSocketMessage &msg) -> void { GCList ls2(ls.GetGC()); TObject v; if (msg.isBinary) { auto r = TByteArray::Create(ls2); r->data = msg.data; v = r; } else { v = msg.ToString(); } cls->CallMethod(ls2, "", "Receive", {v}); }, [cls, &ls](bool close) -> void { GCList ls2(ls.GetGC()); cls->CallMethod(ls2, "", "Close", {close}); }); } } return Undefined(); } void Finish() { this->ctx = nullptr; } }; TObjectHttpServer::TObjectHttpServer(std::shared_ptr gc, TObject obj) { this->ls = new GCList(gc); this->ls->Add(obj); this->obj = obj; } class TDictionaryHttpRequestBody : public HttpRequestBody { std::shared_ptr gc; TDictionary *req; public: TDictionaryHttpRequestBody(std::shared_ptr gc, TDictionary *req) { this->gc = gc; this->req = req; } void HandleHeaders(HttpDictionary &dict) { GCList ls(gc); auto dummy = TNativeObject::Create( ls); auto res = TNativeObject::Create(ls, &dict, dummy); req->CallMethod(ls, "HandleHeaders", {res}); dummy->Close(); } void Write(std::shared_ptr strm) { GCList ls(gc); req->CallMethod(ls, "Write", {strm}); } ~TDictionaryHttpRequestBody() { GCList ls(gc); req->CallMethod(ls, "Close", {}); } }; bool TObjectHttpServer::Handle(ServerContext &ctx) { TCallable *callable; TDictionary *dict; TClassObject *clsObj; if (GetObjectHeap(this->obj, callable)) { GCList ls2(this->ls->GetGC()); auto res = TNativeObject::Create(ls2, &ctx); bool result; auto out = callable->Call(ls2, {res}); if (GetObject(out, result)) { res->Finish(); return result; } res->Finish(); } else if (GetObjectHeap(this->obj, dict)) { GCList ls2(this->ls->GetGC()); auto res = TNativeObject::Create(ls2, &ctx); bool result; auto out = dict->CallMethod(ls2, "Handle", {res}); if (GetObject(out, result)) { res->Finish(); return result; } res->Finish(); } else if (GetObjectHeap(this->obj, clsObj) && clsObj->HasMethod("", "Handle")) { GCList ls2(this->ls->GetGC()); auto res = TNativeObject::Create(ls2, &ctx); bool result; this->ls->GetGC()->BarrierBegin(); auto callableO = clsObj->GetValue("", "Handle"); this->ls->GetGC()->BarrierEnd(); TCallable *callable; if (GetObjectHeap(callableO, callable)) { auto out = callable->Call(ls2, {res}); if (GetObject(out, result)) { res->Finish(); return result; } res->Finish(); } } return false; } TObjectHttpServer::~TObjectHttpServer() { delete ls; } static TObject Net_UrlDecode(GCList &ls, std::vector args) { if (args.size() == 1 && std::holds_alternative(args[0])) { return HttpUtils::UrlDecode(std::get(args[0])); } return Undefined(); } static TObject Net_HtmlEncode(GCList &ls, std::vector args) { if (args.size() == 1 && std::holds_alternative(args[0])) { return HttpUtils::HtmlEncode(std::get(args[0])); } return Undefined(); } static TObject Net_UrlEncode(GCList &ls, std::vector args) { if (args.size() == 1 && std::holds_alternative(args[0])) { return HttpUtils::UrlEncode(std::get(args[0])); } return Undefined(); } static TObject Net_UrlPathDecode(GCList &ls, std::vector args) { if (args.size() == 1 && std::holds_alternative(args[0])) { return HttpUtils::UrlPathDecode(std::get(args[0])); } return Undefined(); } static TObject Net_UrlPathEncode(GCList &ls, std::vector args) { if (args.size() == 1 && std::holds_alternative(args[0])) { return HttpUtils::UrlPathEncode(std::get(args[0])); } return Undefined(); } static TObject New_NetworkStream(GCList &ls, std::vector args) { bool ipv6; bool datagram; if (GetArgument(args, 0, ipv6) && GetArgument(args, 1, datagram)) { return std::make_shared(ipv6, datagram); } return nullptr; } class HttpServerNativeObject : public TNativeObject { HttpServer *server; public: HttpServerNativeObject(uint16_t port, std::shared_ptr httpServer, bool printIps) { server = new HttpServer(port, httpServer, printIps); } HttpServerNativeObject(std::string unixPath, std::shared_ptr httpServer) { server = new HttpServer(unixPath, httpServer); } std::string TypeName() { return "Net.Http.HttpServer"; } TObject CallMethod(GCList &ls, std::string key, std::vector args) { if (key == "getPort") { return (int64_t)server->GetPort(); } if (key == "StartAccepting") server->StartAccepting(); return nullptr; } ~HttpServerNativeObject() { delete server; } }; static TObject New_HttpServer(GCList &ls, std::vector args, TRootEnvironment *env) { int64_t port; std::string pathStr; if (GetArgument(args, 1, port)) { bool printIPs = true; GetArgument(args, 2, printIPs); std::shared_ptr httpSvr = ToHttpServer(ls.GetGC(), args[0]); if (httpSvr) { uint16_t p = (uint16_t)port; return TNativeObject::Create( ls, port, httpSvr, printIPs); } } if (GetArgument(args, 1, pathStr) && env->permissions.localfs) { std::shared_ptr httpSvr = ToHttpServer(ls.GetGC(), args[0]); if (httpSvr) { return TNativeObject::Create(ls, pathStr, httpSvr); } } return nullptr; } static TObject Net_Http_ListenSimpleWithLoop(GCList &ls, std::vector args) { int64_t port; if (GetArgument(args, 1, port)) { std::shared_ptr httpSvr = ToHttpServer(ls.GetGC(), args[0]); if (httpSvr) { uint16_t p = (uint16_t)port; HttpServer server(p, httpSvr); server.StartAccepting(); Tesses::Framework::TF_RunEventLoop(); } } return nullptr; } static TObject Net_Http_ListenOnUnusedPort(GCList &ls, std::vector args) { if (args.empty()) return nullptr; std::shared_ptr httpSvr = ToHttpServer(ls.GetGC(), args.front()); if (httpSvr) { uint16_t p = 0; HttpServer server(p, httpSvr); std::cout << "Port: " << server.GetPort() << std::endl; server.StartAccepting(); Tesses::Framework::TF_RunEventLoop(); } return nullptr; } static TObject Net_Http_MimeType(GCList &ls, std::vector args) { Tesses::Framework::Filesystem::VFSPath p; if (GetArgumentAsPath(args, 0, p)) { return HttpUtils::GetMimeTypePath(p); } return std::string("application/octet-stream"); } class THttpRequestBody : public TNativeObject { std::shared_ptr body; public: THttpRequestBody(std::shared_ptr body) { this->body = body; } bool IsClosed() { return body == nullptr; } std::shared_ptr GetBody() { return this->body; } std::string TypeName() { return "Net.Http.HttpRequestBody"; } TObject CallMethod(GCList &ls, std::string key, std::vector args) { return Undefined(); } void Close() { this->body = nullptr; } }; class THttpResponse : public TNativeObjectThatReturnsHttpDictionary { HttpResponse *response; public: THttpResponse(HttpResponse *resp) { this->response = resp; } bool IsAvailable() { return this->response != nullptr; } void Close() { delete this->response; this->response = nullptr; } ~THttpResponse() { delete this->response; this->response = nullptr; } std::string TypeName() { return "Net.Http.HttpResponse"; } TObject CallMethod(GCList &ls, std::string key, std::vector args) { if (!this->IsAvailable()) return Undefined(); else if (key == "CopyToStream") { std::shared_ptr strm; if (GetArgument(args, 0, strm)) { response->CopyToStream(strm); } } else if (key == "ReadAsString") { return response->ReadAsString(); } else if (key == "ReadAsJson") { return Json_Decode(ls, response->ReadAsString()); } else if (key == "ReadAsStream") { return response->ReadAsStream(); } else if (key == "getStatusCode") { return (int64_t)response->statusCode; } else if (key == "getVersion") { return response->version; } else if (key == "getResponseHeaders") { return TNativeObject::Create( ls, &response->responseHeaders, this); } return Undefined(); } }; static TObject Net_Http_MakeRequest(GCList &ls, std::vector args, TRootEnvironment *env) { auto gc = ls.GetGC(); std::string url; if (GetArgument(args, 0, url)) { TDictionary *body1 = nullptr; THttpRequestBody *body2 = nullptr; HttpRequest req; req.method = "GET"; req.ignoreSSLErrors = false; req.followRedirects = false; req.trusted_root_cert_bundle = ""; req.url = url; req.body = nullptr; TDictionary *options; if (GetArgumentHeap(args, 1, options)) { gc->BarrierBegin(); auto _obj = options->GetValue("Method"); GetObject(_obj, req.method); _obj = options->GetValue("IgnoreSSLErrors"); GetObject(_obj, req.ignoreSSLErrors); _obj = options->GetValue("FollowRedirects"); GetObject(_obj, req.followRedirects); _obj = options->GetValue("TrustedRootCertBundle"); GetObject(_obj, req.trusted_root_cert_bundle); if (env->permissions.localfs) { _obj = options->GetValue("UnixSocket"); GetObject(_obj, req.unixSocket); } _obj = options->GetValue("RequestHeaders"); TList *headers; if (GetObjectHeap(_obj, headers)) { for (int64_t index = 0; index < headers->Count(); index++) { _obj = headers->Get(index); TDictionary *dict; if (GetObjectHeap(_obj, dict)) { std::string key = {}; std::string value = {}; _obj = dict->GetValue("Key"); GetObject(_obj, key); _obj = dict->GetValue("Value"); GetObject(_obj, value); req.requestHeaders.AddValue(key, value); } } } _obj = options->GetValue("Body"); if (GetObjectHeap(_obj, body1)) { req.body = std::make_shared(gc, body1); } else if (GetObjectHeap(_obj, body2) && !body2->IsClosed()) { req.body = body2->GetBody(); } gc->BarrierEnd(); } HttpResponse *resp = new HttpResponse(req); if (req.body != nullptr) { if (body2 != nullptr) { body2->Close(); } } return TNativeObject::Create(ls, resp); } return nullptr; } static TObject Net_Smtp_Send(GCList &ls, std::vector args) { TDictionary *dict; if (GetArgumentHeap(args, 0, dict)) { // the body should be either type text/plain or text/html // the body and attachment data can also point to bytearray of stream // server can also be a stream // as of right now the email is fire and forget (ie no error checking) // check function return type just in case (this function returns a // empty string if no error) we rolled our own smtp client /* dict looks like this from crosslang's point of view { server = { host = "smtp.example.com", tls = true }, auth = { username = "from", password = "THEPASSWORD" }, domain = "example.com", from = { name = "The name shown in the mail where it is from", email = "from@example.com" }, to = "to@example.com", subject = "My little message", body = { type = "text/html", data = "

Hello, world

" }, attachments = [ { name = "myimg.png", type = "image/png", data = embed("myimg.png") } ] } */ ls.GetGC()->BarrierBegin(); auto server = dict->GetValue("server"); TDictionary *dict2; std::shared_ptr strm; bool ownsStream = true; std::shared_ptr objStrm; if (GetObjectHeap(server, dict2)) { auto tlsO = dict2->GetValue("tls"); auto hostO = dict2->GetValue("host"); auto portO = dict2->GetValue("port"); std::string host; bool tls = false; int64_t port; GetObject(tlsO, tls); if (!GetObject(portO, port)) port = tls ? 465 : 25; GetObject(hostO, host); strm = std::make_shared(host, (uint16_t)port, false, false, false); if (tls) { strm = std::make_shared( strm, true, host); } } else if (GetObject(server, objStrm)) { ownsStream = false; strm = objStrm; } Tesses::Framework::Mail::SMTPClient client(strm); auto o = dict->GetValue("domain"); GetObject(o, client.domain); o = dict->GetValue("to"); GetObject(o, client.to); o = dict->GetValue("subject"); GetObject(o, client.subject); o = dict->GetValue("auth"); if (GetObjectHeap(o, dict2)) { o = dict2->GetValue("username"); GetObject(o, client.username); o = dict2->GetValue("password"); GetObject(o, client.password); } o = dict->GetValue("from"); if (GetObjectHeap(o, dict2)) { o = dict2->GetValue("email"); GetObject(o, client.from); o = dict2->GetValue("name"); GetObject(o, client.from_name); } o = dict->GetValue("body"); if (GetObjectHeap(o, dict2)) { // type, data std::string type = "text/plain"; o = dict2->GetValue("type"); GetObject(o, type); o = dict2->GetValue("data"); client.body = TObjectToSMTPBody(ls, type, o); } o = dict->GetValue("attachments"); TList *als; if (GetObjectHeap(o, als)) { for (int64_t i = 0; i < als->Count(); i++) { auto item = als->Get(i); if (GetObjectHeap(item, dict2)) { o = dict2->GetValue("name"); std::string name; GetObject(o, name); std::string type = "text/plain"; o = dict2->GetValue("type"); GetObject(o, type); o = dict2->GetValue("data"); client.attachments.push_back( std::pair>( name, TObjectToSMTPBody(ls, type, o))); } } } ls.GetGC()->BarrierEnd(); client.Send(); return ""; } return nullptr; } static TObject Net_Http_WebSocketClient(GCList &ls, std::vector args) { std::string url; TList *headers; TDictionary *dict; TClassObject *co; TCallable *callable = nullptr; TObject _obj; if (GetArgument(args, 0, url) && GetArgumentHeap(args, 1, headers) && GetArgumentHeap(args, 2, dict)) { GetArgumentHeap(args, 3, callable); HttpDictionary hdict(false); for (int64_t index = 0; index < headers->Count(); index++) { _obj = headers->Get(index); TDictionary *dict; if (GetObjectHeap(_obj, dict)) { std::string key = {}; std::string value = {}; _obj = dict->GetValue("Key"); GetObject(_obj, key); _obj = dict->GetValue("Value"); GetObject(_obj, value); hdict.AddValue(key, value); } } auto conn = std::make_shared( [dict, &ls](std::function sendMessage, std::function ping, std::function close) -> void { GCList ls2(ls.GetGC()); dict->CallMethod( ls2, "Open", {TExternalMethod::Create( ls2, "Send a message", {"messageTextOrByteArray"}, [sendMessage](GCList &ls, std::vector args) -> TObject { std::string str; TByteArray *bArray; if (GetArgument(args, 0, str)) { WebSocketMessage msg(str); sendMessage(msg); } else if (GetArgumentHeap(args, 0, bArray)) { WebSocketMessage msg(bArray->data); sendMessage(msg); } return nullptr; }), TExternalMethod::Create( ls2, "Ping client", {}, [ping](GCList &ls, std::vector args) -> TObject { ping(); return nullptr; }), TExternalMethod::Create( ls2, "Close client", {}, [close](GCList &ls, std::vector args) -> TObject { close(); return nullptr; })}); }, [dict, &ls](WebSocketMessage &msg) -> void { GCList ls2(ls.GetGC()); TObject v; if (msg.isBinary) { auto r = TByteArray::Create(ls2); r->data = msg.data; v = r; } else { v = msg.ToString(); } dict->CallMethod(ls2, "Receive", {v}); }, [dict, &ls](bool close) -> void { GCList ls2(ls.GetGC()); dict->CallMethod(ls2, "Close", {close}); }); WebSocketClient( url, hdict, conn, [&ls, callable](Tesses::Framework::Http::HttpDictionary &dict, bool success) -> bool { if (callable != nullptr) { auto dummy = TNativeObject::Create< DummyTNativeObjectThatReturnsHttpDictionary>(ls); bool res = ToBool(callable->Call( ls, {TNativeObject::Create(ls, &dict, dummy), success})); dummy->Close(); return res; } return true; }); } else if (GetArgument(args, 0, url) && GetArgumentHeap(args, 1, co)) { TObject hobj; if (co->HasMethod("", "GetRequestHeaders")) hobj = co->CallMethod(ls, "", "GetRequestHeaders", {}); else if (co->HasField("", "RequestHeaders")) hobj = co->GetValue("", "RequestHeaders"); else return Undefined(); if (!GetObjectHeap(hobj, headers)) return Undefined(); HttpDictionary hdict(false); for (int64_t index = 0; index < headers->Count(); index++) { _obj = headers->Get(index); TDictionary *dict; if (GetObjectHeap(_obj, dict)) { std::string key = {}; std::string value = {}; _obj = dict->GetValue("Key"); GetObject(_obj, key); _obj = dict->GetValue("Value"); GetObject(_obj, value); hdict.AddValue(key, value); } } auto conn = std::make_shared( [co, &ls](std::function sendMessage, std::function ping, std::function close) -> void { GCList ls2(ls.GetGC()); co->CallMethod( ls2, "", "Open", {TExternalMethod::Create( ls2, "Send a message", {"messageTextOrByteArray"}, [sendMessage](GCList &ls, std::vector args) -> TObject { std::string str; TByteArray *bArray; if (GetArgument(args, 0, str)) { WebSocketMessage msg(str); sendMessage(msg); } else if (GetArgumentHeap(args, 0, bArray)) { WebSocketMessage msg(bArray->data); sendMessage(msg); } return nullptr; }), TExternalMethod::Create( ls2, "Ping client", {}, [ping](GCList &ls, std::vector args) -> TObject { ping(); return nullptr; }), TExternalMethod::Create( ls2, "Close client", {}, [close](GCList &ls, std::vector args) -> TObject { close(); return nullptr; })}); }, [co, &ls](WebSocketMessage &msg) -> void { GCList ls2(ls.GetGC()); TObject v; if (msg.isBinary) { auto r = TByteArray::Create(ls2); r->data = msg.data; v = r; } else { v = msg.ToString(); } co->CallMethod(ls2, "", "Receive", {v}); }, [co, &ls](bool close) -> void { GCList ls2(ls.GetGC()); co->CallMethod(ls2, "", "Close", {close}); }); WebSocketClient( url, hdict, conn, [&ls, co](Tesses::Framework::Http::HttpDictionary &dict, bool success) -> bool { if (co->HasMethod("", "HandleResponseHeaders")) { auto dummy = TNativeObject::Create< DummyTNativeObjectThatReturnsHttpDictionary>(ls); bool res = ToBool( co->CallMethod(ls, "", "HandleResponseHeaders", {TNativeObject::Create( ls, &dict, dummy), success})); dummy->Close(); return res; } return true; }); } return Undefined(); // Net.Http.CreateWebSocketConnection("wss://example.com/",[],conn, (dict, // success)=>{ return true;}) } static TObject Net_Http_DownloadToString(GCList &ls, std::vector args) { std::string url; if (GetArgument(args, 0, url)) return DownloadToStringSimple(url); return nullptr; } static TObject Net_Http_DownloadToStream(GCList &ls, std::vector args) { std::string url; std::shared_ptr strm; if (GetArgument(args, 0, url) && GetArgument(args, 1, strm)) { DownloadToStreamSimple(url, strm); } return nullptr; } static TObject Net_Http_DownloadToFile(GCList &ls, std::vector args) { std::string url; std::shared_ptr vfs; Tesses::Framework::Filesystem::VFSPath path; if (GetArgument(args, 0, url) && GetArgument(args, 1, vfs) && GetArgumentAsPath(args, 2, path)) { DownloadToFileSimple(url, vfs, path); } return nullptr; } bool IHttpServer_Handle(std::shared_ptr svr, std::vector &args) { TServerContext *ctx; if (GetArgumentHeap(args, 0, ctx)) { return svr->Handle(*ctx->GetContext()); } return false; } std::shared_ptr ToHttpServer(std::shared_ptr gc, TObject obj) { if (std::holds_alternative>(obj)) return std::get>(obj); TDictionary *dict; TClassObject *clo; TCallable *call; if (GetObjectHeap(obj, dict)) { return std::make_shared(gc, dict); } else if (GetObjectHeap(obj, clo)) { return std::make_shared(gc, clo); } else if (GetObjectHeap(obj, call)) { return std::make_shared(gc, call); } return nullptr; } static TObject Net_Http_HtmlP(GCList &ls, std::vector args) { std::string str; if (GetArgument(args, 0, str)) { return HttpUtils::HtmlP(str); } return ""; } static TObject Net_Http_StatusCodeString(GCList &ls, std::vector args) { int64_t sc; if (GetArgument(args, 0, sc)) { return HttpUtils::StatusCodeString((StatusCode)sc); } return ""; } static TObject New_FileServer(GCList &ls, std::vector args) { std::shared_ptr vfs; bool allowlisting; bool spa; if (GetArgument(args, 0, vfs) && GetArgument(args, 1, allowlisting) && GetArgument(args, 2, spa)) { return std::make_shared(vfs, allowlisting, spa); } return nullptr; } static TObject New_BasicAuthServer(GCList &ls, std::vector args) { auto bAuth = std::make_shared(); TCallable *cb; if (!args.empty()) bAuth->server = ToHttpServer(ls.GetGC(), args[0]); if (GetArgumentHeap(args, 1, cb)) { auto marked = CreateMarkedTObject(ls, cb); bAuth->authorization = [marked](std::string user, std::string password) -> bool { GCList ls(marked->GetGC()); TCallable *callable; if (GetObjectHeap(marked->GetObject(), callable)) { return ToBool(callable->Call(ls, {user, password})); } return false; }; } GetArgument(args, 2, bAuth->realm); return bAuth; } static TObject New_MountableServer(GCList &ls, std::vector args) { if (args.empty()) return std::make_shared(); return std::make_shared(ToHttpServer(ls.GetGC(), args[0])); } static TObject New_RouteServer(GCList &ls, std::vector args) { if (args.empty()) return std::make_shared(); return std::make_shared(ToHttpServer(ls.GetGC(), args[0])); } static TObject New_StreamHttpRequestBody(GCList &ls, std::vector args) { std::shared_ptr strm; std::string mimeType; if (GetArgument(args, 0, strm) && GetArgument(args, 1, mimeType)) { return TNativeObject::Create( ls, std::make_shared(strm, mimeType)); } return nullptr; } static TObject New_TextHttpRequestBody(GCList &ls, std::vector args) { std::string text; std::string mimeType; if (GetArgument(args, 0, text) && GetArgument(args, 1, mimeType)) { return TNativeObject::Create( ls, std::make_shared(text, mimeType)); } return nullptr; } static TObject New_JsonHttpRequestBody(GCList &ls, std::vector args) { if (!args.empty()) { return TNativeObject::Create( ls, std::make_shared(Json_Encode(args[0]), "application/json")); } return nullptr; } static TObject New_ServerSentEvents(GCList &ls, std::vector args) { return std::make_shared(); } void TStd::RegisterNet(std::shared_ptr gc, TRootEnvironment *env) { env->permissions.canRegisterNet = true; GCList ls(gc); gc->BarrierBegin(); TDictionary *dict = env->EnsureDictionary(gc, "Net"); TDictionary *_new = env->EnsureDictionary(gc, "New"); _new->DeclareFunction(gc, "StreamHttpRequestBody", "Create a stream request body", {"stream", "mimeType"}, New_StreamHttpRequestBody); _new->DeclareFunction(gc, "TextHttpRequestBody", "Create a text request body", {"text", "mimeType"}, New_TextHttpRequestBody); _new->DeclareFunction(gc, "JsonHttpRequestBody", "Create a json request body", {"json"}, New_JsonHttpRequestBody); _new->DeclareFunction( gc, "HttpServer", "Create a http server (allows multiple)", {"server", "portOrUnixPath", "$printIPs"}, [env](GCList &ls, std::vector args) -> TObject { return New_HttpServer(ls, args, env); }); _new->DeclareFunction(gc, "RouteServer", "Create a routeserver", {"$root"}, New_RouteServer); _new->DeclareFunction(gc, "FileServer", "Create a file server", {"vfs", "allowlisting", "spa"}, New_FileServer); _new->DeclareFunction(gc, "BasicAuthServer", "Create a basic auth server", {"$server", "$auth", "$realm"}, New_BasicAuthServer); _new->DeclareFunction( gc, "MountableServer", "Create a server you can mount to, must mount parents before child", {"root"}, New_MountableServer); _new->DeclareFunction(gc, "NetworkStream", "Create a network stream", {"ipv6", "datagram"}, New_NetworkStream); _new->DeclareFunction(gc, "ServerSentEvents", "Create server sent events object", {""}, New_ServerSentEvents); TDictionary *http = TDictionary::Create(ls); TDictionary *smtp = TDictionary::Create(ls); http->DeclareFunction(gc, "StatusCodeString", "Get the status code string", {"statusCode"}, Net_Http_StatusCodeString); http->DeclareFunction(gc, "HtmlP", "Linkify text", {"text"}, Net_Http_HtmlP); http->DeclareFunction(gc, "HtmlEncode", "Html encode", {"param"}, Net_HtmlEncode); http->DeclareFunction(gc, "UrlEncode", "Url encode query param", {"param"}, Net_UrlEncode); http->DeclareFunction(gc, "UrlDecode", "Url decode query param", {"param"}, Net_UrlDecode); http->DeclareFunction(gc, "UrlPathEncode", "Url encode path", {"path"}, Net_UrlPathEncode); http->DeclareFunction(gc, "UrlPathDecode", "Url decode path", {"path"}, Net_UrlPathDecode); http->DeclareFunction(gc, "MimeType", "Get mimetype from extension", {"ext"}, Net_Http_MimeType); http->DeclareFunction(gc, "StreamHttpRequestBody", "Create a stream request body", {"stream", "mimeType"}, New_StreamHttpRequestBody); http->DeclareFunction(gc, "TextHttpRequestBody", "Create a text request body", {"text", "mimeType"}, New_TextHttpRequestBody); http->DeclareFunction(gc, "JsonHttpRequestBody", "Create a text request body", {"json"}, New_JsonHttpRequestBody); http->DeclareFunction( gc, "MakeRequest", "Create an http request", {"url", "$extra"}, [env](GCList &ls, std::vector args) -> TObject { return Net_Http_MakeRequest(ls, args, env); }); http->DeclareFunction( gc, "WebSocketClient", "Create a websocket connection", {"url", "headers", "conn", "$successCB"}, Net_Http_WebSocketClient); http->DeclareFunction(gc, "DownloadToString", "Return the http file's contents as a string", {"url"}, Net_Http_DownloadToString); http->DeclareFunction(gc, "DownloadToStream", "Download file to stream", {"url", "stream"}, Net_Http_DownloadToStream); http->DeclareFunction(gc, "DownloadToFile", "Download file to file in vfs", {"url", "vfs", "path"}, Net_Http_DownloadToFile); http->DeclareFunction( gc, "HttpServer", "Create a http server (allows multiple)", {"server", "portOrUnixPath", "$printIPs"}, [env](GCList &ls, std::vector args) -> TObject { return New_HttpServer(ls, args, env); }); http->DeclareFunction(gc, "ListenSimpleWithLoop", "Listen (creates application loop)", {"server", "port"}, Net_Http_ListenSimpleWithLoop); http->DeclareFunction( gc, "ListenOnUnusedPort", "Listen on unused localhost port and print Port: theport", {"server"}, Net_Http_ListenOnUnusedPort); // FileServer svr() http->DeclareFunction(gc, "RouteServer", "Create a routeserver", {"$root"}, New_RouteServer); http->DeclareFunction(gc, "FileServer", "Create a file server", {"vfs", "allowlisting", "spa"}, New_FileServer); http->DeclareFunction(gc, "BasicAuthServer", "Create a basic auth server", {"$server", "$auth", "$realm"}, New_BasicAuthServer); http->DeclareFunction( gc, "BasicAuthGetCreds", "Get creds from str", {"ctx"}, [](GCList &ls, std::vector args) -> TObject { TServerContext *sc; if (GetArgumentHeap(args, 0, sc)) { std::string user; std::string pass; if (sc->IsAvailable() && BasicAuthServer::GetCreds(*sc->GetContext(), user, pass)) { return TDictionary::Create(ls, {TDItem("Username", user), TDItem("Password", pass)}); } } return nullptr; }); http->DeclareFunction( gc, "MountableServer", "Create a server you can mount to, must mount parents before child", {"root"}, New_MountableServer); dict->DeclareFunction(gc, "NetworkStream", "Create a network stream", {"ipv6", "datagram"}, New_NetworkStream); smtp->DeclareFunction(gc, "Send", "Send email via smtp server", {"messageStruct"}, Net_Smtp_Send); dict->DeclareFunction( gc, "getIPAddresses", "Get the ip addresses of this machine", {"$ipv6"}, [](GCList &ls, std::vector args) -> TObject { TList *a = TList::Create(ls); bool ipv6 = false; GetArgument(args, 0, ipv6); ls.GetGC()->BarrierBegin(); for (auto item : Tesses::Framework::Streams::NetworkStream::GetIPs(ipv6)) { a->Add( TDictionary::Create(ls, {TDItem("Interface", item.first), TDItem("Address", item.second)})); } ls.GetGC()->BarrierEnd(); ; return a; }); dict->SetValue("Http", http); dict->SetValue("Smtp", smtp); gc->BarrierEnd(); } Tesses::Framework::Http::ServerRequestHandler TCallable::ToRouteServerRequestHandler(std::shared_ptr gc) { auto value = CreateMarkedTObject(gc, this); return [value, this](ServerContext &ctx) -> bool { auto v = value; auto gc = v->GetGC(); GCList ls(gc); auto res = TNativeObject::Create(ls, &ctx); bool result; auto out = this->Call(ls, {res}); if (GetObject(out, result)) { res->Finish(); return result; } res->Finish(); return false; }; } } // namespace Tesses::CrossLang