#ifndef ENGINE_SHARED_HTTP_H #define ENGINE_SHARED_HTTP_H #include #include typedef struct _json_value json_value; class IStorage; enum { HTTP_ERROR = -1, HTTP_QUEUED, HTTP_RUNNING, HTTP_DONE, HTTP_ABORTED, }; enum class HTTPLOG { NONE, FAILURE, ALL, }; enum class IPRESOLVE { WHATEVER, V4, V6, }; struct CTimeout { long ConnectTimeoutMs; long LowSpeedLimit; long LowSpeedTime; }; class CHttpRequest : public IJob { enum class REQUEST { GET = 0, HEAD, POST_JSON, }; char m_aUrl[256] = {0}; void *m_pHeaders = nullptr; unsigned char *m_pBody = nullptr; size_t m_BodyLength = 0; CTimeout m_Timeout = CTimeout{0, 0, 0}; REQUEST m_Type = REQUEST::GET; bool m_WriteToFile = false; // If `m_WriteToFile` is false. size_t m_BufferSize = 0; size_t m_BufferLength = 0; unsigned char *m_pBuffer = nullptr; // If `m_WriteToFile` is true. IOHANDLE m_File = nullptr; char m_aDestAbsolute[IO_MAX_PATH_LENGTH] = {0}; char m_aDest[IO_MAX_PATH_LENGTH] = {0}; std::atomic m_Size{0.0}; std::atomic m_Current{0.0}; std::atomic m_Progress{0}; HTTPLOG m_LogProgress = HTTPLOG::ALL; IPRESOLVE m_IpResolve = IPRESOLVE::WHATEVER; std::atomic m_State{HTTP_QUEUED}; std::atomic m_Abort{false}; void Run(); // Abort the request with an error if `BeforeInit()` returns false. bool BeforeInit(); int RunImpl(void *pUser); // Abort the request if `OnData()` returns something other than // `DataSize`. size_t OnData(char *pData, size_t DataSize); static int ProgressCallback(void *pUser, double DlTotal, double DlCurr, double UlTotal, double UlCurr); static size_t WriteCallback(char *pData, size_t Size, size_t Number, void *pUser); protected: virtual void OnProgress() {} virtual int OnCompletion(int State); public: CHttpRequest(const char *pUrl); ~CHttpRequest(); void Timeout(CTimeout Timeout) { m_Timeout = Timeout; } void LogProgress(HTTPLOG LogProgress) { m_LogProgress = LogProgress; } void IpResolve(IPRESOLVE IpResolve) { m_IpResolve = IpResolve; } void WriteToFile(IStorage *pStorage, const char *pDest, int StorageType); void Head() { m_Type = REQUEST::HEAD; } void PostJson(const char *pJson) { m_Type = REQUEST::POST_JSON; m_BodyLength = str_length(pJson); m_pBody = (unsigned char *)malloc(m_BodyLength); mem_copy(m_pBody, pJson, m_BodyLength); } const char *Dest() { if(m_WriteToFile) { return m_aDest; } else { return nullptr; } } double Current() const { return m_Current.load(std::memory_order_relaxed); } double Size() const { return m_Size.load(std::memory_order_relaxed); } int Progress() const { return m_Progress.load(std::memory_order_relaxed); } int State() const { return m_State; } void Abort() { m_Abort = true; } void Result(unsigned char **ppResult, size_t *pResultLength) const; json_value *ResultJson() const; }; inline std::unique_ptr HttpHead(const char *pUrl) { std::unique_ptr pResult = std::unique_ptr(new CHttpRequest(pUrl)); pResult->Head(); return pResult; } inline std::unique_ptr HttpGet(const char *pUrl) { return std::unique_ptr(new CHttpRequest(pUrl)); } inline std::unique_ptr HttpGetFile(const char *pUrl, IStorage *pStorage, const char *pOutputFile, int StorageType) { std::unique_ptr pResult = HttpGet(pUrl); pResult->WriteToFile(pStorage, pOutputFile, StorageType); pResult->Timeout(CTimeout{4000, 500, 5}); return pResult; } inline std::unique_ptr HttpPostJson(const char *pUrl, const char *pJson) { std::unique_ptr pResult = std::unique_ptr(new CHttpRequest(pUrl)); pResult->PostJson(pJson); return pResult; } bool HttpInit(IStorage *pStorage); void EscapeUrl(char *pBuf, int Size, const char *pStr); #endif // ENGINE_SHARED_HTTP_H