Files
crosslang/src/runtime_methods/net.cpp

1641 lines
62 KiB
C++

#include "CrossLang.hpp"
#include <TessesFramework/Crypto/ClientTLSStream.hpp>
#include <TessesFramework/Http/HttpClient.hpp>
#include <TessesFramework/Mail/Smtp.hpp>
#include <TessesFramework/Streams/MemoryStream.hpp>
#include <TessesFramework/Streams/NetworkStream.hpp>
#include <TessesFramework/Streams/Stream.hpp>
#include <sys/types.h>
#include <csignal>
#include <iostream>
#include <cstring>
using namespace Tesses::Framework::Streams;
using namespace Tesses::Framework::Http;
using namespace Tesses::Framework::Mail;
namespace Tesses::CrossLang
{
static std::shared_ptr<SMTPBody> TObjectToSMTPBody(GCList& ls,std::string mimeType, TObject obj)
{
std::shared_ptr<SMTPBody> body;
std::string text;
TByteArray* ba;
std::shared_ptr<Tesses::Framework::Streams::Stream> sho;
if(GetObject(obj,text))
{
body = std::make_shared<SMTPStringBody>(text,mimeType);
}
else if(GetObjectHeap(obj,ba)) {
std::shared_ptr<MemoryStream> ms = std::make_shared<MemoryStream>(true);
ms->WriteBlock(ba->data.data(), ba->data.size());
ms->Seek(0L, SeekOrigin::Begin);
body = std::make_shared<SMTPStreamBody>(mimeType,ms);
}
else if(GetObject(obj,sho))
{
ls.Add(sho);
body = std::make_shared<SMTPStreamBody>(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<TObject> 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<TObject> args)
{
if(!ctx->IsAvailable()) return Undefined();
else if(key == "AddValue")
{
std::string key;
std::string value;
int64_t i64;
double d64;
std::shared_ptr<Tesses::Framework::Date::DateTime> 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<Tesses::Framework::Date::DateTime> 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<Tesses::Framework::Date::DateTime>(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<TObject> 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 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 ctx->statusCode;
else if(key == "getQueryParams") return TNativeObject::Create<THttpDictionary>(ls, &ctx->queryParams,this);
else if(key == "getRequestHeaders") return TNativeObject::Create<THttpDictionary>(ls, &ctx->requestHeaders,this);
else if(key == "getResponseHeaders") return TNativeObject::Create<THttpDictionary>(ls, &ctx->responseHeaders,this);
else if(key == "getPathArguments") return TNativeObject::Create<THttpDictionary>(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<Tesses::Framework::Filesystem::VFS> vfs;
if(GetArgumentHeap(args, 0, callable))
{
ctx->ParseFormData([callable,&ls](std::string a,std::string b, std::string c)->std::shared_ptr<Tesses::Framework::Streams::Stream> {
auto res = callable->Call(ls,{a,b,c});
std::shared_ptr<Tesses::Framework::Streams::Stream> strm;
if(GetObject(res,strm))
{
return strm;
}
return nullptr;
});
}
else if(GetArgument(args,0,vfs))
{
int i = 1;
std::vector<TObject> response;
ctx->ParseFormData([vfs,&response,&ls,&i](std::string mime,std::string filename, std::string name)->std::shared_ptr<Tesses::Framework::Streams::Stream> {
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<Tesses::Framework::Streams::Stream> 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<Tesses::Framework::Date::DateTime> da;
if(GetArgument(args,0,da))
ctx->WithLastModified(*da);
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<Tesses::Framework::Streams::Stream> 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<void(WebSocketMessage&)> sendMessage,std::function<void()> ping,std::function<void()> close)->void{
GCList ls2(ls.GetGC());
dict->CallMethod(ls2,"Open",{
TExternalMethod::Create(ls2,"Send a message",{"messageTextOrByteArray"},[sendMessage](GCList& ls,std::vector<TObject> 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<TObject> args)->TObject {
ping();
return nullptr;
}),
TExternalMethod::Create(ls2, "Close client",{},[close](GCList& ls,std::vector<TObject> 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<void(WebSocketMessage&)> sendMessage,std::function<void()> ping,std::function<void()> close)->void{
GCList ls2(ls.GetGC());
cls->CallMethod(ls2,"","Open",{
TExternalMethod::Create(ls2,"Send a message",{"messageTextOrByteArray"},[sendMessage](GCList& ls,std::vector<TObject> 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<TObject> args)->TObject {
ping();
return nullptr;
}),
TExternalMethod::Create(ls2, "Close client",{},[close](GCList& ls,std::vector<TObject> 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(GC* gc,TObject obj)
{
this->ls=new GCList(gc);
this->ls->Add(obj);
this->obj = obj;
}
class TDictionaryHttpRequestBody : public HttpRequestBody
{
GC* gc;
TDictionary* req;
public:
TDictionaryHttpRequestBody(GC* gc,TDictionary* req)
{
this->gc = gc;
this->req = req;
}
void HandleHeaders(HttpDictionary& dict)
{
GCList ls(gc);
auto dummy = TNativeObject::Create<DummyTNativeObjectThatReturnsHttpDictionary>(ls);
auto res=TNativeObject::Create<THttpDictionary>(ls,&dict, dummy);
req->CallMethod(ls,"HandleHeaders",{res});
dummy->Close();
}
void Write(std::shared_ptr<Tesses::Framework::Streams::Stream> 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<TCallable*>(this->obj,callable))
{
GCList ls2(this->ls->GetGC());
auto res = TNativeObject::Create<TServerContext>(ls2, &ctx);
bool result;
auto out = callable->Call(ls2,{res});
if(GetObject(out,result))
{
res->Finish();
return result;
}
res->Finish();
}
else if(GetObjectHeap<TDictionary*>(this->obj,dict))
{
GCList ls2(this->ls->GetGC());
auto res = TNativeObject::Create<TServerContext>(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<TServerContext>(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<TObject> args)
{
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
{
return HttpUtils::UrlDecode(std::get<std::string>(args[0]));
}
return Undefined();
}
static TObject Net_HtmlEncode(GCList& ls, std::vector<TObject> args)
{
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
{
return HttpUtils::HtmlEncode(std::get<std::string>(args[0]));
}
return Undefined();
}
static TObject Net_UrlEncode(GCList& ls, std::vector<TObject> args)
{
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
{
return HttpUtils::UrlEncode(std::get<std::string>(args[0]));
}
return Undefined();
}
static TObject Net_UrlPathDecode(GCList& ls, std::vector<TObject> args)
{
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
{
return HttpUtils::UrlPathDecode(std::get<std::string>(args[0]));
}
return Undefined();
}
static TObject Net_UrlPathEncode(GCList& ls, std::vector<TObject> args)
{
if(args.size() == 1 && std::holds_alternative<std::string>(args[0]))
{
return HttpUtils::UrlPathEncode(std::get<std::string>(args[0]));
}
return Undefined();
}
static TObject New_NetworkStream(GCList& ls, std::vector<TObject> args)
{
bool ipv6;
bool datagram;
if(GetArgument(args,0,ipv6) && GetArgument(args,1,datagram))
{
return std::make_shared<NetworkStream>(ipv6,datagram);
}
return nullptr;
}
class HttpServerNativeObject : public TNativeObject
{
HttpServer* server;
public:
HttpServerNativeObject(uint16_t port, std::shared_ptr<IHttpServer> httpServer,bool printIps)
{
server=new HttpServer(port,httpServer,printIps);
}
HttpServerNativeObject(std::string unixPath, std::shared_ptr<IHttpServer> httpServer)
{
server=new HttpServer(unixPath,httpServer);
}
std::string TypeName()
{
return "Net.Http.HttpServer";
}
TObject CallMethod(GCList& ls, std::string key, std::vector<TObject> 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<TObject> args,TRootEnvironment* env)
{
int64_t port;
std::string pathStr;
if(GetArgument(args,1,port))
{
bool printIPs=true;
GetArgument(args,2,printIPs);
std::shared_ptr<IHttpServer> httpSvr = ToHttpServer(ls.GetGC(),args[0]);
if(httpSvr) {
uint16_t p = (uint16_t)port;
return TNativeObject::Create<HttpServerNativeObject>(ls,port,httpSvr,printIPs);
}
}
if(GetArgument(args,1,pathStr) && env->permissions.canRegisterLocalFS)
{
std::shared_ptr<IHttpServer> httpSvr = ToHttpServer(ls.GetGC(),args[0]);
if(httpSvr) {
return TNativeObject::Create<HttpServerNativeObject>(ls,pathStr,httpSvr);
}
}
return nullptr;
}
static TObject Net_Http_ListenSimpleWithLoop(GCList& ls, std::vector<TObject> args)
{
int64_t port;
if(GetArgument(args,1,port))
{
std::shared_ptr<IHttpServer> 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<TObject> args)
{
if(args.empty()) return nullptr;
std::shared_ptr<IHttpServer> 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<TObject> args)
{
Tesses::Framework::Filesystem::VFSPath p;
if(GetArgumentAsPath(args,0,p))
{
std::filesystem::path p2 = p.GetFileName();
return HttpUtils::MimeType(p2);
}
return std::string("application/octet-stream");
}
class THttpRequestBody : public TNativeObject {
HttpRequestBody* body;
public:
THttpRequestBody(HttpRequestBody* body)
{
this->body = body;
}
bool IsClosed()
{
return body == nullptr;
}
HttpRequestBody* GetBody()
{
return this->body;
}
std::string TypeName()
{
return "Net.Http.HttpRequestBody";
}
TObject CallMethod(GCList& ls, std::string key, std::vector<TObject> args)
{
return Undefined();
}
void Close()
{
delete this->body;
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<TObject> args)
{
if(!this->IsAvailable()) return Undefined();
else if(key == "CopyToStream")
{
std::shared_ptr<Stream> 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<THttpDictionary>(ls,&response->responseHeaders, this);
}
return Undefined();
}
};
static TObject Net_Http_MakeRequest(GCList& ls, std::vector<TObject> 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.canRegisterLocalFS)
{
_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 = new TDictionaryHttpRequestBody(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();
}
else if(body1 != nullptr)
{
delete req.body;
}
}
return TNativeObject::Create<THttpResponse>(ls, resp);
}
return nullptr;
}
static TObject Net_Smtp_Send(GCList& ls, std::vector<TObject> 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 = "<h1>Hello, world</h1>"
},
attachments = [
{
name = "myimg.png",
type = "image/png",
data = embed("myimg.png")
}
]
}
*/
ls.GetGC()->BarrierBegin();
auto server = dict->GetValue("server");
TDictionary* dict2;
std::shared_ptr<Tesses::Framework::Streams::Stream> strm;
bool ownsStream=true;
std::shared_ptr<Stream> 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<NetworkStream>(host,(uint16_t)port,false,false,false);
if(tls)
{
strm = std::make_shared<Framework::Crypto::ClientTLSStream>(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<std::string,std::shared_ptr<SMTPBody>>(name,TObjectToSMTPBody(ls, type, o)));
}
}
}
ls.GetGC()->BarrierEnd();
client.Send();
return "";
}
return nullptr;
}
static TObject Net_Http_WebSocketClient(GCList& ls, std::vector<TObject> 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;
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<CallbackWebSocketConnection>([dict,&ls](std::function<void(WebSocketMessage&)> sendMessage,std::function<void()> ping,std::function<void()> close)->void{
GCList ls2(ls.GetGC());
dict->CallMethod(ls2,"Open",{
TExternalMethod::Create(ls2,"Send a message",{"messageTextOrByteArray"},[sendMessage](GCList& ls,std::vector<TObject> 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<TObject> args)->TObject {
ping();
return nullptr;
}),
TExternalMethod::Create(ls2, "Close client",{},[close](GCList& ls,std::vector<TObject> 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<THttpDictionary>(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;
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<CallbackWebSocketConnection>([co,&ls](std::function<void(WebSocketMessage&)> sendMessage,std::function<void()> ping,std::function<void()> close)->void{
GCList ls2(ls.GetGC());
co->CallMethod(ls2,"","Open",{
TExternalMethod::Create(ls2,"Send a message",{"messageTextOrByteArray"},[sendMessage](GCList& ls,std::vector<TObject> 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<TObject> args)->TObject {
ping();
return nullptr;
}),
TExternalMethod::Create(ls2, "Close client",{},[close](GCList& ls,std::vector<TObject> 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<THttpDictionary>(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<TObject> args)
{
std::string url;
if(GetArgument(args,0,url))
return DownloadToStringSimple(url);
return nullptr;
}
static TObject Net_Http_DownloadToStream(GCList& ls, std::vector<TObject> args)
{
std::string url;
std::shared_ptr<Stream> strm;
if(GetArgument(args,0,url) && GetArgument(args,1,strm))
{
DownloadToStreamSimple(url,strm);
}
return nullptr;
}
static TObject Net_Http_DownloadToFile(GCList& ls, std::vector<TObject> args)
{
std::string url;
std::shared_ptr<Tesses::Framework::Filesystem::VFS> 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<IHttpServer> svr,std::vector<TObject>& args)
{
TServerContext* ctx;
if(GetArgumentHeap(args,0,ctx))
{
return svr->Handle(*ctx->GetContext());
}
return false;
}
std::shared_ptr<IHttpServer> ToHttpServer(GC* gc, TObject obj)
{
if(std::holds_alternative<std::shared_ptr<IHttpServer>>(obj)) return std::get<std::shared_ptr<IHttpServer>>(obj);
TDictionary* dict;
TClassObject* clo;
TCallable* call;
if(GetObjectHeap(obj,dict))
{
return std::make_shared<TObjectHttpServer>(gc,dict);
}
else if(GetObjectHeap(obj,clo))
{
return std::make_shared<TObjectHttpServer>(gc,clo);
}
else if(GetObjectHeap(obj,call))
{
return std::make_shared<TObjectHttpServer>(gc,call);
}
return nullptr;
}
static TObject Net_Http_HtmlP(GCList& ls, std::vector<TObject> args)
{
std::string str;
if(GetArgument(args,0,str))
{
return HttpUtils::HtmlP(str);
}
return "";
}
static TObject Net_Http_StatusCodeString(GCList& ls, std::vector<TObject> args)
{
int64_t sc;
if(GetArgument(args,0,sc))
{
return HttpUtils::StatusCodeString((StatusCode)sc);
}
return "";
}
static TObject New_FileServer(GCList& ls, std::vector<TObject> args)
{
std::shared_ptr<Tesses::Framework::Filesystem::VFS> vfs;
bool allowlisting;
bool spa;
if(GetArgument(args,0,vfs) && GetArgument(args,1,allowlisting) && GetArgument(args,2,spa))
{
return std::make_shared<FileServer>(vfs,allowlisting,spa);
}
return nullptr;
}
static TObject New_BasicAuthServer(GCList& ls, std::vector<TObject> args)
{
auto bAuth = std::make_shared<BasicAuthServer>();
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<TObject> args)
{
if(args.empty()) return std::make_shared<MountableServer>();
return std::make_shared<MountableServer>(ToHttpServer(ls.GetGC(),args[0]));
}
static TObject New_RouteServer(GCList& ls, std::vector<TObject> args)
{
if(args.empty()) return std::make_shared<RouteServer>();
return std::make_shared<RouteServer>(ToHttpServer(ls.GetGC(),args[0]));
}
static TObject New_StreamHttpRequestBody(GCList& ls, std::vector<TObject> args)
{
std::shared_ptr<Stream> strm;
std::string mimeType;
if(GetArgument(args,0,strm) && GetArgument(args, 1, mimeType))
{
return TNativeObject::Create<THttpRequestBody>(ls, new StreamHttpRequestBody(strm, mimeType));
}
return nullptr;
}
static TObject New_TextHttpRequestBody(GCList& ls, std::vector<TObject> args)
{
std::string text;
std::string mimeType;
if(GetArgument(args, 0, text) && GetArgument(args, 1, mimeType))
{
return TNativeObject::Create<THttpRequestBody>(ls,new TextHttpRequestBody(text, mimeType));
}
return nullptr;
}
static TObject New_JsonHttpRequestBody(GCList& ls, std::vector<TObject> args)
{
if(!args.empty())
{
return TNativeObject::Create<THttpRequestBody>(ls,new TextHttpRequestBody(Json_Encode(args[0]), "application/json"));
}
return nullptr;
}
void TStd::RegisterNet(GC* 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 text request body",{"json"},New_JsonHttpRequestBody);
_new->DeclareFunction(gc, "HttpServer", "Create a http server (allows multiple)",{"server","portOrUnixPath","$printIPs"},[env](GCList& ls, std::vector<TObject> 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);
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<TObject> 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<TObject> 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<TObject> 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<TObject> 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(GC* 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<TServerContext>(ls, &ctx);
bool result;
auto out = this->Call(ls,{res});
if(GetObject(out,result))
{
res->Finish();
return result;
}
res->Finish();
return false;
};
}
}