From 58c974a3a1fe6acfbc44ae824aa258517f839656 Mon Sep 17 00:00:00 2001 From: MihailRis Date: Sun, 13 Sep 2026 16:16:59 +0300 Subject: [PATCH] add HttpResponse --- src/logic/scripting/lua/libs/libnetwork.cpp | 16 ++- src/network/Curl.cpp | 136 ++++++++++++-------- src/network/commons.hpp | 12 +- test/network/curltest.cpp | 7 +- 4 files changed, 108 insertions(+), 63 deletions(-) diff --git a/src/logic/scripting/lua/libs/libnetwork.cpp b/src/logic/scripting/lua/libs/libnetwork.cpp index 577eee7a9..37ef3029d 100644 --- a/src/logic/scripting/lua/libs/libnetwork.cpp +++ b/src/logic/scripting/lua/libs/libnetwork.cpp @@ -115,16 +115,24 @@ static int l_request(lua::State* L, network::Network& network) { } int currentRequestId = request_id++; - request.onResponse = [currentRequestId](std::vector bytes) { + request.onResponse = [currentRequestId](network::HttpResponse response) { push_event(NetworkEvent( RESPONSE, - ResponseEventDto {200, false, currentRequestId, std::move(bytes)} + ResponseEventDto { + response.status, + false, + currentRequestId, + std::move(response.body)} )); }; - request.onReject = [currentRequestId](int code, std::vector bytes) { + request.onReject = [currentRequestId](network::HttpResponse response) { push_event(NetworkEvent( RESPONSE, - ResponseEventDto {code, false, currentRequestId, std::move(bytes)} + ResponseEventDto { + response.status, + false, + currentRequestId, + std::move(response.body)} )); }; diff --git a/src/network/Curl.cpp b/src/network/Curl.cpp index fd1b0ad14..afa4be009 100644 --- a/src/network/Curl.cpp +++ b/src/network/Curl.cpp @@ -23,11 +23,28 @@ static size_t write_callback( return size * nmemb; } +static size_t header_callback( + char* buffer, size_t size, size_t nitems, void* userdata +) { + auto* headers = static_cast*>(userdata); + size_t len = size * nitems; + std::string header(buffer, len); + + while (!header.empty() && + (header.back() == '\r' || header.back() == '\n')) { + header.pop_back(); + } + + headers->push_back(std::move(header)); + return len; +} + struct ProcessingRequest { CURLM* multiHandle; CURL* curl; HttpRequest request; std::vector buffer; + std::vector headers; ProcessingRequest(CURLM* multiHandle) : multiHandle(multiHandle) { curl = curl_easy_init(); @@ -81,6 +98,8 @@ public: curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request.followLocation); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &entry->buffer); + curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, header_callback); + curl_easy_setopt(curl, CURLOPT_HEADERDATA, &entry->headers); curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.81.0"); if (request.timeoutMs > 0) { curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, request.timeoutMs); @@ -102,7 +121,7 @@ public: auto message = curl_multi_strerror(res); logger.error() << message << " (" << request.url << ")"; if (request.onReject) { - request.onReject(HTTP_BAD_GATEWAY, {}); + request.onReject({HTTP_BAD_GATEWAY, {}, {}}); } } entry->request = std::move(request); @@ -112,69 +131,78 @@ public: void update() override { int messagesLeft; int running; - CURLMsg* msg; CURLMcode res = curl_multi_perform(multiHandle, &running); if (res != CURLM_OK) { auto message = curl_multi_strerror(res); logger.error() << message; return; } - if ((msg = curl_multi_info_read(multiHandle, &messagesLeft)) != nullptr) { - auto curl = msg->easy_handle; - auto found = std::find_if( - requests.begin(), requests.end(), - [curl](const std::unique_ptr& entry) { - return entry->curl == curl; - } - ); - if (found == requests.end()) { - logger.error() << "could not find request for cURL handle"; - return; + CURLMsg* msg = curl_multi_info_read(multiHandle, &messagesLeft); + if (msg == nullptr) { + return; + } + auto curl = msg->easy_handle; + auto found = std::find_if( + requests.begin(), + requests.end(), + [curl](const std::unique_ptr& entry) { + return entry->curl == curl; } - auto entry = std::move(*found); - auto& req = entry->request; + ); + if (found == requests.end()) { + logger.error() << "could not find request for cURL handle"; + return; + } + auto entry = std::move(*found); + auto& req = entry->request; - requests.erase(found); + requests.erase(found); - if(msg->msg == CURLMSG_DONE) { - curl_multi_remove_handle(multiHandle, curl); + if (msg->msg == CURLMSG_DONE) { + curl_multi_remove_handle(multiHandle, curl); + } + int response = -1; + CURLcode result = msg->data.result; + curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response); + auto headers = std::move(entry->headers); + if (response == HTTP_OK) { + long size; + if (!curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &size)) { + totalUpload += size; } - int response = -1; - CURLcode result = msg->data.result; - curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response); - if (response == HTTP_OK) { - long size; - if (!curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &size)) { - totalUpload += size; - } - if (!curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size)) { - totalDownload += size; - } - totalDownload += entry->buffer.size(); - if (req.onResponse) { - req.onResponse(std::move(entry->buffer)); - } - } else if (response == 0) { - auto message = std::string(curl_easy_strerror(result)); - logger.error() << message << " (" << req.url << ")"; - if (req.onReject) { - req.onReject( - response, - std::vector( - message.data(), message.data() + message.size() - ) - ); - } - } else { - logger.error() - << "response code " << response << " (" << req.url << ")" - << (entry->buffer.empty() - ? "" - : std::to_string(entry->buffer.size()) + " byte(s)"); - totalDownload += entry->buffer.size(); - if (req.onReject) { - req.onReject(response, std::move(entry->buffer)); - } + if (!curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size)) { + totalDownload += size; + } + totalDownload += entry->buffer.size(); + if (req.onResponse) { + req.onResponse({ + response, + std::move(headers), + std::move(entry->buffer), + }); + } + } else if (response == 0) { + auto message = std::string(curl_easy_strerror(result)); + logger.error() << message << " (" << req.url << ")"; + if (req.onReject) { + req.onReject( + {response, + std::move(headers), + std::vector( + message.data(), message.data() + message.size() + )} + ); + } + } else { + logger.error() << "response code " << response << " (" << req.url + << ")" + << (entry->buffer.empty() + ? "" + : std::to_string(entry->buffer.size()) + + " byte(s)"); + totalDownload += entry->buffer.size(); + if (req.onReject) { + req.onReject({response, {}, std::move(entry->buffer)}); } } } diff --git a/src/network/commons.hpp b/src/network/commons.hpp index 79e4bdb9d..15fe69850 100644 --- a/src/network/commons.hpp +++ b/src/network/commons.hpp @@ -10,8 +10,10 @@ #include namespace network { - using OnResponse = std::function)>; - using OnReject = std::function)>; + struct HttpResponse; + + using OnResponse = std::function; + using OnReject = std::function; using ConnectCallback = std::function; using ConnectErrorCallback = std::function; using ServerDatagramCallback = std::function; @@ -31,6 +33,12 @@ namespace network { long timeoutMs = 0; }; + struct HttpResponse { + int status; + std::vector headers; + std::vector body; + }; + class Requests { public: virtual ~Requests() {} diff --git a/test/network/curltest.cpp b/test/network/curltest.cpp index 44f87d17e..e7fc4840d 100644 --- a/test/network/curltest.cpp +++ b/test/network/curltest.cpp @@ -10,11 +10,12 @@ TEST(curltest, curltest) { network::HttpRequest request {}; request.url = "https://raw.githubusercontent.com/MihailRis/VoxelEngine-Cpp/refs/" "heads/curl/res/content/base/blocks/lamp.json"; - request.onResponse = [](std::vector data) { - if (data.empty()) { + request.onResponse = [](network::HttpResponse response) { + if (response.body.empty()) { return; } - auto view = std::string_view(data.data(), data.size()); + auto view = + std::string_view(response.body.data(), response.body.size()); auto value = json::parse(view); std::cout << value << std::endl; };