#include "APIHelp.h" #include #include #include #include #include "NetworkAPI.h" #include
#include #include #include #include using namespace std; using namespace cyanray; //////////////////// Classes //////////////////// ClassDefine NetworkClassBuilder = defineClass("network") .function("httpGet", &NetworkClass::httpGet) .function("httpPost", &NetworkClass::httpPost) .function("httpGetSync", &NetworkClass::httpGetSync) // For compatibility .function("newWebSocket", &NetworkClass::newWebSocket) .build(); ClassDefine WSClientClassBuilder = defineClass("WSClient") .constructor(&WSClientClass::constructor) .instanceProperty("status", &WSClientClass::getStatus) .instanceFunction("connect", &WSClientClass::connect) .instanceFunction("connectAsync", &WSClientClass::connectAsync) .instanceFunction("send", &WSClientClass::send) .instanceFunction("listen", &WSClientClass::listen) .instanceFunction("close", &WSClientClass::close) .instanceFunction("shutdown", &WSClientClass::shutdown) .instanceFunction("errorCode", &WSClientClass::errorCode) .property("Open", [] { return Number::newNumber((int)WebSocketClient::Status::Open); }) .property("Closing", [] { return Number::newNumber((int)WebSocketClient::Status::Closing); }) .property("Closed", [] { return Number::newNumber((int)WebSocketClient::Status::Closed); }) .build(); ClassDefine HttpServerClassBuilder = defineClass("HttpServer") .constructor(&HttpServerClass::constructor) .instanceFunction("onGet", &HttpServerClass::onGet) .instanceFunction("onPut", &HttpServerClass::onPut) .instanceFunction("onPost", &HttpServerClass::onPost) .instanceFunction("onPatch", &HttpServerClass::onPatch) .instanceFunction("onDelete", &HttpServerClass::onDelete) .instanceFunction("onOptions", &HttpServerClass::onOptions) .instanceFunction("onPreRouting", &HttpServerClass::onPreRouting) .instanceFunction("onPostRouting", &HttpServerClass::onPostRouting) .instanceFunction("onError", &HttpServerClass::onError) .instanceFunction("onException", &HttpServerClass::onException) .instanceFunction("listen", &HttpServerClass::listen) .instanceFunction("startAt", &HttpServerClass::listen) .instanceFunction("stop", &HttpServerClass::stop) .instanceFunction("close", &HttpServerClass::stop) .instanceFunction("isRunning", &HttpServerClass::isRunning) .build(); ClassDefine HttpRequestClassBuilder = defineClass("HttpRequest") .constructor(nullptr) .instanceFunction("getHeader", &HttpRequestClass::getHeader) .instanceProperty("headers", &HttpRequestClass::getHeaders) .instanceProperty("method", &HttpRequestClass::getMethod) .instanceProperty("body", &HttpRequestClass::getBody) .instanceProperty("path", &HttpRequestClass::getPath) .instanceProperty("params", &HttpRequestClass::getParams) .instanceProperty("query", &HttpRequestClass::getParams) .instanceProperty("remoteAddr", &HttpRequestClass::getRemoteAddr) .instanceProperty("remotePort", &HttpRequestClass::getRemotePort) .instanceProperty("version", &HttpRequestClass::getVersion) .instanceProperty("matches", &HttpRequestClass::getRegexMatches) .build(); ClassDefine HttpResponseClassBuilder = defineClass("HttpResponse") .constructor(nullptr) .instanceFunction("getHeader", &HttpResponseClass::getHeader) .instanceFunction("setHeader", &HttpResponseClass::setHeader) .instanceFunction("write", &HttpResponseClass::write) .instanceProperty("headers", &HttpResponseClass::getHeaders, &HttpResponseClass::setHeaders) .instanceProperty("status", &HttpResponseClass::getStatus, &HttpResponseClass::setStatus) .instanceProperty("body", &HttpResponseClass::getBody, &HttpResponseClass::setBody) .instanceProperty("version", &HttpResponseClass::getVersion, &HttpResponseClass::setVersion) .instanceProperty("reason", &HttpResponseClass::getReason, &HttpResponseClass::setReason) .build(); //生成函数 WSClientClass::WSClientClass(const Local& scriptObj) : ScriptClass(scriptObj) , ws(std::make_shared()) { initListeners(); } WSClientClass::WSClientClass() : ScriptClass(ScriptClass::ConstructFromCpp{}) , ws(std::make_shared()) { initListeners(); } void WSClientClass::initListeners() { ws->OnTextReceived([nowList{&listeners[int(WSClientEvents::onTextReceived)]}](WebSocketClient& client, string msg) { if (!nowList->empty()) for (auto& listener : *nowList) { if (!EngineManager::isValid(listener.engine)) return; EngineScope enter(listener.engine); // dangerous NewTimeout(listener.func.get(), {String::newString(msg)}, 1); } }); ws->OnBinaryReceived([nowList{&listeners[int(WSClientEvents::onBinaryReceived)]}](WebSocketClient& client, vector data) { if (!nowList->empty()) for (auto& listener : *nowList) { if (!EngineManager::isValid(listener.engine)) return; EngineScope enter(listener.engine); NewTimeout(listener.func.get(), {ByteBuffer::newByteBuffer(data.data(), data.size())}, 1); } }); ws->OnError([nowList{&listeners[int(WSClientEvents::onError)]}](WebSocketClient& client, string msg) { if (!nowList->empty()) for (auto& listener : *nowList) { if (!EngineManager::isValid(listener.engine)) return; EngineScope enter(listener.engine); NewTimeout(listener.func.get(), {String::newString(msg)}, 1); } }); ws->OnLostConnection([nowList{&listeners[int(WSClientEvents::onLostConnection)]}](WebSocketClient& client, int code) { if (!nowList->empty()) for (auto& listener : *nowList) { if (!EngineManager::isValid(listener.engine)) return; EngineScope enter(listener.engine); NewTimeout(listener.func.get(), {Number::newNumber(code)}, 1); } }); } void WSClientClass::initListeners_s() { ws->OnTextReceived([nowList{&listeners[int(WSClientEvents::onTextReceived)]}, engine = EngineScope::currentEngine()](WebSocketClient& client, string msg) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; Schedule::nextTick([nowList, engine, msg = std::move(msg)]() { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); if (!nowList->empty()) for (auto& listener : *nowList) { listener.func.get().call({}, {String::newString(msg)}); } }); }); ws->OnBinaryReceived([nowList{&listeners[int(WSClientEvents::onBinaryReceived)]}, engine = EngineScope::currentEngine()](WebSocketClient& client, vector data) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; Schedule::nextTick([nowList, engine, data = std::move(data)]() mutable { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); if (!nowList->empty()) for (auto& listener : *nowList) { listener.func.get().call({}, {ByteBuffer::newByteBuffer(data.data(), data.size())}); } }); }); ws->OnError([nowList{&listeners[int(WSClientEvents::onError)]}, engine = EngineScope::currentEngine()](WebSocketClient& client, string msg) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; Schedule::nextTick([nowList, engine, msg = std::move(msg)]() { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); if (!nowList->empty()) for (auto& listener : *nowList) { listener.func.get().call({}, {String::newString(msg)}); } }); }); ws->OnLostConnection([nowList{&listeners[int(WSClientEvents::onLostConnection)]}, engine = EngineScope::currentEngine()](WebSocketClient& client, int code) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; Schedule::nextTick([nowList, engine, code]() { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); if (!nowList->empty()) for (auto& listener : *nowList) { listener.func.get().call({}, {Number::newNumber(code)}); } }); }); } void WSClientClass::clearListeners() { ws->OnTextReceived([](WebSocketClient&, std::string) {}); ws->OnBinaryReceived([](WebSocketClient&, std::vector) {}); ws->OnError([](WebSocketClient&, std::string) {}); ws->OnLostConnection([](WebSocketClient&, int) {}); } WSClientClass* WSClientClass::constructor(const Arguments& args) { return new WSClientClass(args.thiz()); } //成员函数 void WSClientClass::addListener(const string& event, Local func) { if (event == "onTextReceived") listeners[(int)WSClientEvents::onTextReceived].push_back({EngineScope::currentEngine(), script::Global(func)}); else if (event == "onBinaryReceived") listeners[(int)WSClientEvents::onBinaryReceived].push_back({EngineScope::currentEngine(), script::Global(func)}); else if (event == "onError") listeners[(int)WSClientEvents::onError].push_back({EngineScope::currentEngine(), script::Global(func)}); else if (event == "onLostConnection") listeners[(int)WSClientEvents::onLostConnection].push_back({EngineScope::currentEngine(), script::Global(func)}); else { LOG_ERROR_WITH_SCRIPT_INFO("WSClient Event \"" + event + "\" No Found!\n"); } } Local WSClientClass::getStatus() { try { return Number::newNumber((int)ws->GetStatus()); } catch (const std::runtime_error& e) { return Local(); } CATCH("Fail in getStatus!"); } Local WSClientClass::connect(const Arguments& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kString); // if (args.size() > 1 && args[1].isFunction()) // return connectAsync(args); try { string target = args[0].toStr(); RecordOperation(ENGINE_OWN_DATA()->pluginName, "ConnectToWebsocketServer", target); ws->Connect(target); return Boolean::newBoolean(true); } catch (const std::runtime_error& e) { return Boolean::newBoolean(false); } CATCH("Fail in connect!"); } // 异步连接ws客户端 Local WSClientClass::connectAsync(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[1], ValueKind::kFunction); try { string target = args[0].toStr(); RecordOperation(ENGINE_OWN_DATA()->pluginName, "ConnectToWebsocketServer", target); script::Global callbackFunc{args[1].asFunction()}; thread( [ws{this->ws}, target, callback{std::move(callbackFunc)}, engine{EngineScope::currentEngine()}, pluginName{ENGINE_OWN_DATA()->pluginName}]() mutable { #ifdef DEBUG SetThreadDescription(GetCurrentThread(), L"LLSE Connect WebSocket"); #endif // DEBUG if (!ll::isDebugMode()) _set_se_translator(seh_exception::TranslateSEHtoCE); try { bool result = false; try { ws->Connect(target); result = true; } catch (const std::runtime_error& e) { result = false; } if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); // fix get on empty Global if (callback.isEmpty()) return; NewTimeout(callback.get(), {Boolean::newBoolean(result)}, 0); } catch (const seh_exception& e) { logger.error("SEH Uncaught Exception Detected!\n{}", e.what()); logger.error("In WSClientClass::connectAsync"); logger.error("In Plugin: " + pluginName); } catch (...) { logger.error("WSClientClass::connectAsync Failed!"); logger.error("Uncaught Exception Detected!"); logger.error("In Plugin: " + pluginName); } }) .detach(); return Boolean::newBoolean(true); } catch (const std::runtime_error& e) { return Boolean::newBoolean(false); } CATCH("Fail in connectAsync!"); } Local WSClientClass::send(const Arguments& args) { CHECK_ARGS_COUNT(args, 1); try { if (args[0].isString()) ws->SendText(args[0].toStr()); else if (args[0].isByteBuffer()) ws->SendBinary((char*)args[0].asByteBuffer().getRawBytes(), args[0].asByteBuffer().byteLength()); else { LOG_WRONG_ARG_TYPE(); return Local(); } return Boolean::newBoolean(true); } catch (const std::runtime_error& e) { return Boolean::newBoolean(false); } CATCH("Fail in send!"); } Local WSClientClass::listen(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[1], ValueKind::kFunction); try { addListener(args[0].toStr(), args[1].asFunction()); return Boolean::newBoolean(true); } catch (const std::runtime_error& e) { return Boolean::newBoolean(false); } CATCH("Fail in listen!"); } Local WSClientClass::close(const Arguments& args) { try { ws->Close(); return Boolean::newBoolean(true); } catch (const std::runtime_error& e) { return Boolean::newBoolean(false); } CATCH("Fail in close!"); } Local WSClientClass::shutdown(const Arguments& args) { try { ws->Shutdown(); return Boolean::newBoolean(true); } catch (const std::runtime_error& e) { return Boolean::newBoolean(false); } CATCH("Fail in shutdown!"); } Local WSClientClass::errorCode(const Arguments& args) { try { return Number::newNumber(WSAGetLastError()); } catch (const std::runtime_error& e) { return Local(); } CATCH("Fail in errorCode!"); } //////////////////// Class HttpServer //////////////////// using namespace httplib; #define ADD_CALLBACK(method, path, func) \ callbacks.emplace(make_pair(path, HttpServerCallback{EngineScope::currentEngine(), script::Global{func}, HttpRequestType::method, path})); \ svr->##method##(path.c_str(), [this, engine = EngineScope::currentEngine()](const Request& req, Response& resp) { \ if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) \ return; \ auto task = Schedule::nextTick([this, engine, req, &resp] { \ if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) \ return; \ EngineScope enter(engine); \ for (auto& [k, v] : this->callbacks) { \ if (v.type != HttpRequestType::method) \ return; \ std::regex rgx(k); \ std::smatch matches; \ if (std::regex_match(req.path, matches, rgx)) { \ if (matches == req.matches) { \ auto reqObj = new HttpRequestClass(req); \ auto respObj = new HttpResponseClass(resp); \ v.func.get().call({}, reqObj, respObj); \ resp = *respObj->get(); \ break; \ } \ } \ } \ }); \ while (!task.isFinished()) \ std::this_thread::sleep_for(std::chrono::milliseconds(1)); \ }); HttpServerClass::HttpServerClass(const Local& scriptObj) : ScriptClass(scriptObj) , svr(new Server) { } HttpServerClass::HttpServerClass() : ScriptClass(ScriptClass::ConstructFromCpp{}) , svr(new Server) { } HttpServerClass::~HttpServerClass() { svr->stop(); } HttpServerClass* HttpServerClass::constructor(const Arguments& args) { return new HttpServerClass(args.thiz()); } Local HttpServerClass::onGet(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[1], ValueKind::kFunction); try { auto path = args[0].toStr(); auto func = args[1].asFunction(); ADD_CALLBACK(Get, path, func); /* for debug callbacks.emplace(make_pair(path, HttpServerCallback{EngineScope::currentEngine(), script::Global{func}, HttpRequestType::Get, path})); svr->Get(path.c_str(), [this, engine = EngineScope::currentEngine()](const Request& req, Response& resp) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; auto task = Schedule::nextTick([this, engine, req, &resp] { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); for (auto& [k, v] : this->callbacks) { if (v.type != HttpRequestType::Get) return; std::regex rgx(k); std::smatch matches; if (std::regex_match(req.path, matches, rgx)) { if (matches == req.matches) { auto reqObj = new HttpRequestClass(req); auto respObj = new HttpResponseClass(resp); v.func.get().call({}, reqObj, respObj); resp = *respObj->get(); break; } } } }); while (!task.isFinished()) std::this_thread::sleep_for(std::chrono::milliseconds(1)); }); */ return this->getScriptObject(); } CATCH("Fail in onGet") } Local HttpServerClass::onPut(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[1], ValueKind::kFunction); try { auto path = args[0].toStr(); auto func = args[1].asFunction(); ADD_CALLBACK(Put, path, func); return this->getScriptObject(); } CATCH("Fail in onPut!"); } Local HttpServerClass::onPost(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[1], ValueKind::kFunction); try { auto path = args[0].toStr(); auto func = args[1].asFunction(); ADD_CALLBACK(Post, path, func); return this->getScriptObject(); } CATCH("Fail in onPost!"); } Local HttpServerClass::onPatch(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[1], ValueKind::kFunction); try { auto path = args[0].toStr(); auto func = args[1].asFunction(); ADD_CALLBACK(Patch, path, func); return this->getScriptObject(); } CATCH("Fail in onPatch!"); } Local HttpServerClass::onDelete(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[1], ValueKind::kFunction); try { auto path = args[0].toStr(); auto func = args[1].asFunction(); ADD_CALLBACK(Delete, path, func); return this->getScriptObject(); } CATCH("Fail in onDelete!"); } Local HttpServerClass::onOptions(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[1], ValueKind::kFunction); try { auto path = args[0].toStr(); auto func = args[1].asFunction(); ADD_CALLBACK(Options, path, func); return this->getScriptObject(); } CATCH("Fail in onOptions!"); } Local HttpServerClass::onPreRouting(const Arguments& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kFunction); try { preRoutingCallback = {EngineScope::currentEngine(), script::Global{args[0].asFunction()}}; svr->set_pre_routing_handler([this, engine = EngineScope::currentEngine()](const Request& req, Response& resp) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return Server::HandlerResponse::Unhandled; bool handled = false; auto task = Schedule::nextTick([this, engine, req, &resp, &handled] { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); auto reqObj = new HttpRequestClass(req); auto respObj = new HttpResponseClass(resp); auto res = this->preRoutingCallback.func.get().call({}, reqObj, respObj); if (res.isBoolean() && res.asBoolean().value() == false) { handled = true; } resp = *respObj->get(); }); while (!task.isFinished()) std::this_thread::sleep_for(std::chrono::milliseconds(1)); return handled ? Server::HandlerResponse::Handled : Server::HandlerResponse::Unhandled; }); return this->getScriptObject(); } CATCH("Fail in onPreRouting!"); } Local HttpServerClass::onPostRouting(const Arguments& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kFunction); try { postRoutingCallback = {EngineScope::currentEngine(), script::Global{args[0].asFunction()}}; svr->set_post_routing_handler([this, engine = EngineScope::currentEngine()](const Request& req, Response& resp) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; auto task = Schedule::nextTick([this, engine, req, &resp] { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); auto reqObj = new HttpRequestClass(req); auto respObj = new HttpResponseClass(resp); this->postRoutingCallback.func.get().call({}, reqObj, respObj); resp = *respObj->get(); }); while (!task.isFinished()) std::this_thread::sleep_for(std::chrono::milliseconds(1)); }); return this->getScriptObject(); } CATCH("Fail in onPostRouting!"); } Local HttpServerClass::onError(const Arguments& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kFunction); try { errorCallback = {EngineScope::currentEngine(), script::Global{args[0].asFunction()}}; svr->set_error_handler([this, engine = EngineScope::currentEngine()](const Request& req, Response& resp) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; auto task = Schedule::nextTick([this, engine, req, &resp] { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); auto reqObj = new HttpRequestClass(req); auto respObj = new HttpResponseClass(resp); this->errorCallback.func.get().call({}, reqObj, respObj); resp = *respObj->get(); }); while (!task.isFinished()) std::this_thread::sleep_for(std::chrono::milliseconds(1)); }); return this->getScriptObject(); } CATCH("Fail in onError!"); } Local HttpServerClass::onException(const Arguments& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kFunction); try { exceptionCallback = {EngineScope::currentEngine(), script::Global{args[0].asFunction()}}; svr->set_exception_handler([this, engine = EngineScope::currentEngine()](const Request& req, Response& resp, std::exception_ptr e) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; auto task = Schedule::nextTick([this, engine, req, &resp, e] { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope enter(engine); auto reqObj = new HttpRequestClass(req); auto respObj = new HttpResponseClass(resp); try { if (e) { std::rethrow_exception(e); } } catch(const std::exception& exp) { this->exceptionCallback.func.get().call({}, reqObj, respObj, String::newString(exp.what())); } resp = *respObj->get(); }); while (!task.isFinished()) std::this_thread::sleep_for(std::chrono::milliseconds(1)); }); return this->getScriptObject(); } CATCH("Fail in onException!"); } Local HttpServerClass::listen(const Arguments& args) { if (args.size() == 1) { CHECK_ARG_TYPE(args[0], ValueKind::kNumber); } else if (args.size() == 2) { CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[1], ValueKind::kNumber); } else { LOG_WRONG_ARG_TYPE(); return Local(); } try { string addr = "127.0.0.1"; int port = 80; if (args.size() == 2) { addr = args[0].toStr(); port = args[1].asNumber().toInt32(); } else { port = args[0].asNumber().toInt32(); } if (port < 0 || port > 65535) { throw script::Exception("Invalid port number! (0~65535)"); } RecordOperation(ENGINE_OWN_DATA()->pluginName, "StartHttpServer", fmt::format("on {}:{}", addr, port)); thread th([this](string addr, int port) { svr->listen(addr.c_str(), port); }, addr, port); th.detach(); return this->getScriptObject(); // return self } CATCH("Fail in listen!"); } Local HttpServerClass::stop(const Arguments& args) { CHECK_ARGS_COUNT(args, 0); try { RecordOperation(ENGINE_OWN_DATA()->pluginName, "StopHttpServer", ""); svr->stop(); return Local(); } CATCH("Fail in stop!"); } Local HttpServerClass::isRunning(const Arguments& args) { CHECK_ARGS_COUNT(args, 0); try { return Boolean::newBoolean(svr->is_running()); } CATCH("Fail in isRunning!"); } Local Headers2Object(const Headers& headers) { auto obj = Object::newObject(); for (auto& header : headers) { obj.set(header.first, Array::newArray()); } for (auto& header : headers) { auto arr = obj.get(header.first).asArray(); arr.add(String::newString(header.second)); obj.set(header.first, arr); } return obj; } Local Params2Object(const Params& params) { auto obj = Object::newObject(); for (auto& param : params) { if (params.count(param.first) == 1) obj.set(param.first, param.second); else { if (!obj.has(param.first)) { obj.set(param.first, Array::newArray()); } auto arr = obj.get(param.first).asArray(); arr.add(String::newString(param.second)); obj.set(param.first, arr); } } return obj; } HttpRequestClass::HttpRequestClass(const Local& scriptObj, const Request& req) : ScriptClass(scriptObj) , req(new Request(req)) { } HttpRequestClass::HttpRequestClass(const Request& req) : ScriptClass(ScriptClass::ConstructFromCpp{}) , req(new Request(req)) { } std::shared_ptr HttpRequestClass::get() { return req; } Local HttpRequestClass::getHeaders() { try { return Headers2Object(req->headers); } CATCH("Fail in getHeaders!"); } Local HttpRequestClass::getHeader(const Arguments& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kString); try { auto key = args[0].toStr(); if (req->headers.count(key) == 0) return Array::newArray(); auto value = req->headers.equal_range(key); auto arr = Array::newArray(); for (auto it = value.first; it != value.second; ++it) { arr.add(String::newString(it->second)); } return arr; } CATCH("Fail in getHeader!"); } Local HttpRequestClass::getBody() { try { return String::newString(req->body); } CATCH("Fail in getBody!"); } Local HttpRequestClass::getMethod() { try { return String::newString(req->method); } CATCH("Fail in getMethod!"); } Local HttpRequestClass::getPath() { try { return String::newString(req->path); } CATCH("Fail in getPath!"); } Local HttpRequestClass::getParams() { try { return Params2Object(req->params); } CATCH("Fail in getParams!"); } Local HttpRequestClass::getRemoteAddr() { try { return String::newString(req->remote_addr); } CATCH("Fail in getRemoteAddr!"); } Local HttpRequestClass::getRemotePort() { try { return Number::newNumber(req->remote_port); } CATCH("Fail in getRemotePort!"); } Local HttpRequestClass::getVersion() { try { return String::newString(req->version); } CATCH("Fail in getVersion!"); } Local HttpRequestClass::getRegexMatches() { try { auto smt = req->matches; auto arr = Array::newArray(); for (auto& match : smt) { arr.add(String::newString(match)); } return arr; } CATCH("Fail in getRegexMatches!"); } HttpResponseClass::HttpResponseClass(const Local& scriptObj, const Response& resp) : ScriptClass(scriptObj) , resp(new Response(resp)) { } HttpResponseClass::HttpResponseClass(const Response& resp) : ScriptClass(ScriptClass::ConstructFromCpp{}) , resp(new Response(resp)) { } std::shared_ptr HttpResponseClass::get() { return resp; } Local HttpResponseClass::setHeader(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); try { auto key = args[0].toStr(); string value; if (args[1].isString()) value = args[1].toStr(); else value = ValueToString(args[1]); resp->headers.insert(make_pair(key, value)); return this->getScriptObject(); // return self } CATCH("Fail in setHeader!"); } Local HttpResponseClass::getHeader(const Arguments& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kString); try { auto key = args[0].toStr(); if (resp->headers.count(key) == 0) return Array::newArray(); auto value = resp->headers.equal_range(key); auto arr = Array::newArray(); for (auto it = value.first; it != value.second; ++it) { arr.add(String::newString(it->second)); } return arr; } CATCH("Fail in getHeader!"); } Local HttpResponseClass::write(const Arguments& args) { try { for (size_t i = 0ULL, mEnd = args.size(); i < mEnd; ++i) { resp->body += ValueToString(args[i]); } return this->getScriptObject(); } CATCH("Fail in write!"); } void HttpResponseClass::setHeaders(const Local& headers) { CHECK_ARG_TYPE_S(headers, ValueKind::kObject); try { auto keys = headers.asObject().getKeys(); for (auto& key : keys) { auto key_str = key.toString(); auto value = headers.asObject().get(key); if (value.isArray()) { for (size_t i = 0ULL; i < value.asArray().size(); ++i) { resp->headers.insert(make_pair(key_str, ValueToString(value.asArray().get(i)))); } return; } auto val_str = ValueToString(value); resp->headers.insert(make_pair(key_str, val_str)); } } CATCH_S("Fail in setHeaders!"); } void HttpResponseClass::setStatus(const Local& status) { CHECK_ARG_TYPE_S(status, ValueKind::kNumber); try { resp->status = status.toInt(); } CATCH_S("Fail in setStatus!"); } void HttpResponseClass::setBody(const Local& body) { CHECK_ARG_TYPE_S(body, ValueKind::kString); try { resp->body = body.toStr(); } CATCH_S("Fail in setBody!"); } void HttpResponseClass::setReason(const Local& reason) { CHECK_ARG_TYPE_S(reason, ValueKind::kString); try { resp->reason = reason.toStr(); } CATCH_S("Fail in setReason!"); } void HttpResponseClass::setVersion(const Local& version) { CHECK_ARG_TYPE_S(version, ValueKind::kString); try { resp->version = version.toStr(); } CATCH_S("Fail in setVersion!"); } Local HttpResponseClass::getHeaders() { try { return Headers2Object(resp->headers); } CATCH("Fail in getHeaders!"); } Local HttpResponseClass::getStatus() { try { return Number::newNumber(resp->status); } CATCH("Fail in getStatus!"); } Local HttpResponseClass::getBody() { try { return String::newString(resp->body); } CATCH("Fail in getBody!"); } Local HttpResponseClass::getReason() { try { return String::newString(resp->reason); } CATCH("Fail in getReason!"); } Local HttpResponseClass::getVersion() { try { return String::newString(resp->version); } CATCH("Fail in getVersion!"); } //////////////////// APIs //////////////////// Local NetworkClass::httpGet(const Arguments& args) { CHECK_ARGS_COUNT(args, 2); CHECK_ARG_TYPE(args[0], ValueKind::kString); if (args.size() > 2) { CHECK_ARG_TYPE(args[1], ValueKind::kObject); CHECK_ARG_TYPE(args[2], ValueKind::kFunction); } else { CHECK_ARG_TYPE(args[1], ValueKind::kFunction); } try { string target = args[0].toStr(); RecordOperation(ENGINE_OWN_DATA()->pluginName, "HttpGet", target); script::Global callbackFunc{args[args.size() - 1].asFunction()}; auto lambda = [callback{std::move(callbackFunc)}, engine{EngineScope::currentEngine()}](int status, string body) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope scope(engine); try { NewTimeout(callback.get(), {Number::newNumber(status), String::newString(body)}, 1); // callback.get().call({}, {Number::newNumber(status), String::newString(body)}); } CATCH_IN_CALLBACK("HttpGet") }; if (args.size() > 2) { httplib::Headers maps; auto obj = args[1].asObject(); auto keys = obj.getKeyNames(); if (keys.size() > 0) { for (size_t i = 0ULL, mEnd = keys.size(); i < mEnd; ++i) { maps.insert({keys[i], obj.get(keys[i]).toStr()}); } } return Boolean::newBoolean(HttpGet(target, maps, lambda)); } return Boolean::newBoolean(HttpGet(target, lambda)); } CATCH("Fail in HttpGet"); } Local NetworkClass::httpPost(const Arguments& args) { CHECK_ARGS_COUNT(args, 4); CHECK_ARG_TYPE(args[0], ValueKind::kString); CHECK_ARG_TYPE(args[2], ValueKind::kString); if (args.size() > 4) { CHECK_ARG_TYPE(args[1], ValueKind::kObject); CHECK_ARG_TYPE(args[3], ValueKind::kString); CHECK_ARG_TYPE(args[4], ValueKind::kFunction); } else { CHECK_ARG_TYPE(args[1], ValueKind::kString); CHECK_ARG_TYPE(args[3], ValueKind::kFunction); } try { string target = args[0].toStr(); RecordOperation(ENGINE_OWN_DATA()->pluginName, "HttpPost", target); script::Global callbackFunc{args[args.size() - 1].asFunction()}; auto lambda = [callback{std::move(callbackFunc)}, engine{EngineScope::currentEngine()}](int status, string body) { if (ll::isServerStopping() || !EngineManager::isValid(engine) || engine->isDestroying()) return; EngineScope scope(engine); try { NewTimeout(callback.get(), {Number::newNumber(status), String::newString(body)}, 1); // callback.get().call({}, {Number::newNumber(status), String::newString(body)}); } CATCH_IN_CALLBACK("HttpPost") }; if (args.size() > 4) { httplib::Headers maps; auto obj = args[1].asObject(); auto keys = obj.getKeyNames(); if (keys.size() > 0) { for (size_t i = 0ULL, mEnd = keys.size(); i < mEnd; ++i) { maps.insert({keys[i], obj.get(keys[i]).toStr()}); } } return Boolean::newBoolean(HttpPost(target, maps, args[2].toStr(), args[3].toStr(), lambda)); } return Boolean::newBoolean(HttpPost(target, args[1].toStr(), args[2].toStr(), lambda)); } CATCH("Fail in HttpPost"); } Local NetworkClass::httpGetSync(const Arguments& args) { CHECK_ARGS_COUNT(args, 1); CHECK_ARG_TYPE(args[0], ValueKind::kString); try { string target = args[0].toStr(); RecordOperation(ENGINE_OWN_DATA()->pluginName, "HttpGetSync", target); int status; string result; HttpGetSync(target, &status, &result); Local res = Object::newObject(); res.set("status", status); res.set("data", result); return res; } CATCH("Fail in HttpGetSync"); } // For compatibility Local NetworkClass::newWebSocket(const Arguments& args) { auto newp = new WSClientClass(); return newp->getScriptObject(); } Local WSClientClass::newWSClient() { auto newp = new WSClientClass(); return newp->getScriptObject(); }