Tier2: implement cURL upload file utility

This commit is contained in:
Kawe Mazidjatari 2024-09-13 19:34:33 +02:00
parent 0f89a4e3db
commit 9f935ec4b4
2 changed files with 161 additions and 35 deletions

View File

@ -12,14 +12,15 @@ struct CURLProgress
CURL* curl;
const char* name;
void* cust; // custom pointer to anything.
void* cust; // custom pointer to anything, todo(amos): rename to 'user'.
size_t size;
};
struct CURLParams
{
CURLParams()
: writeFunction(nullptr)
: readFunction(nullptr)
, writeFunction(nullptr)
, statusFunction(nullptr)
, timeout(0)
, verifyPeer(false)
@ -27,6 +28,7 @@ struct CURLParams
, verbose(false)
{}
void* readFunction;
void* writeFunction;
void* statusFunction;
@ -36,9 +38,14 @@ struct CURLParams
bool verbose;
};
size_t CURLReadFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream);
size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream);
size_t CURLWriteStringCallback(char* contents, const size_t size, const size_t nmemb, string* userp);
size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* userp);
curl_slist* CURLSlistAppend(curl_slist* slist, const char* string);
bool CURLUploadFile(const char* remote, const char* filePath, const char* options,
void* customPointer, const bool usePost, const curl_slist* slist, const CURLParams& params);
bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName,
const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params);

View File

@ -6,20 +6,27 @@
#include "tier1/cvar.h"
#include "tier2/curlutils.h"
size_t CURLReadFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream)
{
const size_t numBytesRead = fread(data, size, nmemb, stream);
return numBytesRead;
}
size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* stream)
{
const size_t numBytesWritten = fwrite(data, size, nmemb, stream);
return numBytesWritten;
}
size_t CURLWriteStringCallback(char* data, const size_t size, const size_t nmemb, string* userp)
{
userp->append(data, size * nmemb);
return size * nmemb;
}
size_t CURLWriteFileCallback(void* data, const size_t size, const size_t nmemb, FILE* userp)
{
const size_t numBytesWritten = fwrite(data, size, nmemb, userp);
return numBytesWritten;
}
void CURLInitCommonOptions(CURL* curl, const char* remote,
const void* writeData, const CURLParams& params)
const void* readData, const void* writeData,
const CURLParams& params, const CURLProgress* progressData)
{
curl_easy_setopt(curl, CURLOPT_TIMEOUT, params.timeout);
curl_easy_setopt(curl, CURLOPT_VERBOSE, params.verbose);
@ -29,17 +36,146 @@ void CURLInitCommonOptions(CURL* curl, const char* remote,
curl_easy_setopt(curl, CURLOPT_USE_SSL, CURLUSESSL_ALL);
curl_easy_setopt(curl, CURLOPT_URL, remote);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, writeData);
curl_easy_setopt(curl, CURLOPT_READDATA, readData);
curl_easy_setopt(curl, CURLOPT_USERAGENT, "R5R HTTPS/1.0");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, params.writeFunction);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, params.readFunction);
if (params.statusFunction)
{
Assert(progressData);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0l);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progressData);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, params.statusFunction);
}
}
bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName,
const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params)
static CURL* EasyInit()
{
CURL* curl = curl_easy_init();
if (!curl)
{
Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Easy init failed");
}
return curl;
}
static FILE* OpenFile(const char* filePath, const char* options)
{
FILE* file = fopen(filePath, options);
if (!file)
{
Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Open file failed");
}
return file;
}
static bool FileStat(const char* fileName, struct _stat64& stat)
{
if (_stat64(fileName, &stat) != 0)
{
Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "File status query failed");
return false;
}
return true;
}
curl_slist* CURLSlistAppend(curl_slist* slist, const char* string)
{
slist = curl_slist_append(slist, string);
if (!slist)
{
Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Slist append failed");
}
return slist;
}
bool CURLUploadFile(const char* remote, const char* filePath,
const char* options, void* customPointer, const bool usePost,
const curl_slist* slist, const CURLParams& params)
{
CURL* curl = EasyInit();
if (!curl)
{
return false;
}
FILE* file = OpenFile(filePath, options);
if (!file)
{
return false;
}
struct _stat64 fileStatus;
if (!FileStat(filePath, fileStatus))
{
fclose(file);
return false;
}
CURLProgress progressData;
progressData.curl = curl;
progressData.name = filePath;
progressData.cust = customPointer;
progressData.size = fileStatus.st_size;
string response;
CURLInitCommonOptions(curl, remote, file, &response, params, &progressData);
if (slist)
{
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
}
const bool largeFile = fileStatus.st_size > INT_MAX;
CURLoption fileSizeOption;
if (usePost)
{
curl_easy_setopt(curl, CURLOPT_POST, 1L);
fileSizeOption = largeFile
? CURLOPT_POSTFIELDSIZE_LARGE
: CURLOPT_POSTFIELDSIZE;
}
else
{
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
fileSizeOption = largeFile
? CURLOPT_INFILESIZE_LARGE
: CURLOPT_INFILESIZE;
}
curl_easy_setopt(curl, fileSizeOption, (curl_off_t)fileStatus.st_size);
CURLcode res = curl_easy_perform(curl);
const bool success = res == CURLE_OK;
if (!success)
{
Error(eDLL_T::COMMON, NO_ERROR, "CURL: Upload of file '%s' failed; %s\n",
filePath, curl_easy_strerror(res));
}
curl_easy_cleanup(curl);
fclose(file);
return success;
}
bool CURLDownloadFile(const char* remote, const char* savePath, const char* fileName,
const char* options, curl_off_t dataSize, void* customPointer, const CURLParams& params)
{
CURL* curl = EasyInit();
if (!curl)
{
return false;
}
@ -48,12 +184,10 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file
AppendSlash(filePath);
filePath.append(fileName);
FILE* file = fopen(filePath.c_str(), options);
FILE* file = OpenFile(filePath.c_str(), options);
if (!file)
{
Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", "Open file failed");
curl_easy_cleanup(curl);
return false;
}
@ -64,14 +198,7 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file
progressData.cust = customPointer;
progressData.size = dataSize;
CURLInitCommonOptions(curl, remote, file, params);
if (params.statusFunction)
{
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0l);
curl_easy_setopt(curl, CURLOPT_XFERINFODATA, &progressData);
curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, params.statusFunction);
}
CURLInitCommonOptions(curl, remote, nullptr, file, params, &progressData);
CURLcode res = curl_easy_perform(curl);
@ -95,33 +222,25 @@ bool CURLDownloadFile(const char* remote, const char* savePath, const char* file
CURL* CURLInitRequest(const char* remote, const char* request,
string& outResponse, curl_slist*& slist, const CURLParams& params)
{
std::function<void(const char*)> fnError = [&](const char* errorMsg)
{
Error(eDLL_T::COMMON, NO_ERROR, "CURL: %s\n", errorMsg);
curl_slist_free_all(slist);
};
slist = curl_slist_append(slist, "Content-Type: application/json");
slist = CURLSlistAppend(slist, "Content-Type: application/json");
if (!slist)
{
fnError("Slist init failed");
return nullptr;
}
CURL* curl = curl_easy_init();
CURL* curl = EasyInit();
if (!curl)
{
fnError("Easy init failed");
return nullptr;
}
CURLInitCommonOptions(curl, remote, &outResponse, params);
CURLInitCommonOptions(curl, remote, nullptr, &outResponse, params, nullptr);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
if (request)
{
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, request);
}
return curl;