HTTPClient.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. /*
  2. * @file HTTPClient.h
  3. * @brief libcurl wrapper for HTTP requests
  4. *
  5. * @author Mohamed Amine Mzoughi <mohamed-amine.mzoughi@laposte.net>
  6. * @date 2017-01-04
  7. */
  8. #pragma once
  9. #ifndef INCLUDE_HTTPCLIENT_H_
  10. #define INCLUDE_HTTPCLIENT_H_
  11. #include "IHttpFunc.h"
  12. #define CLIENT_USERAGENT "httpclientcpp-agent/1.0"
  13. #include <algorithm>
  14. #include <cstddef> // std::size_t
  15. #include <cstdio> // snprintf
  16. #include <cstdlib>
  17. #include <cstring> // strerror, strlen, memcpy, strcpy
  18. #include <ctime>
  19. #include <curl/curl.h>
  20. #include <fstream>
  21. #include <functional>
  22. #include <iostream>
  23. #include <memory> // std::unique_ptr
  24. #include <stdio.h>
  25. #include <stdlib.h>
  26. #include <sstream>
  27. #include <stdarg.h> // va_start, etc.
  28. #include <string>
  29. #include <sys/stat.h>
  30. #include <sys/types.h>
  31. #include <unordered_map>
  32. #include <vector>
  33. #if defined(_MSC_VER)
  34. #include <WinSock2.h>
  35. #include <Windows.h>
  36. #endif //_MSC_VER
  37. //typedef std::function<void(const std::string&)> LogFnCallback;
  38. class CHTTPClient : public IHttpFunc
  39. {
  40. public:
  41. // Public definitions
  42. typedef int (*ProgressFnCallback)(void*, double, double, double, double);
  43. typedef std::unordered_map<std::string, std::string> HeadersMap;
  44. typedef std::vector<char> ByteBuffer;
  45. /* This struct represents the form information to send on POST Form requests */
  46. struct PostFormInfo
  47. {
  48. PostFormInfo();
  49. ~PostFormInfo();
  50. /* Fill in the file upload field */
  51. void AddFormFile(const std::string& fieldName, const std::string& fieldValue);
  52. /* Fill in the filename or the submit field */
  53. void AddFormContent(const std::string& fieldName, const std::string& fieldValue);
  54. struct curl_httppost* m_pFormPost;
  55. struct curl_httppost* m_pLastFormptr;
  56. };
  57. // Progress Function Data Object - parameter void* of ProgressFnCallback references it
  58. struct ProgressFnStruct
  59. {
  60. ProgressFnStruct() : dLastRunTime(0), pCurl(nullptr), pOwner(nullptr) {}
  61. double dLastRunTime;
  62. CURL* pCurl;
  63. /* owner of the CHTTPClient object. can be used in the body of the progress
  64. * function to send signals to the owner (e.g. to update a GUI's progress bar)
  65. */
  66. void* pOwner;
  67. };
  68. // HTTP response data
  69. struct HttpResponse
  70. {
  71. HttpResponse() : iCode(0) {}
  72. int iCode; // HTTP response code
  73. HeadersMap mapHeaders; // HTTP response headers fields
  74. std::string strBody; // HTTP response body
  75. };
  76. /* Please provide your logger thread-safe routine, otherwise, you can turn off
  77. * error log messages printing by not using the flag ALL_FLAGS or ENABLE_LOG */
  78. explicit CHTTPClient(LogFnCallback oLogger);
  79. virtual ~CHTTPClient();
  80. void Destory(){delete this;} //销毁改对象
  81. // Setters - Getters (for unit tests)
  82. /*inline*/ void SetProgressFnCallback(void* pOwner, const ProgressFnCallback& fnCallback);
  83. /*inline*/ void SetProxy(const std::string& strProxy);
  84. inline void SetTimeout(const int& iTimeout) { m_iCurlTimeout = iTimeout; }
  85. inline void SetNoSignal(const bool& bNoSignal) { m_bNoSignal = bNoSignal; }
  86. inline void SetHTTPS(const bool& bEnableHTTPS) { m_bHTTPS = bEnableHTTPS; }
  87. inline ProgressFnCallback GetProgressFnCallback() const
  88. {
  89. return m_fnProgressCallback;
  90. }
  91. inline void* GetProgressFnCallbackOwner() const { return m_ProgressStruct.pOwner; }
  92. inline const std::string& GetProxy() const { return m_strProxy; }
  93. inline const int GetTimeout() const { return m_iCurlTimeout; }
  94. inline const bool GetNoSignal() const { return m_bNoSignal; }
  95. inline const std::string& GetURL() const { return m_strURL; }
  96. inline const unsigned char GetSettingsFlags() const { return m_eSettingsFlags; }
  97. inline const bool GetHTTPS() const { return m_bHTTPS; }
  98. // Session
  99. const bool InitSession(const bool& bHTTPS = false, const SettingsFlag& SettingsFlags = ALL_FLAGS);
  100. virtual const bool CleanupSession();
  101. static int GetCurlSessionCount() { return s_iCurlSession; }
  102. const CURL* GetCurlPointer() const { return m_pCurlSession; }
  103. // HTTP requests
  104. const bool GetText(const std::string& strURL,
  105. std::string& strText,
  106. long& lHTTPStatusCode);
  107. const bool DownloadFile(const std::string& strLocalFile,
  108. const std::string& strURL,
  109. long& lHTTPStatusCode);
  110. const bool UploadForm(const std::string& strURL,
  111. const PostFormInfo& data,
  112. long& lHTTPStatusCode);
  113. inline void AddHeader(const std::string& strHeader)
  114. {
  115. m_pHeaderlist = curl_slist_append(m_pHeaderlist, strHeader.c_str());
  116. }
  117. // REST requests
  118. const bool Head(const std::string& strUrl, const HeadersMap& Headers, HttpResponse& Response);
  119. const bool Get(const std::string& strUrl, const HeadersMap& Headers, HttpResponse& Response);
  120. const bool Del(const std::string& strUrl, const HeadersMap& Headers, HttpResponse& Response);
  121. const bool Post(const std::string& strUrl, const HeadersMap& Headers,
  122. const std::string& strPostData, HttpResponse& Response);
  123. const bool Put(const std::string& strUrl, const HeadersMap& Headers,
  124. const std::string& strPutData, HttpResponse& Response);
  125. const bool Put(const std::string& strUrl, const HeadersMap& Headers,
  126. const ByteBuffer& Data, HttpResponse& Response);
  127. //add by cfq override IHttpFunc
  128. virtual const bool Get(CHTTPReq &req, CHTTPRet &ret);
  129. virtual const bool Post(CHTTPReq &req, CHTTPRet &ret);
  130. virtual const bool UploadFileBlock(CHTTPUploadReq &req, CHTTPUploadRet &ret);
  131. virtual const bool DownloadFileBlock(const char* url, const char* jsonReqStr, fileContentArray& content,
  132. long &httpCode, unordered_map<string, string>& responseHeaders, long timeout);
  133. // SSL certs
  134. static const std::string& GetCertificateFile() { return s_strCertificationAuthorityFile; }
  135. static void SetCertificateFile(const std::string& strPath) { s_strCertificationAuthorityFile = strPath; }
  136. void SetSSLCertFile(const std::string& strPath) { m_strSSLCertFile = strPath; }
  137. const std::string& GetSSLCertFile() const { return m_strSSLCertFile; }
  138. void SetSSLKeyFile(const std::string& strPath) { m_strSSLKeyFile = strPath; }
  139. const std::string& GetSSLKeyFile() const { return m_strSSLKeyFile; }
  140. void SetSSLKeyPassword(const std::string& strPwd) { m_strSSLKeyPwd = strPwd; }
  141. const std::string& GetSSLKeyPwd() const { return m_strSSLKeyPwd; }
  142. #ifdef DEBUG_CURL
  143. static void SetCurlTraceLogDirectory(const std::string& strPath);
  144. #endif
  145. protected:
  146. // payload to upload on POST requests.
  147. struct UploadObject
  148. {
  149. UploadObject() : pszData(nullptr), usLength(0) {}
  150. const char* pszData; // data to upload
  151. size_t usLength; // length of the data to upload
  152. };
  153. /* common operations are performed here */
  154. inline const CURLcode Perform();
  155. inline void CheckURL(const std::string& strURL);
  156. inline const bool InitRestRequest(const std::string& strUrl, const HeadersMap& Headers,
  157. HttpResponse& Response);
  158. inline const bool PostRestRequest(const CURLcode ePerformCode, HttpResponse& Response);
  159. // Curl callbacks
  160. static size_t WriteInStringCallback(void* ptr, size_t size, size_t nmemb, void* data);
  161. static size_t WriteToFileCallback(void* ptr, size_t size, size_t nmemb, void* data);
  162. static size_t ReadFromFileCallback(void* ptr, size_t size, size_t nmemb, void* stream);
  163. static size_t ThrowAwayCallback(void* ptr, size_t size, size_t nmemb, void* data);
  164. static size_t RestWriteCallback(void* ptr, size_t size, size_t nmemb, void* userdata);
  165. static size_t RestHeaderCallback(void* ptr, size_t size, size_t nmemb, void* userdata);
  166. static size_t RestReadCallback(void* ptr, size_t size, size_t nmemb, void* userdata);
  167. //add by cfq
  168. static size_t HeaderCallback(void* ptr, size_t size, size_t nmemb, void* userdata);
  169. static size_t WriteInByteCallback(void* ptr, size_t size, size_t nmemb, void* userdata);
  170. // String Helpers
  171. static std::string StringFormat(const std::string strFormat, ...);
  172. static inline void TrimSpaces(std::string& str);
  173. // Curl Debug informations
  174. #ifdef DEBUG_CURL
  175. static int DebugCallback(CURL* curl, curl_infotype curl_info_type, char* strace, size_t nSize, void* pFile);
  176. inline void StartCurlDebug() const;
  177. inline void EndCurlDebug() const;
  178. #endif
  179. std::string m_strURL;
  180. std::string m_strProxy;
  181. bool m_bNoSignal;
  182. bool m_bHTTPS;
  183. SettingsFlag m_eSettingsFlags;
  184. struct curl_slist* m_pHeaderlist;
  185. // SSL
  186. static std::string s_strCertificationAuthorityFile;
  187. std::string m_strSSLCertFile;
  188. std::string m_strSSLKeyFile;
  189. std::string m_strSSLKeyPwd;
  190. static HANDLE s_mtxCurlSession; // mutex used to manage API global operations
  191. volatile static int s_iCurlSession; // Count of the actual sessions
  192. CURL* m_pCurlSession;
  193. int m_iCurlTimeout;
  194. // Progress function
  195. ProgressFnCallback m_fnProgressCallback;
  196. ProgressFnStruct m_ProgressStruct;
  197. bool m_bProgressCallbackSet;
  198. // Log printer callback
  199. LogFnCallback m_oLog;
  200. private:
  201. #ifdef DEBUG_CURL
  202. static std::string s_strCurlTraceLogDirectory;
  203. mutable std::ofstream m_ofFileCurlTrace;
  204. #endif
  205. static HANDLE m_hLock;
  206. static BOOL Lock(DWORD dwTime);
  207. static void Unlock();
  208. // copy constructor and assignment operator are disabled
  209. CHTTPClient(const CHTTPClient& Copy);
  210. CHTTPClient& operator=(const CHTTPClient& Copy);
  211. };
  212. ///*TODO(80374374@3/9/2023): 抽出作为公共函数 */
  213. #if defined(_MSC_VER)
  214. char* ConvertUtf8ToGBK(const char* strUtf8)
  215. {
  216. int len = MultiByteToWideChar(CP_UTF8, 0, strUtf8, -1, NULL, 0);
  217. WCHAR* wszGBK = new WCHAR[len + 1];
  218. memset(wszGBK, 0, len * 2 + 2);
  219. MultiByteToWideChar(CP_UTF8, 0, strUtf8, -1, wszGBK, len);
  220. len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);
  221. char* szGBK = new char[len + 1];
  222. memset(szGBK, 0, len + 1);
  223. WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);
  224. delete[] wszGBK;
  225. return szGBK;
  226. }
  227. void ConvertUtf8ToGBK(std::string& str)
  228. {
  229. char* dst = ConvertUtf8ToGBK(str.c_str());
  230. str = dst;
  231. delete[] dst;
  232. }
  233. char* ConvertGBKToUtf8(const char* gbk, int* n)
  234. {
  235. int len = MultiByteToWideChar(CP_ACP, 0, gbk, -1, NULL, 0);
  236. WCHAR* wszGBK = new WCHAR[len + 1];
  237. memset(wszGBK, 0, len * 2 + 2);
  238. MultiByteToWideChar(CP_ACP, 0, gbk, -1, wszGBK, len);
  239. len = WideCharToMultiByte(CP_UTF8, 0, wszGBK, -1, NULL, 0, NULL, NULL);
  240. char* szUtf8 = new char[len + 1];
  241. memset(szUtf8, 0, len + 1);
  242. WideCharToMultiByte(CP_UTF8, 0, wszGBK, -1, szUtf8, len, NULL, NULL);
  243. delete[] wszGBK;
  244. *n = len - 1;
  245. return szUtf8;
  246. }
  247. void ConvertGBKToUtf8(std::string& str)
  248. {
  249. int len = 0;
  250. char* dst = ConvertGBKToUtf8(str.c_str(), &len);
  251. str = dst;
  252. delete[] dst;
  253. }
  254. #endif //_MSC_VER
  255. RVCCOMM_API IHttpFunc* create_http(LogFnCallback oLogger)
  256. {
  257. return new CHTTPClient(oLogger);
  258. }
  259. // Logs messages
  260. #define LOG_ERROR_EMPTY_HOST_MSG "[HTTPClient][Error] Empty hostname."
  261. #define LOG_WARNING_OBJECT_NOT_CLEANED "[HTTPClient][Warning] Object was freed before calling " \
  262. "CHTTPClient::CleanupSession(). The API session was cleaned though."
  263. #define LOG_ERROR_CURL_ALREADY_INIT_MSG "[HTTPClient][Error] Curl session is already initialized ! " \
  264. "Use CleanupSession() to clean the present one."
  265. #define LOG_ERROR_CURL_NOT_INIT_MSG "[HTTPClient][Error] Curl session is not initialized ! Use InitSession() before."
  266. #define LOG_ERROR_CURL_REQ_FAILURE_FORMAT "[HTTPClient][Error] Unable to perform request from '%s' " \
  267. "(Error = %d | %s) (HTTP_Status = %ld)"
  268. #define LOG_ERROR_CURL_REST_FAILURE_FORMAT "[HTTPClient][Error] Unable to perform a REST request from '%s' " \
  269. "(Error = %d | %s)"
  270. #define LOG_ERROR_CURL_DOWNLOAD_FAILURE_FORMAT "[HTTPClient][Error] Unable to perform a request - '%s' from '%s' " \
  271. "(Error = %d | %s) (HTTP_Status = %ld)"
  272. #define LOG_ERROR_DOWNLOAD_FILE_FORMAT "[HTTPClient][Error] Unable to open local file %s"
  273. #define LOG_ERROR_LOAD_SETTINGS_FAILTURE_MSG "[HTTPClient][Error] unable to open/read Config_CenterSetting"
  274. #define LOG_ERROR_PATH_NULL_MSG "[HTTPClient][Error] url is NULL"
  275. #define LOG_ERROR_GET_JSON_FAILTURE_MSG "[HTTPClient][Error] ToJson method have exception"
  276. #define LOG_ERROR_PARSE_JSON_FAILTURE_MSG "[HTTPClient][Error] ParseJson method have exception"
  277. #define LOG_ERROR_RESPONSE_FORMAT_FAILTURE_MSG "[HTTPClient][Error] Response format is wrong(%s)"
  278. #endif