diff --git a/src/Http.c b/src/Http.c index 40ec58846..43ece6bda 100644 --- a/src/Http.c +++ b/src/Http.c @@ -26,9 +26,6 @@ #elif defined CC_BUILD_WEB /* Use fetch/XMLHttpRequest api for Emscripten */ #include -#elif defined CC_BUILD_CURL -#include -#include #endif void HttpRequest_Free(struct HttpRequest* request) { @@ -652,10 +649,75 @@ static void Http_SysFree(void) { InternetCloseHandle(hInternet); } #elif defined CC_BUILD_CURL +#include "Errors.h" +#include +typedef void CURL; +struct curl_slist; +typedef int CURLcode; + +#define CURL_GLOBAL_DEFAULT ((1<<0) | (1<<1)) /* SSL and Win32 options */ +#define CURLOPT_WRITEDATA (10000 + 1) +#define CURLOPT_URL (10000 + 2) +#define CURLOPT_ERRORBUFFER (10000 + 10) +#define CURLOPT_WRITEFUNCTION (20000 + 11) +#define CURLOPT_POSTFIELDS (10000 + 15) +#define CURLOPT_USERAGENT (10000 + 18) +#define CURLOPT_HTTPHEADER (10000 + 23) +#define CURLOPT_HEADERDATA (10000 + 29) +#define CURLOPT_VERBOSE (0 + 41) +#define CURLOPT_HEADER (0 + 42) +#define CURLOPT_NOBODY (0 + 44) +#define CURLOPT_POST (0 + 47) +#define CURLOPT_FOLLOWLOCATION (0 + 52) +#define CURLOPT_AUTOREFERER (0 + 58) +#define CURLOPT_POSTFIELDSIZE (0 + 60) +#define CURLOPT_MAXREDIRS (0 + 68) +#define CURLOPT_HEADERFUNCTION (20000 + 79) + +#if defined _WIN32 +#define APIENTRY __cdecl +#else +#define APIENTRY +#endif + +typedef CURLcode (APIENTRY *FP_curl_global_init)(long flags); static FP_curl_global_init _curl_global_init; +typedef void (APIENTRY *FP_curl_global_cleanup)(void); static FP_curl_global_cleanup _curl_global_cleanup; +typedef CURL* (APIENTRY *FP_curl_easy_init)(void); static FP_curl_easy_init _curl_easy_init; +typedef CURLcode (APIENTRY *FP_curl_easy_perform)(CURL *c); static FP_curl_easy_perform _curl_easy_perform; +typedef CURLcode (APIENTRY *FP_curl_easy_setopt)(CURL *c, int opt, ...); static FP_curl_easy_setopt _curl_easy_setopt; +typedef void (APIENTRY *FP_curl_easy_reset)(CURL* c); static FP_curl_easy_reset _curl_easy_reset; +typedef void (APIENTRY *FP_curl_easy_cleanup)(CURL* c); static FP_curl_easy_cleanup _curl_easy_cleanup; +typedef const char* (APIENTRY *FP_curl_easy_strerror)(CURLcode res); static FP_curl_easy_strerror _curl_easy_strerror; +typedef void (APIENTRY *FP_curl_slist_free_all)(struct curl_slist* l); static FP_curl_slist_free_all _curl_slist_free_all; +typedef struct curl_slist* (APIENTRY *FP_curl_slist_append)(struct curl_slist* l, const char* v); static FP_curl_slist_append _curl_slist_append; + +#if defined CC_BUILD_WIN +static const String curlLib = String_FromConst("libcurl.dll"); +#elif defined CC_BUILD_OSX +static const String curlLib = String_FromConst("libcurl.dylib"); +#else +static const String curlLib = String_FromConst("libcurl.so.4"); +#endif + +#define QUOTE(x) #x +#define LoadCurlFunc(sym) (_ ## sym = (FP_ ## sym)DynamicLib_Get2(lib, QUOTE(sym))) +static cc_bool LoadCurlFuncs(void) { + void* lib = DynamicLib_Load2(&curlLib); + if (!lib) { Logger_DynamicLibWarn("loading", &curlLib); return false; } + + return + LoadCurlFunc(curl_global_init) && LoadCurlFunc(curl_global_cleanup) && + LoadCurlFunc(curl_easy_init) && LoadCurlFunc(curl_easy_perform) && + LoadCurlFunc(curl_easy_setopt) && LoadCurlFunc(curl_easy_reset) && + LoadCurlFunc(curl_easy_cleanup) && LoadCurlFunc(curl_easy_strerror) && + LoadCurlFunc(curl_slist_free_all) && LoadCurlFunc(curl_slist_append); +} + static CURL* curl; +static cc_bool curlInited; cc_bool Http_DescribeError(cc_result res, String* dst) { - const char* err = curl_easy_strerror((CURLcode)res); + const char* err = _curl_easy_strerror((CURLcode)res); if (!err) return false; String_AppendConst(dst, err); @@ -663,11 +725,13 @@ cc_bool Http_DescribeError(cc_result res, String* dst) { } static void Http_SysInit(void) { - CURLcode res = curl_global_init(CURL_GLOBAL_DEFAULT); + if (!LoadCurlFuncs()) return; + CURLcode res = _curl_global_init(CURL_GLOBAL_DEFAULT); if (res) Logger_Abort2(res, "Failed to init curl"); - curl = curl_easy_init(); + curl = _curl_easy_init(); if (!curl) Logger_Abort("Failed to init easy curl"); + curlInited = true; } static struct curl_slist* headers_list; @@ -677,7 +741,7 @@ static void Http_AddHeader(const char* key, const String* value) { String_Format2(&tmp, "%c: %s", key, value); tmp.buffer[tmp.length] = '\0'; - headers_list = curl_slist_append(headers_list, tmp.buffer); + headers_list = _curl_slist_append(headers_list, tmp.buffer); } /* Processes a HTTP header downloaded from the server */ @@ -707,14 +771,14 @@ static size_t Http_ProcessData(char *buffer, size_t size, size_t nitems, void* u /* Sets general curl options for a request */ static void Http_SetCurlOpts(struct HttpRequest* req) { - curl_easy_setopt(curl, CURLOPT_USERAGENT, GAME_APP_NAME); - curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); - curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 20L); + _curl_easy_setopt(curl, CURLOPT_USERAGENT, GAME_APP_NAME); + _curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + _curl_easy_setopt(curl, CURLOPT_MAXREDIRS, 20L); - curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, Http_ProcessHeader); - curl_easy_setopt(curl, CURLOPT_HEADERDATA, req); - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Http_ProcessData); - curl_easy_setopt(curl, CURLOPT_WRITEDATA, req); + _curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, Http_ProcessHeader); + _curl_easy_setopt(curl, CURLOPT_HEADERDATA, req); + _curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, Http_ProcessData); + _curl_easy_setopt(curl, CURLOPT_WRITEDATA, req); } static cc_result Http_SysDo(struct HttpRequest* req, String* url) { @@ -722,21 +786,26 @@ static cc_result Http_SysDo(struct HttpRequest* req, String* url) { void* post_data = req->data; CURLcode res; - curl_easy_reset(curl); + if (!curlInited) { + HttpRequest_Free(req); + return ERR_NOT_SUPPORTED; + } + + _curl_easy_reset(curl); headers_list = NULL; Http_SetRequestHeaders(req); - curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_list); + _curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers_list); Http_SetCurlOpts(req); Platform_ConvertString(urlStr, url); - curl_easy_setopt(curl, CURLOPT_URL, urlStr); + _curl_easy_setopt(curl, CURLOPT_URL, urlStr); if (req->requestType == REQUEST_TYPE_HEAD) { - curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); + _curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); } else if (req->requestType == REQUEST_TYPE_POST) { - curl_easy_setopt(curl, CURLOPT_POST, 1L); - curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req->size); - curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->data); + _curl_easy_setopt(curl, CURLOPT_POST, 1L); + _curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, req->size); + _curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req->data); /* per curl docs, we must persist POST data until request finishes */ req->data = NULL; @@ -745,18 +814,19 @@ static cc_result Http_SysDo(struct HttpRequest* req, String* url) { bufferSize = 0; http_curProgress = ASYNC_PROGRESS_FETCHING_DATA; - res = curl_easy_perform(curl); + res = _curl_easy_perform(curl); http_curProgress = 100; - curl_slist_free_all(headers_list); + _curl_slist_free_all(headers_list); /* can free now that request has finished */ Mem_Free(post_data); return res; } static void Http_SysFree(void) { - curl_easy_cleanup(curl); - curl_global_cleanup(); + if (!curlInited) return; + _curl_easy_cleanup(curl); + _curl_global_cleanup(); } #elif defined CC_BUILD_ANDROID struct HttpRequest* java_req;