add HttpResponse

This commit is contained in:
MihailRis 2026-09-13 16:16:59 +03:00
parent fc877f09cb
commit 58c974a3a1
4 changed files with 108 additions and 63 deletions

View file

@ -115,16 +115,24 @@ static int l_request(lua::State* L, network::Network& network) {
} }
int currentRequestId = request_id++; int currentRequestId = request_id++;
request.onResponse = [currentRequestId](std::vector<char> bytes) { request.onResponse = [currentRequestId](network::HttpResponse response) {
push_event(NetworkEvent( push_event(NetworkEvent(
RESPONSE, RESPONSE,
ResponseEventDto {200, false, currentRequestId, std::move(bytes)} ResponseEventDto {
response.status,
false,
currentRequestId,
std::move(response.body)}
)); ));
}; };
request.onReject = [currentRequestId](int code, std::vector<char> bytes) { request.onReject = [currentRequestId](network::HttpResponse response) {
push_event(NetworkEvent( push_event(NetworkEvent(
RESPONSE, RESPONSE,
ResponseEventDto {code, false, currentRequestId, std::move(bytes)} ResponseEventDto {
response.status,
false,
currentRequestId,
std::move(response.body)}
)); ));
}; };

View file

@ -23,11 +23,28 @@ static size_t write_callback(
return size * nmemb; return size * nmemb;
} }
static size_t header_callback(
char* buffer, size_t size, size_t nitems, void* userdata
) {
auto* headers = static_cast<std::vector<std::string>*>(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 { struct ProcessingRequest {
CURLM* multiHandle; CURLM* multiHandle;
CURL* curl; CURL* curl;
HttpRequest request; HttpRequest request;
std::vector<char> buffer; std::vector<char> buffer;
std::vector<std::string> headers;
ProcessingRequest(CURLM* multiHandle) : multiHandle(multiHandle) { ProcessingRequest(CURLM* multiHandle) : multiHandle(multiHandle) {
curl = curl_easy_init(); curl = curl_easy_init();
@ -81,6 +98,8 @@ public:
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request.followLocation); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request.followLocation);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &entry->buffer); 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"); curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.81.0");
if (request.timeoutMs > 0) { if (request.timeoutMs > 0) {
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, request.timeoutMs); curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, request.timeoutMs);
@ -102,7 +121,7 @@ public:
auto message = curl_multi_strerror(res); auto message = curl_multi_strerror(res);
logger.error() << message << " (" << request.url << ")"; logger.error() << message << " (" << request.url << ")";
if (request.onReject) { if (request.onReject) {
request.onReject(HTTP_BAD_GATEWAY, {}); request.onReject({HTTP_BAD_GATEWAY, {}, {}});
} }
} }
entry->request = std::move(request); entry->request = std::move(request);
@ -112,17 +131,20 @@ public:
void update() override { void update() override {
int messagesLeft; int messagesLeft;
int running; int running;
CURLMsg* msg;
CURLMcode res = curl_multi_perform(multiHandle, &running); CURLMcode res = curl_multi_perform(multiHandle, &running);
if (res != CURLM_OK) { if (res != CURLM_OK) {
auto message = curl_multi_strerror(res); auto message = curl_multi_strerror(res);
logger.error() << message; logger.error() << message;
return; return;
} }
if ((msg = curl_multi_info_read(multiHandle, &messagesLeft)) != nullptr) { CURLMsg* msg = curl_multi_info_read(multiHandle, &messagesLeft);
if (msg == nullptr) {
return;
}
auto curl = msg->easy_handle; auto curl = msg->easy_handle;
auto found = std::find_if( auto found = std::find_if(
requests.begin(), requests.end(), requests.begin(),
requests.end(),
[curl](const std::unique_ptr<ProcessingRequest>& entry) { [curl](const std::unique_ptr<ProcessingRequest>& entry) {
return entry->curl == curl; return entry->curl == curl;
} }
@ -136,12 +158,13 @@ public:
requests.erase(found); requests.erase(found);
if(msg->msg == CURLMSG_DONE) { if (msg->msg == CURLMSG_DONE) {
curl_multi_remove_handle(multiHandle, curl); curl_multi_remove_handle(multiHandle, curl);
} }
int response = -1; int response = -1;
CURLcode result = msg->data.result; CURLcode result = msg->data.result;
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response); curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response);
auto headers = std::move(entry->headers);
if (response == HTTP_OK) { if (response == HTTP_OK) {
long size; long size;
if (!curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &size)) { if (!curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &size)) {
@ -152,29 +175,34 @@ public:
} }
totalDownload += entry->buffer.size(); totalDownload += entry->buffer.size();
if (req.onResponse) { if (req.onResponse) {
req.onResponse(std::move(entry->buffer)); req.onResponse({
response,
std::move(headers),
std::move(entry->buffer),
});
} }
} else if (response == 0) { } else if (response == 0) {
auto message = std::string(curl_easy_strerror(result)); auto message = std::string(curl_easy_strerror(result));
logger.error() << message << " (" << req.url << ")"; logger.error() << message << " (" << req.url << ")";
if (req.onReject) { if (req.onReject) {
req.onReject( req.onReject(
response, {response,
std::move(headers),
std::vector<char>( std::vector<char>(
message.data(), message.data() + message.size() message.data(), message.data() + message.size()
) )}
); );
} }
} else { } else {
logger.error() logger.error() << "response code " << response << " (" << req.url
<< "response code " << response << " (" << req.url << ")" << ")"
<< (entry->buffer.empty() << (entry->buffer.empty()
? "" ? ""
: std::to_string(entry->buffer.size()) + " byte(s)"); : std::to_string(entry->buffer.size()) +
" byte(s)");
totalDownload += entry->buffer.size(); totalDownload += entry->buffer.size();
if (req.onReject) { if (req.onReject) {
req.onReject(response, std::move(entry->buffer)); req.onReject({response, {}, std::move(entry->buffer)});
}
} }
} }
} }

View file

@ -10,8 +10,10 @@
#include <mutex> #include <mutex>
namespace network { namespace network {
using OnResponse = std::function<void(std::vector<char>)>; struct HttpResponse;
using OnReject = std::function<void(int, std::vector<char>)>;
using OnResponse = std::function<void(HttpResponse)>;
using OnReject = std::function<void(HttpResponse)>;
using ConnectCallback = std::function<void(u64id_t, u64id_t)>; using ConnectCallback = std::function<void(u64id_t, u64id_t)>;
using ConnectErrorCallback = std::function<void(u64id_t, std::string)>; using ConnectErrorCallback = std::function<void(u64id_t, std::string)>;
using ServerDatagramCallback = std::function<void(u64id_t sid, const std::string& addr, int port, const char* buffer, size_t length)>; using ServerDatagramCallback = std::function<void(u64id_t sid, const std::string& addr, int port, const char* buffer, size_t length)>;
@ -31,6 +33,12 @@ namespace network {
long timeoutMs = 0; long timeoutMs = 0;
}; };
struct HttpResponse {
int status;
std::vector<std::string> headers;
std::vector<char> body;
};
class Requests { class Requests {
public: public:
virtual ~Requests() {} virtual ~Requests() {}

View file

@ -10,11 +10,12 @@ TEST(curltest, curltest) {
network::HttpRequest request {}; network::HttpRequest request {};
request.url = "https://raw.githubusercontent.com/MihailRis/VoxelEngine-Cpp/refs/" request.url = "https://raw.githubusercontent.com/MihailRis/VoxelEngine-Cpp/refs/"
"heads/curl/res/content/base/blocks/lamp.json"; "heads/curl/res/content/base/blocks/lamp.json";
request.onResponse = [](std::vector<char> data) { request.onResponse = [](network::HttpResponse response) {
if (data.empty()) { if (response.body.empty()) {
return; 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); auto value = json::parse(view);
std::cout << value << std::endl; std::cout << value << std::endl;
}; };