mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-10 05:21:49 +00:00
Merge pull request #972 from MihailRis/upgrade-http-in-vc
upgrade http requests
This commit is contained in:
commit
85cc55c63b
16 changed files with 461 additions and 338 deletions
4
.github/workflows/appimage.yml
vendored
4
.github/workflows/appimage.yml
vendored
|
|
@ -48,11 +48,11 @@ jobs:
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: ctest --test-dir build
|
run: ctest --test-dir build
|
||||||
- name: Run engine tests
|
- name: Run engine tests
|
||||||
timeout-minutes: 1
|
timeout-minutes: 5
|
||||||
run: |
|
run: |
|
||||||
chmod +x build/VoxelEngine
|
chmod +x build/VoxelEngine
|
||||||
chmod +x AppDir/usr/bin/vctest
|
chmod +x AppDir/usr/bin/vctest
|
||||||
AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build
|
AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --memchecker valgrind
|
||||||
- name: Build AppImage
|
- name: Build AppImage
|
||||||
uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910
|
uses: AppImageCrafters/build-appimage-action@fe2205a4d6056be47051f7b1b3811106e9814910
|
||||||
env:
|
env:
|
||||||
|
|
|
||||||
8
.github/workflows/macos.yml
vendored
8
.github/workflows/macos.yml
vendored
|
|
@ -23,13 +23,13 @@ jobs:
|
||||||
|
|
||||||
- name: Install dependencies from brew
|
- name: Install dependencies from brew
|
||||||
run: |
|
run: |
|
||||||
brew install glfw3 glew libpng openal-soft luajit libvorbis skypjack/entt/entt googletest glm freetype
|
brew install glfw3 glew libpng openal-soft luajit libvorbis skypjack/entt/entt googletest glm freetype
|
||||||
|
|
||||||
- name: Configure
|
- name: Configure
|
||||||
run: |
|
run: |
|
||||||
cmake -DOPENAL_INCLUDE_DIR=$(brew --prefix openal-soft)/include \
|
cmake -DOPENAL_INCLUDE_DIR=$(brew --prefix openal-soft)/include \
|
||||||
-DOPENAL_LIBRARY=$(brew --prefix openal-soft)/lib/libopenal.dylib \
|
-DOPENAL_LIBRARY=$(brew --prefix openal-soft)/lib/libopenal.dylib \
|
||||||
-S . -B build -DCMAKE_BUILD_TYPE=Release -DVOXELENGINE_BUILD_TESTS=ON -DVOXELENGINE_BUILD_APPDIR=1 -DVC_BUILD_NAME="${{ inputs.build_name }}"
|
-S . -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DVOXELENGINE_BUILD_TESTS=ON -DVOXELENGINE_BUILD_APPDIR=1 -DVC_BUILD_NAME="${{ inputs.build_name }}"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -45,11 +45,11 @@ jobs:
|
||||||
run: ctest --output-on-failure --test-dir build
|
run: ctest --output-on-failure --test-dir build
|
||||||
|
|
||||||
- name: Run engine tests
|
- name: Run engine tests
|
||||||
timeout-minutes: 1
|
timeout-minutes: 5
|
||||||
run: |
|
run: |
|
||||||
chmod +x build/VoxelEngine
|
chmod +x build/VoxelEngine
|
||||||
chmod +x AppDir/usr/bin/vctest
|
chmod +x AppDir/usr/bin/vctest
|
||||||
AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always
|
AppDir/usr/bin/vctest -e build/VoxelEngine -d dev/tests -u build --output-always --debugger "lldb -batch -o 'run' -o 'thread backtrace all' -o 'quit' -- "
|
||||||
- name: Create DMG
|
- name: Create DMG
|
||||||
run: |
|
run: |
|
||||||
mkdir VoxelEngineDmgContent
|
mkdir VoxelEngineDmgContent
|
||||||
|
|
|
||||||
2
.github/workflows/windows.yml
vendored
2
.github/workflows/windows.yml
vendored
|
|
@ -38,7 +38,7 @@ jobs:
|
||||||
- name: Run engine tests
|
- name: Run engine tests
|
||||||
run: |
|
run: |
|
||||||
build/vctest/RelWithDebInfo/vctest.exe -e build/RelWithDebInfo/VoxelEngine.exe -d dev/tests -u build
|
build/vctest/RelWithDebInfo/vctest.exe -e build/RelWithDebInfo/VoxelEngine.exe -d dev/tests -u build
|
||||||
timeout-minutes: 1
|
timeout-minutes: 5
|
||||||
- name: Package for Windows
|
- name: Package for Windows
|
||||||
run: |
|
run: |
|
||||||
mkdir packaged
|
mkdir packaged
|
||||||
|
|
|
||||||
|
|
@ -8,4 +8,4 @@ end, function (code)
|
||||||
response_received = true
|
response_received = true
|
||||||
end)
|
end)
|
||||||
|
|
||||||
app.sleep_until(function () return response_received end, nil, 10)
|
app.sleep_until(function () return response_received end, nil, 5)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,39 @@ A library for working with the network.
|
||||||
|
|
||||||
## HTTP requests
|
## HTTP requests
|
||||||
|
|
||||||
|
There is a configurable function `network.request` that allows performing HTTP requests with various methods (GET, POST, PUT, DELETE, etc.) and configuring headers, request body, timeout, and other parameters.
|
||||||
|
|
||||||
|
> To pass binary data in the request body, use a byte array (Bytearray) or a string. In `on_response`, the `body` string can be converted to a byte array using `Bytearray(response.body)`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
network.request(
|
||||||
|
url: string,
|
||||||
|
parameters: {
|
||||||
|
-- Request method (GET, POST, PUT, DELETE, etc.)
|
||||||
|
method: string,
|
||||||
|
-- Request body as a string or Bytearray
|
||||||
|
body: table|string,
|
||||||
|
-- List of additional request headers
|
||||||
|
headers: table<string>,
|
||||||
|
-- Timeout in milliseconds
|
||||||
|
timeout: int,
|
||||||
|
-- Whether to verify the SSL certificate
|
||||||
|
verify_ssl: boolean,
|
||||||
|
-- Function called when a response is received
|
||||||
|
on_response: function(response: {
|
||||||
|
-- HTTP response status code
|
||||||
|
status: int,
|
||||||
|
-- Response body as a string
|
||||||
|
body: string,
|
||||||
|
-- List of response headers
|
||||||
|
headers: table<string>
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Simplified functions for GET and POST requests
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- Performs a GET request to the specified URL.
|
-- Performs a GET request to the specified URL.
|
||||||
network.get(
|
network.get(
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,39 @@
|
||||||
|
|
||||||
## HTTP-Запросы
|
## HTTP-Запросы
|
||||||
|
|
||||||
|
Существует настраиваемая функция `network.request`, которая позволяет выполнять HTTP-запросы с различными методами (GET, POST, PUT, DELETE и т.д.) и настраивать заголовки, тело запроса, таймаут и другие параметры.
|
||||||
|
|
||||||
|
> Для передачи двоичных данных в теле запроса, используйте массив байт (Bytearray) или строку. В `on_response` строку `body` можно преобразовать в массив байт с помощью `Bytearray(response.body)`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
network.request(
|
||||||
|
url: string,
|
||||||
|
parameters: {
|
||||||
|
-- Метод запроса (GET, POST, PUT, DELETE и т.д.)
|
||||||
|
method: string,
|
||||||
|
-- Тело запроса в виде строки или массива байт (Bytearray)
|
||||||
|
body: table|string,
|
||||||
|
-- Список дополнительных заголовков запроса
|
||||||
|
headers: table<string>,
|
||||||
|
-- Таймаут в миллисекундах
|
||||||
|
timeout: int,
|
||||||
|
-- Проверять ли SSL-сертификат
|
||||||
|
verify_ssl: boolean,
|
||||||
|
-- Функция, вызываемая при получении ответа
|
||||||
|
on_response: function(response: {
|
||||||
|
-- HTTP-код ответа
|
||||||
|
status: int,
|
||||||
|
-- Тело ответа в виде строки
|
||||||
|
body: string,
|
||||||
|
-- Список заголовков ответа
|
||||||
|
headers: table<string>
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Упрощённые функции для GET и POST запросов
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
-- Выполняет GET запрос к указанному URL.
|
-- Выполняет GET запрос к указанному URL.
|
||||||
network.get(
|
network.get(
|
||||||
|
|
@ -231,4 +264,4 @@ network.get_total_download() -> int
|
||||||
```lua
|
```lua
|
||||||
-- Ищет свободный для использования порт.
|
-- Ищет свободный для использования порт.
|
||||||
network.find_free_port() -> int | nil
|
network.find_free_port() -> int | nil
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -98,40 +98,80 @@ local _udp_server_callbacks = {}
|
||||||
local _udp_client_datagram_callbacks = {}
|
local _udp_client_datagram_callbacks = {}
|
||||||
local _udp_client_open_callbacks = {}
|
local _udp_client_open_callbacks = {}
|
||||||
local _http_response_callbacks = {}
|
local _http_response_callbacks = {}
|
||||||
local _http_error_callbacks = {}
|
|
||||||
|
local http_request = network.__request
|
||||||
|
local open_tcp = network.__open_tcp
|
||||||
|
local open_udp = network.__open_udp
|
||||||
|
local connect_tcp = network.__connect_tcp
|
||||||
|
local connect_udp = network.__connect_udp
|
||||||
|
network.__request = nil
|
||||||
|
network.__open_tcp = nil
|
||||||
|
network.__open_udp = nil
|
||||||
|
network.__connect_tcp = nil
|
||||||
|
network.__connect_udp = nil
|
||||||
|
|
||||||
|
local function request(url, params)
|
||||||
|
local id = http_request(url, params)
|
||||||
|
if params.on_response then
|
||||||
|
_http_response_callbacks[id] = params.on_response
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
network.request = request
|
||||||
|
|
||||||
network.get = function(url, callback, errorCallback, headers)
|
network.get = function(url, callback, errorCallback, headers)
|
||||||
local id = network.__get(url, headers)
|
return request(url, {
|
||||||
if callback then
|
method = "GET",
|
||||||
_http_response_callbacks[id] = callback
|
headers = headers,
|
||||||
end
|
on_response = function(response)
|
||||||
if errorCallback then
|
if response.status / 100 == 2 then
|
||||||
_http_error_callbacks[id] = errorCallback
|
return callback(response.body)
|
||||||
end
|
else
|
||||||
|
return errorCallback(response.status, response.body)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_error = function(response)
|
||||||
|
errorCallback(response.status, response.body)
|
||||||
|
end,
|
||||||
|
follow_location = true,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
network.get_binary = function(url, callback, errorCallback, headers)
|
network.get_binary = function(url, callback, errorCallback, headers)
|
||||||
local id = network.__get_binary(url, headers)
|
return request(url, {
|
||||||
if callback then
|
method = "GET",
|
||||||
_http_response_callbacks[id] = callback
|
headers = headers,
|
||||||
end
|
on_response = callback and (function (response)
|
||||||
if errorCallback then
|
if response.code / 100 == 2 then
|
||||||
_http_error_callbacks[id] = errorCallback
|
return callback(Bytearray(response.body))
|
||||||
end
|
else
|
||||||
|
return errorCallback(response.status, response.body)
|
||||||
|
end
|
||||||
|
end),
|
||||||
|
follow_location = true,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
network.post = function(url, data, callback, errorCallback, headers)
|
network.post = function(url, body, callback, errorCallback, headers)
|
||||||
local id = network.__post(url, data, headers)
|
return request(url, {
|
||||||
if callback then
|
method = "POST",
|
||||||
_http_response_callbacks[id] = callback
|
headers = table.extend({
|
||||||
end
|
"Content-Type: application/json"
|
||||||
if errorCallback then
|
}, headers),
|
||||||
_http_error_callbacks[id] = errorCallback
|
body = body,
|
||||||
end
|
on_response = function(response)
|
||||||
|
if response.code / 100 == 2 then
|
||||||
|
return callback(Bytearray(response.body))
|
||||||
|
else
|
||||||
|
return errorCallback(response.status, response.body)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
follow_location = true,
|
||||||
|
})
|
||||||
end
|
end
|
||||||
|
|
||||||
network.tcp_open = function (port, handler)
|
network.tcp_open = function (port, handler)
|
||||||
local socket = setmetatable({id=network.__open_tcp(port)}, ServerSocket)
|
local socket = setmetatable({id=open_tcp(port)}, ServerSocket)
|
||||||
|
|
||||||
_tcp_server_callbacks[socket.id] = function(id)
|
_tcp_server_callbacks[socket.id] = function(id)
|
||||||
handler(setmetatable({id=id}, Socket))
|
handler(setmetatable({id=id}, Socket))
|
||||||
|
|
@ -141,7 +181,7 @@ end
|
||||||
|
|
||||||
network.tcp_connect = function(address, port, callback, errorCallback)
|
network.tcp_connect = function(address, port, callback, errorCallback)
|
||||||
local socket = setmetatable({id=0}, Socket)
|
local socket = setmetatable({id=0}, Socket)
|
||||||
socket.id = network.__connect_tcp(address, port)
|
socket.id = connect_tcp(address, port)
|
||||||
_tcp_client_callbacks[socket.id] = function() callback(socket) end
|
_tcp_client_callbacks[socket.id] = function() callback(socket) end
|
||||||
if errorCallback then
|
if errorCallback then
|
||||||
_tcp_client_error_callbacks[socket.id] = function(message) errorCallback(socket, message) end
|
_tcp_client_error_callbacks[socket.id] = function(message) errorCallback(socket, message) end
|
||||||
|
|
@ -154,7 +194,7 @@ network.udp_open = function (port, datagramHandler)
|
||||||
error "udp server cannot be opened without datagram handler"
|
error "udp server cannot be opened without datagram handler"
|
||||||
end
|
end
|
||||||
|
|
||||||
local socket = setmetatable({id=network.__open_udp(port)}, DatagramServerSocket)
|
local socket = setmetatable({id=open_udp(port)}, DatagramServerSocket)
|
||||||
|
|
||||||
_udp_server_callbacks[socket.id] = function(address, port, data)
|
_udp_server_callbacks[socket.id] = function(address, port, data)
|
||||||
datagramHandler(address, port, data, socket)
|
datagramHandler(address, port, data, socket)
|
||||||
|
|
@ -169,7 +209,7 @@ network.udp_connect = function (address, port, datagramHandler, openCallback)
|
||||||
end
|
end
|
||||||
|
|
||||||
local socket = setmetatable({id=0}, WriteableSocket)
|
local socket = setmetatable({id=0}, WriteableSocket)
|
||||||
socket.id = network.__connect_udp(address, port)
|
socket.id = connect_udp(address, port)
|
||||||
|
|
||||||
_udp_client_datagram_callbacks[socket.id] = datagramHandler
|
_udp_client_datagram_callbacks[socket.id] = datagramHandler
|
||||||
if openCallback then
|
if openCallback then
|
||||||
|
|
@ -239,20 +279,10 @@ network.__process_events = function()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
elseif etype == RESPONSE then
|
elseif etype == RESPONSE then
|
||||||
if event[2] / 100 == 2 then
|
local callback = _http_response_callbacks[event[3]]
|
||||||
local callback = _http_response_callbacks[event[3]]
|
_http_response_callbacks[event[3]] = nil
|
||||||
_http_response_callbacks[event[3]] = nil
|
if callback then
|
||||||
_http_error_callbacks[event[3]] = nil
|
callback(event[4])
|
||||||
if callback then
|
|
||||||
callback(event[4])
|
|
||||||
end
|
|
||||||
else
|
|
||||||
local callback = _http_error_callbacks[event[3]]
|
|
||||||
_http_response_callbacks[event[3]] = nil
|
|
||||||
_http_error_callbacks[event[3]] = nil
|
|
||||||
if callback then
|
|
||||||
callback(event[2], event[4])
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
#include "engine/Engine.hpp"
|
#include "engine/Engine.hpp"
|
||||||
#include "network/Network.hpp"
|
#include "network/Network.hpp"
|
||||||
#include "devtools/Project.hpp"
|
#include "devtools/Project.hpp"
|
||||||
|
#include "util/stringutil.hpp"
|
||||||
|
|
||||||
#include <variant>
|
#include <variant>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
@ -27,7 +28,7 @@ struct ResponseEventDto {
|
||||||
int status;
|
int status;
|
||||||
bool binary;
|
bool binary;
|
||||||
int requestId;
|
int requestId;
|
||||||
std::vector<char> bytes;
|
network::HttpResponse response;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum NetworkDatagramSide {
|
enum NetworkDatagramSide {
|
||||||
|
|
@ -85,74 +86,55 @@ static std::vector<std::string> read_headers(lua::State* L, int index) {
|
||||||
|
|
||||||
static int request_id = 1;
|
static int request_id = 1;
|
||||||
|
|
||||||
static int perform_get(lua::State* L, network::Network& network, bool binary) {
|
static int l_request(lua::State* L, network::Network& network) {
|
||||||
std::string url(lua::require_lstring(L, 1));
|
network::HttpRequest request {};
|
||||||
auto headers = read_headers(L, 2);
|
request.url = lua::require_lstring(L, 1);
|
||||||
|
|
||||||
int currentRequestId = request_id++;
|
if (!lua::istable(L, 2)) {
|
||||||
|
throw std::runtime_error("table expected as argument #2");
|
||||||
network.get(
|
}
|
||||||
url,
|
if (lua::getfield(L, "method", 2)) {
|
||||||
[currentRequestId, binary](std::vector<char> bytes) {
|
request.method = lua::require_string(L, -1);
|
||||||
push_event(NetworkEvent(
|
lua::pop(L);
|
||||||
RESPONSE,
|
}
|
||||||
ResponseEventDto {
|
if (lua::getfield(L, "headers", 2)) {
|
||||||
200, binary, currentRequestId, std::move(bytes)}
|
request.headers = read_headers(L, -1);
|
||||||
));
|
lua::pop(L);
|
||||||
},
|
}
|
||||||
[currentRequestId, binary](int code, std::vector<char> bytes) {
|
if (lua::getfield(L, "body", 2)) {
|
||||||
push_event(NetworkEvent(
|
if (lua::type(L, -1) == LUA_TCDATA) {
|
||||||
RESPONSE,
|
request.body = lua::bytearray_as_string(L, -1);
|
||||||
ResponseEventDto {
|
} else {
|
||||||
code, binary, currentRequestId, std::move(bytes)}
|
request.body = lua::require_lstring(L, -1);
|
||||||
));
|
}
|
||||||
},
|
lua::pop(L);
|
||||||
std::move(headers)
|
}
|
||||||
);
|
if (lua::getfield(L, "follow_location", 2)) {
|
||||||
return lua::pushinteger(L, currentRequestId);
|
request.followLocation = lua::toboolean(L, -1);
|
||||||
}
|
lua::pop(L);
|
||||||
|
}
|
||||||
static int l_get(lua::State* L, network::Network& network) {
|
if (lua::getfield(L, "timeout_ms", 2)) {
|
||||||
return perform_get(L, network, false);
|
request.timeoutMs = lua::tointeger(L, -1);
|
||||||
}
|
lua::pop(L);
|
||||||
|
}
|
||||||
static int l_get_binary(lua::State* L, network::Network& network) {
|
if (lua::getfield(L, "verify_ssl", 2)) {
|
||||||
return perform_get(L, network, true);
|
request.verifySSL = lua::toboolean(L, -1);
|
||||||
}
|
lua::pop(L);
|
||||||
|
|
||||||
static int l_post(lua::State* L, network::Network& network) {
|
|
||||||
std::string url(lua::require_lstring(L, 1));
|
|
||||||
auto data = lua::tovalue(L, 2);
|
|
||||||
|
|
||||||
std::string string;
|
|
||||||
if (data.isString()) {
|
|
||||||
string = data.asString();
|
|
||||||
} else {
|
|
||||||
string = json::stringify(data, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto headers = read_headers(L, 3);
|
|
||||||
int currentRequestId = request_id++;
|
int currentRequestId = request_id++;
|
||||||
|
request.onResponse = [currentRequestId](network::HttpResponse response) {
|
||||||
|
push_event(NetworkEvent(
|
||||||
|
RESPONSE,
|
||||||
|
ResponseEventDto {
|
||||||
|
response.status,
|
||||||
|
false,
|
||||||
|
currentRequestId,
|
||||||
|
std::move(response)}
|
||||||
|
));
|
||||||
|
};
|
||||||
|
|
||||||
network.post(
|
network.request(std::move(request));
|
||||||
url,
|
|
||||||
string,
|
|
||||||
[currentRequestId](std::vector<char> bytes) {
|
|
||||||
push_event(NetworkEvent(
|
|
||||||
RESPONSE,
|
|
||||||
ResponseEventDto {
|
|
||||||
200, false, currentRequestId, std::move(bytes)}
|
|
||||||
));
|
|
||||||
},
|
|
||||||
[currentRequestId](int code, std::vector<char> bytes) {
|
|
||||||
push_event(NetworkEvent(
|
|
||||||
RESPONSE,
|
|
||||||
ResponseEventDto {
|
|
||||||
code, false, currentRequestId, std::move(bytes)}
|
|
||||||
));
|
|
||||||
},
|
|
||||||
std::move(headers)
|
|
||||||
);
|
|
||||||
return lua::pushinteger(L, currentRequestId);
|
return lua::pushinteger(L, currentRequestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -519,11 +501,31 @@ static int l_pull_events(lua::State* L) {
|
||||||
lua::pushinteger(L, dto.requestId);
|
lua::pushinteger(L, dto.requestId);
|
||||||
lua::rawseti(L, 3);
|
lua::rawseti(L, 3);
|
||||||
|
|
||||||
|
lua::createtable(L, 0, 2);
|
||||||
|
lua::pushinteger(L, dto.response.status);
|
||||||
|
lua::setfield(L, "status");
|
||||||
|
|
||||||
if (dto.binary) {
|
if (dto.binary) {
|
||||||
lua::create_bytearray(L, dto.bytes.data(), dto.bytes.size());
|
lua::create_bytearray(
|
||||||
|
L, dto.response.body.data(), dto.response.body.size()
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
lua::pushlstring(L, std::string_view(dto.bytes.data(), dto.bytes.size()));
|
lua::pushlstring(
|
||||||
|
L,
|
||||||
|
std::string_view(
|
||||||
|
dto.response.body.data(), dto.response.body.size()
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
lua::setfield(L, "body");
|
||||||
|
|
||||||
|
lua::createtable(L, dto.response.headers.size(), 0);
|
||||||
|
for (int i = 0; i < dto.response.headers.size(); i++) {
|
||||||
|
lua::pushlstring(L, dto.response.headers[i]);
|
||||||
|
lua::rawseti(L, i + 1);
|
||||||
|
}
|
||||||
|
lua::setfield(L, "headers");
|
||||||
|
|
||||||
lua::rawseti(L, 4);
|
lua::rawseti(L, 4);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -562,9 +564,7 @@ int wrap(lua_State* L) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const luaL_Reg networklib[] = {
|
const luaL_Reg networklib[] = {
|
||||||
{"__get", wrap<l_get>},
|
{"__request", wrap<l_request>},
|
||||||
{"__get_binary", wrap<l_get_binary>},
|
|
||||||
{"__post", wrap<l_post>},
|
|
||||||
{"get_total_upload", wrap<l_get_total_upload>},
|
{"get_total_upload", wrap<l_get_total_upload>},
|
||||||
{"get_total_download", wrap<l_get_total_download>},
|
{"get_total_download", wrap<l_get_total_download>},
|
||||||
{"find_free_port", wrap<l_find_free_port>},
|
{"find_free_port", wrap<l_find_free_port>},
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
#define NOMINMAX
|
#define NOMINMAX
|
||||||
#include <curl/curl.h>
|
#include <curl/curl.h>
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
using namespace network;
|
using namespace network;
|
||||||
|
|
||||||
|
|
@ -23,97 +22,96 @@ static size_t write_callback(
|
||||||
return size * nmemb;
|
return size * nmemb;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class RequestType {
|
static size_t header_callback(
|
||||||
GET, POST
|
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);
|
||||||
|
|
||||||
struct Request {
|
while (!header.empty() &&
|
||||||
RequestType type;
|
(header.back() == '\r' || header.back() == '\n')) {
|
||||||
std::string url;
|
header.pop_back();
|
||||||
OnResponse onResponse;
|
}
|
||||||
OnReject onReject;
|
|
||||||
long maxSize;
|
headers->push_back(std::move(header));
|
||||||
bool followLocation = false;
|
return len;
|
||||||
std::string data;
|
}
|
||||||
|
|
||||||
|
struct ProcessingRequest {
|
||||||
|
CURLM* multiHandle;
|
||||||
|
CURL* curl;
|
||||||
|
HttpRequest request;
|
||||||
|
std::vector<char> buffer;
|
||||||
std::vector<std::string> headers;
|
std::vector<std::string> headers;
|
||||||
|
|
||||||
|
ProcessingRequest(CURLM* multiHandle) : multiHandle(multiHandle) {
|
||||||
|
curl = curl_easy_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessingRequest(const ProcessingRequest&) = delete;
|
||||||
|
ProcessingRequest& operator=(const ProcessingRequest&) = delete;
|
||||||
|
|
||||||
|
ProcessingRequest(ProcessingRequest&& other) noexcept
|
||||||
|
: multiHandle(other.multiHandle),
|
||||||
|
curl(other.curl),
|
||||||
|
request(std::move(other.request)),
|
||||||
|
buffer(std::move(other.buffer)),
|
||||||
|
headers(std::move(other.headers)) {
|
||||||
|
other.curl = nullptr;
|
||||||
|
other.multiHandle = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProcessingRequest& operator=(ProcessingRequest&& other) noexcept {
|
||||||
|
if (this != &other) {
|
||||||
|
if (curl) {
|
||||||
|
curl_multi_remove_handle(multiHandle, curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
}
|
||||||
|
multiHandle = other.multiHandle;
|
||||||
|
curl = other.curl;
|
||||||
|
request = std::move(other.request);
|
||||||
|
buffer = std::move(other.buffer);
|
||||||
|
headers = std::move(other.headers);
|
||||||
|
other.curl = nullptr;
|
||||||
|
other.multiHandle = nullptr;
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ProcessingRequest() {
|
||||||
|
if (curl) {
|
||||||
|
curl_multi_remove_handle(multiHandle, curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CurlRequests : public Requests {
|
class CurlRequests : public Requests {
|
||||||
CURLM* multiHandle;
|
CURLM* multiHandle;
|
||||||
CURL* curl;
|
std::vector<std::unique_ptr<ProcessingRequest>> requests;
|
||||||
|
|
||||||
size_t totalUpload = 0;
|
size_t totalUpload = 0;
|
||||||
size_t totalDownload = 0;
|
size_t totalDownload = 0;
|
||||||
|
|
||||||
OnResponse onResponse;
|
|
||||||
OnReject onReject;
|
|
||||||
std::vector<char> buffer;
|
|
||||||
std::string url;
|
|
||||||
|
|
||||||
std::queue<Request> requests;
|
|
||||||
public:
|
public:
|
||||||
CurlRequests(CURLM* multiHandle, CURL* curl)
|
CurlRequests(CURLM* multiHandle) : multiHandle(multiHandle) {
|
||||||
: multiHandle(multiHandle), curl(curl) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~CurlRequests() {
|
virtual ~CurlRequests() {
|
||||||
curl_multi_remove_handle(multiHandle, curl);
|
requests.clear();
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
curl_multi_cleanup(multiHandle);
|
curl_multi_cleanup(multiHandle);
|
||||||
}
|
}
|
||||||
void get(
|
|
||||||
const std::string& url,
|
void request(HttpRequest request) override {
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject,
|
|
||||||
std::vector<std::string> headers,
|
|
||||||
long maxSize
|
|
||||||
) override {
|
|
||||||
Request request {
|
|
||||||
RequestType::GET,
|
|
||||||
url,
|
|
||||||
onResponse,
|
|
||||||
onReject,
|
|
||||||
maxSize,
|
|
||||||
true,
|
|
||||||
"",
|
|
||||||
std::move(headers)};
|
|
||||||
processRequest(std::move(request));
|
processRequest(std::move(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
void post(
|
void processRequest(HttpRequest request) {
|
||||||
const std::string& url,
|
auto entry = std::make_unique<ProcessingRequest>(multiHandle);
|
||||||
const std::string& data,
|
auto curl = entry->curl;
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject=nullptr,
|
|
||||||
std::vector<std::string> headers = {},
|
|
||||||
long maxSize=0
|
|
||||||
) override {
|
|
||||||
Request request {
|
|
||||||
RequestType::POST,
|
|
||||||
url,
|
|
||||||
onResponse,
|
|
||||||
onReject,
|
|
||||||
maxSize,
|
|
||||||
false,
|
|
||||||
"",
|
|
||||||
std::move(headers)};
|
|
||||||
request.data = data;
|
|
||||||
processRequest(std::move(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
void processRequest(Request request) {
|
curl_easy_setopt(curl, CURLOPT_URL, request.url.c_str());
|
||||||
if (!url.empty()) {
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, request.method.c_str());
|
||||||
requests.push(request);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onResponse = request.onResponse;
|
|
||||||
onReject = request.onReject;
|
|
||||||
url = request.url;
|
|
||||||
|
|
||||||
buffer.clear();
|
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
|
||||||
curl_easy_setopt(curl, CURLOPT_POST, request.type == RequestType::POST);
|
|
||||||
|
|
||||||
curl_slist* hs = nullptr;
|
curl_slist* hs = nullptr;
|
||||||
|
|
||||||
|
|
@ -121,22 +119,25 @@ public:
|
||||||
hs = curl_slist_append(hs, header.c_str());
|
hs = curl_slist_append(hs, header.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (request.type) {
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, static_cast<long>(request.body.length()));
|
||||||
case RequestType::GET:
|
if (!request.body.empty()) {
|
||||||
break;
|
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, request.body.data());
|
||||||
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_SSL_VERIFYPEER, static_cast<long>(request.verifySSL));
|
||||||
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, static_cast<long>(request.verifySSL));
|
||||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs);
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs);
|
||||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request.followLocation);
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, static_cast<long>(request.followLocation));
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &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) {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT_MS, request.timeoutMs);
|
||||||
|
}
|
||||||
|
#ifndef NDEBUG
|
||||||
|
// curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
||||||
|
#endif
|
||||||
if (request.maxSize == 0) {
|
if (request.maxSize == 0) {
|
||||||
curl_easy_setopt(
|
curl_easy_setopt(
|
||||||
curl, CURLOPT_MAXFILESIZE, std::numeric_limits<long>::max()
|
curl, CURLOPT_MAXFILESIZE, std::numeric_limits<long>::max()
|
||||||
|
|
@ -144,69 +145,105 @@ public:
|
||||||
} else {
|
} else {
|
||||||
curl_easy_setopt(curl, CURLOPT_MAXFILESIZE, request.maxSize);
|
curl_easy_setopt(curl, CURLOPT_MAXFILESIZE, request.maxSize);
|
||||||
}
|
}
|
||||||
curl_multi_add_handle(multiHandle, curl);
|
{
|
||||||
int running;
|
CURLMcode res = curl_multi_add_handle(multiHandle, curl);
|
||||||
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 << " (" << request.url << ")";
|
||||||
logger.error() << message << " (" << url << ")";
|
if (request.onResponse) {
|
||||||
if (onReject) {
|
request.onResponse({0, {}, {}});
|
||||||
onReject(HTTP_BAD_GATEWAY, {});
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
url = "";
|
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
int running;
|
||||||
|
CURLMcode res = curl_multi_perform(multiHandle, &running);
|
||||||
|
if (res != CURLM_OK) {
|
||||||
|
auto message = curl_multi_strerror(res);
|
||||||
|
logger.error() << message << " (" << request.url << ")";
|
||||||
|
if (request.onResponse) {
|
||||||
|
request.onResponse({0, {}, {}});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
entry->request = std::move(request);
|
||||||
|
requests.push_back(std::move(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
void update() override {
|
void update() override {
|
||||||
int messagesLeft;
|
if (CURLMcode res = curl_multi_poll(
|
||||||
int running;
|
multiHandle,
|
||||||
CURLMsg* msg;
|
nullptr,
|
||||||
CURLMcode res = curl_multi_perform(multiHandle, &running);
|
0,
|
||||||
if (res != CURLM_OK) {
|
0,
|
||||||
|
nullptr
|
||||||
|
)) {
|
||||||
auto message = curl_multi_strerror(res);
|
auto message = curl_multi_strerror(res);
|
||||||
logger.error() << message << " (" << url << ")";
|
logger.error() << message;
|
||||||
if (onReject) {
|
|
||||||
onReject(HTTP_BAD_GATEWAY, {});
|
|
||||||
}
|
|
||||||
curl_multi_remove_handle(multiHandle, curl);
|
|
||||||
url = "";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((msg = curl_multi_info_read(multiHandle, &messagesLeft)) != nullptr) {
|
|
||||||
if(msg->msg == CURLMSG_DONE) {
|
int messagesLeft;
|
||||||
curl_multi_remove_handle(multiHandle, curl);
|
int running;
|
||||||
}
|
if (CURLMcode res = curl_multi_perform(multiHandle, &running)) {
|
||||||
int response;
|
auto message = curl_multi_strerror(res);
|
||||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response);
|
logger.error() << message;
|
||||||
if (response == HTTP_OK) {
|
return;
|
||||||
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 += buffer.size();
|
|
||||||
if (onResponse) {
|
|
||||||
onResponse(std::move(buffer));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.error()
|
|
||||||
<< "response code " << response << " (" << url << ")"
|
|
||||||
<< (buffer.empty()
|
|
||||||
? ""
|
|
||||||
: std::to_string(buffer.size()) + " byte(s)");
|
|
||||||
totalDownload += buffer.size();
|
|
||||||
if (onReject) {
|
|
||||||
onReject(response, std::move(buffer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
url = "";
|
|
||||||
}
|
}
|
||||||
if (url.empty() && !requests.empty()) {
|
CURLMsg* msg = curl_multi_info_read(multiHandle, &messagesLeft);
|
||||||
auto request = std::move(requests.front());
|
if (msg == nullptr || msg->msg != CURLMSG_DONE) {
|
||||||
requests.pop();
|
return;
|
||||||
processRequest(std::move(request));
|
}
|
||||||
|
auto curl = msg->easy_handle;
|
||||||
|
auto found = std::find_if(
|
||||||
|
requests.begin(),
|
||||||
|
requests.end(),
|
||||||
|
[curl](const std::unique_ptr<ProcessingRequest>& entry) {
|
||||||
|
return entry && entry->curl == curl;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (requests.empty() || 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);
|
||||||
|
|
||||||
|
long 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 == 0) {
|
||||||
|
auto message = std::string(curl_easy_strerror(result));
|
||||||
|
logger.error() << message << " (" << req.url << ")";
|
||||||
|
if (req.onResponse) {
|
||||||
|
req.onResponse(
|
||||||
|
{static_cast<int>(response),
|
||||||
|
std::move(headers),
|
||||||
|
std::vector<char>(
|
||||||
|
message.data(), message.data() + message.size()
|
||||||
|
)}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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({
|
||||||
|
static_cast<int>(response),
|
||||||
|
std::move(headers),
|
||||||
|
std::move(entry->buffer),
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,16 +256,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<CurlRequests> create() {
|
static std::unique_ptr<CurlRequests> create() {
|
||||||
auto curl = curl_easy_init();
|
|
||||||
if (curl == nullptr) {
|
|
||||||
throw std::runtime_error("could not initialzie cURL");
|
|
||||||
}
|
|
||||||
auto multiHandle = curl_multi_init();
|
auto multiHandle = curl_multi_init();
|
||||||
if (multiHandle == nullptr) {
|
if (multiHandle == nullptr) {
|
||||||
curl_easy_cleanup(curl);
|
throw std::runtime_error("could not initialize cURL-multi");
|
||||||
throw std::runtime_error("could not initialzie cURL-multi");
|
|
||||||
}
|
}
|
||||||
return std::make_unique<CurlRequests>(multiHandle, curl);
|
return std::make_unique<CurlRequests>(multiHandle);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,27 +51,8 @@ Network::Network(std::unique_ptr<Requests> requests)
|
||||||
|
|
||||||
Network::~Network() = default;
|
Network::~Network() = default;
|
||||||
|
|
||||||
void Network::get(
|
void Network::request(HttpRequest request) {
|
||||||
const std::string& url,
|
requests->request(std::move(request));
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject,
|
|
||||||
std::vector<std::string> headers,
|
|
||||||
long maxSize
|
|
||||||
) {
|
|
||||||
requests->get(url, onResponse, onReject, std::move(headers), maxSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Network::post(
|
|
||||||
const std::string& url,
|
|
||||||
const std::string& fieldsData,
|
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject,
|
|
||||||
std::vector<std::string> headers,
|
|
||||||
long maxSize
|
|
||||||
) {
|
|
||||||
requests->post(
|
|
||||||
url, fieldsData, onResponse, onReject, std::move(headers), maxSize
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection* Network::getConnection(u64id_t id, bool includePrivate) {
|
Connection* Network::getConnection(u64id_t id, bool includePrivate) {
|
||||||
|
|
|
||||||
|
|
@ -68,22 +68,7 @@ namespace network {
|
||||||
Network(std::unique_ptr<Requests> requests);
|
Network(std::unique_ptr<Requests> requests);
|
||||||
~Network();
|
~Network();
|
||||||
|
|
||||||
void get(
|
void request(HttpRequest request);
|
||||||
const std::string& url,
|
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject = nullptr,
|
|
||||||
std::vector<std::string> headers = {},
|
|
||||||
long maxSize=0
|
|
||||||
);
|
|
||||||
|
|
||||||
void post(
|
|
||||||
const std::string& url,
|
|
||||||
const std::string& fieldsData,
|
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject = nullptr,
|
|
||||||
std::vector<std::string> headers = {},
|
|
||||||
long maxSize=0
|
|
||||||
);
|
|
||||||
|
|
||||||
[[nodiscard]] Connection* getConnection(u64id_t id, bool includePrivate);
|
[[nodiscard]] Connection* getConnection(u64id_t id, bool includePrivate);
|
||||||
[[nodiscard]] Server* getServer(u64id_t id, bool includePrivate) const;
|
[[nodiscard]] Server* getServer(u64id_t id, bool includePrivate) const;
|
||||||
|
|
|
||||||
|
|
@ -10,33 +10,38 @@
|
||||||
#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 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)>;
|
||||||
using ClientDatagramCallback = std::function<void(u64id_t cid, const char* buffer, size_t length)>;
|
using ClientDatagramCallback = std::function<void(u64id_t cid, const char* buffer, size_t length)>;
|
||||||
|
|
||||||
|
struct HttpRequest {
|
||||||
|
std::string method;
|
||||||
|
std::string url;
|
||||||
|
std::string body;
|
||||||
|
std::vector<std::string> headers;
|
||||||
|
|
||||||
|
OnResponse onResponse;
|
||||||
|
bool followLocation = false;
|
||||||
|
bool verifySSL = true;
|
||||||
|
long maxSize = -1;
|
||||||
|
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() {}
|
||||||
|
|
||||||
virtual void get(
|
virtual void request(HttpRequest request) = 0;
|
||||||
const std::string& url,
|
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject=nullptr,
|
|
||||||
std::vector<std::string> headers = {},
|
|
||||||
long maxSize=0
|
|
||||||
) = 0;
|
|
||||||
|
|
||||||
virtual void post(
|
|
||||||
const std::string& url,
|
|
||||||
const std::string& data,
|
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject=nullptr,
|
|
||||||
std::vector<std::string> headers = {},
|
|
||||||
long maxSize=0
|
|
||||||
) = 0;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual size_t getTotalUpload() const = 0;
|
[[nodiscard]] virtual size_t getTotalUpload() const = 0;
|
||||||
[[nodiscard]] virtual size_t getTotalDownload() const = 0;
|
[[nodiscard]] virtual size_t getTotalDownload() const = 0;
|
||||||
|
|
|
||||||
|
|
@ -543,6 +543,14 @@ static std::basic_string<CharT> upper_case(const std::basic_string<CharT>& str)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string util::lower_case(const std::string& str) {
|
||||||
|
return ::lower_case(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string util::upper_case(const std::string& str) {
|
||||||
|
return ::upper_case(str);
|
||||||
|
}
|
||||||
|
|
||||||
std::wstring util::lower_case(const std::wstring& str) {
|
std::wstring util::lower_case(const std::wstring& str) {
|
||||||
return ::lower_case(str);
|
return ::lower_case(str);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,8 @@ namespace util {
|
||||||
|
|
||||||
std::string capitalized(const std::string& str);
|
std::string capitalized(const std::string& str);
|
||||||
|
|
||||||
|
std::string lower_case(const std::string& str);
|
||||||
|
std::string upper_case(const std::string& str);
|
||||||
std::wstring lower_case(const std::wstring& str);
|
std::wstring lower_case(const std::wstring& str);
|
||||||
std::wstring upper_case(const std::wstring& str);
|
std::wstring upper_case(const std::wstring& str);
|
||||||
std::wstring capitalized(const std::wstring& str);
|
std::wstring capitalized(const std::wstring& str);
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,21 @@
|
||||||
TEST(curltest, curltest) {
|
TEST(curltest, curltest) {
|
||||||
NetworkSettings settings {};
|
NetworkSettings settings {};
|
||||||
auto network = network::Network::create(settings);
|
auto network = network::Network::create(settings);
|
||||||
network->get(
|
|
||||||
"https://raw.githubusercontent.com/MihailRis/VoxelEngine-Cpp/refs/"
|
network::HttpRequest request {};
|
||||||
"heads/curl/res/content/base/blocks/lamp.json",
|
request.url = "https://raw.githubusercontent.com/MihailRis/VoxelEngine-Cpp/refs/"
|
||||||
[](std::vector<char> data) {
|
"heads/curl/res/content/base/blocks/lamp.json";
|
||||||
if (data.empty()) {
|
request.onResponse = [](network::HttpResponse response) {
|
||||||
return;
|
if (response.body.empty()) {
|
||||||
}
|
return;
|
||||||
auto view = std::string_view(data.data(), data.size());
|
}
|
||||||
auto value = json::parse(view);
|
auto view =
|
||||||
std::cout << value << std::endl;
|
std::string_view(response.body.data(), response.body.size());
|
||||||
}, [](auto, auto){}
|
auto value = json::parse(view);
|
||||||
);
|
std::cout << value << std::endl;
|
||||||
|
};
|
||||||
|
network->request(std::move(request));
|
||||||
|
|
||||||
std::cout << "upload: " << network->getTotalUpload() << " B" << std::endl;
|
std::cout << "upload: " << network->getTotalUpload() << " B" << std::endl;
|
||||||
std::cout << "download: " << network->getTotalDownload() << " B" << std::endl;
|
std::cout << "download: " << network->getTotalDownload() << " B" << std::endl;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,8 @@ struct Config {
|
||||||
fs::path directory;
|
fs::path directory;
|
||||||
fs::path resDir {"res"};
|
fs::path resDir {"res"};
|
||||||
fs::path workingDir {"."};
|
fs::path workingDir {"."};
|
||||||
std::string memchecker = "valgrind";
|
std::string memchecker = "";
|
||||||
|
std::string debugger = "";
|
||||||
bool outputAlways = false;
|
bool outputAlways = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -30,6 +31,7 @@ static bool perform_keyword(
|
||||||
std::cout << " --tests <path>, -d <path> = tests directory path\n";
|
std::cout << " --tests <path>, -d <path> = tests directory path\n";
|
||||||
std::cout << " --res <path>, -r <path> = 'res' directory path\n";
|
std::cout << " --res <path>, -r <path> = 'res' directory path\n";
|
||||||
std::cout << " --user <path>, -u <path> = user directory path\n";
|
std::cout << " --user <path>, -u <path> = user directory path\n";
|
||||||
|
std::cout << " --debugger <command> = debugger with run command\n";
|
||||||
std::cout << " --memchecker <path> = path to valgrind\n";
|
std::cout << " --memchecker <path> = path to valgrind\n";
|
||||||
std::cout << " --output-always = always show tests output\n";
|
std::cout << " --output-always = always show tests output\n";
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
|
|
@ -44,6 +46,8 @@ static bool perform_keyword(
|
||||||
config.workingDir = fs::path(reader.next());
|
config.workingDir = fs::path(reader.next());
|
||||||
} else if (keyword == "--output-always") {
|
} else if (keyword == "--output-always") {
|
||||||
config.outputAlways = true;
|
config.outputAlways = true;
|
||||||
|
} else if (keyword == "--debugger") {
|
||||||
|
config.debugger = reader.next();
|
||||||
} else if (keyword == "--memchecker") {
|
} else if (keyword == "--memchecker") {
|
||||||
config.memchecker = reader.next();
|
config.memchecker = reader.next();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -172,7 +176,7 @@ static std::string fix_path(std::string s) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool run_test(const Config& config, const fs::path& path, bool memcheck = false) {
|
static bool run_test(const Config& config, const fs::path& path, bool debugRun = false) {
|
||||||
using std::chrono::duration_cast;
|
using std::chrono::duration_cast;
|
||||||
using std::chrono::high_resolution_clock;
|
using std::chrono::high_resolution_clock;
|
||||||
using std::chrono::milliseconds;
|
using std::chrono::milliseconds;
|
||||||
|
|
@ -182,9 +186,16 @@ static bool run_test(const Config& config, const fs::path& path, bool memcheck =
|
||||||
|
|
||||||
auto name = path.stem();
|
auto name = path.stem();
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
if (memcheck) {
|
if (debugRun) {
|
||||||
ss << config.memchecker << " --log-file="
|
if (!config.memchecker.empty()) {
|
||||||
<< fix_path(memcheckLogFile.string()) << " ";
|
ss << config.memchecker;
|
||||||
|
ss << " --log-file=" << fix_path(memcheckLogFile.string());
|
||||||
|
ss << " ";
|
||||||
|
}
|
||||||
|
if (!config.debugger.empty()) {
|
||||||
|
ss << config.debugger;
|
||||||
|
ss << " ";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ss << fs::canonical(config.executable) << " --headless";
|
ss << fs::canonical(config.executable) << " --headless";
|
||||||
ss << " --test " << fix_path(path.string());
|
ss << " --test " << fix_path(path.string());
|
||||||
|
|
@ -203,7 +214,7 @@ static bool run_test(const Config& config, const fs::path& path, bool memcheck =
|
||||||
.count();
|
.count();
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
if (memcheck) {
|
if (debugRun) {
|
||||||
// valgrind-specific output
|
// valgrind-specific output
|
||||||
display_segfault_valgrind(memcheckLogFile, name, std::cerr);
|
display_segfault_valgrind(memcheckLogFile, name, std::cerr);
|
||||||
fs::remove(memcheckLogFile);
|
fs::remove(memcheckLogFile);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue