mirror of
https://github.com/The-Powder-Toy/The-Powder-Toy.git
synced 2025-01-17 14:28:30 +01:00
Refactor HTTP
Request ownership is no longer flaky. Requests are now owned by the code that makes requests, and Requests and the RequestManager co-own RequestHandles. RequestManager disowns a RequestHandle if it's done with it or if Request code reports that it's no longer needed. All libcurl code has been moved to RequestManager. This is nice because once NOHTTP is removed, we can add any number of RequestManager implementations, for example one for Android. Client outliving RequestManager is still a problem, this will have to be addressed later.
This commit is contained in:
parent
29d4d4e91c
commit
91a9973bfd
@ -39,6 +39,7 @@
|
||||
#include "client/GameSave.h"
|
||||
#include "client/SaveFile.h"
|
||||
#include "client/SaveInfo.h"
|
||||
#include "client/http/RequestManager.h"
|
||||
#include "common/Platform.h"
|
||||
#include "graphics/Graphics.h"
|
||||
#include "gui/Style.h"
|
||||
@ -779,10 +780,10 @@ int main(int argc, char * argv[])
|
||||
ByteString proxyString = clientConfig(arguments["proxy"], "Proxy", "");
|
||||
ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", "");
|
||||
ByteString capathString = clientConfig(arguments["capath"], "CAPath", "");
|
||||
|
||||
bool disableNetwork = true_arg(arguments["disable-network"]);
|
||||
auto requestManager = http::RequestManager::Create(proxyString, cafileString, capathString, disableNetwork);
|
||||
|
||||
Client::Ref().Initialise(proxyString, cafileString, capathString, disableNetwork);
|
||||
Client::Ref().Initialize();
|
||||
|
||||
// TODO: maybe bind the maximum allowed scale to screen size somehow
|
||||
if(scale < 1 || scale > SCALE_MAXIMUM)
|
||||
|
@ -54,7 +54,6 @@
|
||||
|
||||
#include "lua/CommandInterface.h"
|
||||
|
||||
#include "client/http/RequestManager.h"
|
||||
#include "gui/preview/Comment.h"
|
||||
|
||||
Client::Client():
|
||||
@ -102,7 +101,7 @@ Client::Client():
|
||||
firstRun = true;
|
||||
}
|
||||
|
||||
void Client::Initialise(ByteString proxy, ByteString cafile, ByteString capath, bool disableNetwork)
|
||||
void Client::Initialize()
|
||||
{
|
||||
#if !defined(FONTEDITOR) && !defined(RENDERER)
|
||||
if (GetPrefBool("version.update", false))
|
||||
@ -112,11 +111,6 @@ void Client::Initialise(ByteString proxy, ByteString cafile, ByteString capath,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NOHTTP
|
||||
if (!disableNetwork)
|
||||
http::RequestManager::Ref().Initialise(proxy, cafile, capath);
|
||||
#endif
|
||||
|
||||
//Read stamps library
|
||||
std::ifstream stampsLib;
|
||||
stampsLib.open(ByteString::Build(STAMPS_DIR, PATH_SEP, "stamps.def"), std::ios::binary);
|
||||
@ -132,7 +126,7 @@ void Client::Initialise(ByteString proxy, ByteString cafile, ByteString capath,
|
||||
stampsLib.close();
|
||||
|
||||
//Begin version check
|
||||
versionCheckRequest = new http::Request(ByteString::Build(SCHEME, SERVER, "/Startup.json"));
|
||||
versionCheckRequest = std::make_unique<http::Request>(ByteString::Build(SCHEME, SERVER, "/Startup.json"));
|
||||
|
||||
if (authUser.UserID)
|
||||
{
|
||||
@ -143,7 +137,7 @@ void Client::Initialise(ByteString proxy, ByteString cafile, ByteString capath,
|
||||
if constexpr (USE_UPDATESERVER)
|
||||
{
|
||||
// use an alternate update server
|
||||
alternateVersionCheckRequest = new http::Request(ByteString::Build(SCHEME, UPDATESERVER, "/Startup.json"));
|
||||
alternateVersionCheckRequest = std::make_unique<http::Request>(ByteString::Build(SCHEME, UPDATESERVER, "/Startup.json"));
|
||||
usingAltUpdateServer = true;
|
||||
if (authUser.UserID)
|
||||
{
|
||||
@ -242,25 +236,16 @@ RequestStatus Client::ParseServerReturn(ByteString &result, int status, bool jso
|
||||
|
||||
void Client::Tick()
|
||||
{
|
||||
if (versionCheckRequest)
|
||||
{
|
||||
if (CheckUpdate(versionCheckRequest, true))
|
||||
versionCheckRequest = nullptr;
|
||||
}
|
||||
if (alternateVersionCheckRequest)
|
||||
{
|
||||
if (CheckUpdate(alternateVersionCheckRequest, false))
|
||||
alternateVersionCheckRequest = nullptr;
|
||||
}
|
||||
CheckUpdate(versionCheckRequest, true);
|
||||
CheckUpdate(alternateVersionCheckRequest, false);
|
||||
}
|
||||
|
||||
bool Client::CheckUpdate(http::Request *updateRequest, bool checkSession)
|
||||
void Client::CheckUpdate(std::unique_ptr<http::Request> &updateRequest, bool checkSession)
|
||||
{
|
||||
//Check status on version check request
|
||||
if (updateRequest->CheckDone())
|
||||
if (updateRequest && updateRequest->CheckDone())
|
||||
{
|
||||
int status;
|
||||
ByteString data = updateRequest->Finish(&status);
|
||||
auto [ status, data ] = updateRequest->Finish();
|
||||
|
||||
if (checkSession && status == 618)
|
||||
{
|
||||
@ -370,9 +355,8 @@ bool Client::CheckUpdate(http::Request *updateRequest, bool checkSession)
|
||||
//Do nothing
|
||||
}
|
||||
}
|
||||
return true;
|
||||
updateRequest.reset();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
UpdateInfo Client::GetUpdateInfo()
|
||||
@ -461,19 +445,6 @@ void Client::WritePrefs()
|
||||
|
||||
void Client::Shutdown()
|
||||
{
|
||||
if (versionCheckRequest)
|
||||
{
|
||||
versionCheckRequest->Cancel();
|
||||
}
|
||||
if (alternateVersionCheckRequest)
|
||||
{
|
||||
alternateVersionCheckRequest->Cancel();
|
||||
}
|
||||
|
||||
#ifndef NOHTTP
|
||||
http::RequestManager::Ref().Shutdown();
|
||||
#endif
|
||||
|
||||
//Save config
|
||||
WritePrefs();
|
||||
}
|
||||
@ -525,7 +496,7 @@ RequestStatus Client::UploadSave(SaveInfo & save)
|
||||
return RequestFailure;
|
||||
}
|
||||
|
||||
data = http::Request::SimpleAuth(ByteString::Build(SCHEME, SERVER, "/Save.api"), &dataStatus, userID, authUser.SessionID, {
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(ByteString::Build(SCHEME, SERVER, "/Save.api"), userID, authUser.SessionID, {
|
||||
{ "Name", save.GetName().ToUtf8() },
|
||||
{ "Description", save.GetDescription().ToUtf8() },
|
||||
{ "Data:save.bin", ByteString(gameData.begin(), gameData.end()) },
|
||||
@ -700,7 +671,7 @@ RequestStatus Client::ExecVote(int saveID, int direction)
|
||||
{
|
||||
ByteString saveIDText = ByteString::Build(saveID);
|
||||
ByteString userIDText = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(ByteString::Build(SCHEME, SERVER, "/Vote.api"), &dataStatus, userIDText, authUser.SessionID, {
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(ByteString::Build(SCHEME, SERVER, "/Vote.api"), userIDText, authUser.SessionID, {
|
||||
{ "ID", saveIDText },
|
||||
{ "Action", direction ? (direction == 1 ? "Up" : "Down") : "Reset" },
|
||||
{ "Key", authUser.SessionKey }
|
||||
@ -718,15 +689,13 @@ RequestStatus Client::ExecVote(int saveID, int direction)
|
||||
std::vector<char> Client::GetSaveData(int saveID, int saveDate)
|
||||
{
|
||||
lastError = "";
|
||||
int dataStatus;
|
||||
ByteString data;
|
||||
ByteString urlStr;
|
||||
if (saveDate)
|
||||
urlStr = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, "_", saveDate, ".cps");
|
||||
else
|
||||
urlStr = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, ".cps");
|
||||
|
||||
data = http::Request::Simple(urlStr, &dataStatus);
|
||||
auto [ dataStatus, data ] = http::Request::Simple(urlStr);
|
||||
|
||||
// will always return failure
|
||||
ParseServerReturn(data, dataStatus, false);
|
||||
@ -746,9 +715,7 @@ LoginStatus Client::Login(ByteString username, ByteString password, User & user)
|
||||
user.SessionID = "";
|
||||
user.SessionKey = "";
|
||||
|
||||
ByteString data;
|
||||
int dataStatus;
|
||||
data = http::Request::Simple(ByteString::Build("https://", SERVER, "/Login.json"), &dataStatus, {
|
||||
auto [ dataStatus, data ] = http::Request::Simple(ByteString::Build("https://", SERVER, "/Login.json"), {
|
||||
{ "name", username },
|
||||
{ "pass", password },
|
||||
});
|
||||
@ -809,7 +776,7 @@ RequestStatus Client::DeleteSave(int saveID)
|
||||
if(authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID);
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -829,7 +796,7 @@ RequestStatus Client::AddComment(int saveID, String comment)
|
||||
if(authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID, {
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID, {
|
||||
{ "Comment", comment.ToUtf8() },
|
||||
{ "Key", authUser.SessionKey }
|
||||
});
|
||||
@ -855,7 +822,7 @@ RequestStatus Client::FavouriteSave(int saveID, bool favourite)
|
||||
if(authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(urlStream.Build(), &dataStatus, userID, authUser.SessionID);
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(urlStream.Build(), userID, authUser.SessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -875,7 +842,7 @@ RequestStatus Client::ReportSave(int saveID, String message)
|
||||
if(authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID, {
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID, {
|
||||
{ "Reason", message.ToUtf8() },
|
||||
});
|
||||
}
|
||||
@ -897,7 +864,7 @@ RequestStatus Client::UnpublishSave(int saveID)
|
||||
if(authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID);
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -917,7 +884,7 @@ RequestStatus Client::PublishSave(int saveID)
|
||||
if (authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID, {
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID, {
|
||||
{ "ActionPublish", "bagels" },
|
||||
});
|
||||
}
|
||||
@ -944,12 +911,11 @@ SaveInfo * Client::GetSave(int saveID, int saveDate)
|
||||
if(authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
|
||||
data = http::Request::SimpleAuth(urlStream.Build(), &dataStatus, userID, authUser.SessionID);
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(urlStream.Build(), userID, authUser.SessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = http::Request::Simple(urlStream.Build(), &dataStatus);
|
||||
std::tie(dataStatus, data) = http::Request::Simple(urlStream.Build());
|
||||
}
|
||||
if(dataStatus == 200 && data.size())
|
||||
{
|
||||
@ -1047,8 +1013,6 @@ std::vector<std::pair<ByteString, int> > * Client::GetTags(int start, int count,
|
||||
resultCount = 0;
|
||||
std::vector<std::pair<ByteString, int> > * tagArray = new std::vector<std::pair<ByteString, int> >();
|
||||
ByteStringBuilder urlStream;
|
||||
ByteString data;
|
||||
int dataStatus;
|
||||
urlStream << SCHEME << SERVER << "/Browse/Tags.json?Start=" << start << "&Count=" << count;
|
||||
if(query.length())
|
||||
{
|
||||
@ -1057,7 +1021,7 @@ std::vector<std::pair<ByteString, int> > * Client::GetTags(int start, int count,
|
||||
urlStream << format::URLEncode(query.ToUtf8());
|
||||
}
|
||||
|
||||
data = http::Request::Simple(urlStream.Build(), &dataStatus);
|
||||
auto [ dataStatus, data ] = http::Request::Simple(urlStream.Build());
|
||||
if(dataStatus == 200 && data.size())
|
||||
{
|
||||
try
|
||||
@ -1115,11 +1079,11 @@ std::vector<SaveInfo*> * Client::SearchSaves(int start, int count, String query,
|
||||
if(authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(urlStream.Build(), &dataStatus, userID, authUser.SessionID);
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(urlStream.Build(), userID, authUser.SessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
data = http::Request::Simple(urlStream.Build(), &dataStatus);
|
||||
std::tie(dataStatus, data) = http::Request::Simple(urlStream.Build());
|
||||
}
|
||||
ParseServerReturn(data, dataStatus, true);
|
||||
if (dataStatus == 200 && data.size())
|
||||
@ -1167,7 +1131,7 @@ std::list<ByteString> * Client::RemoveTag(int saveID, ByteString tag)
|
||||
if(authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID);
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1206,7 +1170,7 @@ std::list<ByteString> * Client::AddTag(int saveID, ByteString tag)
|
||||
if(authUser.UserID)
|
||||
{
|
||||
ByteString userID = ByteString::Build(authUser.UserID);
|
||||
data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID);
|
||||
std::tie(dataStatus, data) = http::Request::SimpleAuth(url, userID, authUser.SessionID);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "common/String.h"
|
||||
#include "common/Singleton.h"
|
||||
@ -51,8 +52,8 @@ private:
|
||||
String messageOfTheDay;
|
||||
std::vector<std::pair<String, ByteString> > serverNotifications;
|
||||
|
||||
http::Request *versionCheckRequest;
|
||||
http::Request *alternateVersionCheckRequest;
|
||||
std::unique_ptr<http::Request> versionCheckRequest;
|
||||
std::unique_ptr<http::Request> alternateVersionCheckRequest;
|
||||
bool usingAltUpdateServer;
|
||||
bool updateAvailable;
|
||||
UpdateInfo updateInfo;
|
||||
@ -109,7 +110,7 @@ public:
|
||||
void SetMessageOfTheDay(String message);
|
||||
String GetMessageOfTheDay();
|
||||
|
||||
void Initialise(ByteString proxy, ByteString cafile, ByteString capath, bool disableNetwork);
|
||||
void Initialize();
|
||||
bool IsFirstRun();
|
||||
|
||||
void AddListener(ClientListener * listener);
|
||||
@ -153,7 +154,7 @@ public:
|
||||
}
|
||||
RequestStatus ParseServerReturn(ByteString &result, int status, bool json);
|
||||
void Tick();
|
||||
bool CheckUpdate(http::Request *updateRequest, bool checkSession);
|
||||
void CheckUpdate(std::unique_ptr<http::Request> &updateRequest, bool checkSession);
|
||||
void Shutdown();
|
||||
|
||||
// preferences functions
|
||||
|
@ -19,10 +19,8 @@ namespace http
|
||||
Result result;
|
||||
try
|
||||
{
|
||||
ByteString data = Request::Finish(&result.status);
|
||||
// Note that at this point it's not safe to use any member of the
|
||||
// APIRequest object as Request::Finish signals RequestManager
|
||||
// to delete it.
|
||||
ByteString data;
|
||||
std::tie(result.status, data) = Request::Finish();
|
||||
Client::Ref().ParseServerReturn(data, result.status, true);
|
||||
if (result.status == 200 && data.size())
|
||||
{
|
||||
|
15
src/client/http/CurlError.h
Normal file
15
src/client/http/CurlError.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
#include <curl/curl.h> // Has to come first because windows(tm).
|
||||
#include <stdexcept>
|
||||
|
||||
namespace http
|
||||
{
|
||||
struct CurlError : public std::runtime_error
|
||||
{
|
||||
using runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
void SetupCurlEasyCiphers(CURL *easy);
|
||||
void HandleCURLcode(CURLcode code);
|
||||
void HandleCURLMcode(CURLMcode code);
|
||||
}
|
@ -18,9 +18,6 @@ namespace http
|
||||
{
|
||||
std::unique_ptr<UserInfo> user_info;
|
||||
auto result = APIRequest::Finish();
|
||||
// Note that at this point it's not safe to use any member of the
|
||||
// GetUserInfoRequest object as Request::Finish signals RequestManager
|
||||
// to delete it.
|
||||
if (result.document)
|
||||
{
|
||||
auto &user = (*result.document)["User"];
|
||||
|
@ -23,10 +23,8 @@ namespace http
|
||||
{
|
||||
int width = Width;
|
||||
int height = Height;
|
||||
ByteString data = Request::Finish(nullptr);
|
||||
// Note that at this point it's not safe to use any member of the
|
||||
// ImageRequest object as Request::Finish signals RequestManager
|
||||
// to delete it.
|
||||
auto [ status, data ] = Request::Finish();
|
||||
(void)status; // We don't use this for anything, not ideal >_>
|
||||
std::unique_ptr<VideoBuffer> vb;
|
||||
if (data.size())
|
||||
{
|
||||
|
@ -1,139 +1,54 @@
|
||||
#include "Request.h"
|
||||
|
||||
#include "RequestManager.h"
|
||||
|
||||
#ifndef NOHTTP
|
||||
void SetupCurlEasyCiphers(CURL *easy)
|
||||
{
|
||||
#ifdef SECURE_CIPHERS_ONLY
|
||||
curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW);
|
||||
ByteString ssl_type = version_info->ssl_version;
|
||||
if (ssl_type.Contains("OpenSSL"))
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST, "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256");
|
||||
#ifdef REQUEST_USE_CURL_TLSV13CL
|
||||
curl_easy_setopt(easy, CURLOPT_TLS13_CIPHERS, "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256");
|
||||
#endif
|
||||
}
|
||||
else if (ssl_type.Contains("Schannel"))
|
||||
{
|
||||
// TODO: add more cipher algorithms
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST, "CALG_ECDH_EPHEM");
|
||||
}
|
||||
#endif
|
||||
// TODO: Find out what TLS1.2 is supported on, might need to also allow TLS1.0
|
||||
curl_easy_setopt(easy, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2);
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 70, 0)
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT);
|
||||
#elif defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 44, 0)
|
||||
curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE);
|
||||
#elif defined(WIN)
|
||||
# error "That's unfortunate."
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#include <memory>
|
||||
|
||||
namespace http
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
Request::Request(ByteString uri_):
|
||||
uri(uri_),
|
||||
rm_total(0),
|
||||
rm_done(0),
|
||||
rm_finished(false),
|
||||
rm_canceled(false),
|
||||
rm_started(false),
|
||||
added_to_multi(false),
|
||||
status(0),
|
||||
headers(NULL),
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
post_fields(NULL)
|
||||
#else
|
||||
post_fields_first(NULL),
|
||||
post_fields_last(NULL)
|
||||
#endif
|
||||
Request::Request(ByteString newUri)
|
||||
{
|
||||
easy = curl_easy_init();
|
||||
if (!RequestManager::Ref().AddRequest(this))
|
||||
{
|
||||
status = 604;
|
||||
rm_finished = true;
|
||||
}
|
||||
handle = RequestHandle::Create();
|
||||
handle->uri = newUri;
|
||||
}
|
||||
#else
|
||||
Request::Request(ByteString uri_) {}
|
||||
#endif
|
||||
|
||||
Request::~Request()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
curl_easy_cleanup(easy);
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
curl_mime_free(post_fields);
|
||||
#else
|
||||
curl_formfree(post_fields_first);
|
||||
#endif
|
||||
curl_slist_free_all(headers);
|
||||
#endif
|
||||
if (handle->state != RequestHandle::ready)
|
||||
{
|
||||
// TODO: Fix bad design.
|
||||
// Bad design: Client should not outlive RequestManager because it has its own requests, but
|
||||
// RequestManager needs Client because Client is also responsible for configuration >_>
|
||||
// Problem: Client outlives RequestManager, RequestManager doesn't necessarily exist at this point.
|
||||
// Solution: Check if it does >_> ExplicitSingleton::Exists exists for no other reason than this.
|
||||
if (RequestManager::Exists())
|
||||
{
|
||||
RequestManager::Ref().UnregisterRequest(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Request::Verb(ByteString newVerb)
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
verb = newVerb;
|
||||
#endif
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
handle->verb = newVerb;
|
||||
}
|
||||
|
||||
void Request::AddHeader(ByteString header)
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
headers = curl_slist_append(headers, header.c_str());
|
||||
#endif
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
handle->headers.push_back(header);
|
||||
}
|
||||
|
||||
// add post data to a request
|
||||
void Request::AddPostData(std::map<ByteString, ByteString> data)
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
// Even if the map is empty, calling this function signifies you want to do a POST request
|
||||
isPost = true;
|
||||
if (!data.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (easy)
|
||||
{
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
if (!post_fields)
|
||||
{
|
||||
post_fields = curl_mime_init(easy);
|
||||
}
|
||||
|
||||
for (auto &field : data)
|
||||
{
|
||||
curl_mimepart *part = curl_mime_addpart(post_fields);
|
||||
curl_mime_data(part, &field.second[0], field.second.size());
|
||||
if (auto split = field.first.SplitBy(':'))
|
||||
{
|
||||
curl_mime_name(part, split.Before().c_str());
|
||||
curl_mime_filename(part, split.After().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
curl_mime_name(part, field.first.c_str());
|
||||
}
|
||||
}
|
||||
#else
|
||||
post_fields_map.insert(data.begin(), data.end());
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
handle->isPost = true;
|
||||
handle->postData.insert(data.begin(), data.end());
|
||||
}
|
||||
|
||||
// add userID and sessionID headers to the request
|
||||
void Request::AuthHeaders(ByteString ID, ByteString session)
|
||||
{
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
if (ID.size())
|
||||
{
|
||||
if (session.size())
|
||||
@ -148,263 +63,71 @@ namespace http
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef NOHTTP
|
||||
size_t Request::HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata)
|
||||
{
|
||||
Request *req = (Request *)userdata;
|
||||
auto actual_size = size * count;
|
||||
if (actual_size >= 2 && ptr[actual_size - 2] == '\r' && ptr[actual_size - 1] == '\n')
|
||||
{
|
||||
if (actual_size > 2) // don't include header list terminator (but include the status line)
|
||||
{
|
||||
req->response_headers.push_back(ByteString(ptr, ptr + actual_size - 2));
|
||||
}
|
||||
return actual_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t Request::WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata)
|
||||
{
|
||||
Request *req = (Request *)userdata;
|
||||
auto actual_size = size * count;
|
||||
req->response_body.append(ptr, actual_size);
|
||||
return actual_size;
|
||||
}
|
||||
#endif
|
||||
|
||||
// start the request thread
|
||||
void Request::Start()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
if (CheckStarted() || CheckDone())
|
||||
assert(handle->state == RequestHandle::ready);
|
||||
handle->state = RequestHandle::running;
|
||||
// TODO: Fix bad design.
|
||||
// Bad design: Client should not outlive RequestManager because it has its own requests, but
|
||||
// RequestManager needs Client because Client is also responsible for configuration >_>
|
||||
// Problem: Client outlives RequestManager, RequestManager doesn't necessarily exist at this point.
|
||||
// Solution: Check if it does >_> ExplicitSingleton::Exists exists for no other reason than this.
|
||||
if (RequestManager::Exists())
|
||||
{
|
||||
return;
|
||||
RequestManager::Ref().RegisterRequest(*this);
|
||||
}
|
||||
|
||||
if (easy)
|
||||
{
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
if (post_fields)
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_MIMEPOST, post_fields);
|
||||
}
|
||||
#else
|
||||
if (!post_fields_map.empty())
|
||||
{
|
||||
for (auto &field : post_fields_map)
|
||||
{
|
||||
if (auto split = field.first.SplitBy(':'))
|
||||
{
|
||||
curl_formadd(&post_fields_first, &post_fields_last,
|
||||
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
|
||||
{
|
||||
curl_formadd(&post_fields_first, &post_fields_last,
|
||||
CURLFORM_COPYNAME, field.first.c_str(),
|
||||
CURLFORM_PTRCONTENTS, &field.second[0],
|
||||
CURLFORM_CONTENTLEN, field.second.size(),
|
||||
CURLFORM_END);
|
||||
}
|
||||
}
|
||||
curl_easy_setopt(easy, CURLOPT_HTTPPOST, post_fields_first);
|
||||
}
|
||||
#endif
|
||||
else if (isPost)
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_POST, 1L);
|
||||
curl_easy_setopt(easy, CURLOPT_POSTFIELDS, "");
|
||||
}
|
||||
else
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L);
|
||||
}
|
||||
if (verb.size())
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, verb.c_str());
|
||||
}
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1L);
|
||||
if constexpr (ENFORCE_HTTPS)
|
||||
{
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
|
||||
curl_easy_setopt(easy, CURLOPT_PROTOCOLS_STR, "https");
|
||||
curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS_STR, "https");
|
||||
#else
|
||||
curl_easy_setopt(easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
|
||||
curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
|
||||
curl_easy_setopt(easy, CURLOPT_PROTOCOLS_STR, "https,http");
|
||||
curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS_STR, "https,http");
|
||||
#else
|
||||
curl_easy_setopt(easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
|
||||
curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP);
|
||||
#endif
|
||||
}
|
||||
|
||||
SetupCurlEasyCiphers(easy);
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_MAXREDIRS, 10L);
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_ERRORBUFFER, error_buffer);
|
||||
error_buffer[0] = 0;
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT, timeout);
|
||||
curl_easy_setopt(easy, CURLOPT_HTTPHEADER, headers);
|
||||
curl_easy_setopt(easy, CURLOPT_URL, uri.c_str());
|
||||
|
||||
if (proxy.size())
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_PROXY, proxy.c_str());
|
||||
}
|
||||
if (cafile.size())
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_CAINFO, cafile.c_str());
|
||||
}
|
||||
if (capath.size())
|
||||
{
|
||||
curl_easy_setopt(easy, CURLOPT_CAPATH, capath.c_str());
|
||||
}
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_PRIVATE, (void *)this);
|
||||
curl_easy_setopt(easy, CURLOPT_USERAGENT, user_agent.c_str());
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_HEADERDATA, (void *)this);
|
||||
curl_easy_setopt(easy, CURLOPT_HEADERFUNCTION, Request::HeaderDataHandler);
|
||||
|
||||
curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *)this);
|
||||
curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, Request::WriteDataHandler);
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
rm_started = true;
|
||||
}
|
||||
RequestManager::Ref().StartRequest(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// finish the request (if called before the request is done, this will block)
|
||||
ByteString Request::Finish(int *status_out, std::vector<ByteString> *headers_out)
|
||||
bool Request::CheckDone() const
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
if (CheckCanceled())
|
||||
std::lock_guard lk(handle->stateMx);
|
||||
assert(handle->state == RequestHandle::running || handle->state == RequestHandle::done);
|
||||
return handle->state == RequestHandle::done;
|
||||
}
|
||||
|
||||
std::pair<int, int> Request::CheckProgress() const
|
||||
{
|
||||
std::lock_guard lk(handle->stateMx);
|
||||
assert(handle->state == RequestHandle::running || handle->state == RequestHandle::done);
|
||||
return { handle->bytesTotal, handle->bytesDone };
|
||||
}
|
||||
|
||||
const std::vector<ByteString> &Request::ResponseHeaders() const
|
||||
{
|
||||
std::lock_guard lk(handle->stateMx);
|
||||
assert(handle->state == RequestHandle::done);
|
||||
return handle->responseHeaders;
|
||||
}
|
||||
|
||||
std::pair<int, ByteString> Request::Finish()
|
||||
{
|
||||
std::unique_lock lk(handle->stateMx);
|
||||
if (handle->state == RequestHandle::running)
|
||||
{
|
||||
return ""; // shouldn't happen but just in case
|
||||
handle->stateCv.wait(lk, [this]() {
|
||||
return handle->state == RequestHandle::done;
|
||||
});
|
||||
}
|
||||
|
||||
ByteString response_out;
|
||||
{
|
||||
std::unique_lock<std::mutex> l(rm_mutex);
|
||||
done_cv.wait(l, [this]() { return rm_finished; });
|
||||
rm_started = false;
|
||||
rm_canceled = true;
|
||||
if (status_out)
|
||||
{
|
||||
*status_out = status;
|
||||
}
|
||||
if (headers_out)
|
||||
{
|
||||
*headers_out = std::move(response_headers);
|
||||
}
|
||||
response_out = std::move(response_body);
|
||||
}
|
||||
|
||||
RequestManager::Ref().RemoveRequest(this);
|
||||
return response_out;
|
||||
#else
|
||||
if (status_out)
|
||||
*status_out = 604;
|
||||
return "";
|
||||
#endif
|
||||
assert(handle->state == RequestHandle::done);
|
||||
handle->state = RequestHandle::dead;
|
||||
return { handle->statusCode, std::move(handle->responseData) };
|
||||
}
|
||||
|
||||
void Request::CheckProgress(int *total, int *done)
|
||||
std::pair<int, ByteString> Request::Simple(ByteString uri, std::map<ByteString, ByteString> post_data)
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
if (total)
|
||||
{
|
||||
*total = rm_total;
|
||||
}
|
||||
if (done)
|
||||
{
|
||||
*done = rm_done;
|
||||
}
|
||||
#endif
|
||||
return SimpleAuth(uri, "", "", post_data);
|
||||
}
|
||||
|
||||
// returns true if the request has finished
|
||||
bool Request::CheckDone()
|
||||
std::pair<int, ByteString> Request::SimpleAuth(ByteString uri, ByteString ID, ByteString session, std::map<ByteString, ByteString> post_data)
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
return rm_finished;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns true if the request was canceled
|
||||
bool Request::CheckCanceled()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
return rm_canceled;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
// returns true if the request is running
|
||||
bool Request::CheckStarted()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
return rm_started;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// cancels the request, the request thread will delete the Request* when it finishes (do not use Request in any way after canceling)
|
||||
void Request::Cancel()
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rm_mutex);
|
||||
rm_canceled = true;
|
||||
}
|
||||
RequestManager::Ref().RemoveRequest(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
ByteString Request::Simple(ByteString uri, int *status, std::map<ByteString, ByteString> post_data)
|
||||
{
|
||||
return SimpleAuth(uri, status, "", "", post_data);
|
||||
}
|
||||
|
||||
ByteString Request::SimpleAuth(ByteString uri, int *status, ByteString ID, ByteString session, std::map<ByteString, ByteString> post_data)
|
||||
{
|
||||
Request *request = new Request(uri);
|
||||
auto request = std::make_unique<Request>(uri);
|
||||
if (!post_data.empty())
|
||||
{
|
||||
request->AddPostData(post_data);
|
||||
}
|
||||
request->AuthHeaders(ID, session);
|
||||
request->Start();
|
||||
return request->Finish(status);
|
||||
return request->Finish();
|
||||
}
|
||||
|
||||
String StatusText(int ret)
|
||||
@ -494,4 +217,3 @@ namespace http
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,68 +1,24 @@
|
||||
#pragma once
|
||||
#include "Config.h"
|
||||
|
||||
#ifndef NOHTTP
|
||||
# include <curl/curl.h>
|
||||
# include "common/tpt-minmax.h" // for MSVC, ensures windows.h doesn't cause compile errors by defining min/max
|
||||
# include <mutex>
|
||||
# include <condition_variable>
|
||||
# if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 55, 0)
|
||||
# define REQUEST_USE_CURL_OFFSET_T
|
||||
# endif
|
||||
# if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 56, 0)
|
||||
# define REQUEST_USE_CURL_MIMEPOST
|
||||
# endif
|
||||
# if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 61, 0)
|
||||
# define REQUEST_USE_CURL_TLSV13CL
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#include <map>
|
||||
#include "common/String.h"
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace http
|
||||
{
|
||||
class RequestManager;
|
||||
struct RequestHandle;
|
||||
|
||||
class Request
|
||||
{
|
||||
#ifndef NOHTTP
|
||||
ByteString uri;
|
||||
std::vector<ByteString> response_headers;
|
||||
ByteString response_body;
|
||||
|
||||
CURL *easy;
|
||||
char error_buffer[CURL_ERROR_SIZE];
|
||||
|
||||
volatile curl_off_t rm_total;
|
||||
volatile curl_off_t rm_done;
|
||||
volatile bool rm_finished;
|
||||
volatile bool rm_canceled;
|
||||
volatile bool rm_started;
|
||||
std::mutex rm_mutex;
|
||||
|
||||
bool added_to_multi;
|
||||
int status;
|
||||
|
||||
ByteString verb;
|
||||
struct curl_slist *headers;
|
||||
|
||||
bool isPost = false;
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
curl_mime *post_fields;
|
||||
#else
|
||||
curl_httppost *post_fields_first, *post_fields_last;
|
||||
std::map<ByteString, ByteString> post_fields_map;
|
||||
#endif
|
||||
|
||||
std::condition_variable done_cv;
|
||||
|
||||
static size_t HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata);
|
||||
static size_t WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata);
|
||||
#endif
|
||||
std::shared_ptr<RequestHandle> handle;
|
||||
|
||||
public:
|
||||
Request(ByteString uri);
|
||||
virtual ~Request();
|
||||
Request(ByteString newUri);
|
||||
Request(const Request &) = delete;
|
||||
Request &operator =(const Request &) = delete;
|
||||
~Request();
|
||||
|
||||
void Verb(ByteString newVerb);
|
||||
void AddHeader(ByteString header);
|
||||
@ -70,18 +26,16 @@ namespace http
|
||||
void AuthHeaders(ByteString ID, ByteString session);
|
||||
|
||||
void Start();
|
||||
ByteString Finish(int *status, std::vector<ByteString> *headers = nullptr);
|
||||
void Cancel();
|
||||
bool CheckDone() const;
|
||||
|
||||
void CheckProgress(int *total, int *done);
|
||||
bool CheckDone();
|
||||
bool CheckCanceled();
|
||||
bool CheckStarted();
|
||||
std::pair<int, int> CheckProgress() const; // total, done
|
||||
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 = {});
|
||||
|
||||
friend class RequestManager;
|
||||
|
||||
static ByteString Simple(ByteString uri, int *status, std::map<ByteString, ByteString> post_data = std::map<ByteString, ByteString>{});
|
||||
static ByteString SimpleAuth(ByteString uri, int *status, ByteString ID, ByteString session, std::map<ByteString, ByteString> post_data = std::map<ByteString, ByteString>{});
|
||||
};
|
||||
|
||||
String StatusText(int code);
|
||||
|
@ -1,273 +1,114 @@
|
||||
#ifndef NOHTTP
|
||||
#include "Request.h" // includes curl.h, needs to come first to silence a warning on windows
|
||||
#include "RequestManager.h"
|
||||
|
||||
#include "Request.h"
|
||||
#include <iostream>
|
||||
|
||||
const int curl_multi_wait_timeout_ms = 100;
|
||||
const long curl_max_host_connections = 1;
|
||||
const long curl_max_concurrent_streams = 50;
|
||||
|
||||
namespace http
|
||||
{
|
||||
const long timeout = 15;
|
||||
ByteString proxy;
|
||||
ByteString cafile;
|
||||
ByteString capath;
|
||||
ByteString user_agent;
|
||||
|
||||
void RequestManager::Shutdown()
|
||||
RequestManager::RequestManager(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork) :
|
||||
proxy(newProxy),
|
||||
cafile(newCafile),
|
||||
capath(newCapath),
|
||||
disableNetwork(newDisableNetwork)
|
||||
{
|
||||
if (rt_shutting_down)
|
||||
return;
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rt_mutex);
|
||||
rt_shutting_down = true;
|
||||
}
|
||||
rt_cv.notify_one();
|
||||
|
||||
if (initialized)
|
||||
{
|
||||
worker_thread.join();
|
||||
|
||||
curl_multi_cleanup(multi);
|
||||
multi = NULL;
|
||||
curl_global_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void RequestManager::Initialise(ByteString newProxy, ByteString newCafile, ByteString newCapath)
|
||||
{
|
||||
curl_global_init(CURL_GLOBAL_DEFAULT);
|
||||
multi = curl_multi_init();
|
||||
if (multi)
|
||||
{
|
||||
curl_multi_setopt(multi, CURLMOPT_MAX_HOST_CONNECTIONS, curl_max_host_connections);
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 67, 0)
|
||||
curl_multi_setopt(multi, CURLMOPT_MAX_CONCURRENT_STREAMS, curl_max_concurrent_streams);
|
||||
#endif
|
||||
}
|
||||
|
||||
proxy = newProxy;
|
||||
cafile = newCafile;
|
||||
capath = newCapath;
|
||||
|
||||
user_agent = ByteString::Build(
|
||||
userAgent = ByteString::Build(
|
||||
"PowderToy/", SAVE_VERSION, ".", MINOR_VERSION,
|
||||
" (", IDENT_PLATFORM,
|
||||
"; NO", // Unused, used to be SSE level.
|
||||
"; M", MOD_ID,
|
||||
"; ", IDENT,
|
||||
") TPTPP/", SAVE_VERSION, ".", MINOR_VERSION, ".", BUILD_NUM, ByteString(1, IDENT_RELTYPE), ".", SNAPSHOT_ID
|
||||
") TPTPP/", SAVE_VERSION, ".", MINOR_VERSION, ".", BUILD_NUM, IDENT_RELTYPE, ".", SNAPSHOT_ID
|
||||
);
|
||||
worker = std::thread([this]() {
|
||||
Worker();
|
||||
});
|
||||
}
|
||||
|
||||
worker_thread = std::thread([this]() { Worker(); });
|
||||
initialized = true;
|
||||
RequestManager::~RequestManager()
|
||||
{
|
||||
{
|
||||
std::lock_guard lk(sharedStateMx);
|
||||
running = false;
|
||||
}
|
||||
worker.join();
|
||||
}
|
||||
|
||||
void RequestManager::Worker()
|
||||
{
|
||||
bool shutting_down = false;
|
||||
while (!shutting_down)
|
||||
InitWorker();
|
||||
while (true)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> l(rt_mutex);
|
||||
if (!requests_added_to_multi)
|
||||
std::lock_guard lk(sharedStateMx);
|
||||
if (!running)
|
||||
{
|
||||
while (!rt_shutting_down && requests_to_add.empty() && !requests_to_start && !requests_to_remove)
|
||||
// TODO: Fix bad design.
|
||||
// Bad design: Client should not outlive RequestManager because it has its own requests, but
|
||||
// RequestManager needs Client because Client is also responsible for configuration >_>
|
||||
// Problem: RequestManager's worker needs all requests to have been unregistered when it exits.
|
||||
// Solution: Knock out all live requests here on shutdown.
|
||||
for (auto &requestHandle : requestHandles)
|
||||
{
|
||||
rt_cv.wait(l);
|
||||
requestHandle->statusCode = 610;
|
||||
}
|
||||
}
|
||||
shutting_down = rt_shutting_down;
|
||||
requests_to_remove = false;
|
||||
requests_to_start = false;
|
||||
for (Request *request : requests_to_add)
|
||||
for (auto &requestHandle : requestHandles)
|
||||
{
|
||||
request->status = 0;
|
||||
requests.insert(request);
|
||||
}
|
||||
requests_to_add.clear();
|
||||
}
|
||||
|
||||
if (multi && requests_added_to_multi)
|
||||
{
|
||||
int dontcare;
|
||||
struct CURLMsg *msg;
|
||||
|
||||
curl_multi_wait(multi, nullptr, 0, curl_multi_wait_timeout_ms, &dontcare);
|
||||
curl_multi_perform(multi, &dontcare);
|
||||
while ((msg = curl_multi_info_read(multi, &dontcare)))
|
||||
{
|
||||
if (msg->msg == CURLMSG_DONE)
|
||||
if (requestHandle->statusCode)
|
||||
{
|
||||
Request *request;
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &request);
|
||||
|
||||
int finish_with = 600;
|
||||
|
||||
switch (msg->data.result)
|
||||
{
|
||||
case CURLE_OK:
|
||||
long code;
|
||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code);
|
||||
finish_with = (int)code;
|
||||
break;
|
||||
|
||||
case CURLE_UNSUPPORTED_PROTOCOL: finish_with = 601; break;
|
||||
case CURLE_COULDNT_RESOLVE_HOST: finish_with = 602; break;
|
||||
case CURLE_OPERATION_TIMEDOUT: finish_with = 605; break;
|
||||
case CURLE_URL_MALFORMAT: finish_with = 606; break;
|
||||
case CURLE_COULDNT_CONNECT: finish_with = 607; break;
|
||||
case CURLE_COULDNT_RESOLVE_PROXY: finish_with = 608; break;
|
||||
case CURLE_TOO_MANY_REDIRECTS: finish_with = 611; break;
|
||||
|
||||
case CURLE_SSL_CONNECT_ERROR: finish_with = 612; break;
|
||||
case CURLE_SSL_ENGINE_NOTFOUND: finish_with = 613; break;
|
||||
case CURLE_SSL_ENGINE_SETFAILED: finish_with = 614; break;
|
||||
case CURLE_SSL_CERTPROBLEM: finish_with = 615; break;
|
||||
case CURLE_SSL_CIPHER: finish_with = 616; break;
|
||||
case CURLE_SSL_ENGINE_INITFAILED: finish_with = 617; break;
|
||||
case CURLE_SSL_CACERT_BADFILE: finish_with = 618; break;
|
||||
case CURLE_SSL_CRL_BADFILE: finish_with = 619; break;
|
||||
case CURLE_SSL_ISSUER_ERROR: finish_with = 620; break;
|
||||
case CURLE_SSL_PINNEDPUBKEYNOTMATCH: finish_with = 621; break;
|
||||
case CURLE_SSL_INVALIDCERTSTATUS: finish_with = 609; break;
|
||||
|
||||
case CURLE_HTTP2:
|
||||
case CURLE_HTTP2_STREAM:
|
||||
|
||||
case CURLE_FAILED_INIT:
|
||||
case CURLE_NOT_BUILT_IN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (finish_with >= 600)
|
||||
{
|
||||
std::cerr << request->error_buffer << std::endl;
|
||||
}
|
||||
|
||||
request->status = finish_with;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::set<Request *> requests_to_remove;
|
||||
for (Request *request : requests)
|
||||
{
|
||||
bool signal_done = false;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> g(request->rm_mutex);
|
||||
if (shutting_down)
|
||||
{
|
||||
// In the weird case that a http::Request::Simple* call is
|
||||
// waiting on this Request, we should fail the request
|
||||
// instead of cancelling it ourselves.
|
||||
request->status = 610;
|
||||
}
|
||||
if (!request->rm_canceled && request->rm_started && !request->added_to_multi && !request->status)
|
||||
{
|
||||
if (multi && request->easy)
|
||||
{
|
||||
MultiAdd(request);
|
||||
}
|
||||
else
|
||||
{
|
||||
request->status = 604;
|
||||
}
|
||||
}
|
||||
if (!request->rm_canceled && request->rm_started && !request->rm_finished)
|
||||
{
|
||||
if (multi && request->easy)
|
||||
{
|
||||
#ifdef REQUEST_USE_CURL_OFFSET_T
|
||||
curl_easy_getinfo(request->easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &request->rm_total);
|
||||
curl_easy_getinfo(request->easy, CURLINFO_SIZE_DOWNLOAD_T, &request->rm_done);
|
||||
#else
|
||||
double total, done;
|
||||
curl_easy_getinfo(request->easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &total);
|
||||
curl_easy_getinfo(request->easy, CURLINFO_SIZE_DOWNLOAD, &done);
|
||||
request->rm_total = (curl_off_t)total;
|
||||
request->rm_done = (curl_off_t)done;
|
||||
#endif
|
||||
}
|
||||
if (request->status)
|
||||
{
|
||||
request->rm_finished = true;
|
||||
MultiRemove(request);
|
||||
signal_done = true;
|
||||
}
|
||||
}
|
||||
if (request->rm_canceled)
|
||||
{
|
||||
requests_to_remove.insert(request);
|
||||
requestHandlesToUnregister.push_back(requestHandle);
|
||||
}
|
||||
}
|
||||
|
||||
if (signal_done)
|
||||
for (auto &requestHandle : requestHandlesToRegister)
|
||||
{
|
||||
request->done_cv.notify_one();
|
||||
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;
|
||||
}
|
||||
}
|
||||
for (Request *request : requests_to_remove)
|
||||
{
|
||||
requests.erase(request);
|
||||
MultiRemove(request);
|
||||
delete request;
|
||||
}
|
||||
Tick();
|
||||
}
|
||||
assert(!requestHandles.size());
|
||||
ExitWorker();
|
||||
}
|
||||
|
||||
void RequestManager::MultiAdd(Request *request)
|
||||
bool RequestManager::DisableNetwork() const
|
||||
{
|
||||
if (multi && request->easy && !request->added_to_multi)
|
||||
{
|
||||
curl_multi_add_handle(multi, request->easy);
|
||||
request->added_to_multi = true;
|
||||
++requests_added_to_multi;
|
||||
}
|
||||
return disableNetwork;
|
||||
}
|
||||
|
||||
void RequestManager::MultiRemove(Request *request)
|
||||
void RequestManager::RegisterRequest(Request &request)
|
||||
{
|
||||
if (request->added_to_multi)
|
||||
{
|
||||
curl_multi_remove_handle(multi, request->easy);
|
||||
request->added_to_multi = false;
|
||||
--requests_added_to_multi;
|
||||
}
|
||||
std::lock_guard lk(sharedStateMx);
|
||||
requestHandlesToRegister.push_back(request.handle);
|
||||
}
|
||||
|
||||
bool RequestManager::AddRequest(Request *request)
|
||||
void RequestManager::UnregisterRequest(Request &request)
|
||||
{
|
||||
if (!initialized)
|
||||
return false;
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rt_mutex);
|
||||
requests_to_add.insert(request);
|
||||
}
|
||||
rt_cv.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
void RequestManager::StartRequest(Request *request)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rt_mutex);
|
||||
requests_to_start = true;
|
||||
}
|
||||
rt_cv.notify_one();
|
||||
}
|
||||
|
||||
void RequestManager::RemoveRequest(Request *request)
|
||||
{
|
||||
{
|
||||
std::lock_guard<std::mutex> g(rt_mutex);
|
||||
requests_to_remove = true;
|
||||
}
|
||||
rt_cv.notify_one();
|
||||
std::lock_guard lk(sharedStateMx);
|
||||
requestHandlesToUnregister.push_back(request.handle);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -1,57 +1,101 @@
|
||||
#pragma once
|
||||
#include "Config.h"
|
||||
#ifndef NOHTTP
|
||||
#include "Config.h"
|
||||
#include "common/tpt-minmax.h" // for MSVC, ensures windows.h doesn't cause compile errors by defining min/max
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
#include <set>
|
||||
#include <curl/curl.h>
|
||||
#include "common/Singleton.h"
|
||||
#include "common/ExplicitSingleton.h"
|
||||
#include "common/String.h"
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace http
|
||||
{
|
||||
class Request;
|
||||
class RequestManager : public Singleton<RequestManager>
|
||||
|
||||
struct RequestHandle
|
||||
{
|
||||
std::thread worker_thread;
|
||||
std::set<Request *> requests;
|
||||
int requests_added_to_multi = 0;
|
||||
|
||||
std::set<Request *> requests_to_add;
|
||||
bool requests_to_start = false;
|
||||
bool requests_to_remove = false;
|
||||
bool initialized = false;
|
||||
bool rt_shutting_down = false;
|
||||
std::mutex rt_mutex;
|
||||
std::condition_variable rt_cv;
|
||||
|
||||
CURLM *multi = nullptr;
|
||||
|
||||
void Start();
|
||||
void Worker();
|
||||
void MultiAdd(Request *request);
|
||||
void MultiRemove(Request *request);
|
||||
bool AddRequest(Request *request);
|
||||
void StartRequest(Request *request);
|
||||
void RemoveRequest(Request *request);
|
||||
protected:
|
||||
struct CtorTag
|
||||
{
|
||||
};
|
||||
|
||||
public:
|
||||
RequestManager() { }
|
||||
~RequestManager() { }
|
||||
ByteString uri;
|
||||
ByteString verb;
|
||||
bool isPost = false;
|
||||
std::map<ByteString, ByteString> postData;
|
||||
std::vector<ByteString> headers;
|
||||
|
||||
void Initialise(ByteString newProxy, ByteString newCafile, ByteString newCapath);
|
||||
void Shutdown();
|
||||
enum State
|
||||
{
|
||||
ready,
|
||||
running,
|
||||
done,
|
||||
dead,
|
||||
};
|
||||
State state = ready;
|
||||
std::mutex stateMx;
|
||||
std::condition_variable stateCv;
|
||||
int bytesTotal = 0;
|
||||
int bytesDone = 0;
|
||||
int statusCode = 0;
|
||||
ByteString responseData;
|
||||
std::vector<ByteString> responseHeaders;
|
||||
ByteString error;
|
||||
|
||||
friend class Request;
|
||||
RequestHandle(CtorTag)
|
||||
{
|
||||
}
|
||||
|
||||
RequestHandle(const RequestHandle &) = delete;
|
||||
RequestHandle &operator =(const RequestHandle &) = delete;
|
||||
|
||||
static std::shared_ptr<RequestHandle> Create();
|
||||
};
|
||||
|
||||
extern const long timeout;
|
||||
extern ByteString proxy;
|
||||
extern ByteString cafile;
|
||||
extern ByteString capath;
|
||||
extern ByteString user_agent;
|
||||
class RequestManager;
|
||||
struct RequestManagerDeleter
|
||||
{
|
||||
void operator ()(RequestManager *ptr) const;
|
||||
};
|
||||
using RequestManagerPtr = std::unique_ptr<RequestManager, RequestManagerDeleter>;
|
||||
class RequestManager : public ExplicitSingleton<RequestManager>
|
||||
{
|
||||
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 RegisterRequest(Request &request);
|
||||
void UnregisterRequest(Request &request);
|
||||
|
||||
bool DisableNetwork() const;
|
||||
|
||||
static RequestManagerPtr Create(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork);
|
||||
};
|
||||
|
||||
constexpr int TickMs = 100;
|
||||
}
|
||||
#endif
|
||||
|
451
src/client/http/RequestManagerHttp.cpp
Normal file
451
src/client/http/RequestManagerHttp.cpp
Normal file
@ -0,0 +1,451 @@
|
||||
#include <curl/curl.h> // Has to come first because windows(tm).
|
||||
#include "RequestManager.h"
|
||||
#include "Request.h"
|
||||
#include "CurlError.h"
|
||||
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 55, 0)
|
||||
# define REQUEST_USE_CURL_OFFSET_T
|
||||
#endif
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 56, 0)
|
||||
# define REQUEST_USE_CURL_MIMEPOST
|
||||
#endif
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 61, 0)
|
||||
# define REQUEST_USE_CURL_TLSV13CL
|
||||
#endif
|
||||
|
||||
const long curlMaxHostConnections = 1;
|
||||
const long curlMaxConcurrentStreams = 50;
|
||||
const long curlConnectTimeoutS = 15;
|
||||
|
||||
namespace http
|
||||
{
|
||||
void HandleCURLcode(CURLcode code)
|
||||
{
|
||||
if (code != CURLE_OK)
|
||||
{
|
||||
throw CurlError(curl_easy_strerror(code));
|
||||
}
|
||||
};
|
||||
|
||||
void HandleCURLMcode(CURLMcode code)
|
||||
{
|
||||
if (code != CURLM_OK && code != CURLM_CALL_MULTI_PERFORM)
|
||||
{
|
||||
throw CurlError(curl_multi_strerror(code));
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef REQUEST_USE_CURL_MIMEPOST
|
||||
void HandleCURLFORMcode(CURLFORMcode code)
|
||||
{
|
||||
if (code != CURL_FORMADD_OK)
|
||||
{
|
||||
throw CurlError(ByteString::Build("CURLFORMcode ", code));
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
struct RequestHandleHttp : public RequestHandle
|
||||
{
|
||||
curl_slist *curlHeaders = NULL;
|
||||
#ifdef REQUEST_USE_CURL_MIMEPOST
|
||||
curl_mime *curlPostFields = NULL;
|
||||
#else
|
||||
curl_httppost *curlPostFieldsFirst = NULL;
|
||||
curl_httppost *curlPostFieldsLast = NULL;
|
||||
#endif
|
||||
CURL *curlEasy = NULL;
|
||||
char curlErrorBuffer[CURL_ERROR_SIZE];
|
||||
bool curlAddedToMulti = false;
|
||||
|
||||
RequestHandleHttp() : RequestHandle(CtorTag{})
|
||||
{
|
||||
}
|
||||
|
||||
static size_t HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata)
|
||||
{
|
||||
auto *handle = (RequestHandleHttp *)userdata;
|
||||
auto bytes = size * count;
|
||||
if (bytes >= 2 && ptr[bytes - 2] == '\r' && ptr[bytes - 1] == '\n')
|
||||
{
|
||||
if (bytes > 2) // Don't include header list terminator (but include the status line).
|
||||
{
|
||||
handle->responseHeaders.push_back(ByteString(ptr, ptr + bytes - 2));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static size_t WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata)
|
||||
{
|
||||
auto *handle = (RequestHandleHttp *)userdata;
|
||||
auto bytes = size * count;
|
||||
handle->responseData.append(ptr, bytes);
|
||||
return bytes;
|
||||
}
|
||||
};
|
||||
|
||||
std::shared_ptr<RequestHandle> RequestHandle::Create()
|
||||
{
|
||||
return std::make_shared<RequestHandleHttp>();
|
||||
}
|
||||
|
||||
struct RequestManagerHttp : public RequestManager
|
||||
{
|
||||
using RequestManager::RequestManager;
|
||||
|
||||
bool curlGlobalInit = false;
|
||||
CURLM *curlMulti = NULL;
|
||||
};
|
||||
|
||||
void RequestManager::InitWorker()
|
||||
{
|
||||
auto manager = static_cast<RequestManagerHttp *>(this);
|
||||
if (!curl_global_init(CURL_GLOBAL_DEFAULT))
|
||||
{
|
||||
manager->curlGlobalInit = true;
|
||||
manager->curlMulti = curl_multi_init();
|
||||
if (manager->curlMulti)
|
||||
{
|
||||
HandleCURLMcode(curl_multi_setopt(manager->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));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RequestManager::ExitWorker()
|
||||
{
|
||||
auto manager = static_cast<RequestManagerHttp *>(this);
|
||||
curl_multi_cleanup(manager->curlMulti);
|
||||
manager->curlMulti = NULL;
|
||||
curl_global_cleanup();
|
||||
}
|
||||
|
||||
void RequestManager::RegisterRequestHandle(std::shared_ptr<RequestHandle> requestHandle)
|
||||
{
|
||||
auto manager = static_cast<RequestManagerHttp *>(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<RequestManagerHttp *>(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<RequestManagerHttp *>(this);
|
||||
int dontcare;
|
||||
HandleCURLMcode(curl_multi_wait(manager->curlMulti, NULL, 0, TickMs, &dontcare));
|
||||
HandleCURLMcode(curl_multi_perform(manager->curlMulti, &dontcare));
|
||||
while (auto msg = curl_multi_info_read(manager->curlMulti, &dontcare))
|
||||
{
|
||||
if (msg->msg == CURLMSG_DONE)
|
||||
{
|
||||
RequestHandleHttp *handle;
|
||||
HandleCURLcode(curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &handle));
|
||||
handle->statusCode = 600;
|
||||
switch (msg->data.result)
|
||||
{
|
||||
case CURLE_OK:
|
||||
{
|
||||
long code;
|
||||
HandleCURLcode(curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code));
|
||||
assert(code);
|
||||
handle->statusCode = int(code);
|
||||
}
|
||||
break;
|
||||
|
||||
case CURLE_UNSUPPORTED_PROTOCOL: handle->statusCode = 601; break;
|
||||
case CURLE_COULDNT_RESOLVE_HOST: handle->statusCode = 602; break;
|
||||
case CURLE_OPERATION_TIMEDOUT: handle->statusCode = 605; break;
|
||||
case CURLE_URL_MALFORMAT: handle->statusCode = 606; break;
|
||||
case CURLE_COULDNT_CONNECT: handle->statusCode = 607; break;
|
||||
case CURLE_COULDNT_RESOLVE_PROXY: handle->statusCode = 608; break;
|
||||
case CURLE_TOO_MANY_REDIRECTS: handle->statusCode = 611; break;
|
||||
case CURLE_SSL_CONNECT_ERROR: handle->statusCode = 612; break;
|
||||
case CURLE_SSL_ENGINE_NOTFOUND: handle->statusCode = 613; break;
|
||||
case CURLE_SSL_ENGINE_SETFAILED: handle->statusCode = 614; break;
|
||||
case CURLE_SSL_CERTPROBLEM: handle->statusCode = 615; break;
|
||||
case CURLE_SSL_CIPHER: handle->statusCode = 616; break;
|
||||
case CURLE_SSL_ENGINE_INITFAILED: handle->statusCode = 617; break;
|
||||
case CURLE_SSL_CACERT_BADFILE: handle->statusCode = 618; break;
|
||||
case CURLE_SSL_CRL_BADFILE: handle->statusCode = 619; break;
|
||||
case CURLE_SSL_ISSUER_ERROR: handle->statusCode = 620; break;
|
||||
case CURLE_SSL_PINNEDPUBKEYNOTMATCH: handle->statusCode = 621; break;
|
||||
case CURLE_SSL_INVALIDCERTSTATUS: handle->statusCode = 609; break;
|
||||
case CURLE_HTTP2:
|
||||
case CURLE_HTTP2_STREAM:
|
||||
case CURLE_FAILED_INIT:
|
||||
case CURLE_NOT_BUILT_IN:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (handle->statusCode >= 600)
|
||||
{
|
||||
handle->error = handle->curlErrorBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto &requestHandle : requestHandles)
|
||||
{
|
||||
auto handle = static_cast<RequestHandleHttp *>(requestHandle.get());
|
||||
if (handle->curlEasy)
|
||||
{
|
||||
#ifdef REQUEST_USE_CURL_OFFSET_T
|
||||
curl_off_t total, done;
|
||||
HandleCURLcode(curl_easy_getinfo(handle->curlEasy, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &total));
|
||||
HandleCURLcode(curl_easy_getinfo(handle->curlEasy, CURLINFO_SIZE_DOWNLOAD_T, &done));
|
||||
#else
|
||||
double total, done;
|
||||
HandleCURLcode(curl_easy_getinfo(handle->curlEasy, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &total));
|
||||
HandleCURLcode(curl_easy_getinfo(handle->curlEasy, CURLINFO_SIZE_DOWNLOAD, &done));
|
||||
#endif
|
||||
handle->bytesTotal = int(total);
|
||||
handle->bytesDone = int(done);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle->bytesTotal = 0;
|
||||
handle->bytesDone = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RequestManagerPtr RequestManager::Create(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork)
|
||||
{
|
||||
return RequestManagerPtr(new RequestManagerHttp(newProxy, newCafile, newCapath, newDisableNetwork));
|
||||
}
|
||||
|
||||
void RequestManagerDeleter::operator ()(RequestManager *ptr) const
|
||||
{
|
||||
delete static_cast<RequestManagerHttp *>(ptr);
|
||||
}
|
||||
|
||||
void SetupCurlEasyCiphers(CURL *easy)
|
||||
{
|
||||
#ifdef SECURE_CIPHERS_ONLY
|
||||
curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW);
|
||||
ByteString ssl_type = version_info->ssl_version;
|
||||
if (ssl_type.Contains("OpenSSL"))
|
||||
{
|
||||
HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST,
|
||||
"ECDHE-ECDSA-AES256-GCM-SHA384" ":"
|
||||
"ECDHE-ECDSA-AES128-GCM-SHA256" ":"
|
||||
"ECDHE-ECDSA-AES256-SHA384" ":"
|
||||
"DHE-RSA-AES256-GCM-SHA384" ":"
|
||||
"ECDHE-RSA-AES256-GCM-SHA384" ":"
|
||||
"ECDHE-RSA-AES128-GCM-SHA256" ":"
|
||||
"ECDHE-ECDSA-AES128-SHA" ":"
|
||||
"ECDHE-ECDSA-AES128-SHA256" ":"
|
||||
"ECDHE-RSA-CHACHA20-POLY1305" ":"
|
||||
"ECDHE-RSA-AES256-SHA384" ":"
|
||||
"ECDHE-RSA-AES128-SHA256" ":"
|
||||
"ECDHE-ECDSA-CHACHA20-POLY1305" ":"
|
||||
"ECDHE-ECDSA-AES256-SHA" ":"
|
||||
"ECDHE-RSA-AES128-SHA" ":"
|
||||
"DHE-RSA-AES128-GCM-SHA256"
|
||||
));
|
||||
#ifdef REQUEST_USE_CURL_TLSV13CL
|
||||
HandleCURLcode(curl_easy_setopt(easy, CURLOPT_TLS13_CIPHERS,
|
||||
"TLS_AES_256_GCM_SHA384" ":"
|
||||
"TLS_CHACHA20_POLY1305_SHA256" ":"
|
||||
"TLS_AES_128_GCM_SHA256" ":"
|
||||
"TLS_AES_128_CCM_8_SHA256" ":"
|
||||
"TLS_AES_128_CCM_SHA256"
|
||||
));
|
||||
#endif
|
||||
}
|
||||
else if (ssl_type.Contains("Schannel"))
|
||||
{
|
||||
// TODO: add more cipher algorithms
|
||||
HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST, "CALG_ECDH_EPHEM"));
|
||||
}
|
||||
#endif
|
||||
// TODO: Find out what TLS1.2 is supported on, might need to also allow TLS1.0
|
||||
HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2));
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 70, 0)
|
||||
HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT));
|
||||
#elif defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 44, 0)
|
||||
HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE));
|
||||
#elif defined(WIN)
|
||||
# error "That's unfortunate."
|
||||
#endif
|
||||
}
|
||||
}
|
43
src/client/http/RequestManagerNoHttp.cpp
Normal file
43
src/client/http/RequestManagerNoHttp.cpp
Normal file
@ -0,0 +1,43 @@
|
||||
#include "RequestManager.h"
|
||||
#include "Request.h"
|
||||
|
||||
namespace http
|
||||
{
|
||||
std::shared_ptr<RequestHandle> RequestHandle::Create()
|
||||
{
|
||||
return std::make_shared<RequestHandle>(CtorTag{});
|
||||
}
|
||||
|
||||
void RequestManager::InitWorker()
|
||||
{
|
||||
}
|
||||
|
||||
void RequestManager::ExitWorker()
|
||||
{
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
void RequestManagerDeleter::operator ()(RequestManager *ptr) const
|
||||
{
|
||||
delete ptr;
|
||||
}
|
||||
}
|
@ -1,50 +0,0 @@
|
||||
#pragma once
|
||||
#include <type_traits>
|
||||
#include <cassert>
|
||||
|
||||
namespace http
|
||||
{
|
||||
template<class R>
|
||||
class RequestMonitor
|
||||
{
|
||||
R *request;
|
||||
|
||||
protected:
|
||||
RequestMonitor() :
|
||||
request(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~RequestMonitor()
|
||||
{
|
||||
if (request)
|
||||
{
|
||||
request->Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
void RequestPoll()
|
||||
{
|
||||
if (request && request->CheckDone())
|
||||
{
|
||||
OnResponse(request->Finish());
|
||||
request = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template<class... Args>
|
||||
void RequestSetup(Args&&... args)
|
||||
{
|
||||
assert(!request);
|
||||
request = new R(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void RequestStart()
|
||||
{
|
||||
assert(request);
|
||||
request->Start();
|
||||
}
|
||||
|
||||
virtual void OnResponse(typename std::invoke_result<decltype(&R::Finish), R>::type v) = 0;
|
||||
};
|
||||
}
|
@ -21,9 +21,6 @@ namespace http
|
||||
bool SaveUserInfoRequest::Finish()
|
||||
{
|
||||
auto result = APIRequest::Finish();
|
||||
// Note that at this point it's not safe to use any member of the
|
||||
// SaveUserInfoRequest object as Request::Finish signals RequestManager
|
||||
// to delete it.
|
||||
if (result.document)
|
||||
{
|
||||
return (*result.document)["Status"].asInt() == 1;
|
||||
|
@ -5,10 +5,11 @@ client_files += files(
|
||||
'Request.cpp',
|
||||
'SaveUserInfoRequest.cpp',
|
||||
'ThumbnailRequest.cpp',
|
||||
'RequestManager.cpp',
|
||||
)
|
||||
|
||||
if enable_http
|
||||
client_files += files(
|
||||
'RequestManager.cpp',
|
||||
)
|
||||
client_files += files('RequestManagerHttp.cpp')
|
||||
else
|
||||
client_files += files('RequestManagerNoHttp.cpp')
|
||||
endif
|
||||
|
36
src/common/ExplicitSingleton.h
Normal file
36
src/common/ExplicitSingleton.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
#include <cassert>
|
||||
|
||||
template<class Type>
|
||||
class ExplicitSingleton
|
||||
{
|
||||
static Type *&Instance()
|
||||
{
|
||||
// [dcl.fct.spec]: A static local variable in an extern inline function always refers to the same object.
|
||||
static Type *instance = nullptr;
|
||||
return instance;
|
||||
}
|
||||
|
||||
public:
|
||||
ExplicitSingleton()
|
||||
{
|
||||
auto &instance = Instance();
|
||||
assert(!instance);
|
||||
instance = static_cast<Type *>(this);
|
||||
}
|
||||
|
||||
~ExplicitSingleton()
|
||||
{
|
||||
Instance() = nullptr;
|
||||
}
|
||||
|
||||
static Type &Ref()
|
||||
{
|
||||
return *Instance();
|
||||
}
|
||||
|
||||
static bool Exists()
|
||||
{
|
||||
return Instance();
|
||||
}
|
||||
};
|
@ -17,21 +17,20 @@ AvatarButton::AvatarButton(Point position, Point size, ByteString username):
|
||||
|
||||
}
|
||||
|
||||
void AvatarButton::OnResponse(std::unique_ptr<VideoBuffer> Avatar)
|
||||
{
|
||||
avatar = std::move(Avatar);
|
||||
}
|
||||
|
||||
void AvatarButton::Tick(float dt)
|
||||
{
|
||||
if (!avatar && !tried && name.size() > 0)
|
||||
{
|
||||
tried = true;
|
||||
RequestSetup(ByteString::Build(SCHEME, STATICSERVER, "/avatars/", name, ".png"), Size.X, Size.Y);
|
||||
RequestStart();
|
||||
imageRequest = std::make_unique<http::ImageRequest>(ByteString::Build(SCHEME, STATICSERVER, "/avatars/", name, ".png"), Size.X, Size.Y);
|
||||
imageRequest->Start();
|
||||
}
|
||||
|
||||
RequestPoll();
|
||||
if (imageRequest && imageRequest->CheckDone())
|
||||
{
|
||||
avatar = imageRequest->Finish();
|
||||
imageRequest.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void AvatarButton::Draw(const Point& screenPos)
|
||||
|
@ -5,14 +5,13 @@
|
||||
#include "graphics/Graphics.h"
|
||||
#include "gui/interface/Colour.h"
|
||||
#include "client/http/ImageRequest.h"
|
||||
#include "client/http/RequestMonitor.h"
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class AvatarButton : public Component, public http::RequestMonitor<http::ImageRequest>
|
||||
class AvatarButton : public Component
|
||||
{
|
||||
std::unique_ptr<VideoBuffer> avatar;
|
||||
ByteString name;
|
||||
@ -24,6 +23,8 @@ class AvatarButton : public Component, public http::RequestMonitor<http::ImageRe
|
||||
};
|
||||
AvatarButtonAction actionCallback;
|
||||
|
||||
std::unique_ptr<http::ImageRequest> imageRequest;
|
||||
|
||||
public:
|
||||
AvatarButton(Point position, Point size, ByteString username);
|
||||
virtual ~AvatarButton() = default;
|
||||
@ -39,8 +40,6 @@ public:
|
||||
void Draw(const Point& screenPos) override;
|
||||
void Tick(float dt) override;
|
||||
|
||||
void OnResponse(std::unique_ptr<VideoBuffer> avatar) override;
|
||||
|
||||
void DoAction();
|
||||
|
||||
void SetUsername(ByteString username) { name = username; }
|
||||
|
@ -118,11 +118,6 @@ SaveButton::~SaveButton()
|
||||
delete file;
|
||||
}
|
||||
|
||||
void SaveButton::OnResponse(std::unique_ptr<VideoBuffer> Thumbnail)
|
||||
{
|
||||
thumbnail = std::move(Thumbnail);
|
||||
}
|
||||
|
||||
void SaveButton::Tick(float dt)
|
||||
{
|
||||
if (!thumbnail)
|
||||
@ -141,8 +136,8 @@ void SaveButton::Tick(float dt)
|
||||
}
|
||||
else if (save->GetID())
|
||||
{
|
||||
RequestSetup(save->GetID(), save->GetVersion(), thumbBoxSize.X, thumbBoxSize.Y);
|
||||
RequestStart();
|
||||
thumbnailRequest = std::make_unique<http::ThumbnailRequest>(save->GetID(), save->GetVersion(), thumbBoxSize.X, thumbBoxSize.Y);
|
||||
thumbnailRequest->Start();
|
||||
triedThumbnail = true;
|
||||
}
|
||||
}
|
||||
@ -154,7 +149,11 @@ void SaveButton::Tick(float dt)
|
||||
}
|
||||
}
|
||||
|
||||
RequestPoll();
|
||||
if (thumbnailRequest && thumbnailRequest->CheckDone())
|
||||
{
|
||||
thumbnail = thumbnailRequest->Finish();
|
||||
thumbnailRequest.reset();
|
||||
}
|
||||
|
||||
if (thumbnailRenderer)
|
||||
{
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
#include "Component.h"
|
||||
#include "client/http/ThumbnailRequest.h"
|
||||
#include "client/http/RequestMonitor.h"
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
@ -14,7 +13,7 @@ class SaveInfo;
|
||||
class ThumbnailRendererTask;
|
||||
namespace ui
|
||||
{
|
||||
class SaveButton : public Component, public http::RequestMonitor<http::ThumbnailRequest>
|
||||
class SaveButton : public Component
|
||||
{
|
||||
SaveFile * file;
|
||||
SaveInfo * save;
|
||||
@ -33,6 +32,8 @@ class SaveButton : public Component, public http::RequestMonitor<http::Thumbnail
|
||||
bool showVotes;
|
||||
ThumbnailRendererTask *thumbnailRenderer;
|
||||
|
||||
std::unique_ptr<http::ThumbnailRequest> thumbnailRequest;
|
||||
|
||||
struct SaveButtonAction
|
||||
{
|
||||
std::function<void ()> action, altAction, altAltAction, selected;
|
||||
@ -60,8 +61,6 @@ public:
|
||||
void Draw(const Point& screenPos) override;
|
||||
void Tick(float dt) override;
|
||||
|
||||
void OnResponse(std::unique_ptr<VideoBuffer> thumbnail) override;
|
||||
|
||||
void SetSelected(bool selected_) { selected = selected_; }
|
||||
bool GetSelected() { return selected; }
|
||||
void SetSelectable(bool selectable_) { selectable = selectable_; }
|
||||
|
@ -23,14 +23,18 @@ PreviewModel::PreviewModel():
|
||||
saveInfo(NULL),
|
||||
saveData(NULL),
|
||||
saveComments(NULL),
|
||||
saveDataDownload(NULL),
|
||||
commentsDownload(NULL),
|
||||
commentBoxEnabled(false),
|
||||
commentsLoaded(false),
|
||||
commentsTotal(0),
|
||||
commentsPageNumber(1)
|
||||
{
|
||||
}
|
||||
|
||||
PreviewModel::~PreviewModel()
|
||||
{
|
||||
delete saveInfo;
|
||||
delete saveData;
|
||||
ClearComments();
|
||||
}
|
||||
|
||||
void PreviewModel::SetFavourite(bool favourite)
|
||||
@ -85,13 +89,13 @@ void PreviewModel::UpdateSave(int saveID, int saveDate)
|
||||
url = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, "_", saveDate, ".cps");
|
||||
else
|
||||
url = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, ".cps");
|
||||
saveDataDownload = new http::Request(url);
|
||||
saveDataDownload = std::make_unique<http::Request>(url);
|
||||
saveDataDownload->Start();
|
||||
|
||||
url = ByteString::Build(SCHEME, SERVER , "/Browse/View.json?ID=", saveID);
|
||||
if (saveDate)
|
||||
url += ByteString::Build("&Date=", saveDate);
|
||||
saveInfoDownload = new http::Request(url);
|
||||
saveInfoDownload = std::make_unique<http::Request>(url);
|
||||
saveInfoDownload->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID);
|
||||
saveInfoDownload->Start();
|
||||
|
||||
@ -100,7 +104,7 @@ void PreviewModel::UpdateSave(int saveID, int saveDate)
|
||||
commentsLoaded = false;
|
||||
|
||||
url = ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID, "&Start=", (commentsPageNumber-1)*20, "&Count=20");
|
||||
commentsDownload = new http::Request(url);
|
||||
commentsDownload = std::make_unique<http::Request>(url);
|
||||
commentsDownload->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID);
|
||||
commentsDownload->Start();
|
||||
}
|
||||
@ -152,7 +156,7 @@ void PreviewModel::UpdateComments(int pageNumber)
|
||||
if (!GetDoOpen())
|
||||
{
|
||||
ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID, "&Start=", (commentsPageNumber-1)*20, "&Count=20");
|
||||
commentsDownload = new http::Request(url);
|
||||
commentsDownload = std::make_unique<http::Request>(url);
|
||||
commentsDownload->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID);
|
||||
commentsDownload->Start();
|
||||
}
|
||||
@ -245,11 +249,9 @@ bool PreviewModel::ParseSaveInfo(ByteString &saveInfoResponse)
|
||||
// Redownload the .cps file for a fixed version of the 404 save
|
||||
if (tempID == 404 && this->saveID != 404)
|
||||
{
|
||||
if (saveDataDownload)
|
||||
saveDataDownload->Cancel();
|
||||
delete saveData;
|
||||
saveData = NULL;
|
||||
saveDataDownload = new http::Request(ByteString::Build(STATICSCHEME, STATICSERVER, "/2157797.cps"));
|
||||
saveDataDownload = std::make_unique<http::Request>(ByteString::Build(STATICSCHEME, STATICSERVER, "/2157797.cps"));
|
||||
saveDataDownload->Start();
|
||||
}
|
||||
return true;
|
||||
@ -293,8 +295,7 @@ void PreviewModel::Update()
|
||||
{
|
||||
if (saveDataDownload && saveDataDownload->CheckDone())
|
||||
{
|
||||
int status;
|
||||
ByteString ret = saveDataDownload->Finish(&status);
|
||||
auto [ status, ret ] = saveDataDownload->Finish();
|
||||
|
||||
ByteString nothing;
|
||||
Client::Ref().ParseServerReturn(nothing, status, true);
|
||||
@ -312,13 +313,12 @@ void PreviewModel::Update()
|
||||
observers[i]->SaveLoadingError(Client::Ref().GetLastError());
|
||||
}
|
||||
}
|
||||
saveDataDownload = NULL;
|
||||
saveDataDownload.reset();
|
||||
}
|
||||
|
||||
if (saveInfoDownload && saveInfoDownload->CheckDone())
|
||||
{
|
||||
int status;
|
||||
ByteString ret = saveInfoDownload->Finish(&status);
|
||||
auto [ status, ret ] = saveInfoDownload->Finish();
|
||||
|
||||
ByteString nothing;
|
||||
Client::Ref().ParseServerReturn(nothing, status, true);
|
||||
@ -340,13 +340,12 @@ void PreviewModel::Update()
|
||||
for (size_t i = 0; i < observers.size(); i++)
|
||||
observers[i]->SaveLoadingError(Client::Ref().GetLastError());
|
||||
}
|
||||
saveInfoDownload = NULL;
|
||||
saveInfoDownload.reset();
|
||||
}
|
||||
|
||||
if (commentsDownload && commentsDownload->CheckDone())
|
||||
{
|
||||
int status;
|
||||
ByteString ret = commentsDownload->Finish(&status);
|
||||
auto [ status, ret ] = commentsDownload->Finish();
|
||||
ClearComments();
|
||||
|
||||
ByteString nothing;
|
||||
@ -358,7 +357,7 @@ void PreviewModel::Update()
|
||||
notifySaveCommentsChanged();
|
||||
notifyCommentsPageChanged();
|
||||
|
||||
commentsDownload = NULL;
|
||||
commentsDownload.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@ -407,17 +406,3 @@ void PreviewModel::AddObserver(PreviewView * observer)
|
||||
observer->NotifyCommentsPageChanged(this);
|
||||
observer->NotifyCommentBoxEnabledChanged(this);
|
||||
}
|
||||
|
||||
|
||||
PreviewModel::~PreviewModel()
|
||||
{
|
||||
if (saveDataDownload)
|
||||
saveDataDownload->Cancel();
|
||||
if (saveInfoDownload)
|
||||
saveInfoDownload->Cancel();
|
||||
if (commentsDownload)
|
||||
commentsDownload->Cancel();
|
||||
delete saveInfo;
|
||||
delete saveData;
|
||||
ClearComments();
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "Config.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "common/String.h"
|
||||
|
||||
namespace http
|
||||
@ -25,9 +26,9 @@ class PreviewModel
|
||||
void notifyCommentsPageChanged();
|
||||
void notifyCommentBoxEnabledChanged();
|
||||
|
||||
http::Request * saveDataDownload;
|
||||
http::Request * saveInfoDownload;
|
||||
http::Request * commentsDownload;
|
||||
std::unique_ptr<http::Request> saveDataDownload;
|
||||
std::unique_ptr<http::Request> saveInfoDownload;
|
||||
std::unique_ptr<http::Request> commentsDownload;
|
||||
int saveID;
|
||||
int saveDate;
|
||||
|
||||
@ -38,6 +39,8 @@ class PreviewModel
|
||||
|
||||
public:
|
||||
PreviewModel();
|
||||
~PreviewModel();
|
||||
|
||||
SaveInfo * GetSaveInfo();
|
||||
std::vector<SaveComment*> * GetComments();
|
||||
|
||||
@ -61,5 +64,4 @@ public:
|
||||
void OnSaveReady();
|
||||
bool ParseSaveInfo(ByteString &saveInfoResponse);
|
||||
bool ParseComments(ByteString &commentsResponse);
|
||||
virtual ~PreviewModel();
|
||||
};
|
||||
|
@ -37,8 +37,8 @@ ProfileActivity::ProfileActivity(ByteString username) :
|
||||
saving = true;
|
||||
info.location = location->GetText();
|
||||
info.biography = bio->GetText();
|
||||
SaveUserInfoRequestMonitor::RequestSetup(info);
|
||||
SaveUserInfoRequestMonitor::RequestStart();
|
||||
saveUserInfoRequest = std::make_unique<http::SaveUserInfoRequest>(info);
|
||||
saveUserInfoRequest->Start();
|
||||
}
|
||||
} });
|
||||
AddComponent(saveButton);
|
||||
@ -48,8 +48,8 @@ ProfileActivity::ProfileActivity(ByteString username) :
|
||||
|
||||
loading = true;
|
||||
|
||||
GetUserInfoRequestMonitor::RequestSetup(username);
|
||||
GetUserInfoRequestMonitor::RequestStart();
|
||||
getUserInfoRequest = std::make_unique<http::GetUserInfoRequest>(username);
|
||||
getUserInfoRequest->Start();
|
||||
}
|
||||
|
||||
void ProfileActivity::setUserInfo(UserInfo newInfo)
|
||||
@ -191,33 +191,6 @@ void ProfileActivity::setUserInfo(UserInfo newInfo)
|
||||
scrollPanel->InnerSize = ui::Point(Size.X, currentY);
|
||||
}
|
||||
|
||||
void ProfileActivity::OnResponse(bool SaveUserInfoStatus)
|
||||
{
|
||||
if (SaveUserInfoStatus)
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
else
|
||||
{
|
||||
doError = true;
|
||||
doErrorMessage = "Could not save user info: " + Client::Ref().GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileActivity::OnResponse(std::unique_ptr<UserInfo> getUserInfoResult)
|
||||
{
|
||||
if (getUserInfoResult)
|
||||
{
|
||||
loading = false;
|
||||
setUserInfo(*getUserInfoResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
doError = true;
|
||||
doErrorMessage = "Could not load user info: " + Client::Ref().GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileActivity::OnTick(float dt)
|
||||
{
|
||||
if (doError)
|
||||
@ -226,8 +199,35 @@ void ProfileActivity::OnTick(float dt)
|
||||
Exit();
|
||||
}
|
||||
|
||||
SaveUserInfoRequestMonitor::RequestPoll();
|
||||
GetUserInfoRequestMonitor::RequestPoll();
|
||||
if (saveUserInfoRequest && saveUserInfoRequest->CheckDone())
|
||||
{
|
||||
auto SaveUserInfoStatus = saveUserInfoRequest->Finish();
|
||||
if (SaveUserInfoStatus)
|
||||
{
|
||||
Exit();
|
||||
}
|
||||
else
|
||||
{
|
||||
doError = true;
|
||||
doErrorMessage = "Could not save user info: " + Client::Ref().GetLastError();
|
||||
}
|
||||
saveUserInfoRequest.reset();
|
||||
}
|
||||
if (getUserInfoRequest && getUserInfoRequest->CheckDone())
|
||||
{
|
||||
auto getUserInfoResult = getUserInfoRequest->Finish();
|
||||
if (getUserInfoResult)
|
||||
{
|
||||
loading = false;
|
||||
setUserInfo(*getUserInfoResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
doError = true;
|
||||
doErrorMessage = "Could not load user info: " + Client::Ref().GetLastError();
|
||||
}
|
||||
getUserInfoRequest.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void ProfileActivity::OnDraw()
|
||||
|
@ -4,16 +4,14 @@
|
||||
#include "client/UserInfo.h"
|
||||
#include "client/http/SaveUserInfoRequest.h"
|
||||
#include "client/http/GetUserInfoRequest.h"
|
||||
#include "client/http/RequestMonitor.h"
|
||||
#include <memory>
|
||||
|
||||
namespace ui
|
||||
{
|
||||
class Label;
|
||||
class ScrollPanel;
|
||||
}
|
||||
using SaveUserInfoRequestMonitor = http::RequestMonitor<http::SaveUserInfoRequest>;
|
||||
using GetUserInfoRequestMonitor = http::RequestMonitor<http::GetUserInfoRequest>;
|
||||
class ProfileActivity: public WindowActivity, public SaveUserInfoRequestMonitor, public GetUserInfoRequestMonitor {
|
||||
class ProfileActivity: public WindowActivity {
|
||||
ui::ScrollPanel *scrollPanel;
|
||||
ui::Label *location;
|
||||
ui::Label *bio;
|
||||
@ -24,6 +22,10 @@ class ProfileActivity: public WindowActivity, public SaveUserInfoRequestMonitor,
|
||||
bool doError;
|
||||
String doErrorMessage;
|
||||
void setUserInfo(UserInfo newInfo);
|
||||
|
||||
std::unique_ptr<http::SaveUserInfoRequest> saveUserInfoRequest;
|
||||
std::unique_ptr<http::GetUserInfoRequest> getUserInfoRequest;
|
||||
|
||||
public:
|
||||
ProfileActivity(ByteString username);
|
||||
virtual ~ProfileActivity();
|
||||
@ -31,8 +33,5 @@ public:
|
||||
void OnDraw() override;
|
||||
void OnTryExit(ExitMethod method) override;
|
||||
|
||||
void OnResponse(bool saveUserInfoStatus) override;
|
||||
void OnResponse(std::unique_ptr<UserInfo> getUserInfoResult) override;
|
||||
|
||||
void ResizeArea();
|
||||
};
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "UpdateActivity.h"
|
||||
|
||||
#include <bzlib.h>
|
||||
#include <memory>
|
||||
|
||||
#include "Config.h"
|
||||
#include "Update.h"
|
||||
@ -32,20 +33,19 @@ private:
|
||||
bool doWork() override
|
||||
{
|
||||
String error;
|
||||
http::Request *request = new http::Request(updateName);
|
||||
auto request = std::make_unique<http::Request>(updateName);
|
||||
request->Start();
|
||||
notifyStatus("Downloading update");
|
||||
notifyProgress(-1);
|
||||
while(!request->CheckDone())
|
||||
{
|
||||
int total, done;
|
||||
request->CheckProgress(&total, &done);
|
||||
std::tie(total, done) = request->CheckProgress();
|
||||
notifyProgress(total ? done * 100 / total : 0);
|
||||
Platform::Millisleep(1);
|
||||
}
|
||||
|
||||
int status;
|
||||
ByteString data = request->Finish(&status);
|
||||
auto [ status, data ] = request->Finish();
|
||||
if (status!=200)
|
||||
{
|
||||
error = String::Build("Server responded with Status ", status);
|
||||
|
@ -1284,8 +1284,7 @@ int luatpt_getscript(lua_State* l)
|
||||
if (confirmPrompt && !ConfirmPrompt::Blocking("Do you want to install script?", url.FromUtf8(), "Install"))
|
||||
return 0;
|
||||
|
||||
int ret;
|
||||
ByteString scriptData = http::Request::Simple(url, &ret);
|
||||
auto [ ret, scriptData ] = http::Request::Simple(url);
|
||||
if (!scriptData.size())
|
||||
{
|
||||
return luaL_error(l, "Server did not return data");
|
||||
|
@ -4233,7 +4233,7 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
http::Request *request;
|
||||
std::unique_ptr<http::Request> request;
|
||||
bool dead = false;
|
||||
RequestType type;
|
||||
|
||||
@ -4284,7 +4284,7 @@ public:
|
||||
}
|
||||
new(rh) RequestHandle();
|
||||
rh->type = type;
|
||||
rh->request = new http::Request(uri);
|
||||
rh->request = std::make_unique<http::Request>(uri);
|
||||
if (verb.size())
|
||||
{
|
||||
rh->request->Verb(verb);
|
||||
@ -4322,14 +4322,14 @@ public:
|
||||
|
||||
bool Done() const
|
||||
{
|
||||
return dead || request->CheckDone();
|
||||
return request->CheckDone();
|
||||
}
|
||||
|
||||
void Progress(int *total, int *done)
|
||||
{
|
||||
if (!dead)
|
||||
{
|
||||
request->CheckProgress(total, done);
|
||||
std::tie(*total, *done) = request->CheckProgress();
|
||||
}
|
||||
}
|
||||
|
||||
@ -4337,7 +4337,7 @@ public:
|
||||
{
|
||||
if (!dead)
|
||||
{
|
||||
request->Cancel();
|
||||
request.reset();
|
||||
dead = true;
|
||||
}
|
||||
}
|
||||
@ -4349,7 +4349,9 @@ public:
|
||||
{
|
||||
if (request->CheckDone())
|
||||
{
|
||||
data = request->Finish(&status_out, &headers);
|
||||
headers = request->ResponseHeaders();
|
||||
std::tie(status_out, data) = request->Finish();
|
||||
request.reset();
|
||||
if (type == getAuthToken && status_out == 200)
|
||||
{
|
||||
FinishGetAuthToken(data, status_out, headers);
|
||||
|
@ -18,10 +18,10 @@
|
||||
#endif
|
||||
|
||||
#include "LuaScriptInterface.h"
|
||||
#include "client/http/RequestManager.h"
|
||||
#include "client/http/CurlError.h"
|
||||
#include "Misc.h"
|
||||
|
||||
void SetupCurlEasyCiphers(CURL *easy);
|
||||
|
||||
namespace LuaTCPSocket
|
||||
{
|
||||
static double Now()
|
||||
@ -97,9 +97,10 @@ namespace LuaTCPSocket
|
||||
|
||||
static void Reset(TCPSocket *tcps)
|
||||
{
|
||||
using http::HandleCURLMcode;
|
||||
if (tcps->multi)
|
||||
{
|
||||
curl_multi_remove_handle(tcps->multi, tcps->easy);
|
||||
HandleCURLMcode(curl_multi_remove_handle(tcps->multi, tcps->easy));
|
||||
curl_multi_cleanup(tcps->multi);
|
||||
tcps->multi = nullptr;
|
||||
}
|
||||
@ -113,10 +114,12 @@ namespace LuaTCPSocket
|
||||
|
||||
static bool ConnectPerform(TCPSocket *tcps, CURLcode *res)
|
||||
{
|
||||
using http::HandleCURLMcode;
|
||||
while (true)
|
||||
{
|
||||
int dontcare;
|
||||
auto mres = curl_multi_perform(tcps->multi, &dontcare);
|
||||
http::HandleCURLMcode(mres);
|
||||
struct CURLMsg *msg;
|
||||
while ((msg = curl_multi_info_read(tcps->multi, &dontcare)))
|
||||
{
|
||||
@ -136,8 +139,13 @@ namespace LuaTCPSocket
|
||||
|
||||
static int New(lua_State *l)
|
||||
{
|
||||
using http::HandleCURLMcode;
|
||||
if (http::RequestManager::Ref().DisableNetwork())
|
||||
{
|
||||
return luaL_error(l, "network disabled");
|
||||
}
|
||||
auto *tcps = (TCPSocket *)lua_newuserdata(l, sizeof(TCPSocket));
|
||||
new (tcps) TCPSocket;
|
||||
new(tcps) TCPSocket;
|
||||
tcps->errorBuf[0] = 0;
|
||||
tcps->easy = curl_easy_init();
|
||||
tcps->status = StatusReady;
|
||||
@ -157,7 +165,7 @@ namespace LuaTCPSocket
|
||||
Reset(tcps);
|
||||
return luaL_error(l, "curl_multi_init failed");
|
||||
}
|
||||
curl_multi_add_handle(tcps->multi, tcps->easy);
|
||||
HandleCURLMcode(curl_multi_add_handle(tcps->multi, tcps->easy));
|
||||
luaL_newmetatable(l, "TCPSocket");
|
||||
lua_setmetatable(l, -2);
|
||||
return 1;
|
||||
@ -440,6 +448,7 @@ namespace LuaTCPSocket
|
||||
|
||||
static int Connect(lua_State *l)
|
||||
{
|
||||
using http::HandleCURLcode;
|
||||
auto *tcps = (TCPSocket *)luaL_checkudata(l, 1, "TCPSocket");
|
||||
if (tcps->status == StatusDead)
|
||||
{
|
||||
@ -454,43 +463,51 @@ namespace LuaTCPSocket
|
||||
{
|
||||
if (tcps->status != StatusConnecting)
|
||||
{
|
||||
tcps->status = StatusConnecting;
|
||||
// * Using CURLPROTO_HTTPS and CURLPROTO_HTTP with CURL_HTTP_VERSION_1_0
|
||||
// because these really don't send anything while connecting if
|
||||
// CURLOPT_CONNECT_ONLY is 1 and there are no proxies involved. The
|
||||
// only ugly bit is that we have to prepend http:// or https:// to
|
||||
// the hostnames.
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_ERRORBUFFER, tcps->errorBuf);
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_CONNECT_ONLY, 1L);
|
||||
ByteString address = tpt_lua_checkByteString(l, 2);
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_PORT, long(luaL_checkinteger(l, 3)));
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_NOSIGNAL, 1L);
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
|
||||
if (lua_toboolean(l, 4))
|
||||
try
|
||||
{
|
||||
tcps->status = StatusConnecting;
|
||||
// * Using CURLPROTO_HTTPS and CURLPROTO_HTTP with CURL_HTTP_VERSION_1_0
|
||||
// because these really don't send anything while connecting if
|
||||
// CURLOPT_CONNECT_ONLY is 1 and there are no proxies involved. The
|
||||
// only ugly bit is that we have to prepend http:// or https:// to
|
||||
// the hostnames.
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_ERRORBUFFER, tcps->errorBuf));
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_CONNECT_ONLY, 1L));
|
||||
ByteString address = tpt_lua_checkByteString(l, 2);
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PORT, long(luaL_checkinteger(l, 3))));
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_NOSIGNAL, 1L));
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0));
|
||||
if (lua_toboolean(l, 4))
|
||||
{
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS_STR, "https");
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS_STR, "https");
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS_STR, "https"));
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS_STR, "https"));
|
||||
#else
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS));
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS));
|
||||
#endif
|
||||
SetupCurlEasyCiphers(tcps->easy);
|
||||
address = "https://" + address;
|
||||
http::SetupCurlEasyCiphers(tcps->easy);
|
||||
address = "https://" + address;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS_STR, "http"));
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS_STR, "http"));
|
||||
#else
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTP));
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP));
|
||||
#endif
|
||||
address = "http://" + address;
|
||||
}
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_URL, address.c_str()));
|
||||
}
|
||||
else
|
||||
catch (const http::CurlError &ex)
|
||||
{
|
||||
#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0)
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS_STR, "http");
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS_STR, "http");
|
||||
#else
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP);
|
||||
#endif
|
||||
address = "http://" + address;
|
||||
return luaL_error(l, ex.what());
|
||||
}
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_URL, address.c_str());
|
||||
}
|
||||
|
||||
CURLcode res;
|
||||
if (!ConnectPerform(tcps, &res))
|
||||
{
|
||||
@ -542,16 +559,17 @@ namespace LuaTCPSocket
|
||||
|
||||
static int GetPeerName(lua_State *l)
|
||||
{
|
||||
using http::HandleCURLcode;
|
||||
auto *tcps = (TCPSocket *)luaL_checkudata(l, 1, "TCPSocket");
|
||||
if (tcps->status != StatusConnected)
|
||||
{
|
||||
return luaL_error(l, "attempt to get remote socket info while not connected");
|
||||
}
|
||||
char *address;
|
||||
curl_easy_getinfo(tcps->easy, CURLINFO_PRIMARY_IP, &address);
|
||||
HandleCURLcode(curl_easy_getinfo(tcps->easy, CURLINFO_PRIMARY_IP, &address));
|
||||
lua_pushstring(l, address);
|
||||
long port;
|
||||
curl_easy_getinfo(tcps->easy, CURLINFO_PRIMARY_PORT, &port);
|
||||
HandleCURLcode(curl_easy_getinfo(tcps->easy, CURLINFO_PRIMARY_PORT, &port));
|
||||
lua_pushinteger(l, port);
|
||||
return 2;
|
||||
}
|
||||
@ -578,38 +596,47 @@ namespace LuaTCPSocket
|
||||
|
||||
static int GetSockName(lua_State *l)
|
||||
{
|
||||
using http::HandleCURLcode;
|
||||
auto *tcps = (TCPSocket *)luaL_checkudata(l, 1, "TCPSocket");
|
||||
if (tcps->status != StatusConnected)
|
||||
{
|
||||
return luaL_error(l, "attempt to get local socket info while not connected");
|
||||
}
|
||||
char *address;
|
||||
curl_easy_getinfo(tcps->easy, CURLINFO_LOCAL_IP, &address);
|
||||
HandleCURLcode(curl_easy_getinfo(tcps->easy, CURLINFO_LOCAL_IP, &address));
|
||||
lua_pushstring(l, address);
|
||||
long port;
|
||||
curl_easy_getinfo(tcps->easy, CURLINFO_LOCAL_PORT, &port);
|
||||
HandleCURLcode(curl_easy_getinfo(tcps->easy, CURLINFO_LOCAL_PORT, &port));
|
||||
lua_pushinteger(l, port);
|
||||
return 2;
|
||||
}
|
||||
|
||||
static int SetOption(lua_State *l)
|
||||
{
|
||||
using http::HandleCURLcode;
|
||||
auto *tcps = (TCPSocket *)luaL_checkudata(l, 1, "TCPSocket");
|
||||
auto option = tpt_lua_checkByteString(l, 2);
|
||||
if (byteStringEqualsLiteral(option, "keepalive"))
|
||||
try
|
||||
{
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_TCP_KEEPALIVE, long(lua_toboolean(l, 3)));
|
||||
return 0;
|
||||
if (byteStringEqualsLiteral(option, "keepalive"))
|
||||
{
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_TCP_KEEPALIVE, long(lua_toboolean(l, 3))));
|
||||
return 0;
|
||||
}
|
||||
else if (byteStringEqualsLiteral(option, "tcp-nodelay"))
|
||||
{
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_TCP_NODELAY, long(lua_toboolean(l, 3))));
|
||||
return 0;
|
||||
}
|
||||
else if (byteStringEqualsLiteral(option, "verify-peer"))
|
||||
{
|
||||
HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_SSL_VERIFYPEER, long(lua_toboolean(l, 3))));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else if (byteStringEqualsLiteral(option, "tcp-nodelay"))
|
||||
catch (const http::CurlError &ex)
|
||||
{
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_TCP_NODELAY, long(lua_toboolean(l, 3)));
|
||||
return 0;
|
||||
}
|
||||
else if (byteStringEqualsLiteral(option, "verify-peer"))
|
||||
{
|
||||
curl_easy_setopt(tcps->easy, CURLOPT_SSL_VERIFYPEER, long(lua_toboolean(l, 3)));
|
||||
return 0;
|
||||
return luaL_error(l, ex.what());
|
||||
}
|
||||
return luaL_error(l, "unknown option");
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user