From 96b904e61f4f82222b4c587fcb2624158484f7d5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 8 Jan 2025 17:02:07 +0300 Subject: [PATCH 1/2] add network.post --- src/logic/scripting/lua/libs/libnetwork.cpp | 21 +++++++ src/network/Network.cpp | 63 +++++++++++++++++---- src/network/Network.hpp | 17 ++++++ 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index b3417a13..d1edf185 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -2,6 +2,7 @@ #include "engine/Engine.hpp" #include "network/Network.hpp" +#include "coders/json.hpp" using namespace scripting; @@ -36,6 +37,25 @@ static int l_get_binary(lua::State* L) { return 0; } +static int l_post(lua::State* L) { + std::string url(lua::require_lstring(L, 1)); + auto data = lua::tovalue(L, 2); + + lua::pushvalue(L, 3); + auto onResponse = lua::create_lambda_nothrow(L); + + auto string = json::stringify(data, false); + engine->getNetwork().post(url, string, [onResponse](std::vector bytes) { + auto buffer = std::make_shared>( + reinterpret_cast(bytes.data()), bytes.size() + ); + engine->postRunnable([=]() { + onResponse({std::string(bytes.data(), bytes.size())}); + }); + }); + return 0; +} + static int l_connect(lua::State* L) { std::string address = lua::require_string(L, 1); int port = lua::tointeger(L, 2); @@ -200,6 +220,7 @@ static int l_get_total_download(lua::State* L) { const luaL_Reg networklib[] = { {"get", lua::wrap}, {"get_binary", lua::wrap}, + {"post", lua::wrap}, {"get_total_upload", lua::wrap}, {"get_total_download", lua::wrap}, {"__open", lua::wrap}, diff --git a/src/network/Network.cpp b/src/network/Network.cpp index 57fcab40..7a99b0c5 100644 --- a/src/network/Network.cpp +++ b/src/network/Network.cpp @@ -42,12 +42,18 @@ static size_t write_callback( return size * nmemb; } +enum class RequestType { + GET, POST +}; + struct Request { + RequestType type; std::string url; OnResponse onResponse; OnReject onReject; long maxSize; bool followLocation = false; + std::string data; }; class CurlRequests : public Requests { @@ -73,22 +79,33 @@ public: curl_easy_cleanup(curl); curl_multi_cleanup(multiHandle); } - void get( const std::string& url, OnResponse onResponse, OnReject onReject, long maxSize ) override { - Request request {url, onResponse, onReject, maxSize}; - if (url.empty()) { - processRequest(request); - } else { - requests.push(request); - } + Request request {RequestType::GET, url, onResponse, onReject, maxSize}; + processRequest(std::move(request)); } - void processRequest(const Request& request) { + void post( + const std::string& url, + const std::string& data, + OnResponse onResponse, + OnReject onReject=nullptr, + long maxSize=0 + ) override { + Request request {RequestType::POST, url, onResponse, onReject, maxSize}; + request.data = data; + processRequest(std::move(request)); + } + + void processRequest(Request request) { + if (!url.empty()) { + requests.push(request); + return; + } onResponse = request.onResponse; onReject = request.onReject; url = request.url; @@ -96,9 +113,25 @@ public: buffer.clear(); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(curl, CURLOPT_POST, request.type == RequestType::POST); + + curl_slist* hs = NULL; + + switch (request.type) { + case RequestType::GET: + break; + case RequestType::POST: + hs = curl_slist_append(hs, "Content-Type: application/json"); + curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request.data.length()); + curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, request.data.c_str()); + break; + default: + throw std::runtime_error("not implemented"); + } + curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request.followLocation); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request.followLocation); curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.81.0"); if (request.maxSize == 0) { curl_easy_setopt( @@ -164,7 +197,7 @@ public: if (url.empty() && !requests.empty()) { auto request = std::move(requests.front()); requests.pop(); - processRequest(request); + processRequest(std::move(request)); } } @@ -562,6 +595,16 @@ void Network::get( requests->get(url, onResponse, onReject, maxSize); } +void Network::post( + const std::string& url, + const std::string& fieldsData, + OnResponse onResponse, + OnReject onReject, + long maxSize +) { + requests->post(url, fieldsData, onResponse, onReject, maxSize); +} + Connection* Network::getConnection(u64id_t id) { std::lock_guard lock(connectionsMutex); diff --git a/src/network/Network.hpp b/src/network/Network.hpp index c6170f39..0cc0803c 100644 --- a/src/network/Network.hpp +++ b/src/network/Network.hpp @@ -23,6 +23,15 @@ namespace network { OnReject onReject=nullptr, long maxSize=0 ) = 0; + + virtual void post( + const std::string& url, + const std::string& data, + OnResponse onResponse, + OnReject onReject=nullptr, + long maxSize=0 + ) = 0; + virtual size_t getTotalUpload() const = 0; virtual size_t getTotalDownload() const = 0; @@ -84,6 +93,14 @@ namespace network { long maxSize=0 ); + void post( + const std::string& url, + const std::string& fieldsData, + OnResponse onResponse, + OnReject onReject = nullptr, + long maxSize=0 + ); + [[nodiscard]] Connection* getConnection(u64id_t id); [[nodiscard]] TcpServer* getServer(u64id_t id) const; From 93b2040a0d3a5496e313654d5091de875cda55c5 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Wed, 8 Jan 2025 18:17:23 +0300 Subject: [PATCH 2/2] update doc/*/scripting/builtins/libnetwork.md --- doc/en/scripting/builtins/libnetwork.md | 5 +++++ doc/ru/scripting/builtins/libnetwork.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/doc/en/scripting/builtins/libnetwork.md b/doc/en/scripting/builtins/libnetwork.md index a3546336..cb24e7bf 100644 --- a/doc/en/scripting/builtins/libnetwork.md +++ b/doc/en/scripting/builtins/libnetwork.md @@ -16,6 +16,11 @@ end) -- A variant for binary files, with a byte array instead of a string in the response. network.get_binary(url: str, callback: function(table|ByteArray)) + +-- Performs a POST request to the specified URL. +-- Currently, only `Content-Type: application/json` is supported +-- After receiving the response, passes the text to the callback function. +network.post(url: str, data: table, callback: function(str)) ``` ## TCP Connections diff --git a/doc/ru/scripting/builtins/libnetwork.md b/doc/ru/scripting/builtins/libnetwork.md index 0770ea67..657ad7cf 100644 --- a/doc/ru/scripting/builtins/libnetwork.md +++ b/doc/ru/scripting/builtins/libnetwork.md @@ -16,6 +16,11 @@ end) -- Вариант для двоичных файлов, с массивом байт вместо строки в ответе. network.get_binary(url: str, callback: function(table|ByteArray)) + +-- Выполняет POST запрос к указанному URL. +-- На данный момент реализована поддержка только `Content-Type: application/json` +-- После получения ответа, передаёт текст в функцию callback. +network.post(url: str, data: table, callback: function(str)) ``` ## TCP-Соединения