/** * @file HTTPClient.cpp * @brief implementation of the HTTP client class * @author Mohamed Amine Mzoughi */ #include "StdAfx.h" #include "HTTPClient.h" #include "json/json.h" #if defined(_MSC_VER) #else #include "toolkit.h" #include #endif //_MSC_VER #include "charset.h" #define DEBUG_LOG(msg) \ do { \ if (m_eSettingsFlags & ENABLE_LOG) \ m_oLog(msg); \ } while (false) ///*TODO(80374374@3/9/2023): */ GetTokenCallBack g_tokenCall = NULL; #ifndef _WIN32 static toolkit_once_t once4curl = TOOLKIT_ONCE_INIT; class CLibCurlLifeManage { public: CLibCurlLifeManage() :bInited(false) {} void Init() { if (!bInited) { curl_global_init(CURL_GLOBAL_ALL); bInited = true; } } ~CLibCurlLifeManage() { if (bInited) { curl_global_cleanup(); bInited = false; } } private: bool bInited; }; static CLibCurlLifeManage gLibcurlMnt; static void init_libcurl_once(void) { gLibcurlMnt.Init(); } static std::string GBK2UTF8(const std::string str) { std::string result(""); if (str.empty()) return result; if (toolkit_detect_utf8_str(str.c_str())) { result = str; } else { auto ret = toolkit_gbk2utf8((char*)str.c_str(), str.length(), NULL, 0); if (ret <= 0) return result; char* arr = new char[ret]; ret = toolkit_gbk2utf8((char*)str.c_str(), str.length(), arr, ret); if (ret > 0) { result = arr; } delete[] arr; } return result; } static std::string UTF8ToGBK(const std::string str) { std::string result(""); if (str.empty()) return result; if (!toolkit_detect_utf8_str(str.c_str())) { result = str; } else { auto ret = toolkit_utf82gbk((char*)str.c_str(), str.length(), NULL, 0); if (ret <= 0) return result; char* arr = new char[ret]; ret = toolkit_utf82gbk((char*)str.c_str(), str.length(), arr, ret); if (ret > 0) { result = arr; } delete[] arr; } return result; } void ConvertUtf8ToGBK(std::string& str) { const std::string result = UTF8ToGBK(str); str = result; } void ConvertGBKToUtf8(std::string& str) { const std::string result = GBK2UTF8(str); str = result; } #else void ConvertUtf8ToGBK(std::string& str) { char* dst = ConvertUtf8ToGBK(str.c_str()); str = dst; if(dst != NULL) free(dst); } void ConvertGBKToUtf8(std::string& str) { int len = 0; char* dst = ConvertGBKToUtf8(str.c_str(), &len); str = dst; if (dst != NULL) free(dst); } #endif // Static members initialization volatile int CHTTPClient::s_iCurlSession = 0; std::string CHTTPClient::s_strCertificationAuthorityFile; HANDLE CHTTPClient::s_mtxCurlSession; HANDLE CHTTPClient::m_hLock; #ifdef DEBUG_CURL std::string CHTTPClient::s_strCurlTraceLogDirectory; #endif RVCCOMM_API void SetTokenCallBack(GetTokenCallBack t_callBack) { g_tokenCall = t_callBack; } BOOL CHTTPClient::Lock(DWORD dwTime) { // 如果还没有创建锁就先创建一个 if (!m_hLock) { m_hLock = ::CreateMutex(NULL, FALSE, NULL); if (!m_hLock) return FALSE; } // 哪个线程最先调用等待函数就最先占用这个互斥量 DWORD dwRet = ::WaitForSingleObject(m_hLock, dwTime); return (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_ABANDONED); } void CHTTPClient::Unlock() { if (m_hLock) ::ReleaseMutex(m_hLock); } /** * @brief constructor of the HTTP client object * * @param Logger - a callabck to a logger function void(const std::string&) * */ CHTTPClient::CHTTPClient(LogFnCallback Logger) : m_oLog(Logger), m_iCurlTimeout(30), m_bHTTPS(false), m_bNoSignal(false), m_bProgressCallbackSet(false), m_eSettingsFlags(ENABLE_LOG), m_pCurlSession(nullptr), m_pHeaderlist(nullptr) { #if defined(_MSC_VER) Lock(INFINITE); if (s_iCurlSession++ == 0) { /* In windows, this will init the winsock stuff */ curl_global_init(CURL_GLOBAL_ALL); } Unlock(); #else toolkit_once(&once4curl, init_libcurl_once); #endif //RVC_OS_WIN InitSession(false, ENABLE_LOG); } /** * @brief destructor of the HTTP client object * */ CHTTPClient::~CHTTPClient() { if (m_pCurlSession != nullptr) { CleanupSession(); } #if defined(RVC_OS_WIN) Lock(INFINITE); if (--s_iCurlSession <= 0) { curl_global_cleanup(); } Unlock(); #endif //RVC_OS_WIN } /** * @brief Starts a new HTTP session, initializes the cURL API session * * If a new session was already started, the method has no effect. * * @param [in] bHTTPS Enable/Disable HTTPS (disabled by default) * @param [in] eSettingsFlags optional use | operator to choose multiple options * * @retval true Successfully initialized the session. * @retval false The session is already initialized * Use CleanupSession() before initializing a new one or the Curl API is not initialized. * * Example Usage: * @code * m_pHTTPClient->InitSession(); * @endcode */ const bool CHTTPClient::InitSession(const bool& bHTTPS /* = false */, const SettingsFlag& eSettingsFlags /* = ALL_FLAGS */) { if (m_pCurlSession) { if (eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_ALREADY_INIT_MSG); return false; } m_pCurlSession = curl_easy_init(); curl_easy_setopt(m_pCurlSession, CURLOPT_FORBID_REUSE, 1); //避免大量的TCP连接保持在CLOSE_WAIT状态 m_bHTTPS = bHTTPS; m_eSettingsFlags = eSettingsFlags; return (m_pCurlSession != nullptr); } /** * @brief Cleans the current HTTP session * * If a session was not already started, the method has no effect * * @retval true Successfully cleaned the current session. * @retval false The session is not already initialized. * * Example Usage: * @code * m_pHTTPClient->CleanupSession(); * @endcode */ const bool CHTTPClient::CleanupSession() { if (!m_pCurlSession) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); return false; } #ifdef DEBUG_CURL if (m_ofFileCurlTrace.is_open()) { m_ofFileCurlTrace.close(); } #endif curl_easy_cleanup(m_pCurlSession); m_pCurlSession = nullptr; if (m_pHeaderlist) { curl_slist_free_all(m_pHeaderlist); m_pHeaderlist = nullptr; } return true; } /** * @brief sets the progress function callback and the owner of the client * * @param [in] pOwner pointer to the object owning the client, nullptr otherwise * @param [in] fnCallback callback to progress function * */ /*inline*/ void CHTTPClient::SetProgressFnCallback(void* pOwner, const ProgressFnCallback& fnCallback) { m_ProgressStruct.pOwner = pOwner; m_fnProgressCallback = fnCallback; m_ProgressStruct.pCurl = m_pCurlSession; m_ProgressStruct.dLastRunTime = 0; m_bProgressCallbackSet = true; } /** * @brief sets the HTTP Proxy address to tunnel the operation through it * * @param [in] strProxy URI of the HTTP Proxy * */ /*inline*/ void CHTTPClient::SetProxy(const std::string& strProxy) { if (strProxy.empty()) return; std::string strUri = strProxy; std::transform(strUri.begin(), strUri.end(), strUri.begin(), ::toupper); if (strUri.compare(0, 4, "HTTP") != 0) m_strProxy = "http://" + strProxy; else m_strProxy = strProxy; }; /** * @brief checks a URI * adds the proper protocol scheme (HTTP:// or HTTPS://) * if the URI has no protocol scheme, the added protocol scheme * will depend on m_bHTTPS that can be set when initializing a session * or with the SetHTTPS(bool) * * @param [in] strURL user URI */ inline void CHTTPClient::CheckURL(const std::string& strURL) { std::string strTmp = strURL; std::transform(strTmp.begin(), strTmp.end(), strTmp.begin(), ::toupper); if (strTmp.compare(0, 7, "HTTP://") == 0) m_bHTTPS = false; else if (strTmp.compare(0, 8, "HTTPS://") == 0) m_bHTTPS = true; else { m_strURL = ((m_bHTTPS) ? "https://" : "http://") + strURL; return; } m_strURL = strURL; } /** * @brief performs the chosen HTTP request * sets up the common settings (Timeout, proxy,...) * * * @retval true Successfully performed the request. * @retval false An error occured while CURL was performing the request. */ const CURLcode CHTTPClient::Perform() { if (!m_pCurlSession) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); return CURLE_FAILED_INIT; } CURLcode res = CURLE_OK; curl_easy_setopt(m_pCurlSession, CURLOPT_URL, m_strURL.c_str()); curl_easy_setopt(m_pCurlSession, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); if (m_pHeaderlist != nullptr) curl_easy_setopt(m_pCurlSession, CURLOPT_HTTPHEADER, m_pHeaderlist); curl_easy_setopt(m_pCurlSession, CURLOPT_USERAGENT, CLIENT_USERAGENT); curl_easy_setopt(m_pCurlSession, CURLOPT_AUTOREFERER, 1L); curl_easy_setopt(m_pCurlSession, CURLOPT_FOLLOWLOCATION, 1L); if (m_iCurlTimeout > 0) { curl_easy_setopt(m_pCurlSession, CURLOPT_TIMEOUT, m_iCurlTimeout); // don't want to get a sig alarm on timeout curl_easy_setopt(m_pCurlSession, CURLOPT_NOSIGNAL, 1L); } if (!m_strProxy.empty()) { curl_easy_setopt(m_pCurlSession, CURLOPT_PROXY, m_strProxy.c_str()); curl_easy_setopt(m_pCurlSession, CURLOPT_HTTPPROXYTUNNEL, 1L); } if (m_bNoSignal) { curl_easy_setopt(m_pCurlSession, CURLOPT_NOSIGNAL, 1L); } if (m_bProgressCallbackSet) { curl_easy_setopt(m_pCurlSession, CURLOPT_PROGRESSFUNCTION, GetProgressFnCallback()); curl_easy_setopt(m_pCurlSession, CURLOPT_PROGRESSDATA, &m_ProgressStruct); curl_easy_setopt(m_pCurlSession, CURLOPT_NOPROGRESS, 0L); } // SSL if (m_bHTTPS) { curl_easy_setopt(m_pCurlSession, CURLOPT_USE_SSL, CURLUSESSL_ALL); } if (m_bHTTPS && !(m_eSettingsFlags & VERIFY_PEER)){ curl_easy_setopt(m_pCurlSession, CURLOPT_SSL_VERIFYPEER, 0L); //m_oLog("不验证证书!"); } else{ //m_oLog("验证证书!"); } if (m_bHTTPS && !(m_eSettingsFlags & VERIFY_HOST)){ curl_easy_setopt(m_pCurlSession, CURLOPT_SSL_VERIFYHOST, 0L); // use 2L for strict name check } if (m_bHTTPS && !s_strCertificationAuthorityFile.empty()) curl_easy_setopt(m_pCurlSession, CURLOPT_CAINFO, s_strCertificationAuthorityFile.c_str()); if (m_bHTTPS && !m_strSSLCertFile.empty()) curl_easy_setopt(m_pCurlSession, CURLOPT_SSLCERT, m_strSSLCertFile.c_str()); if (m_bHTTPS && !m_strSSLKeyFile.empty()) curl_easy_setopt(m_pCurlSession, CURLOPT_SSLKEY, m_strSSLKeyFile.c_str()); if (m_bHTTPS && !m_strSSLKeyPwd.empty()) curl_easy_setopt(m_pCurlSession, CURLOPT_KEYPASSWD, m_strSSLKeyPwd.c_str()); #ifdef DEBUG_CURL StartCurlDebug(); #endif // Perform the requested operation res = curl_easy_perform(m_pCurlSession); #ifdef DEBUG_CURL EndCurlDebug(); #endif if (m_pHeaderlist) { curl_slist_free_all(m_pHeaderlist); m_pHeaderlist = nullptr; } return res; } /** * @brief requests the content of a URI * * @param [in] strURL URI of the remote location (with the file name). * @param [out] strOutput reference to an output string. * @param [out] lHTTPStatusCode HTTP Status code of the response. * * @retval true Successfully requested the URI. * @retval false Encountered a problem. * * Example Usage: * @code * std::string strWebPage; * long lHTTPStatusCode = 0; * m_pHTTOClient->GetText("https://www.google.com", strWebPage, lHTTPStatusCode); * @endcode */ const bool CHTTPClient::GetText(const std::string& strURL, std::string& strOutput, long& lHTTPStatusCode) { if (strURL.empty()) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_EMPTY_HOST_MSG); return false; } if (!m_pCurlSession) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); return false; } // Reset is mandatory to avoid bad surprises curl_easy_reset(m_pCurlSession); CheckURL(strURL); curl_easy_setopt(m_pCurlSession, CURLOPT_HTTPGET, 1L); curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEFUNCTION, WriteInStringCallback); curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEDATA, &strOutput); CURLcode res = Perform(); curl_easy_getinfo(m_pCurlSession, CURLINFO_RESPONSE_CODE, &lHTTPStatusCode); // Check for errors if (res != CURLE_OK) { if (m_eSettingsFlags & ENABLE_LOG){ char errMsg[1024] = {0}; _snprintf(errMsg, 1023, LOG_ERROR_CURL_REQ_FAILURE_FORMAT, m_strURL.c_str(), res, curl_easy_strerror(res), lHTTPStatusCode); m_oLog(errMsg); } return false; } return true; } /** * @brief Downloads a remote file to a local file. * * @param [in] strLocalFile Complete path of the local file to download. * @param [in] strURL URI of the remote location (with the file name). * @param [out] lHTTPStatusCode HTTP Status code of the response. * * @retval true Successfully downloaded the file. * @retval false The file couldn't be downloaded. Check the log messages for more information. */ const bool CHTTPClient::DownloadFile(const std::string& strLocalFile, const std::string& strURL, long& lHTTPStatusCode) { if (strURL.empty() || strLocalFile.empty()) return false; if (!m_pCurlSession) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); return false; } // Reset is mandatory to avoid bad surprises curl_easy_reset(m_pCurlSession); CheckURL(strURL); std::ofstream ofsOutput; ofsOutput.open(strLocalFile, std::ofstream::out | std::ofstream::binary | std::ofstream::trunc); if (ofsOutput) { curl_easy_setopt(m_pCurlSession, CURLOPT_HTTPGET, 1L); curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEFUNCTION, WriteToFileCallback); curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEDATA, &ofsOutput); CURLcode res = Perform(); ofsOutput.close(); curl_easy_getinfo(m_pCurlSession, CURLINFO_RESPONSE_CODE, &lHTTPStatusCode); //double dUploadLength = 0; //curl_easy_getinfo(m_pCurlSession, CURLINFO_CONTENT_LENGTH_UPLOAD, &dUploadLength); // number of bytes uploaded /* Delete downloaded file if status code != 200 as server's response body may contain error 404 */ if (lHTTPStatusCode != 200) remove(strLocalFile.c_str()); if (res != CURLE_OK) { if (m_eSettingsFlags & ENABLE_LOG){ char errMsg[1024] = {0}; _snprintf(errMsg, 1023, LOG_ERROR_CURL_DOWNLOAD_FAILURE_FORMAT, strLocalFile.c_str(), strURL.c_str(), res, curl_easy_strerror(res), lHTTPStatusCode); m_oLog(errMsg); } return false; } } else if (m_eSettingsFlags & ENABLE_LOG) { char errMsg[1024] = {0}; _snprintf(errMsg, 1023, LOG_ERROR_DOWNLOAD_FILE_FORMAT, strLocalFile.c_str()); m_oLog(errMsg); return false; } return true; } /** * @brief uploads a POST form * * * @param [in] strURL URL to which the form will be posted. * @param [in] data post form information * @param [out] lHTTPStatusCode HTTP Status code of the response. * * @retval true Successfully posted the header. * @retval false The header couldn't be posted. */ const bool CHTTPClient::UploadForm(const std::string& strURL, const PostFormInfo& data, long& lHTTPStatusCode) { if (strURL.empty()) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_EMPTY_HOST_MSG); return false; } if (!m_pCurlSession) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); return false; } // Reset is mandatory to avoid bad surprises curl_easy_reset(m_pCurlSession); CheckURL(strURL); /** Now specify we want to POST data */ curl_easy_setopt(m_pCurlSession, CURLOPT_POST, 1L); /* stating that Expect: 100-continue is not wanted */ AddHeader("Expect:"); /** set post form */ if (data.m_pFormPost != nullptr) curl_easy_setopt(m_pCurlSession, CURLOPT_HTTPPOST, data.m_pFormPost); /* to avoid printing response's body to stdout. * CURLOPT_WRITEDATA : by default, this is a FILE * to stdout. */ curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEFUNCTION, ThrowAwayCallback); CURLcode res = Perform(); curl_easy_getinfo(m_pCurlSession, CURLINFO_RESPONSE_CODE, &lHTTPStatusCode); // Check for errors if (res != CURLE_OK) { if (m_eSettingsFlags & ENABLE_LOG) { char errMsg[1024] = {0}; _snprintf(errMsg,1023, LOG_ERROR_CURL_REQ_FAILURE_FORMAT, m_strURL.c_str(), res, curl_easy_strerror(res), lHTTPStatusCode); m_oLog(errMsg); } return false; } return true; } /************************************************************************/ /* add by cfq 添加文件块上传 */ /************************************************************************/ const bool CHTTPClient:: UploadFileBlock(CHTTPUploadReq &req, CHTTPUploadRet &ret, HttpClientTraceLink* pTrace) { if(req.url.empty()){ if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_EMPTY_HOST_MSG); return false; } if (!m_pCurlSession) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); return false; } // Reset is mandatory to avoid bad surprises curl_easy_reset(m_pCurlSession); CheckURL(req.url); if(req.m_printDbg){ m_oLog(req.url.c_str()); std::string msg = "是否是https请求:"; msg.append(m_bHTTPS ? "true" : "false"); m_oLog(msg.c_str()); } /** Now specify we want to POST data */ curl_easy_setopt(m_pCurlSession, CURLOPT_POST, 1L); unordered_map m_headers; if (pTrace != NULL && strlen(pTrace->X_B3_BusinessId) > 0 && strlen(pTrace->X_B3_TraceId) > 0 && strlen(pTrace->X_B3_SpanId) > 0 && strlen(pTrace->X_B3_ParentSpanId) > 0 && strlen(pTrace->X_B3_Timestamp) > 0) { m_headers.emplace(std::make_pair("X_B3_BusinessId", pTrace->X_B3_BusinessId)); m_headers.emplace(std::make_pair("X_B3_TraceId", pTrace->X_B3_TraceId)); m_headers.emplace(std::make_pair("X_B3_SpanId", pTrace->X_B3_SpanId)); m_headers.emplace(std::make_pair("X_B3_ParentSpanId", pTrace->X_B3_ParentSpanId)); m_headers.emplace(std::make_pair("X_B3_Timestamp", pTrace->X_B3_Timestamp)); } std::string strHeader; for (HeadersMap::const_iterator it = m_headers.cbegin(); it != m_headers.cend(); ++it) { strHeader = it->first + ": " + it->second; // build header string AddHeader(strHeader); } /* stating that Expect: 100-continue is not wanted */ AddHeader("Expect:"); curl_httppost* pFormPost = NULL; curl_httppost* pLastFormptr = NULL; //添加参数 if(req.m_bTransCode){ if(req.m_printDbg){ m_oLog("url转码 GBK=>UTF-8"); } ConvertGBKToUtf8(req.paramContent); //转码 } curl_formadd(&pFormPost, &pLastFormptr, CURLFORM_COPYNAME, "params", CURLFORM_COPYCONTENTS, req.paramContent.c_str(), CURLFORM_END); //添加文件流 curl_formadd(&pFormPost, &pLastFormptr, CURLFORM_COPYNAME, "file_content", CURLFORM_BUFFER, req.fileName.c_str(), CURLFORM_BUFFERPTR, req.fileContent, CURLFORM_BUFFERLENGTH, req.fileContentSize, CURLFORM_CONTENTTYPE, "application/octet-stream", CURLFORM_END); /** set post form */ if (pFormPost != nullptr) curl_easy_setopt(m_pCurlSession, CURLOPT_HTTPPOST, pFormPost); //接受响应body curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEFUNCTION, WriteInStringCallback); curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEDATA, &ret.strBody); //接受响应头 curl_easy_setopt(m_pCurlSession, CURLOPT_HEADERFUNCTION, HeaderCallback); curl_easy_setopt(m_pCurlSession, CURLOPT_HEADERDATA, &ret.m_headers); //set timeout m_iCurlTimeout = req.m_timeOut; if(req.m_printDbg){ m_oLog("开始发起上传请求!"); } CURLcode res = Perform(); curl_easy_getinfo(m_pCurlSession, CURLINFO_RESPONSE_CODE, &ret.httpCode); curl_formfree(pFormPost); //添加参数 if(req.m_bTransCode){ if(req.m_printDbg){ m_oLog("url转码 UTF-8=>GBK"); } ConvertUtf8ToGBK(ret.strBody); } // Check for errors if (res != CURLE_OK) { ret.httpCode = -1; ret.strBody.clear(); if (m_eSettingsFlags & ENABLE_LOG) { char errMsg[1024] = {0}; _snprintf(errMsg, 1023, LOG_ERROR_CURL_REQ_FAILURE_FORMAT, m_strURL.c_str(), res, curl_easy_strerror(res), ret.httpCode); m_oLog(errMsg); } return false; } else{ if(req.m_printDbg){ m_oLog("文件块上传成功!"); } try { Json::Value root; Json::Reader reader; reader.parse(ret.strBody,root,false); if(root.isMember("code") && root.isMember("message")) { ret.m_userCode = root["code"].asString(); ret.m_errMsg = root["message"].asString(); return true; } else { char errMsg[1024] = {0}; _snprintf(errMsg, 1023, LOG_ERROR_RESPONSE_FORMAT_FAILTURE_MSG, ret.strBody.c_str()); ret.m_userCode = "0"; ret.m_errMsg = "Unexpected format of stupid previous assuption!"; return true; } } catch(...){ m_oLog(LOG_ERROR_PARSE_JSON_FAILTURE_MSG); return false; } } return true; } /************************************************************************/ /* add by cfq 添加文件块下载 */ /************************************************************************/ const bool CHTTPClient:: DownloadFileBlock(const char* url, const char* jsonReqStr, fileContentArray& content, long &httpCode, unordered_map& responseHeaders, long timeout, HttpClientTraceLink* pTrace) { if (url == nullptr) return false; if (!m_pCurlSession) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); return false; } // Reset is mandatory to avoid bad surprises curl_easy_reset(m_pCurlSession); CheckURL(url); if(content.copyPtr != NULL){ // Add headers unordered_map m_headers; if (pTrace != NULL && strlen(pTrace->X_B3_BusinessId) > 0 && strlen(pTrace->X_B3_TraceId) > 0 && strlen(pTrace->X_B3_SpanId) > 0 && strlen(pTrace->X_B3_ParentSpanId) > 0 && strlen(pTrace->X_B3_Timestamp) > 0) { m_headers.emplace(std::make_pair("X_B3_BusinessId", pTrace->X_B3_BusinessId)); m_headers.emplace(std::make_pair("X_B3_TraceId", pTrace->X_B3_TraceId)); m_headers.emplace(std::make_pair("X_B3_SpanId", pTrace->X_B3_SpanId)); m_headers.emplace(std::make_pair("X_B3_ParentSpanId", pTrace->X_B3_ParentSpanId)); m_headers.emplace(std::make_pair("X_B3_Timestamp", pTrace->X_B3_Timestamp)); } std::string strHeader; for (HeadersMap::const_iterator it = m_headers.cbegin(); it != m_headers.cend(); ++it) { strHeader = it->first + ": " + it->second; // build header string AddHeader(strHeader); } curl_easy_setopt(m_pCurlSession, CURLOPT_HTTPPOST, 1); curl_easy_setopt(m_pCurlSession, CURLOPT_POSTFIELDS, jsonReqStr); //接受响应头 curl_easy_setopt(m_pCurlSession, CURLOPT_HEADERFUNCTION, HeaderCallback); curl_easy_setopt(m_pCurlSession, CURLOPT_HEADERDATA, &responseHeaders); *(content.curLen) = 0; curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEFUNCTION, WriteInByteCallback); curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEDATA, &content); m_iCurlTimeout = timeout;//下载读写数据超时 AddHeader("Content-Type:application/json"); CURLcode res = Perform(); curl_easy_getinfo(m_pCurlSession, CURLINFO_RESPONSE_CODE, &httpCode); // Check for errors if (res != CURLE_OK) { httpCode = -1; if (m_eSettingsFlags & ENABLE_LOG) { char errMsg[1024] = {0}; _snprintf(errMsg, 1023, LOG_ERROR_CURL_REQ_FAILURE_FORMAT, m_strURL.c_str(), res, curl_easy_strerror(res), httpCode); m_oLog(errMsg); } return false; } return true; } else{ return false; } } /** * @brief PostFormInfo constructor */ CHTTPClient::PostFormInfo::PostFormInfo() : m_pFormPost(nullptr), m_pLastFormptr(nullptr) { } /** * @brief PostFormInfo destructor */ CHTTPClient::PostFormInfo::~PostFormInfo() { // cleanup the formpost chain if (m_pFormPost) { curl_formfree(m_pFormPost); m_pFormPost = nullptr; m_pLastFormptr = nullptr; } } /** * @brief set the name and the value of the HTML "file" form's input * * @param fieldName name of the "file" input * @param fieldValue path to the file to upload */ void CHTTPClient::PostFormInfo::AddFormFile(const std::string& strFieldName, const std::string& strFieldValue) { curl_formadd(&m_pFormPost, &m_pLastFormptr, CURLFORM_COPYNAME, strFieldName.c_str(), CURLFORM_FILE, strFieldValue.c_str(), CURLFORM_END); } /** * @brief set the name and the value of an HTML form's input * (other than "file" like "text", "hidden" or "submit") * * @param fieldName name of the input element * @param fieldValue value to be assigned to the input element */ void CHTTPClient::PostFormInfo::AddFormContent(const std::string& strFieldName, const std::string& strFieldValue) { curl_formadd(&m_pFormPost, &m_pLastFormptr, CURLFORM_COPYNAME, strFieldName.c_str(), CURLFORM_COPYCONTENTS, strFieldValue.c_str(), CURLFORM_END); } // REST REQUESTS /** * @brief initializes a REST request * some common operations to REST requests are performed here, * the others are performed in Perform method * * @param [in] Headers headers to send * @param [out] Response response data */ inline const bool CHTTPClient::InitRestRequest(const std::string& strUrl, const CHTTPClient::HeadersMap& Headers, CHTTPClient::HttpResponse& Response) { if (strUrl.empty()) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_EMPTY_HOST_MSG); return false; } if (!m_pCurlSession) { if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); return false; } // Reset is mandatory to avoid bad surprises curl_easy_reset(m_pCurlSession); CheckURL(strUrl); // set the received body's callback function curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEFUNCTION, &CHTTPClient::RestWriteCallback); // set data object to pass to callback function above curl_easy_setopt(m_pCurlSession, CURLOPT_WRITEDATA, &Response); // set the response's headers processing callback function curl_easy_setopt(m_pCurlSession, CURLOPT_HEADERFUNCTION, &CHTTPClient::RestHeaderCallback); // callback object for server's responses headers curl_easy_setopt(m_pCurlSession, CURLOPT_HEADERDATA, &Response); std::string strHeader; for (HeadersMap::const_iterator it = Headers.cbegin(); it != Headers.cend(); ++it) { strHeader = it->first + ": " + it->second; // build header string AddHeader(strHeader); } return true; } /** * @brief post REST request operations are performed here * * @param [in] ePerformCode curl easy perform returned code * @param [out] Response response data */ inline const bool CHTTPClient::PostRestRequest(const CURLcode ePerformCode, CHTTPClient::HttpResponse& Response) { // Check for errors if (ePerformCode != CURLE_OK) { Response.strBody.clear(); Response.iCode = -1; if (m_eSettingsFlags & ENABLE_LOG){ char errMsg[1024] = {0}; _snprintf(errMsg, 1023, LOG_ERROR_CURL_REST_FAILURE_FORMAT, m_strURL.c_str(), ePerformCode, curl_easy_strerror(ePerformCode)); m_oLog(errMsg); Response.iCode = ePerformCode; Response.strBody.append(errMsg); } return false; } long lHttpCode = 0; curl_easy_getinfo(m_pCurlSession, CURLINFO_RESPONSE_CODE, &lHttpCode); Response.iCode = static_cast(lHttpCode); return true; } /** * @brief performs a HEAD request * * @param [in] strUrl url to request * @param [in] Headers headers to send * @param [out] Response response data * * @retval true Successfully requested the URI. * @retval false Encountered a problem. */ const bool CHTTPClient::Head(const std::string& strUrl, const CHTTPClient::HeadersMap& Headers, CHTTPClient::HttpResponse& Response) { if (InitRestRequest(strUrl, Headers, Response)) { /** set HTTP HEAD METHOD */ curl_easy_setopt(m_pCurlSession, CURLOPT_CUSTOMREQUEST, "HEAD"); curl_easy_setopt(m_pCurlSession, CURLOPT_NOBODY, 1L); CURLcode res = Perform(); return PostRestRequest(res, Response); } else return false; } /** * @brief performs a GET request * * @param [in] strUrl url to request * @param [in] Headers headers to send * @param [out] Response response data * * @retval true Successfully requested the URI. * @retval false Encountered a problem. */ const bool CHTTPClient::Get(const std::string& strUrl, const CHTTPClient::HeadersMap& Headers, CHTTPClient::HttpResponse& Response) { if (InitRestRequest(strUrl, Headers, Response)) { // specify a GET request curl_easy_setopt(m_pCurlSession, CURLOPT_HTTPGET, 1L); CURLcode res = Perform(); return PostRestRequest(res, Response); } else return false; } /** * @brief performs a DELETE request * * @param [in] strUrl url to request * @param [in] Headers headers to send * @param [out] Response response data * * @retval true Successfully requested the URI. * @retval false Encountered a problem. */ const bool CHTTPClient::Del(const std::string& strUrl, const CHTTPClient::HeadersMap& Headers, CHTTPClient::HttpResponse& Response) { if (InitRestRequest(strUrl, Headers, Response)) { curl_easy_setopt(m_pCurlSession, CURLOPT_CUSTOMREQUEST, "DELETE"); CURLcode res = Perform(); return PostRestRequest(res, Response); } else return false; } const bool CHTTPClient::Post(const std::string& strUrl, const CHTTPClient::HeadersMap& Headers, const std::string& strPostData, CHTTPClient::HttpResponse& Response) { if (InitRestRequest(strUrl, Headers, Response)) { // specify a POST request curl_easy_setopt(m_pCurlSession, CURLOPT_POST, 1L); // set post informations curl_easy_setopt(m_pCurlSession, CURLOPT_POSTFIELDS, strPostData.c_str()); curl_easy_setopt(m_pCurlSession, CURLOPT_POSTFIELDSIZE, strPostData.size()); CURLcode res = Perform(); return PostRestRequest(res, Response); } else return false; } /** * @brief performs a PUT request with a string * * @param [in] strUrl url to request * @param [in] Headers headers to send * @param [out] Response response data * * @retval true Successfully requested the URI. * @retval false Encountered a problem. */ const bool CHTTPClient::Put(const std::string& strUrl, const CHTTPClient::HeadersMap& Headers, const std::string& strPutData, CHTTPClient::HttpResponse& Response) { if (InitRestRequest(strUrl, Headers, Response)) { CHTTPClient::UploadObject Payload; Payload.pszData = strPutData.c_str(); Payload.usLength = strPutData.size(); // specify a PUT request curl_easy_setopt(m_pCurlSession, CURLOPT_PUT, 1L); curl_easy_setopt(m_pCurlSession, CURLOPT_UPLOAD, 1L); // set read callback function curl_easy_setopt(m_pCurlSession, CURLOPT_READFUNCTION, &CHTTPClient::RestReadCallback); // set data object to pass to callback function curl_easy_setopt(m_pCurlSession, CURLOPT_READDATA, &Payload); // set data size curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE, static_cast(Payload.usLength)); CURLcode res = Perform(); return PostRestRequest(res, Response); } else return false; } /** * @brief performs a PUT request with a byte buffer (vector of char) * * @param [in] strUrl url to request * @param [in] Headers headers to send * @param [out] Response response data * * @retval true Successfully requested the URI. * @retval false Encountered a problem. */ const bool CHTTPClient::Put(const std::string& strUrl, const CHTTPClient::HeadersMap& Headers, const CHTTPClient::ByteBuffer& Data, CHTTPClient::HttpResponse& Response) { if (InitRestRequest(strUrl, Headers, Response)) { CHTTPClient::UploadObject Payload; Payload.pszData = Data.data(); Payload.usLength = Data.size(); // specify a PUT request curl_easy_setopt(m_pCurlSession, CURLOPT_PUT, 1L); curl_easy_setopt(m_pCurlSession, CURLOPT_UPLOAD, 1L); // set read callback function curl_easy_setopt(m_pCurlSession, CURLOPT_READFUNCTION, &CHTTPClient::RestReadCallback); // set data object to pass to callback function curl_easy_setopt(m_pCurlSession, CURLOPT_READDATA, &Payload); // set data size curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE, static_cast(Payload.usLength)); CURLcode res = Perform(); return PostRestRequest(res, Response); } else return false; } // STRING HELPERS /** * @brief returns a formatted string * * @param [in] strFormat string with one or many format specifiers * @param [in] parameters to be placed in the format specifiers of strFormat * * @retval string formatted string */ std::string CHTTPClient::StringFormat(const std::string strFormat, ...) { int n = (static_cast(strFormat.size())) * 2; // Reserve two times as much as the length of the strFormat std::unique_ptr pFormatted; va_list ap; while(true) { pFormatted.reset(new char[n]); // Wrap the plain char array into the unique_ptr strcpy(&pFormatted[0], strFormat.c_str()); va_start(ap, strFormat); int iFinaln = vsnprintf(&pFormatted[0], n, strFormat.c_str(), ap); va_end(ap); if (iFinaln < 0 || iFinaln >= n) { n += abs(iFinaln - n + 1); } else { break; } } return std::string(pFormatted.get()); } /*std::string CHTTPClient::StringFormat(const std::string strFormat, ...) { char buffer[1024]; va_list args; va_start(args, strFormat); vsnprintf(buffer, 1024, strFormat.c_str(), args); va_end (args); return std::string(buffer); } */ /** * @brief removes leading and trailing whitespace from a string * * @param [in/out] str string to be trimmed */ inline void CHTTPClient::TrimSpaces(std::string& str) { // trim from left str.erase(str.begin(), std::find_if(str.begin(), str.end(), [](char c) {return !isspace(c); }) ); // trim from right str.erase(std::find_if(str.rbegin(), str.rend(), [](char c) {return !isspace(c); }).base(), str.end() ); } // CURL CALLBACKS size_t CHTTPClient::ThrowAwayCallback(void* ptr, size_t size, size_t nmemb, void* data) { reinterpret_cast(ptr); reinterpret_cast(data); /* we are not interested in the headers itself, so we only return the size we would have saved ... */ return size * nmemb; } /** * @brief stores the server response in a string * * @param ptr pointer of max size (size*nmemb) to read data from it * @param size size parameter * @param nmemb memblock parameter * @param data pointer to user data (string) * * @return (size * nmemb) */ size_t CHTTPClient::WriteInStringCallback(void* ptr, size_t size, size_t nmemb, void* data) { std::string* strWriteHere = reinterpret_cast(data); if (strWriteHere != nullptr) { strWriteHere->append(reinterpret_cast(ptr), size * nmemb); return size * nmemb; } return 0; } /** * @brief stores the server response in an already opened file stream * used by DownloadFile() * * @param buff pointer of max size (size*nmemb) to read data from it * @param size size parameter * @param nmemb memblock parameter * @param userdata pointer to user data (file stream) * * @return (size * nmemb) */ size_t CHTTPClient::WriteToFileCallback(void* buff, size_t size, size_t nmemb, void* data) { if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1) || (data == nullptr)) return 0; std::ofstream* pFileStream = reinterpret_cast(data); if (pFileStream->is_open()) { pFileStream->write(reinterpret_cast(buff), size * nmemb); } return size * nmemb; } /** * @brief reads the content of an already opened file stream * used by UploadFile() * * @param ptr pointer of max size (size*nmemb) to write data to it * @param size size parameter * @param nmemb memblock parameter * @param stream pointer to user data (file stream) * * @return (size * nmemb) */ size_t CHTTPClient::ReadFromFileCallback(void* ptr, size_t size, size_t nmemb, void* stream) { std::ifstream* pFileStream = reinterpret_cast(stream); if (pFileStream->is_open()) { pFileStream->read(reinterpret_cast(ptr), size * nmemb); return pFileStream->gcount(); } return 0; } // REST CALLBACKS /** * @brief write callback function for libcurl * this callback will be called to store the server's Body reponse * in a struct response * * we can also use an std::vector instead of an std::string but in this case * there isn't a big difference... maybe resizing the container with a max size can * enhance performances... * * @param data returned data of size (size*nmemb) * @param size size parameter * @param nmemb memblock parameter * @param userdata pointer to user data to save/work with return data * * @return (size * nmemb) */ size_t CHTTPClient::RestWriteCallback(void* pCurlData, size_t usBlockCount, size_t usBlockSize, void* pUserData) { CHTTPClient::HttpResponse* pServerResponse; pServerResponse = reinterpret_cast(pUserData); pServerResponse->strBody.append(reinterpret_cast(pCurlData), usBlockCount * usBlockSize); return (usBlockCount * usBlockSize); } /** * @brief header callback for libcurl * callback used to process response's headers (received) * * @param data returned (header line) * @param size of data * @param nmemb memblock * @param userdata pointer to user data object to save header data * @return size * nmemb; */ size_t CHTTPClient::RestHeaderCallback(void* pCurlData, size_t usBlockCount, size_t usBlockSize, void* pUserData) { CHTTPClient::HttpResponse* pServerResponse; pServerResponse = reinterpret_cast(pUserData); std::string strHeader(reinterpret_cast(pCurlData), usBlockCount * usBlockSize); size_t usSeperator = strHeader.find_first_of(":"); if (std::string::npos == usSeperator) { //roll with non seperated headers or response's line TrimSpaces(strHeader); if (0 == strHeader.length()) { return (usBlockCount * usBlockSize); //blank line; } pServerResponse->mapHeaders[strHeader] = "present"; } else { std::string strKey = strHeader.substr(0, usSeperator); TrimSpaces(strKey); std::string strValue = strHeader.substr(usSeperator + 1); TrimSpaces(strValue); pServerResponse->mapHeaders[strKey] = strValue; } return (usBlockCount * usBlockSize); } size_t CHTTPClient::HeaderCallback(void* pCurlData, size_t usBlockCount, size_t usBlockSize, void* pUserData) { CHTTPClient::HeadersMap *pheaderMap; pheaderMap = reinterpret_cast(pUserData); std::string strHeader(reinterpret_cast(pCurlData), usBlockCount * usBlockSize); size_t usSeperator = strHeader.find_first_of(":"); if (std::string::npos == usSeperator) { //roll with non seperated headers or response's line TrimSpaces(strHeader); if (0 == strHeader.length()) { return (usBlockCount * usBlockSize); //blank line; } (*pheaderMap)[strHeader] = "present"; } else { std::string strKey = strHeader.substr(0, usSeperator); TrimSpaces(strKey); std::string strValue = strHeader.substr(usSeperator + 1); TrimSpaces(strValue); (*pheaderMap)[strKey] = strValue; } return (usBlockCount * usBlockSize); } size_t CHTTPClient:: WriteInByteCallback(void* ptr, size_t size, size_t nmemb, void* pUserData) { fileContentArray *wbyte = reinterpret_cast(pUserData); if (wbyte == NULL || wbyte->copyPtr == NULL) { return -1; } else { size_t len = size * nmemb; *(wbyte->curLen) += len; if (len != 0 && *(wbyte->curLen) <= wbyte->maxLen) { memcpy(wbyte->copyPtr, ptr, len); wbyte->copyPtr += len;//移动指针 return len; } else { return -2; } } } /** * @brief read callback function for libcurl * used to send (or upload) a content to the server * * @param pointer of max size (size*nmemb) to write data to (used by cURL to send data) * @param size size parameter * @param nmemb memblock parameter * @param userdata pointer to user data to read data from * * @return (size * nmemb) */ size_t CHTTPClient::RestReadCallback(void* pCurlData, size_t usBlockCount, size_t usBlockSize, void* pUserData) { // get upload struct CHTTPClient::UploadObject* Payload; Payload = reinterpret_cast(pUserData); // set correct sizes size_t usCurlSize = usBlockCount * usBlockSize; size_t usCopySize = (Payload->usLength < usCurlSize) ? Payload->usLength : usCurlSize; /** copy data to buffer */ std::memcpy(pCurlData, Payload->pszData, usCopySize); // decrement length and increment data pointer Payload->usLength -= usCopySize; // remaining bytes to be sent Payload->pszData += usCopySize; // next byte to the chunk that will be sent /** return copied size */ return usCopySize; } //add by cfq const bool CHTTPClient::Get(CHTTPReq &req, CHTTPRet &ret, HttpClientTraceLink* pTrace){ //拼接请求参数 string param; std::string jsonStr=""; try { jsonStr= req.ToJson(); } catch (...) { m_oLog(LOG_ERROR_GET_JSON_FAILTURE_MSG); } if(jsonStr.empty()) { param = ""; } else { Json::Value root; Json::Reader reader; reader.parse(jsonStr,root,false); Json::Value::Members mem = root.getMemberNames(); for(auto iter = mem.begin(); iter != mem.end(); iter++){ switch(root[*iter].type()){ case Json::intValue: char a[20]; _snprintf(a, sizeof(a), "%d", root[*iter].asInt()); param = param.append((*iter).c_str()).append("=").append(a); break; case Json::stringValue: param = param.append((*iter).c_str()).append("=").append(root[*iter].asCString()); break; case Json::realValue: char b[20]; _snprintf(b, sizeof(b), "%.21f", root[*iter].asDouble()); param = param.append((*iter).c_str()).append("=").append(b); break; case Json::booleanValue: param = param.append((*iter).c_str()).append("=").append(root[*iter].asBool() ? "true": "false"); break; default: break; } if((iter + 1) != mem.end()){ param = param.append("&"); } //root.clear(); } } char requestData[20000]; _snprintf(requestData, sizeof(requestData), "%s", param.c_str()); if(req.m_printDbg){ m_oLog(requestData); } //urlEncode char *requestData_encode = curl_easy_escape(m_pCurlSession, requestData, 0); if(req.m_printDbg){ string str; str.append("queryString encode:"); str.append(requestData); m_oLog(str.c_str()); } if(!req.m_url.empty()){ char *url = new char[req.m_url.length()+param.length()+2]; _snprintf(url, req.m_url.length()+param.length()+1, "%s?%s", req.m_url.c_str(), param.c_str()); m_oLog(url); delete[] url; } else{ m_oLog(LOG_ERROR_PATH_NULL_MSG); return false; } if(req.m_headers.empty()){ if(req.m_printDbg){ m_oLog("没有指定请求头内容,使用默认值Accept=application/json"); } req.m_headers.emplace(std::make_pair("Accept", "application/json")); } if (req.m_withToken && g_tokenCall != NULL) { std::string channelId = ""; std::string token = ""; std::string terminalno = ""; std::string reserve1 = ""; g_tokenCall(channelId, token, terminalno, reserve1); if (channelId.length() != 0 && token.length() != 0) { req.m_headers.emplace(std::make_pair("channelId", channelId)); req.m_headers.emplace(std::make_pair("token", token)); req.m_headers.emplace(std::make_pair("terminalno", terminalno)); string out = "channelId:" + channelId + ", token:" + token; if (req.m_printDbg) m_oLog(out.c_str()); } } if (pTrace != NULL && strlen(pTrace->X_B3_BusinessId) > 0 && strlen(pTrace->X_B3_TraceId) > 0 && strlen(pTrace->X_B3_SpanId) > 0 && strlen(pTrace->X_B3_ParentSpanId) > 0 && strlen(pTrace->X_B3_Timestamp) > 0) { req.m_headers.emplace(std::make_pair("X_B3_BusinessId", pTrace->X_B3_BusinessId)); req.m_headers.emplace(std::make_pair("X_B3_TraceId", pTrace->X_B3_TraceId)); req.m_headers.emplace(std::make_pair("X_B3_SpanId", pTrace->X_B3_SpanId)); req.m_headers.emplace(std::make_pair("X_B3_ParentSpanId", pTrace->X_B3_ParentSpanId)); req.m_headers.emplace(std::make_pair("X_B3_Timestamp", pTrace->X_B3_Timestamp)); } //set timeout m_iCurlTimeout = req.m_timeOut; CHTTPClient::HttpResponse g_Response; string url = req.m_url + "?" + param; if(req.m_bTransCode){ if(req.m_printDbg) m_oLog("url转码 GBK=>UTF-8"); ConvertGBKToUtf8(url); } bool isSuccess = Get(url, req.m_headers, g_Response); //转码 if(req.m_bTransCode){ if(req.m_printDbg) m_oLog("responseData转码 UTF-8=>GBK"); ConvertUtf8ToGBK(g_Response.strBody); } if(req.m_printDbg){ std::string msg = "是否是https请求:"; msg.append(m_bHTTPS ? "true" : "false"); m_oLog(msg.c_str()); } //处理响应值 ret.m_sysCode = g_Response.iCode; ret.m_resultData = g_Response.strBody; if(isSuccess == false || (g_Response.iCode != 200 && g_Response.iCode != 201)){ ret.m_errMsg = g_Response.strBody; char errMsg[1024] = {0}; _snprintf(errMsg, 1023, "http请求报错,错误码:%d", g_Response.iCode); m_oLog(errMsg); return false; } else { if(req.m_printDbg) m_oLog("http请求成功,开始解析json"); try { Json::Value root; Json::Reader reader; reader.parse(g_Response.strBody,root,false); if(root.isMember("code") && root.isMember("message")) { ret.m_userCode = root["code"].asString(); ret.m_errMsg = root["message"].asString(); return ret.Parse(g_Response.strBody); } else { char errMsg[1024] = {0}; _snprintf(errMsg, 1023, LOG_ERROR_RESPONSE_FORMAT_FAILTURE_MSG, g_Response.strBody.c_str()); ret.m_userCode = "0"; ret.m_errMsg = "Unexpected format of stupid previous assuption!"; return true; } } catch(...){ m_oLog(LOG_ERROR_PARSE_JSON_FAILTURE_MSG); return false; } } } //add by cfq const bool CHTTPClient::Post(CHTTPReq &req, CHTTPRet &ret, HttpClientTraceLink* pTrace){ if(!req.m_url.empty()){ if(req.m_printDbg){ char *url = new char[req.m_url.length() + 1]; memset(url, 0, req.m_url.length() + 1); _snprintf(url, req.m_url.length(), "%s", req.m_url.c_str()); m_oLog(url); delete[] url; } } else{ m_oLog(LOG_ERROR_PATH_NULL_MSG); return false; } std::string request; try { request= req.ToJson(); } catch (...) { m_oLog(LOG_ERROR_GET_JSON_FAILTURE_MSG); } //转码 if(req.m_bTransCode){ if(req.m_printDbg){ m_oLog("url转码 GBK=>UTF-8"); } ConvertGBKToUtf8(request); } if(req.m_printDbg){ std:string msg = "strPostDate:"; msg.append(request); m_oLog(msg.c_str()); } if(req.m_headers.empty()){ if(req.m_printDbg){ m_oLog("没有指定请求头内容,使用默认值Content-Type=application/json"); } req.m_headers.emplace(std::make_pair("Content-Type", "application/json")); } if (req.m_withToken && g_tokenCall != NULL) { std::string channelId = ""; std::string token = ""; std::string terminalno = ""; std::string reserve1 = ""; g_tokenCall(channelId, token, terminalno, reserve1); if (channelId.length() != 0 && token.length() != 0) { req.m_headers.emplace(std::make_pair("channelId", channelId)); req.m_headers.emplace(std::make_pair("token", token)); req.m_headers.emplace(std::make_pair("terminalno", terminalno)); string out = "channelId:" + channelId + ", token:" + token; if (req.m_printDbg) m_oLog(out.c_str()); } } if (pTrace != NULL && strlen(pTrace->X_B3_BusinessId) > 0 && strlen(pTrace->X_B3_TraceId) > 0 && strlen(pTrace->X_B3_SpanId) > 0 && strlen(pTrace->X_B3_ParentSpanId) > 0 && strlen(pTrace->X_B3_Timestamp) > 0) { req.m_headers.emplace(std::make_pair("X_B3_BusinessId", pTrace->X_B3_BusinessId)); req.m_headers.emplace(std::make_pair("X_B3_TraceId", pTrace->X_B3_TraceId)); req.m_headers.emplace(std::make_pair("X_B3_SpanId", pTrace->X_B3_SpanId)); req.m_headers.emplace(std::make_pair("X_B3_ParentSpanId", pTrace->X_B3_ParentSpanId)); req.m_headers.emplace(std::make_pair("X_B3_Timestamp", pTrace->X_B3_Timestamp)); } //set timeout m_iCurlTimeout = req.m_timeOut; CHTTPClient::HttpResponse g_Response; bool isSuccess = Post(req.m_url, req.m_headers, request, g_Response); //转码 if(req.m_bTransCode){ if(req.m_printDbg){ m_oLog("url转码 GBK=>UTF-8"); } ConvertUtf8ToGBK(g_Response.strBody); } if(req.m_printDbg){ std::string msg = "是否是https请求:"; msg.append(m_bHTTPS ? "true" : "false"); m_oLog(msg.c_str()); } //处理响应值 ret.m_sysCode = g_Response.iCode; ret.m_resultData = g_Response.strBody; if(isSuccess == false || (g_Response.iCode != 200 && g_Response.iCode != 201)){ ret.m_errMsg = g_Response.strBody; char errMsg[1024] = {0}; _snprintf(errMsg, 1023, "http请求报错,错误码:%d", g_Response.iCode); m_oLog(errMsg); return false; } else { if(req.m_printDbg){ m_oLog("http请求成功,开始解析json"); } try { Json::Value root; Json::Reader reader; reader.parse(g_Response.strBody,root,false); if(root.isMember("code") && root.isMember("message")) { ret.m_userCode = root["code"].asString(); ret.m_errMsg = root["message"].asString(); return ret.Parse(g_Response.strBody); } else { char errMsg[1024] = {0}; _snprintf(errMsg, 1023, LOG_ERROR_RESPONSE_FORMAT_FAILTURE_MSG, g_Response.strBody.c_str()); ret.m_userCode = "0"; ret.m_errMsg = "Unexpected format of stupid previous assuption!"; return true; } } catch(...){ m_oLog(LOG_ERROR_PARSE_JSON_FAILTURE_MSG); return false; } } } // CURL DEBUG INFO CALLBACKS #ifdef DEBUG_CURL void CHTTPClient::SetCurlTraceLogDirectory(const std::string& strPath) { s_strCurlTraceLogDirectory = strPath; if (!s_strCurlTraceLogDirectory.empty() #ifdef WINDOWS && s_strCurlTraceLogDirectory.at(s_strCurlTraceLogDirectory.length() - 1) != '\\') { s_strCurlTraceLogDirectory += '\\'; } #else && s_strCurlTraceLogDirectory.at(s_strCurlTraceLogDirectory.length() - 1) != '/') { s_strCurlTraceLogDirectory += '/'; } #endif } int CHTTPClient::DebugCallback(CURL* curl, curl_infotype curl_info_type, char* pszTrace, size_t usSize, void* pFile) { std::string strText; std::string strTrace(pszTrace, usSize); switch (curl_info_type) { case CURLINFO_TEXT: strText = "# Information : "; break; case CURLINFO_HEADER_OUT: strText = "-> Sending header : "; break; case CURLINFO_DATA_OUT: strText = "-> Sending data : "; break; case CURLINFO_SSL_DATA_OUT: strText = "-> Sending SSL data : "; break; case CURLINFO_HEADER_IN: strText = "<- Receiving header : "; break; case CURLINFO_DATA_IN: strText = "<- Receiving unencrypted data : "; break; case CURLINFO_SSL_DATA_IN: strText = "<- Receiving SSL data : "; break; default: break; } std::ofstream* pofTraceFile = reinterpret_cast(pFile); if (pofTraceFile == nullptr) { std::cout << "[DEBUG] cURL debug log [" << curl_info_type << "]: " << " - " << strTrace; } else { (*pofTraceFile) << strText << strTrace; } return 0; } void CHTTPClient::StartCurlDebug() const { if (!m_ofFileCurlTrace.is_open()) { curl_easy_setopt(m_pCurlSession, CURLOPT_VERBOSE, 1L); curl_easy_setopt(m_pCurlSession, CURLOPT_DEBUGFUNCTION, DebugCallback); std::string strFileCurlTraceFullName(s_strCurlTraceLogDirectory); if (!strFileCurlTraceFullName.empty()) { char szDate[32]; memset(szDate, 0, 32); time_t tNow; time(&tNow); // new trace file for each hour strftime(szDate, 32, "%Y%m%d_%H", localtime(&tNow)); strFileCurlTraceFullName += "TraceLog_"; strFileCurlTraceFullName += szDate; strFileCurlTraceFullName += ".txt"; m_ofFileCurlTrace.open(strFileCurlTraceFullName, std::ifstream::app | std::ifstream::binary); if (m_ofFileCurlTrace) curl_easy_setopt(m_pCurlSession, CURLOPT_DEBUGDATA, &m_ofFileCurlTrace); } } } void CHTTPClient::EndCurlDebug() const { if (m_ofFileCurlTrace && m_ofFileCurlTrace.is_open()) { m_ofFileCurlTrace << "###########################################" << std::endl; m_ofFileCurlTrace.close(); } } #endif