Merge branch 'The-Powder-Toy:master' into dev

This commit is contained in:
Abrams11 2023-05-31 15:45:00 +02:00 committed by GitHub
commit 66986538c2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 378 additions and 365 deletions

View File

@ -10,10 +10,6 @@ namespace http
AuthHeaders(ByteString::Build(user.UserID), user.SessionID);
}
APIRequest::~APIRequest()
{
}
APIRequest::Result APIRequest::Finish()
{
Result result;

View File

@ -17,7 +17,6 @@ namespace http
};
APIRequest(ByteString url);
virtual ~APIRequest();
Result Finish();
};

View File

@ -9,10 +9,6 @@ namespace http
{
}
GetUserInfoRequest::~GetUserInfoRequest()
{
}
std::unique_ptr<UserInfo> GetUserInfoRequest::Finish()
{
std::unique_ptr<UserInfo> user_info;

View File

@ -9,7 +9,6 @@ namespace http
{
public:
GetUserInfoRequest(ByteString username);
virtual ~GetUserInfoRequest();
std::unique_ptr<UserInfo> Finish();
};

View File

@ -9,9 +9,6 @@ namespace http
size(size)
{}
ImageRequest::~ImageRequest()
{}
std::unique_ptr<VideoBuffer> ImageRequest::Finish()
{
auto [ status, data ] = Request::Finish();

View File

@ -15,7 +15,6 @@ namespace http
public:
ImageRequest(ByteString url, Vec2<int> size);
virtual ~ImageRequest();
std::unique_ptr<VideoBuffer> Finish();
};

View File

@ -0,0 +1,11 @@
#pragma once
#include "common/String.h"
#include <map>
#include <variant>
namespace http
{
using StringData = ByteString;
using FormData = std::map<ByteString, ByteString>;
using PostData = std::variant<StringData, FormData>;
};

View File

@ -1,6 +1,7 @@
#include "Request.h"
#include "requestmanager/RequestManager.h"
#include <memory>
#include <iostream>
namespace http
{
@ -30,12 +31,12 @@ namespace http
handle->headers.push_back(header);
}
void Request::AddPostData(std::map<ByteString, ByteString> data)
void Request::AddPostData(PostData data)
{
assert(handle->state == RequestHandle::ready);
// Even if the map is empty, calling this function signifies you want to do a POST request
handle->isPost = true;
handle->postData.insert(data.begin(), data.end());
handle->postData = data;
}
void Request::AuthHeaders(ByteString ID, ByteString session)
@ -97,17 +98,31 @@ namespace http
return { handle->statusCode, std::move(handle->responseData) };
}
std::pair<int, ByteString> Request::Simple(ByteString uri, std::map<ByteString, ByteString> post_data)
void RequestHandle::MarkDone()
{
return SimpleAuth(uri, "", "", post_data);
{
std::lock_guard lk(stateMx);
assert(state == RequestHandle::running);
state = RequestHandle::done;
}
stateCv.notify_one();
if (error.size())
{
std::cerr << error << std::endl;
}
}
std::pair<int, ByteString> Request::SimpleAuth(ByteString uri, ByteString ID, ByteString session, std::map<ByteString, ByteString> post_data)
std::pair<int, ByteString> Request::Simple(ByteString uri, FormData postData)
{
return SimpleAuth(uri, "", "", postData);
}
std::pair<int, ByteString> Request::SimpleAuth(ByteString uri, ByteString ID, ByteString session, FormData postData)
{
auto request = std::make_unique<Request>(uri);
if (!post_data.empty())
if (!postData.empty())
{
request->AddPostData(post_data);
request->AddPostData(postData);
}
request->AuthHeaders(ID, session);
request->Start();

View File

@ -1,5 +1,6 @@
#pragma once
#include "common/String.h"
#include "PostData.h"
#include <map>
#include <utility>
#include <vector>
@ -22,7 +23,8 @@ namespace http
void Verb(ByteString newVerb);
void AddHeader(ByteString header);
void AddPostData(std::map<ByteString, ByteString> data);
void AddPostData(PostData data);
void AuthHeaders(ByteString ID, ByteString session);
void Start();
@ -32,8 +34,8 @@ namespace http
const std::vector<ByteString> &ResponseHeaders() const;
std::pair<int, ByteString> Finish(); // status, data
static std::pair<int, ByteString> Simple(ByteString uri, std::map<ByteString, ByteString> post_data = {});
static std::pair<int, ByteString> SimpleAuth(ByteString uri, ByteString ID, ByteString session, std::map<ByteString, ByteString> post_data = {});
static std::pair<int, ByteString> Simple(ByteString uri, FormData postData = {});
static std::pair<int, ByteString> SimpleAuth(ByteString uri, ByteString ID, ByteString session, FormData postData = {});
friend class RequestManager;
};

View File

@ -7,16 +7,12 @@ namespace http
SaveUserInfoRequest::SaveUserInfoRequest(UserInfo &info) :
APIRequest(ByteString::Build(SCHEME, SERVER, "/Profile.json"))
{
AddPostData({
AddPostData(FormData{
{ "Location", info.location.ToUtf8() },
{ "Biography", info.biography.ToUtf8() }
});
}
SaveUserInfoRequest::~SaveUserInfoRequest()
{
}
bool SaveUserInfoRequest::Finish()
{
auto result = APIRequest::Finish();

View File

@ -9,7 +9,6 @@ namespace http
{
public:
SaveUserInfoRequest(UserInfo &info);
virtual ~SaveUserInfoRequest();
bool Finish();
};

View File

@ -11,9 +11,5 @@ namespace http
), size)
{
}
ThumbnailRequest::~ThumbnailRequest()
{
}
}

View File

@ -7,6 +7,5 @@ namespace http
{
public:
ThumbnailRequest(int saveID, int saveDate, Vec2<int> size);
virtual ~ThumbnailRequest();
};
}

View File

@ -1,7 +1,6 @@
#include "RequestManager.h"
#include "client/http/Request.h"
#include "Config.h"
#include <iostream>
namespace http
{
@ -19,85 +18,22 @@ namespace http
"; ", IDENT,
") TPTPP/", SAVE_VERSION, ".", MINOR_VERSION, ".", BUILD_NUM, IDENT_RELTYPE, ".", SNAPSHOT_ID
);
worker = std::thread([this]() {
Worker();
});
}
RequestManager::~RequestManager()
{
{
std::lock_guard lk(sharedStateMx);
running = false;
}
worker.join();
}
void RequestManager::Worker()
{
InitWorker();
while (true)
{
{
std::lock_guard lk(sharedStateMx);
for (auto &requestHandle : requestHandles)
{
if (requestHandle->statusCode)
{
requestHandlesToUnregister.push_back(requestHandle);
}
}
for (auto &requestHandle : requestHandlesToRegister)
{
requestHandles.push_back(requestHandle);
RegisterRequestHandle(requestHandle);
}
requestHandlesToRegister.clear();
for (auto &requestHandle : requestHandlesToUnregister)
{
auto eraseFrom = std::remove(requestHandles.begin(), requestHandles.end(), requestHandle);
if (eraseFrom != requestHandles.end())
{
assert(eraseFrom + 1 == requestHandles.end());
UnregisterRequestHandle(requestHandle);
requestHandles.erase(eraseFrom, requestHandles.end());
if (requestHandle->error.size())
{
std::cerr << requestHandle->error << std::endl;
}
{
std::lock_guard lk(requestHandle->stateMx);
requestHandle->state = RequestHandle::done;
}
requestHandle->stateCv.notify_one();
}
}
requestHandlesToUnregister.clear();
if (!running)
{
break;
}
}
Tick();
}
assert(!requestHandles.size());
ExitWorker();
}
bool RequestManager::DisableNetwork() const
{
return disableNetwork;
}
void RequestManager::RegisterRequest(Request &request)
{
std::lock_guard lk(sharedStateMx);
requestHandlesToRegister.push_back(request.handle);
if (disableNetwork)
{
request.handle->statusCode = 604;
request.handle->error = "network disabled upon request";
request.handle->MarkDone();
return;
}
RegisterRequestImpl(request);
}
void RequestManager::UnregisterRequest(Request &request)
{
std::lock_guard lk(sharedStateMx);
requestHandlesToUnregister.push_back(request.handle);
UnregisterRequestImpl(request);
}
}

View File

@ -96,224 +96,67 @@ namespace http
{
using RequestManager::RequestManager;
RequestManagerImpl(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork);
~RequestManagerImpl();
std::thread worker;
void Worker();
void WorkerInit();
void WorkerPerform();
void WorkerExit();
// State shared between Request threads and the worker thread.
std::vector<std::shared_ptr<RequestHandle>> requestHandlesToRegister;
std::vector<std::shared_ptr<RequestHandle>> requestHandlesToUnregister;
bool running = true;
std::mutex sharedStateMx;
std::vector<std::shared_ptr<RequestHandle>> requestHandles;
void RegisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle);
void UnregisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle);
bool curlGlobalInit = false;
CURLM *curlMulti = NULL;
};
void RequestManager::InitWorker()
RequestManagerImpl::RequestManagerImpl(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork) :
RequestManager(newProxy, newCafile, newCapath, newDisableNetwork)
{
worker = std::thread([this]() {
Worker();
});
}
RequestManagerImpl::~RequestManagerImpl()
{
{
std::lock_guard lk(sharedStateMx);
running = false;
}
worker.join();
}
void RequestManagerImpl::WorkerInit()
{
auto manager = static_cast<RequestManagerImpl *>(this);
if (!curl_global_init(CURL_GLOBAL_DEFAULT))
{
manager->curlGlobalInit = true;
manager->curlMulti = curl_multi_init();
if (manager->curlMulti)
curlGlobalInit = true;
curlMulti = curl_multi_init();
if (curlMulti)
{
HandleCURLMcode(curl_multi_setopt(manager->curlMulti, CURLMOPT_MAX_HOST_CONNECTIONS, curlMaxHostConnections));
HandleCURLMcode(curl_multi_setopt(curlMulti, CURLMOPT_MAX_HOST_CONNECTIONS, curlMaxHostConnections));
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 67, 0)
HandleCURLMcode(curl_multi_setopt(manager->curlMulti, CURLMOPT_MAX_CONCURRENT_STREAMS, curlMaxConcurrentStreams));
HandleCURLMcode(curl_multi_setopt(curlMulti, CURLMOPT_MAX_CONCURRENT_STREAMS, curlMaxConcurrentStreams));
#endif
}
}
}
void RequestManager::ExitWorker()
void RequestManagerImpl::WorkerPerform()
{
auto manager = static_cast<RequestManagerImpl *>(this);
curl_multi_cleanup(manager->curlMulti);
manager->curlMulti = NULL;
curl_global_cleanup();
}
void RequestManager::RegisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle)
{
auto manager = static_cast<RequestManagerImpl *>(this);
auto handle = static_cast<RequestHandleHttp *>(requestHandle.get());
auto failEarly = [&requestHandle](int statusCode, ByteString error) {
requestHandle->statusCode = statusCode;
requestHandle->error = error;
};
if (disableNetwork)
{
return failEarly(604, "network disabled upon request");
}
if (!manager->curlGlobalInit)
{
return failEarly(600, "no CURL");
}
if (!manager->curlMulti)
{
return failEarly(600, "no CURL multi handle");
}
try
{
handle->curlEasy = curl_easy_init();
if (!handle->curlEasy)
{
return failEarly(600, "no CURL easy handle");
}
for (auto &header : handle->headers)
{
auto *newHeaders = curl_slist_append(handle->curlHeaders, header.c_str());
if (!newHeaders)
{
// Hopefully this is what a NULL from curl_slist_append means.
HandleCURLcode(CURLE_OUT_OF_MEMORY);
}
handle->curlHeaders = newHeaders;
}
{
auto postData = handle->postData;
if (postData.size())
{
#ifdef REQUEST_USE_CURL_MIMEPOST
handle->curlPostFields = curl_mime_init(handle->curlEasy);
if (!handle->curlPostFields)
{
// Hopefully this is what a NULL from curl_mime_init means.
HandleCURLcode(CURLE_OUT_OF_MEMORY);
}
for (auto &field : postData)
{
curl_mimepart *part = curl_mime_addpart(handle->curlPostFields);
if (!part)
{
// Hopefully this is what a NULL from curl_mime_addpart means.
HandleCURLcode(CURLE_OUT_OF_MEMORY);
}
HandleCURLcode(curl_mime_data(part, &field.second[0], field.second.size()));
if (auto split = field.first.SplitBy(':'))
{
HandleCURLcode(curl_mime_name(part, split.Before().c_str()));
HandleCURLcode(curl_mime_filename(part, split.After().c_str()));
}
else
{
HandleCURLcode(curl_mime_name(part, field.first.c_str()));
}
}
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_MIMEPOST, handle->curlPostFields));
#else
for (auto &field : postData)
{
if (auto split = field.first.SplitBy(':'))
{
HandleCURLFORMcode(curl_formadd(&handle->curlPostFieldsFirst, &handle->curlPostFieldsLast,
CURLFORM_COPYNAME, split.Before().c_str(),
CURLFORM_BUFFER, split.After().c_str(),
CURLFORM_BUFFERPTR, &field.second[0],
CURLFORM_BUFFERLENGTH, field.second.size(),
CURLFORM_END));
}
else
{
HandleCURLFORMcode(curl_formadd(&handle->curlPostFieldsFirst, &handle->curlPostFieldsLast,
CURLFORM_COPYNAME, field.first.c_str(),
CURLFORM_PTRCONTENTS, &field.second[0],
CURLFORM_CONTENTLEN, field.second.size(),
CURLFORM_END));
}
}
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HTTPPOST, handle->curlPostFieldsFirst));
#endif
}
else if (handle->isPost)
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POST, 1L));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POSTFIELDS, ""));
}
else
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HTTPGET, 1L));
}
if (handle->verb.size())
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CUSTOMREQUEST, handle->verb.c_str()));
}
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_FOLLOWLOCATION, 1L));
if constexpr (ENFORCE_HTTPS)
{
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS_STR, "https"));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS_STR, "https"));
#else
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS));
#endif
}
else
{
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS_STR, "https,http"));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS_STR, "https,http"));
#else
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP));
#endif
}
SetupCurlEasyCiphers(handle->curlEasy);
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_MAXREDIRS, 10L));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_ERRORBUFFER, handle->curlErrorBuffer));
handle->curlErrorBuffer[0] = 0;
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CONNECTTIMEOUT, curlConnectTimeoutS));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HTTPHEADER, handle->curlHeaders));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_URL, handle->uri.c_str()));
if (proxy.size())
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROXY, proxy.c_str()));
}
if (cafile.size())
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CAINFO, cafile.c_str()));
}
if (capath.size())
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CAPATH, capath.c_str()));
}
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PRIVATE, (void *)handle));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_USERAGENT, userAgent.c_str()));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HEADERDATA, (void *)handle));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HEADERFUNCTION, &RequestHandleHttp::HeaderDataHandler));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_WRITEDATA, (void *)handle));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_WRITEFUNCTION, &RequestHandleHttp::WriteDataHandler));
}
}
catch (const CurlError &ex)
{
return failEarly(600, ex.what());
}
HandleCURLMcode(curl_multi_add_handle(manager->curlMulti, handle->curlEasy));
handle->curlAddedToMulti = true;
}
void RequestManager::UnregisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle)
{
auto manager = static_cast<RequestManagerImpl *>(this);
auto handle = static_cast<RequestHandleHttp *>(requestHandle.get());
if (handle->curlAddedToMulti)
{
HandleCURLMcode(curl_multi_remove_handle(manager->curlMulti, handle->curlEasy));
handle->curlAddedToMulti = false;
}
curl_easy_cleanup(handle->curlEasy);
#ifdef REQUEST_USE_CURL_MIMEPOST
curl_mime_free(handle->curlPostFields);
#else
curl_formfree(handle->curlPostFieldsFirst);
#endif
curl_slist_free_all(handle->curlHeaders);
}
void RequestManager::Tick()
{
if (!requestHandles.size())
{
std::this_thread::sleep_for(std::chrono::milliseconds(TickMs));
return;
}
auto manager = static_cast<RequestManagerImpl *>(this);
int dontcare;
HandleCURLMcode(curl_multi_wait(manager->curlMulti, NULL, 0, TickMs, &dontcare));
HandleCURLMcode(curl_multi_poll(manager->curlMulti, NULL, 0, 1000, &dontcare));
HandleCURLMcode(curl_multi_perform(manager->curlMulti, &dontcare));
while (auto msg = curl_multi_info_read(manager->curlMulti, &dontcare))
{
@ -389,6 +232,254 @@ namespace http
}
}
void RequestManagerImpl::WorkerExit()
{
curl_multi_cleanup(curlMulti);
curlMulti = NULL;
curl_global_cleanup();
}
void RequestManagerImpl::Worker()
{
WorkerInit();
while (true)
{
{
std::lock_guard lk(sharedStateMx);
for (auto &requestHandle : requestHandles)
{
if (requestHandle->statusCode)
{
requestHandlesToUnregister.push_back(requestHandle);
}
}
for (auto &requestHandle : requestHandlesToRegister)
{
requestHandles.push_back(requestHandle);
RegisterRequestHandle(requestHandle);
}
requestHandlesToRegister.clear();
for (auto &requestHandle : requestHandlesToUnregister)
{
auto eraseFrom = std::remove(requestHandles.begin(), requestHandles.end(), requestHandle);
if (eraseFrom != requestHandles.end())
{
assert(eraseFrom + 1 == requestHandles.end());
UnregisterRequestHandle(requestHandle);
requestHandles.erase(eraseFrom, requestHandles.end());
requestHandle->MarkDone();
}
}
requestHandlesToUnregister.clear();
if (!running)
{
break;
}
}
WorkerPerform();
}
assert(!requestHandles.size());
WorkerExit();
}
void RequestManager::RegisterRequestImpl(Request &request)
{
auto manager = static_cast<RequestManagerImpl *>(this);
std::lock_guard lk(manager->sharedStateMx);
manager->requestHandlesToRegister.push_back(request.handle);
curl_multi_wakeup(manager->curlMulti);
}
void RequestManager::UnregisterRequestImpl(Request &request)
{
auto manager = static_cast<RequestManagerImpl *>(this);
std::lock_guard lk(manager->sharedStateMx);
manager->requestHandlesToUnregister.push_back(request.handle);
curl_multi_wakeup(manager->curlMulti);
}
void RequestManagerImpl::RegisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle)
{
auto manager = static_cast<RequestManagerImpl *>(this);
auto handle = static_cast<RequestHandleHttp *>(requestHandle.get());
auto failEarly = [&requestHandle](int statusCode, ByteString error) {
requestHandle->statusCode = statusCode;
requestHandle->error = error;
};
if (!manager->curlGlobalInit)
{
return failEarly(600, "no CURL");
}
if (!manager->curlMulti)
{
return failEarly(600, "no CURL multi handle");
}
try
{
handle->curlEasy = curl_easy_init();
if (!handle->curlEasy)
{
return failEarly(600, "no CURL easy handle");
}
for (auto &header : handle->headers)
{
auto *newHeaders = curl_slist_append(handle->curlHeaders, header.c_str());
if (!newHeaders)
{
// Hopefully this is what a NULL from curl_slist_append means.
HandleCURLcode(CURLE_OUT_OF_MEMORY);
}
handle->curlHeaders = newHeaders;
}
{
auto &postData = handle->postData;
if (std::holds_alternative<http::FormData>(postData) && std::get<http::FormData>(postData).size())
{
auto &formData = std::get<http::FormData>(postData);
#ifdef REQUEST_USE_CURL_MIMEPOST
handle->curlPostFields = curl_mime_init(handle->curlEasy);
if (!handle->curlPostFields)
{
// Hopefully this is what a NULL from curl_mime_init means.
HandleCURLcode(CURLE_OUT_OF_MEMORY);
}
for (auto &field : formData)
{
curl_mimepart *part = curl_mime_addpart(handle->curlPostFields);
if (!part)
{
// Hopefully this is what a NULL from curl_mime_addpart means.
HandleCURLcode(CURLE_OUT_OF_MEMORY);
}
HandleCURLcode(curl_mime_data(part, &field.second[0], field.second.size()));
if (auto split = field.first.SplitBy(':'))
{
HandleCURLcode(curl_mime_name(part, split.Before().c_str()));
HandleCURLcode(curl_mime_filename(part, split.After().c_str()));
}
else
{
HandleCURLcode(curl_mime_name(part, field.first.c_str()));
}
}
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_MIMEPOST, handle->curlPostFields));
#else
for (auto &field : formData)
{
if (auto split = field.first.SplitBy(':'))
{
HandleCURLFORMcode(curl_formadd(&handle->curlPostFieldsFirst, &handle->curlPostFieldsLast,
CURLFORM_COPYNAME, split.Before().c_str(),
CURLFORM_BUFFER, split.After().c_str(),
CURLFORM_BUFFERPTR, &field.second[0],
CURLFORM_BUFFERLENGTH, field.second.size(),
CURLFORM_END));
}
else
{
HandleCURLFORMcode(curl_formadd(&handle->curlPostFieldsFirst, &handle->curlPostFieldsLast,
CURLFORM_COPYNAME, field.first.c_str(),
CURLFORM_PTRCONTENTS, &field.second[0],
CURLFORM_CONTENTLEN, field.second.size(),
CURLFORM_END));
}
}
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HTTPPOST, handle->curlPostFieldsFirst));
#endif
}
else if (std::holds_alternative<http::StringData>(postData) && std::get<http::StringData>(postData).size())
{
auto &stringData = std::get<http::StringData>(postData);
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POSTFIELDS, &stringData[0]));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POSTFIELDSIZE_LARGE, curl_off_t(stringData.size())));
}
else if (handle->isPost)
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POST, 1L));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POSTFIELDS, ""));
}
else
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HTTPGET, 1L));
}
if (handle->verb.size())
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CUSTOMREQUEST, handle->verb.c_str()));
}
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_FOLLOWLOCATION, 1L));
if constexpr (ENFORCE_HTTPS)
{
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS_STR, "https"));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS_STR, "https"));
#else
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS));
#endif
}
else
{
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS_STR, "https,http"));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS_STR, "https,http"));
#else
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP));
#endif
}
SetupCurlEasyCiphers(handle->curlEasy);
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_MAXREDIRS, 10L));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_ERRORBUFFER, handle->curlErrorBuffer));
handle->curlErrorBuffer[0] = 0;
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CONNECTTIMEOUT, curlConnectTimeoutS));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HTTPHEADER, handle->curlHeaders));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_URL, handle->uri.c_str()));
if (proxy.size())
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROXY, proxy.c_str()));
}
if (cafile.size())
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CAINFO, cafile.c_str()));
}
if (capath.size())
{
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CAPATH, capath.c_str()));
}
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PRIVATE, (void *)handle));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_USERAGENT, userAgent.c_str()));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HEADERDATA, (void *)handle));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HEADERFUNCTION, &RequestHandleHttp::HeaderDataHandler));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_WRITEDATA, (void *)handle));
HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_WRITEFUNCTION, &RequestHandleHttp::WriteDataHandler));
}
}
catch (const CurlError &ex)
{
return failEarly(600, ex.what());
}
HandleCURLMcode(curl_multi_add_handle(manager->curlMulti, handle->curlEasy));
handle->curlAddedToMulti = true;
}
void RequestManagerImpl::UnregisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle)
{
auto manager = static_cast<RequestManagerImpl *>(this);
auto handle = static_cast<RequestHandleHttp *>(requestHandle.get());
if (handle->curlAddedToMulti)
{
HandleCURLMcode(curl_multi_remove_handle(manager->curlMulti, handle->curlEasy));
handle->curlAddedToMulti = false;
}
curl_easy_cleanup(handle->curlEasy);
#ifdef REQUEST_USE_CURL_MIMEPOST
curl_mime_free(handle->curlPostFields);
#else
curl_formfree(handle->curlPostFieldsFirst);
#endif
curl_slist_free_all(handle->curlHeaders);
}
RequestManagerPtr RequestManager::Create(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork)
{
return RequestManagerPtr(new RequestManagerImpl(newProxy, newCafile, newCapath, newDisableNetwork));

View File

@ -8,29 +8,17 @@ namespace http
return std::make_shared<RequestHandle>(CtorTag{});
}
void RequestManager::InitWorker()
void RequestManager::RegisterRequestImpl(Request &request)
{
request.handle->statusCode = 604;
request.handle->error = "network support not compiled in";
request.handle->MarkDone();
}
void RequestManager::ExitWorker()
void RequestManager::UnregisterRequestImpl(Request &request)
{
}
void RequestManager::RegisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle)
{
requestHandle->statusCode = 604;
requestHandle->error = "network support not compiled in";
}
void RequestManager::UnregisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle)
{
}
void RequestManager::Tick()
{
std::this_thread::sleep_for(std::chrono::milliseconds(TickMs));
}
RequestManagerPtr RequestManager::Create(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork)
{
return RequestManagerPtr(new RequestManager(newProxy, newCafile, newCapath, newDisableNetwork));

View File

@ -1,12 +1,12 @@
#pragma once
#include "common/ExplicitSingleton.h"
#include "common/String.h"
#include "client/http/PostData.h"
#include <atomic>
#include <thread>
#include <vector>
#include <memory>
#include <mutex>
#include <map>
#include <condition_variable>
namespace http
@ -24,7 +24,7 @@ namespace http
ByteString uri;
ByteString verb;
bool isPost = false;
std::map<ByteString, ByteString> postData;
PostData postData;
std::vector<ByteString> headers;
enum State
@ -51,6 +51,8 @@ namespace http
RequestHandle(const RequestHandle &) = delete;
RequestHandle &operator =(const RequestHandle &) = delete;
void MarkDone();
static std::shared_ptr<RequestHandle> Create();
};
@ -62,41 +64,27 @@ namespace http
using RequestManagerPtr = std::unique_ptr<RequestManager, RequestManagerDeleter>;
class RequestManager : public ExplicitSingleton<RequestManager>
{
protected:
ByteString proxy;
ByteString cafile;
ByteString capath;
ByteString userAgent;
bool disableNetwork;
std::thread worker;
void InitWorker();
void Worker();
void ExitWorker();
std::vector<std::shared_ptr<RequestHandle>> requestHandles;
void RegisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle);
void UnregisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle);
void Tick();
// State shared between Request threads and the worker thread.
std::vector<std::shared_ptr<RequestHandle>> requestHandlesToRegister;
std::vector<std::shared_ptr<RequestHandle>> requestHandlesToUnregister;
bool running = true;
std::mutex sharedStateMx;
protected:
RequestManager(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork);
public:
~RequestManager();
void RegisterRequestImpl(Request &request);
void UnregisterRequestImpl(Request &request);
public:
void RegisterRequest(Request &request);
void UnregisterRequest(Request &request);
bool DisableNetwork() const;
bool DisableNetwork() const
{
return disableNetwork;
}
static RequestManagerPtr Create(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork);
};
constexpr int TickMs = 100;
}

View File

@ -442,7 +442,7 @@ static Rect<int> SaneSaveRect(Vec2<int> point1, Vec2<int> point2)
auto tly = std::min(point1.Y, point2.Y);
auto brx = std::max(point1.X, point2.X);
auto bry = std::max(point1.Y, point2.Y);
return RectBetween(Vec2{ tlx, tly } / CELL, Vec2{ brx, bry } / CELL);
return RectBetween(Vec2{ tlx, tly }, Vec2{ brx, bry });
}
ByteString GameController::StampRegion(ui::Point point1, ui::Point point2)
@ -1146,7 +1146,7 @@ void GameController::OpenSearch(String searchText)
void GameController::OpenLocalSaveWindow(bool asCurrent)
{
Simulation * sim = gameModel->GetSimulation();
auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), CELLS.OriginRect());
auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), RES.OriginRect());
if(!gameSave)
{
new ErrorMessage("Error", "Unable to build save.");
@ -1357,7 +1357,7 @@ void GameController::OpenSaveWindow()
if(gameModel->GetUser().UserID)
{
Simulation * sim = gameModel->GetSimulation();
auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), CELLS.OriginRect());
auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), RES.OriginRect());
if(!gameSave)
{
new ErrorMessage("Error", "Unable to build save.");
@ -1399,7 +1399,7 @@ void GameController::SaveAsCurrent()
if(gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetUser().Username == gameModel->GetSave()->GetUserName())
{
Simulation * sim = gameModel->GetSimulation();
auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), CELLS.OriginRect());
auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), RES.OriginRect());
if(!gameSave)
{
new ErrorMessage("Error", "Unable to build save.");

View File

@ -46,7 +46,7 @@ private:
}
public:
static int Make(lua_State *l, const ByteString &uri, bool isPost, const ByteString &verb, RequestType type, const std::map<ByteString, ByteString> &post_data, const std::vector<ByteString> &headers)
static int Make(lua_State *l, const ByteString &uri, bool isPost, const ByteString &verb, RequestType type, const http::PostData &postData, const std::vector<ByteString> &headers)
{
auto authUser = Client::Ref().GetAuthUser();
if (type == getAuthToken && !authUser.UserID)
@ -73,7 +73,7 @@ public:
}
if (isPost)
{
rh->request->AddPostData(post_data);
rh->request->AddPostData(postData);
}
if (type == getAuthToken)
{
@ -220,7 +220,7 @@ static int http_request_finish(lua_State *l)
static int http_request(lua_State *l, bool isPost)
{
ByteString uri = tpt_lua_checkByteString(l, 1);
std::map<ByteString, ByteString> post_data;
http::PostData postData;
auto headersIndex = 2;
auto verbIndex = 3;
@ -228,13 +228,19 @@ static int http_request(lua_State *l, bool isPost)
{
headersIndex += 1;
verbIndex += 1;
if (lua_istable(l, 2))
if (lua_isstring(l, 2))
{
postData = tpt_lua_toByteString(l, 2);
}
else if (lua_istable(l, 2))
{
postData = http::FormData{};
auto &formData = std::get<http::FormData>(postData);
lua_pushnil(l);
while (lua_next(l, 2))
{
lua_pushvalue(l, -2);
post_data.emplace(tpt_lua_toByteString(l, -1), tpt_lua_toByteString(l, -2));
formData.emplace(tpt_lua_toByteString(l, -1), tpt_lua_toByteString(l, -2));
lua_pop(l, 2);
}
}
@ -267,7 +273,7 @@ static int http_request(lua_State *l, bool isPost)
}
auto verb = tpt_lua_optByteString(l, verbIndex, "");
return RequestHandle::Make(l, uri, isPost, verb, RequestHandle::normal, post_data, headers);
return RequestHandle::Make(l, uri, isPost, verb, RequestHandle::normal, postData, headers);
}
static int http_get_auth_token(lua_State *l)

View File

@ -19,7 +19,7 @@ extern int Element_LOLZ_lolz[XRES/9][YRES/9];
extern int Element_LOVE_RuleTable[9][9];
extern int Element_LOVE_love[XRES/9][YRES/9];
void Simulation::Load(const GameSave *originalSave, bool includePressure, Vec2<int> blockP)
void Simulation::Load(const GameSave *originalSave, bool includePressure, Vec2<int> blockP) // block coordinates
{
auto save = std::unique_ptr<GameSave>(new GameSave(*originalSave));
@ -335,10 +335,10 @@ void Simulation::Load(const GameSave *originalSave, bool includePressure, Vec2<i
}
}
std::unique_ptr<GameSave> Simulation::Save(bool includePressure, Rect<int> blockR)
std::unique_ptr<GameSave> Simulation::Save(bool includePressure, Rect<int> partR) // particle coordinates
{
auto blockR = RectBetween(partR.TopLeft / CELL, partR.BottomRight / CELL);
auto blockP = blockR.TopLeft;
auto partR = RectSized(blockR.TopLeft * CELL, blockR.Size() * CELL);
auto newSave = std::make_unique<GameSave>(blockR.Size());
auto &possiblyCarriesType = Particle::PossiblyCarriesType();

View File

@ -121,8 +121,8 @@ public:
uint64_t frameCount;
bool ensureDeterminism;
void Load(const GameSave *save, bool includePressure, Vec2<int> blockP);
std::unique_ptr<GameSave> Save(bool includePressure, Rect<int> blockR);
void Load(const GameSave *save, bool includePressure, Vec2<int> blockP); // block coordinates
std::unique_ptr<GameSave> Save(bool includePressure, Rect<int> partR); // particle coordinates
void SaveSimOptions(GameSave &gameSave);
SimulationSample GetSample(int x, int y);