From e7bf417bd5577b2a2271da775420d3eed01e584f Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 2 Mar 2019 23:01:39 +1100 Subject: [PATCH] cleanup and reorganise http code, remove some pointless debugging messages too --- src/Http.c | 639 ++++++++++++++++++++++++++++---------------------- src/Http.h | 4 +- src/Program.c | 3 + 3 files changed, 359 insertions(+), 287 deletions(-) diff --git a/src/Http.c b/src/Http.c index eb3e7aaa6..e24ea793f 100644 --- a/src/Http.c +++ b/src/Http.c @@ -5,7 +5,7 @@ #include "Stream.h" #include "GameStructs.h" -#ifdef CC_BUILD_WIN +#if defined CC_BUILD_WIN #define WIN32_LEAN_AND_MEAN #define NOSERVICE #define NOMCX @@ -21,15 +21,12 @@ #include #include #define HTTP_QUERY_ETAG 54 /* Missing from some old MingW32 headers */ -#endif - -/* Can't use curl for the web */ -#ifdef CC_BUILD_WEB +#elif defined CC_BUILD_WEB +/* Use javascript web api for Emscripten */ #undef CC_BUILD_POSIX -#endif - +#include +#elif defined CC_BUILD_POSIX /* POSIX is mainly shared between Linux and OSX */ -#ifdef CC_BUILD_POSIX #include #include #endif @@ -40,9 +37,268 @@ void HttpRequest_Free(struct HttpRequest* request) { request->Size = 0; } +/*########################################################################################################################* +*----------------------------------------------------Http requests list---------------------------------------------------* +*#########################################################################################################################*/ +#define HTTP_DEF_ELEMS 10 +struct RequestList { + int MaxElems, Count; + struct HttpRequest* Entries; + struct HttpRequest DefaultEntries[HTTP_DEF_ELEMS]; +}; + +/* Expands request list buffer if there is no room for another request */ +static void RequestList_EnsureSpace(struct RequestList* list) { + if (list->Count < list->MaxElems) return; + list->Entries = Utils_Resize(list->Entries, &list->MaxElems, + sizeof(struct HttpRequest), HTTP_DEF_ELEMS, 10); +} + +/* Adds another request to end (for normal priority request) */ +static void RequestList_Append(struct RequestList* list, struct HttpRequest* item) { + RequestList_EnsureSpace(list); + list->Entries[list->Count++] = *item; +} + +/* Inserts a request at start (for high priority request) */ +static void RequestList_Prepend(struct RequestList* list, struct HttpRequest* item) { + int i; + RequestList_EnsureSpace(list); + + for (i = list->Count; i > 0; i--) { + list->Entries[i] = list->Entries[i - 1]; + } + list->Entries[0] = *item; + list->Count++; +} + +/* Removes the request at the given index */ +static void RequestList_RemoveAt(struct RequestList* list, int i) { + if (i < 0 || i >= list->Count) Logger_Abort("Tried to remove element at list end"); + + for (; i < list->Count - 1; i++) { + list->Entries[i] = list->Entries[i + 1]; + } + list->Count--; +} + +/* Finds index of request whose id matches the given id */ +static int RequestList_Find(struct RequestList* list, const String* id, struct HttpRequest* item) { + String reqID; + int i; + + for (i = 0; i < list->Count; i++) { + reqID = String_FromRawArray(list->Entries[i].ID); + if (!String_Equals(id, &reqID)) continue; + + *item = list->Entries[i]; + return i; + } + return -1; +} + +/* Resets state to default */ +static void RequestList_Init(struct RequestList* list) { + list->MaxElems = HTTP_DEF_ELEMS; + list->Count = 0; + list->Entries = list->DefaultEntries; +} + +/* Frees any dynamically allocated memory, then resets state to default */ +static void RequestList_Free(struct RequestList* list) { + if (list->Entries != list->DefaultEntries) { + Mem_Free(list->Entries); + } + RequestList_Init(list); +} + /*########################################################################################################################* -*-------------------------------------------------System/Native interface-------------------------------------------------* +*--------------------------------------------------Common downloader code-------------------------------------------------* +*#########################################################################################################################*/ +static void* workerWaitable; +static void* workerThread; +static void* pendingMutex; +static void* processedMutex; +static void* curRequestMutex; +static volatile bool http_terminate; + +static struct RequestList pendingReqs; +static struct RequestList processedReqs; +static struct HttpRequest http_curRequest; +static volatile int http_curProgress = ASYNC_PROGRESS_NOTHING; + +#ifdef CC_BUILD_WEB +static void Http_DownloadNextAsync(void); +#endif + +/* Adds a req to the list of pending requests, waking up worker thread if needed. */ +static void Http_Add(const String* url, bool priority, const String* id, uint8_t type, TimeMS* lastModified, const String* etag, const void* data, uint32_t size) { + struct HttpRequest req = { 0 }; + String reqUrl, reqID, reqEtag; + + String_InitArray(reqUrl, req.URL); + String_Copy(&reqUrl, url); + String_InitArray(reqID, req.ID); + String_Copy(&reqID, id); + + req.RequestType = type; + Platform_Log2("Adding %s (type %b)", &reqUrl, &type); + + String_InitArray(reqEtag, req.Etag); + if (lastModified) { req.LastModified = *lastModified; } + if (etag) { String_Copy(&reqEtag, etag); } + + if (data) { + req.Data = Mem_Alloc(size, 1, "Http_PostData"); + Mem_Copy(req.Data, data, size); + req.Size = size; + } + + Mutex_Lock(pendingMutex); + { + req.TimeAdded = DateTime_CurrentUTC_MS(); + if (priority) { + RequestList_Prepend(&pendingReqs, &req); + } else { + RequestList_Append(&pendingReqs, &req); + } + } + Mutex_Unlock(pendingMutex); + +#ifndef CC_BUILD_WEB + Waitable_Signal(workerWaitable); +#else + Http_DownloadNextAsync(); +#endif +} + +/* Sets up state to begin a http request */ +static void Http_BeginRequest(struct HttpRequest* req) { + String url = String_FromRawArray(req->URL); + Platform_Log2("Downloading from %s (type %b)", &url, &req->RequestType); + + Mutex_Lock(curRequestMutex); + { + http_curRequest = *req; + http_curProgress = ASYNC_PROGRESS_MAKING_REQUEST; + } + Mutex_Unlock(curRequestMutex); +} + +/* Adds given request to list of processed/completed requests */ +static void Http_CompleteRequest(struct HttpRequest* req) { + struct HttpRequest older; + String id = String_FromRawArray(req->ID); + int index; + req->TimeDownloaded = DateTime_CurrentUTC_MS(); + + index = RequestList_Find(&processedReqs, &id, &older); + if (index >= 0) { + /* very rare case - priority item was inserted, then inserted again (so put before first item), */ + /* and both items got downloaded before an external function removed them from the queue */ + if (older.TimeAdded > req->TimeAdded) { + HttpRequest_Free(req); + } else { + /* normal case, replace older req */ + HttpRequest_Free(&older); + processedReqs.Entries[index] = *req; + } + } else { + RequestList_Append(&processedReqs, req); + } +} + +/* Updates state after a completed http request */ +static void Http_FinishRequest(struct HttpRequest* req) { + if (req->Data) Platform_Log1("HTTP returned data: %i bytes", &req->Size); + req->Success = !req->Result && req->StatusCode == 200 && req->Data && req->Size; + + Mutex_Lock(processedMutex); + { + Http_CompleteRequest(req); + } + Mutex_Unlock(processedMutex); + + Mutex_Lock(curRequestMutex); + { + http_curRequest.ID[0] = '\0'; + http_curProgress = ASYNC_PROGRESS_NOTHING; + } + Mutex_Unlock(curRequestMutex); +} + + +/*########################################################################################################################* +*------------------------------------------------Emscripten implementation------------------------------------------------* +*#########################################################################################################################*/ +#ifdef CC_BUILD_WEB +static void Http_SysInit(void) { } +static void Http_SysFree(void) { } +static void Http_DownloadAsync(struct HttpRequest* req); + +static void Http_DownloadNextAsync(void) { + struct HttpRequest req; + if (http_terminate || !pendingReqs.Count) return; + /* already working on a request currently */ + if (http_curRequest.ID[0] != '\0') return; + + req = pendingReqs.Entries[0]; + RequestList_RemoveAt(&pendingReqs, 0); + Http_DownloadAsync(&req); +} + +static void Http_UpdateProgress(emscripten_fetch_t* fetch) { + if (!fetch->totalBytes) return; + http_curProgress = (int)(100.0f * fetch->dataOffset / fetch->totalBytes); +} + +static void Http_FinishedAsync(emscripten_fetch_t* fetch) { + struct HttpRequest* req = &http_curRequest; + req->Data = fetch->data; + req->Size = fetch->numBytes; + req->StatusCode = fetch->status; + + /* data needs to persist beyond closing of fetch data */ + fetch->data = NULL; + emscripten_fetch_close(fetch); + + Http_FinishRequest(&http_curRequest); + Http_DownloadNextAsync(); +} + +static void Http_DownloadAsync(struct HttpRequest* req) { + emscripten_fetch_attr_t attr; + emscripten_fetch_attr_init(&attr); + + String url = String_FromRawArray(req->URL); + char urlStr[600]; + Platform_ConvertString(urlStr, &url); + + switch (req->RequestType) { + case REQUEST_TYPE_GET: Mem_Copy(attr.requestMethod, "GET", 4); break; + case REQUEST_TYPE_HEAD: Mem_Copy(attr.requestMethod, "HEAD", 5); break; + case REQUEST_TYPE_POST: Mem_Copy(attr.requestMethod, "POST", 5); break; + } + + if (req->RequestType == REQUEST_TYPE_POST) { + attr.requestData = req->Data; + attr.requestDataSize = req->Size; + } + + attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY; + attr.onsuccess = Http_FinishedAsync; + attr.onerror = Http_FinishedAsync; + attr.onprogress = Http_UpdateProgress; + + Http_BeginRequest(req); + emscripten_fetch(&attr, urlStr); +} +#endif + + +/*########################################################################################################################* +*--------------------------------------------------Native implementation--------------------------------------------------* *#########################################################################################################################*/ #ifdef CC_BUILD_WIN static HINTERNET hInternet; @@ -216,13 +472,13 @@ static ReturnCode Http_ProcessHeaders(struct HttpRequest* req, HINTERNET handle) } /* Downloads the data/contents of a HTTP response */ -static ReturnCode Http_DownloadData(struct HttpRequest* req, HINTERNET handle, volatile int* progress) { +static ReturnCode Http_DownloadData(struct HttpRequest* req, HINTERNET handle) { uint8_t* buffer; uint32_t size, totalRead; uint32_t read, avail; bool success; - *progress = 0; + http_curProgress = 0; size = req->ContentLength ? req->ContentLength : 1; buffer = Mem_Alloc(size, 1, "http get data"); totalRead = 0; @@ -246,26 +502,26 @@ static ReturnCode Http_DownloadData(struct HttpRequest* req, HINTERNET handle, v if (!read) break; totalRead += read; - if (req->ContentLength) *progress = (int)(100.0f * totalRead / size); + if (req->ContentLength) http_curProgress = (int)(100.0f * totalRead / size); req->Size += read; } - *progress = 100; + http_curProgress = 100; return 0; } -static ReturnCode Http_SysDo(struct HttpRequest* req, volatile int* progress) { +static ReturnCode Http_SysDo(struct HttpRequest* req) { HINTERNET handle; ReturnCode res = Http_StartRequest(req, &handle); HttpRequest_Free(req); if (res) return res; - *progress = ASYNC_PROGRESS_FETCHING_DATA; + http_curProgress = ASYNC_PROGRESS_FETCHING_DATA; res = Http_ProcessHeaders(req, handle); if (res) { InternetCloseHandle(handle); return res; } if (req->RequestType != REQUEST_TYPE_HEAD) { - res = Http_DownloadData(req, handle, progress); + res = Http_DownloadData(req, handle); if (res) { InternetCloseHandle(handle); return res; } } @@ -293,9 +549,8 @@ static void Http_SysInit(void) { } /* Updates progress of current download */ -static int Http_UpdateProgress(int* progress, double total, double received, double a, double b) { - if (total == 0) return 0; - *progress = (int)(100 * received / total); +static int Http_UpdateProgress(void* ptr, double total, double received, double a, double b) { + if (total) http_curProgress = (int)(100 * received / total); return 0; } @@ -378,21 +633,17 @@ static size_t Http_ProcessData(char *buffer, size_t size, size_t nitems, struct dst = (uint8_t*)req->Data + req->Size; Mem_Copy(dst, buffer, nitems); req->Size += nitems; - - /* TODO: Set progress */ - //if (req->ContentLength) *progress = (int)(100.0f * req->Size / curlBufferSize); return nitems; } /* Sets general curl options for a request */ -static void Http_SetCurlOpts(struct HttpRequest* req, volatile int* progress) { +static void Http_SetCurlOpts(struct HttpRequest* req) { curl_easy_setopt(curl, CURLOPT_COOKIEJAR, ""); curl_easy_setopt(curl, CURLOPT_USERAGENT, GAME_APP_NAME); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L); curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, Http_UpdateProgress); - curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, progress); curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, Http_ProcessHeader); curl_easy_setopt(curl, CURLOPT_HEADERDATA, req); @@ -400,7 +651,7 @@ static void Http_SetCurlOpts(struct HttpRequest* req, volatile int* progress) { curl_easy_setopt(curl, CURLOPT_WRITEDATA, req); } -static ReturnCode Http_SysDo(struct HttpRequest* req, volatile int* progress) { +static ReturnCode Http_SysDo(struct HttpRequest* req) { String url = String_FromRawArray(req->URL); char urlStr[600]; void* post_data = req->Data; @@ -412,7 +663,7 @@ static ReturnCode Http_SysDo(struct HttpRequest* req, volatile int* progress) { list = Http_MakeHeaders(req); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list); - Http_SetCurlOpts(req, progress); + Http_SetCurlOpts(req); Platform_ConvertString(urlStr, &url); curl_easy_setopt(curl, CURLOPT_URL, urlStr); @@ -429,9 +680,9 @@ static ReturnCode Http_SysDo(struct HttpRequest* req, volatile int* progress) { } curlBufferSize = 0; - *progress = ASYNC_PROGRESS_FETCHING_DATA; + http_curProgress = ASYNC_PROGRESS_FETCHING_DATA; res = curl_easy_perform(curl); - *progress = 100; + http_curProgress = 100; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status); req->StatusCode = status; @@ -447,118 +698,53 @@ static void Http_SysFree(void) { curl_global_cleanup(); } #endif -#ifdef CC_BUILD_WEB -/* TODO: Implement this backend */ -static void Http_SysInit(void) { } -static ReturnCode Http_SysDo(struct HttpRequest* req, volatile int* progress) { - return ReturnCode_NotSupported; + +#ifndef CC_BUILD_WEB +static void Http_WorkerLoop(void) { + struct HttpRequest request; + bool hasRequest, stop; + uint64_t beg, end; + uint32_t elapsed; + + for (;;) { + hasRequest = false; + + Mutex_Lock(pendingMutex); + { + stop = http_terminate; + if (!stop && pendingReqs.Count) { + request = pendingReqs.Entries[0]; + hasRequest = true; + RequestList_RemoveAt(&pendingReqs, 0); + } + } + Mutex_Unlock(pendingMutex); + + if (stop) return; + /* Block until another thread submits a req to do */ + if (!hasRequest) { + Platform_LogConst("Going back to sleep..."); + Waitable_Wait(workerWaitable); + continue; + } + Http_BeginRequest(&request); + + beg = Stopwatch_Measure(); + request.Result = Http_SysDo(&request); + end = Stopwatch_Measure(); + + Platform_Log3("HTTP: return code %i (http %i), in %i ms", + &request.Result, &request.StatusCode, &elapsed); + Http_FinishRequest(&request); + } } -static void Http_SysFree(void) { } #endif /*########################################################################################################################* -*-------------------------------------------------Asynchronous downloader-------------------------------------------------* +*----------------------------------------------------Http public api------------------------------------------------------* *#########################################################################################################################*/ -#define HTTP_DEF_ELEMS 10 -struct HttpRequestList { - int MaxElems, Count; - struct HttpRequest* Requests; - struct HttpRequest DefaultRequests[HTTP_DEF_ELEMS]; -}; - -static void HttpRequestList_EnsureSpace(struct HttpRequestList* list) { - if (list->Count < list->MaxElems) return; - list->Requests = Utils_Resize(list->Requests, &list->MaxElems, - sizeof(struct HttpRequest), HTTP_DEF_ELEMS, 10); -} - -static void HttpRequestList_Append(struct HttpRequestList* list, struct HttpRequest* item) { - HttpRequestList_EnsureSpace(list); - list->Requests[list->Count++] = *item; -} - -static void HttpRequestList_Prepend(struct HttpRequestList* list, struct HttpRequest* item) { - int i; - HttpRequestList_EnsureSpace(list); - - for (i = list->Count; i > 0; i--) { - list->Requests[i] = list->Requests[i - 1]; - } - list->Requests[0] = *item; - list->Count++; -} - -static void HttpRequestList_RemoveAt(struct HttpRequestList* list, int i) { - if (i < 0 || i >= list->Count) Logger_Abort("Tried to remove element at list end"); - - for (; i < list->Count - 1; i++) { - list->Requests[i] = list->Requests[i + 1]; - } - list->Count--; -} - -static void HttpRequestList_Init(struct HttpRequestList* list) { - list->MaxElems = HTTP_DEF_ELEMS; - list->Count = 0; - list->Requests = list->DefaultRequests; -} - -static void HttpRequestList_Free(struct HttpRequestList* list) { - if (list->Requests != list->DefaultRequests) { - Mem_Free(list->Requests); - } - HttpRequestList_Init(list); -} - -static void* http_waitable; -static void* http_workerThread; -static void* http_pendingMutex; -static void* http_processedMutex; -static void* http_curRequestMutex; -static volatile bool http_terminate; - -static struct HttpRequestList http_pending; -static struct HttpRequestList http_processed; -const static String http_skinServer = String_FromConst("http://static.classicube.net/skins/"); -static struct HttpRequest http_curRequest; -static volatile int http_curProgress = ASYNC_PROGRESS_NOTHING; - -/* Adds a req to the list of pending requests, waking up worker thread if needed. */ -static void Http_Add(const String* url, bool priority, const String* id, uint8_t type, TimeMS* lastModified, const String* etag, const void* data, uint32_t size) { - struct HttpRequest req = { 0 }; - String reqUrl, reqID, reqEtag; - - String_InitArray(reqUrl, req.URL); - String_Copy(&reqUrl, url); - String_InitArray(reqID, req.ID); - String_Copy(&reqID, id); - - req.RequestType = type; - Platform_Log2("Adding %s (type %b)", &reqUrl, &type); - - String_InitArray(reqEtag, req.Etag); - if (lastModified) { req.LastModified = *lastModified; } - if (etag) { String_Copy(&reqEtag, etag); } - - if (data) { - req.Data = Mem_Alloc(size, 1, "Http_PostData"); - Mem_Copy(req.Data, data, size); - req.Size = size; - } - - Mutex_Lock(http_pendingMutex); - { - req.TimeAdded = DateTime_CurrentUTC_MS(); - if (priority) { - HttpRequestList_Prepend(&http_pending, &req); - } else { - HttpRequestList_Append(&http_pending, &req); - } - } - Mutex_Unlock(http_pendingMutex); - Waitable_Signal(http_waitable); -} +const static String skinServer = String_FromConst("http://static.classicube.net/skins/"); void Http_AsyncGetSkin(const String* id, const String* skinName) { String url; char urlBuffer[STRING_SIZE]; @@ -567,7 +753,7 @@ void Http_AsyncGetSkin(const String* id, const String* skinName) { if (Utils_IsUrlPrefix(skinName, 0)) { String_Copy(&url, skinName); } else { - String_AppendString(&url, &http_skinServer); + String_AppendString(&url, &skinServer); String_AppendColorless(&url, skinName); String_AppendConst(&url, ".png"); } @@ -577,15 +763,12 @@ void Http_AsyncGetSkin(const String* id, const String* skinName) { void Http_AsyncGetData(const String* url, bool priority, const String* id) { Http_Add(url, priority, id, REQUEST_TYPE_GET, NULL, NULL, NULL, 0); } - void Http_AsyncGetHeaders(const String* url, bool priority, const String* id) { Http_Add(url, priority, id, REQUEST_TYPE_HEAD, NULL, NULL, NULL, 0); } - void Http_AsyncPostData(const String* url, bool priority, const String* id, const void* data, uint32_t size) { Http_Add(url, priority, id, REQUEST_TYPE_POST, NULL, NULL, data, size); } - void Http_AsyncGetDataEx(const String* url, bool priority, const String* id, TimeMS* lastModified, const String* etag) { Http_Add(url, priority, id, REQUEST_TYPE_GET, lastModified, etag, NULL, 0); } @@ -594,168 +777,50 @@ void Http_PurgeOldEntriesTask(struct ScheduledTask* task) { struct HttpRequest* item; int i; - Mutex_Lock(http_processedMutex); + Mutex_Lock(processedMutex); { TimeMS now = DateTime_CurrentUTC_MS(); - for (i = http_processed.Count - 1; i >= 0; i--) { - item = &http_processed.Requests[i]; + for (i = processedReqs.Count - 1; i >= 0; i--) { + item = &processedReqs.Entries[i]; if (item->TimeDownloaded + (10 * 1000) >= now) continue; HttpRequest_Free(item); - HttpRequestList_RemoveAt(&http_processed, i); + RequestList_RemoveAt(&processedReqs, i); } } - Mutex_Unlock(http_processedMutex); -} - -static int HttpRequestList_Find(const String* id, struct HttpRequest* item) { - String reqID; - int i; - - for (i = 0; i < http_processed.Count; i++) { - reqID = String_FromRawArray(http_processed.Requests[i].ID); - if (!String_Equals(id, &reqID)) continue; - - *item = http_processed.Requests[i]; - return i; - } - return -1; + Mutex_Unlock(processedMutex); } bool Http_GetResult(const String* id, struct HttpRequest* item) { int i; - Mutex_Lock(http_processedMutex); + Mutex_Lock(processedMutex); { - i = HttpRequestList_Find(id, item); - if (i >= 0) HttpRequestList_RemoveAt(&http_processed, i); + i = RequestList_Find(&processedReqs, id, item); + if (i >= 0) RequestList_RemoveAt(&processedReqs, i); } - Mutex_Unlock(http_processedMutex); + Mutex_Unlock(processedMutex); return i >= 0; } bool Http_GetCurrent(struct HttpRequest* request, int* progress) { - Mutex_Lock(http_curRequestMutex); + Mutex_Lock(curRequestMutex); { *request = http_curRequest; *progress = http_curProgress; } - Mutex_Unlock(http_curRequestMutex); + Mutex_Unlock(curRequestMutex); return request->ID[0]; } void Http_ClearPending(void) { - Mutex_Lock(http_pendingMutex); + Mutex_Lock(pendingMutex); { - HttpRequestList_Free(&http_pending); + RequestList_Free(&pendingReqs); } - Mutex_Unlock(http_pendingMutex); - Waitable_Signal(http_waitable); + Mutex_Unlock(pendingMutex); + Waitable_Signal(workerWaitable); } -/* Attempts to download the server's response headers and data to the given req */ -static void Http_ProcessRequest(struct HttpRequest* req) { - String url = String_FromRawArray(req->URL); - uint64_t beg, end; - uint32_t size, elapsed; - uintptr_t addr; - - Platform_Log2("Downloading from %s (type %b)", &url, &req->RequestType); - beg = Stopwatch_Measure(); - req->Result = Http_SysDo(req, &http_curProgress); - end = Stopwatch_Measure(); - - elapsed = (int)Stopwatch_ElapsedMicroseconds(beg, end) / 1000; - Platform_Log3("HTTP: return code %i (http %i), in %i ms", - &req->Result, &req->StatusCode, &elapsed); - - if (req->Data) { - size = req->Size; - addr = (uintptr_t)req->Data; - Platform_Log2("HTTP returned data: %i bytes at %x", &size, &addr); - } - req->Success = !req->Result && req->StatusCode == 200 && req->Data && req->Size; -} - -/* Adds given req to list of processed/completed requests */ -static void Http_CompleteRequest(struct HttpRequest* req) { - struct HttpRequest older; - String id = String_FromRawArray(req->ID); - int index; - req->TimeDownloaded = DateTime_CurrentUTC_MS(); - - index = HttpRequestList_Find(&id, &older); - if (index >= 0) { - /* very rare case - priority item was inserted, then inserted again (so put before first item), */ - /* and both items got downloaded before an external function removed them from the queue */ - if (older.TimeAdded > req->TimeAdded) { - HttpRequest_Free(req); - } else { - /* normal case, replace older req */ - HttpRequest_Free(&older); - http_processed.Requests[index] = *req; - } - } else { - HttpRequestList_Append(&http_processed, req); - } -} - -static void Http_WorkerLoop(void) { - struct HttpRequest request; - bool hasRequest, stop; - - for (;;) { - hasRequest = false; - - Mutex_Lock(http_pendingMutex); - { - stop = http_terminate; - if (!stop && http_pending.Count) { - request = http_pending.Requests[0]; - hasRequest = true; - HttpRequestList_RemoveAt(&http_pending, 0); - } - } - Mutex_Unlock(http_pendingMutex); - - if (stop) return; - /* Block until another thread submits a req to do */ - if (!hasRequest) { - Platform_LogConst("Going back to sleep..."); - Waitable_Wait(http_waitable); - continue; - } - - Platform_LogConst("Got something to do!"); - Mutex_Lock(http_curRequestMutex); - { - http_curRequest = request; - http_curProgress = ASYNC_PROGRESS_MAKING_REQUEST; - } - Mutex_Unlock(http_curRequestMutex); - - Platform_LogConst("Doing it"); - /* performing req doesn't need thread safety */ - Http_ProcessRequest(&request); - - Mutex_Lock(http_processedMutex); - { - Http_CompleteRequest(&request); - } - Mutex_Unlock(http_processedMutex); - - Mutex_Lock(http_curRequestMutex); - { - http_curRequest.ID[0] = '\0'; - http_curProgress = ASYNC_PROGRESS_NOTHING; - } - Mutex_Unlock(http_curRequestMutex); - } -} - - -/*########################################################################################################################* -*-------------------------------------------------------Http misc---------------------------------------------------------* -*#########################################################################################################################*/ static bool Http_UrlDirect(uint8_t c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '-' || c == '_' || c == '.' || c == '~'; @@ -793,30 +858,34 @@ void Http_UrlEncodeUtf8(String* dst, const String* src) { *#########################################################################################################################*/ static void Http_Init(void) { ScheduledTask_Add(30, Http_PurgeOldEntriesTask); - HttpRequestList_Init(&http_pending); - HttpRequestList_Init(&http_processed); + RequestList_Init(&pendingReqs); + RequestList_Init(&processedReqs); Http_SysInit(); - http_waitable = Waitable_Create(); - http_pendingMutex = Mutex_Create(); - http_processedMutex = Mutex_Create(); - http_curRequestMutex = Mutex_Create(); - http_workerThread = Thread_Start(Http_WorkerLoop, false); + workerWaitable = Waitable_Create(); + pendingMutex = Mutex_Create(); + processedMutex = Mutex_Create(); + curRequestMutex = Mutex_Create(); +#ifndef CC_BUILD_WEB + workerThread = Thread_Start(Http_WorkerLoop, false); +#endif } static void Http_Free(void) { http_terminate = true; Http_ClearPending(); - Thread_Join(http_workerThread); +#ifndef CC_BUILD_WEB + Thread_Join(workerThread); +#endif - HttpRequestList_Free(&http_pending); - HttpRequestList_Free(&http_processed); + RequestList_Free(&pendingReqs); + RequestList_Free(&processedReqs); Http_SysFree(); - Waitable_Free(http_waitable); - Mutex_Free(http_pendingMutex); - Mutex_Free(http_processedMutex); - Mutex_Free(http_curRequestMutex); + Waitable_Free(workerWaitable); + Mutex_Free(pendingMutex); + Mutex_Free(processedMutex); + Mutex_Free(curRequestMutex); } struct IGameComponent Http_Component = { diff --git a/src/Http.h b/src/Http.h index 60a1b5219..473a58a7d 100644 --- a/src/Http.h +++ b/src/Http.h @@ -33,7 +33,7 @@ struct HttpRequest { TimeMS LastModified; /* Time item cached at (if at all) */ char Etag[STRING_SIZE]; /* ETag of cached item (if any) */ - uint8_t RequestType; /* Whether to fetch contents or just headers. */ + uint8_t RequestType; /* See the various REQUEST_TYPE_ */ bool Success; /* Whether Result is 0, status is 200, and data is not NULL */ }; @@ -49,7 +49,7 @@ void Http_AsyncGetData(const String* url, bool priority, const String* id); /* Asynchronously performs a http HEAD request. (e.g. to get Content-Length header) */ void Http_AsyncGetHeaders(const String* url, bool priority, const String* id); /* Asynchronously performs a http POST request. (e.g. to submit data) */ -/* NOTE: You don't have to persist data, Http makes a copy of it. */ +/* NOTE: You don't have to persist data, a copy is made of it. */ void Http_AsyncPostData(const String* url, bool priority, const String* id, const void* data, uint32_t size); /* Asynchronously performs a http GET request. (e.g. to download data) */ /* Also sets the If-Modified-Since and If-None-Match headers. (if not NULL) */ diff --git a/src/Program.c b/src/Program.c index bb9e0461e..74fb3f853 100644 --- a/src/Program.c +++ b/src/Program.c @@ -65,6 +65,7 @@ static void Program_RunGame(void) { Game_Run(width, height, &title); } +/* Attempts to set current/working directory to the directory exe file is in */ static void Program_SetCurrentDirectory(void) { String path; char pathBuffer[FILENAME_SIZE]; int i; @@ -82,6 +83,7 @@ static void Program_SetCurrentDirectory(void) { if (res) { Logger_Warn(res, "setting current directory"); return; } } +/* Terminates the program due to an invalid command line argument */ CC_NOINLINE static void Exit_InvalidArg(const char* name, const String* arg) { String tmp; char tmpBuffer[256]; String_InitArray(tmp, tmpBuffer); @@ -92,6 +94,7 @@ CC_NOINLINE static void Exit_InvalidArg(const char* name, const String* arg) { Platform_Exit(1); } +/* Terminates the program due to insufficient command line arguments */ CC_NOINLINE static void Exit_MissingArgs(int argsCount, const String* args) { String tmp; char tmpBuffer[256]; int i;