1#ifndef ENGINE_SHARED_HTTP_CURL_H
2#define ENGINE_SHARED_HTTP_CURL_H
3
4#include <base/detect.h>
5#if !defined(CONF_PLATFORM_EMSCRIPTEN)
6
7#include <engine/http.h>
8
9#include <curl/curl.h>
10
11#include <atomic>
12#include <chrono>
13#include <condition_variable>
14#include <deque>
15#include <mutex>
16#include <optional>
17#include <unordered_map>
18
19class CHttpRequestCurl : public IHttpRequest
20{
21 friend class CHttpCurl;
22
23public:
24 CHttpRequestCurl(const char *pUrl);
25 ~CHttpRequestCurl() override;
26
27 void Header(const char *pNameColonValue) override;
28
29private:
30 curl_slist *m_pRequestHeaders = nullptr;
31
32 char m_aErr[CURL_ERROR_SIZE];
33
34 bool m_ResponseHeadersEnded = false;
35
36 bool ConfigureHandle(CURL *pHandle);
37
38 // Abort the request if `OnHeader()` returns something other than
39 // `DataSize`. `pHeader` is NOT null-terminated.
40 size_t OnHeader(char *pHeader, size_t HeaderSize);
41
42 // `pHandle` can be nullptr if no handle was ever created for this request.
43 void OnCompletionInternal(CURL *pHandle, CURLcode Code);
44
45 static int ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr);
46 static size_t HeaderCallback(char *pData, size_t Size, size_t Number, void *pUser);
47 static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser);
48};
49
50class CHttpCurl : public IEngineHttp
51{
52public:
53 // Lifecycle
54 bool Init(std::chrono::milliseconds ShutdownDelay) override;
55 void Shutdown() override;
56 ~CHttpCurl() override;
57
58 // User
59 void Run(std::shared_ptr<IHttpRequest> pRequest) override;
60 bool HasIpresolveBug() const override;
61
62private:
63 enum EState
64 {
65 UNINITIALIZED,
66 RUNNING,
67 ERROR,
68 };
69
70 void *m_pThread = nullptr;
71
72 std::mutex m_Lock;
73 std::condition_variable m_ConditionVariableInit;
74 std::atomic<EState> m_State = UNINITIALIZED;
75 std::deque<std::shared_ptr<CHttpRequestCurl>> m_PendingRequests;
76 std::unordered_map<CURL *, std::shared_ptr<CHttpRequestCurl>> m_RunningRequests;
77 std::chrono::milliseconds m_ShutdownDelay{};
78 std::optional<std::chrono::time_point<std::chrono::steady_clock>> m_ShutdownTime;
79 std::atomic<bool> m_Shutdown = false;
80
81 // Only to be used with curl_multi_wakeup
82 CURLM *m_pMultiH = nullptr;
83 int m_NextTimeout;
84
85 static void ThreadMain(void *pUser);
86 void RunLoop();
87};
88
89#endif // !CONF_PLATFORM_EMSCRIPTEN
90#endif // ENGINE_SHARED_HTTP_CURL_H
91