// 这个接口里面设置的参数选项已经完全覆盖了 Get、Post、HttpPost(文件上传)操作
int HttpClient::initCurl(const HttpClientUrlArgs &urlArgs) {
auto headers = urlArgs.headers;
std::vector<std::string> ips;
if (XXX::XXX::GetInstance().GetProxyConfigInfo().m_httpDnsEnable) {
ips = XXX::XXX::GetInstance().GetIpsByHostName(urlArgs.hostName);
}
if (ips.empty()) {
m_requestUrl = XXX::SplicingUrl(urlArgs.protocol, urlArgs.hostName, urlArgs.port, urlArgs.url, urlArgs.args);
} else {
m_requestUrl = XXX::SplicingUrl(urlArgs.protocol, ips[RandomNum()%ips.size()], urlArgs.port, urlArgs.url, urlArgs.args);
headers.emplace("Host", urlArgs.hostName);
}
if (m_pCurl != nullptr) {
curl_easy_cleanup(m_pCurl);
m_pCurl = nullptr;
}
if (m_pHeadList != nullptr) {
curl_slist_free_all(m_pHeadList);
m_pHeadList = nullptr;
}
m_pCurl = curl_easy_init();
if (m_pCurl == nullptr) {
return -1;
}
curl_easy_setopt(m_pCurl, CURLOPT_URL, m_requestUrl.c_str()); // 请求地址
curl_easy_setopt(m_pCurl, CURLOPT_ACCEPT_ENCODING, "");
curl_easy_setopt(m_pCurl, CURLOPT_NOSIGNAL, 1L);
curl_easy_setopt(m_pCurl, CURLOPT_TIMEOUT, 10L);
curl_easy_setopt(m_pCurl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(m_pCurl, CURLOPT_SSL_VERIFYPEER, false);
curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, HttpClientOnWriteCallback); // 写回调:如Post、HttpPost
curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, this);
curl_easy_setopt(m_pCurl, CURLOPT_READFUNCTION, HttpClientOnReadCallback); // 读回调:如Get
curl_easy_setopt(m_pCurl, CURLOPT_READDATA, this);
curl_easy_setopt(m_pCurl, CURLOPT_MAXREDIRS, 10L);
curl_easy_setopt(m_pCurl, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(m_pCurl, CURLOPT_NOPROGRESS, 0L);
curl_easy_setopt(m_pCurl, CURLOPT_DNS_USE_GLOBAL_CACHE, 0L);
curl_easy_setopt(m_pCurl, CURLOPT_PROGRESSDATA, this);
curl_easy_setopt(m_pCurl, CURLOPT_PROGRESSFUNCTION, HttpClientOnProgressCallback);
curl_easy_setopt(m_pCurl, CURLOPT_FAILONERROR, 1L);
m_pHeadList = curl_slist_append(m_pHeadList, "Expect:");
if (!headers.empty()) {
for (const auto& it : headers) {
auto tmp = it.first + ": " + it.second;
m_pHeadList = curl_slist_append(m_pHeadList, tmp.c_str());
}
}
curl_easy_setopt(m_pCurl, CURLOPT_HTTPHEADER, m_pHeadList);
return 0;
}
int HttpClient::OnProgressCallback(double dt, double dn, double ult, double uln) {
if (m_isExit != nullptr && m_isExit()) {
return -1;
}
return 0;
}
// 读回调
size_t HttpClient::OnReadCallback(void *buffer, size_t size, size_t nmemb, void *arg){
return nmemb*size;
}
// 写回调
size_t HttpClient::OnWriteCallback(void *buffer, size_t size, size_t nmemb, void *arg) {
if (buffer != nullptr && size > 0 && nmemb > 0) {
m_result.append((char*)buffer, size*nmemb);
}
return size*nmemb;
}
// 获取必要的参数 具体看需求
void HttpClient::UpdateInfo() {
if (m_pCurl == nullptr) {
return;
}
double dnsTime = 0;
if (curl_easy_getinfo(m_pCurl, CURLINFO_NAMELOOKUP_TIME, &dnsTime) == CURLE_OK) {
m_iDnsTime = dnsTime*1000;
}
long code = 0;
if (curl_easy_getinfo(m_pCurl, CURLINFO_RESPONSE_CODE, &code) == CURLE_OK) {
m_iCurlCode = code;
}
char *ip = nullptr;
if ((curl_easy_getinfo(m_pCurl, CURLINFO_PRIMARY_IP, &ip) == CURLE_OK) && (ip != nullptr)) {
m_remoteIp = ip;
}
double temp = 0.0;
const unsigned int k_dwThousand = 1000;
if (curl_easy_getinfo(m_pCurl, CURLINFO_TOTAL_TIME, &temp) == CURLE_OK) {
m_interactionTime.dwTotalTime = (uint32_t)(temp * k_dwThousand);
}
if (curl_easy_getinfo(m_pCurl, CURLINFO_NAMELOOKUP_TIME, &temp) == CURLE_OK) {
m_interactionTime.dwNameLookupTime = (uint32_t)(temp * k_dwThousand);
}
if (curl_easy_getinfo(m_pCurl, CURLINFO_CONNECT_TIME, &temp) == CURLE_OK) {
m_interactionTime.dwConnectTime = (uint32_t)(temp * k_dwThousand);
}
if (curl_easy_getinfo(m_pCurl, CURLINFO_PRETRANSFER_TIME, &temp) == CURLE_OK) {
m_interactionTime.dwPretransferTime = (uint32_t)(temp * k_dwThousand);
}
if (curl_easy_getinfo(m_pCurl, CURLINFO_FILETIME, &m_interactionTime.lFileTime) == CURLE_OK) {
}
if (curl_easy_getinfo(m_pCurl, CURLINFO_STARTTRANSFER_TIME, &temp) == CURLE_OK) {
m_interactionTime.dwStartTransferTime = (uint32_t)(temp * k_dwThousand);
}
if (curl_easy_getinfo(m_pCurl, CURLINFO_REDIRECT_TIME, &temp) == CURLE_OK) {
m_interactionTime.dwRedirectTime = (uint32_t)(temp * k_dwThousand);
}
if (curl_easy_getinfo(m_pCurl, CURLINFO_APPCONNECT_TIME, &temp) == CURLE_OK) {
m_interactionTime.dwAppconnetTime = (uint32_t)(temp * k_dwThousand);
}
}
// Get操作
int HttpClient::GetData(std::string &result, const HttpClientUrlArgs &urlArgs) {
if (initCurl(urlArgs) != 0) {
xerror2(TSF"init curl error!");
return -1;
}
m_result.clear();
auto res = curl_easy_perform(m_pCurl);
if (res != CURLE_OK) {
xerror2(TSF"url: %_, res: %_", m_requestUrl.c_str(), res);
}
result.clear();
result = std::move(m_result);
UpdateInfo();
return res;
}
// Post操作
int HttpClient::PostData(std::string &result, const HttpClientUrlArgs &urlArgs, const std::string &content) {
result.clear();
if (initCurl(urlArgs) != 0) {
xerror2(TSF"init curl error!");
return -1;
}
curl_easy_setopt(m_pCurl, CURLOPT_POST, true);
curl_easy_setopt(m_pCurl, CURLOPT_POSTFIELDS, content.c_str());
m_result.clear();
auto res = curl_easy_perform(m_pCurl);
if (res != CURLE_OK) {
xerror2(TSF"url: %_, res: %_", m_requestUrl.c_str(), res);
}
result = std::move(m_result);
UpdateInfo();
return res;
}
// 上传文件操作
int HttpClient::PostBinaryData(std::string &result, const HttpClientUrlArgs &urlArgs, const std::string &filePath, const std::string &fileName) {
if (initCurl(urlArgs) != 0) {
xerror2(TSF"init curl error!");
return -1;
}
if (m_pFormpost != nullptr) {
curl_formfree(m_pFormpost);
m_pFormpost = nullptr;
}
struct curl_httppost *lastPtr = nullptr;
// 注意参数顺序, 这块踩过坑; 之前因为参数顺序问题查了好久
curl_formadd(&m_pFormpost, &lastPtr, CURLFORM_PTRNAME, "log_file", CURLFORM_FILENAME, fileName.c_str(), CURLFORM_FILE, filePath.c_str(), CURLFORM_END);
// 跟服务端定 需要什么字段, 就如下写法加什么字段
curl_formadd(&m_pFormpost, &lastPtr, CURLFORM_PTRNAME, "client_id", CURLFORM_PTRCONTENTS, XXX.c_str(), CURLFORM_END);
curl_easy_setopt(m_pCurl, CURLOPT_HTTPPOST, m_pFormpost);
m_result.clear();
auto res = curl_easy_perform(m_pCurl);
if (res != CURLE_OK) {
xerror2(TSF"curl easy perform error, url: %_, res: %_", m_requestUrl.c_str(), res);
return res;
}
// 获取WriteCallBack中的结果信息来判断这次执行是否成功以及查询出错原因
result = std::move(m_result);
UpdateInfo();
return 0;
}
// 上传文件部分调用核心代码
HttpClientUrlArgs urlArgs;
...
// Content-Type格式——上传文件
urlArgs.headers["Content-Type"] = "multipart/form-data";
// Content-Type格式——Post字符串
urlArgs.headers["Content-Type"] = "text/plain"; // 一般是内存中字符串, 如日志上传
urlArgs.headers["Content-Type"] = "application/json"; // 默认的json格式
// Content-Type格式——Get操作
urlArgs.headers["Content-Type"] = 可以不指定这个字段, 走默认"application/json";
// 注意: Curl提供了客户线程退出标识检查机制(新版本废弃)
int HttpClient::OnProgressCallback(double dt, double dn, double ult, double uln) {
if (m_isExit != nullptr && m_isExit()) {
return -1;
}
return 0;
}
// 如下设置回调isExit标识当前线程是否退出
...
std::shared_ptr<HttpClient> httpClient = std::make_shared<HttpClient>(XXX::bind(&XXX::isExit, this));
...