123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638 |
- /*
- * client.cpp
- *
- * Created on: 2021年8月16日
- * Author: pengzc
- */
- #include "http_client.h"
- #include <string.h>
- #include <algorithm>
- #include <signal.h>
- #include <unistd.h>
- #include <fstream>
- #include "utils/Log.h"
- #include "manager/ConfigManager.h"
- namespace {
- class CURLGlobal {
- public:
- CURLGlobal() {
- CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
- if (res != CURLE_OK) {
- LOGE("CURL curl_global_init failed");
- }
- //忽略SIGPIPE
- struct sigaction sa;
- sa.sa_handler = SIG_IGN;
- sa.sa_flags = 0;
- if (sigemptyset(&sa.sa_mask) == -1 ||
- sigaction(SIGPIPE, &sa, 0) == -1) {
- LOGE("failed ignore SIGPIPE");
- }
- }
- ~ CURLGlobal() {
- curl_global_cleanup();
- }
- };
- } /* anonymous namespace */
- namespace base {
- HttpClient::HttpClient() {
- static CURLGlobal global;
- curl_ = curl_easy_init();
- if (curl_ == NULL) {
- LOGE("curl_easy_init failed");
- }
- timeout_ = 0;
- connection_timeout_ = 0;
- ssl_verify_ = true;
- aborted_ = false;
- low_speed_limit_.bytes = 0;
- low_speed_limit_.seconds = 0;
- debug_ = false;
- ca_info_file_path_ = CONFIGMANAGER->getResFilePath("cacert.pem");
- }
- HttpClient::~HttpClient() {
- if (curl_) {
- curl_easy_cleanup(curl_);
- }
- }
- HttpResponse HttpClient::Do(const HttpRequest& request) {
- return Do(request, "", NULL);
- }
- HttpResponse HttpClient::Do(const HttpRequest& request, ProgressCallback progress) {
- return Do(request, "", progress);
- }
- HttpResponse HttpClient::Do(const HttpRequest& request, const std::string& save_path,
- ProgressCallback progress) {
- Mutex::Autolock lock(do_mutex_);
- if (curl_ == NULL) {
- return HttpResponse();
- }
- curl_easy_reset(curl_);
- aborted_ = false;
- HttpResponse response;
- std::string url = request.url_;
- std::string header_string;
- CURLcode res = CURLE_OK;
- /** set query URL */
- curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
- #if 0
- if (request.method_.compare("download") != 0) {
- /** set callback function */
- curl_easy_setopt(this->curl_, CURLOPT_WRITEFUNCTION,
- Helpers::write_callback);
- /** set data object to pass to callback function */
- curl_easy_setopt(this->curl_, CURLOPT_WRITEDATA, &ret);
- } else
- #endif
- /** set data callback */
- curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION,
- curl_response_write_callback);
- /** set data object to pass to callback function */
- curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response);
- if (request.method_.compare("GET") == 0) {
- } else if (request.method_.compare("POST") == 0) {
- /** Now specify we want to POST data */
- curl_easy_setopt(this->curl_, CURLOPT_POST, 1L);
- if (!request.body_.empty()) {
- /** set post fields */
- curl_easy_setopt(this->curl_, CURLOPT_POSTFIELDS,
- request.body_.c_str());
- curl_easy_setopt(this->curl_, CURLOPT_POSTFIELDSIZE,
- request.body_.size());
- }
- if (!request.multiparts_.empty()) {
- struct curl_httppost *formpost = NULL;
- struct curl_httppost *lastptr = NULL;
- for (auto it = request.multiparts_.begin(); it != request.multiparts_.end(); ++it) {
- curl_formadd(&formpost, &lastptr,
- CURLFORM_PTRNAME, it->first.c_str(),
- CURLFORM_PTRCONTENTS, it->second.data(),
- CURLFORM_CONTENTSLENGTH, it->second.length(),
- CURLFORM_END);
- }
- /** Now specify we want to POST data */
- curl_easy_setopt(this->curl_, CURLOPT_HTTPPOST, formpost);
- }
- }
- #if 0
- /** set the header callback function */
- curl_easy_setopt(this->curl_, CURLOPT_HEADERFUNCTION, header_callback);
- /** callback object for headers */
- curl_easy_setopt(this->curl_, CURLOPT_HEADERDATA, &response);
- #endif
- /** set http headers */
- curl_slist* header_list = NULL;
- for (HttpRequest::HeaderFields::const_iterator it = request.header.begin();
- it != request.header.end(); ++it) {
- header_string = it->first;
- header_string += ": ";
- header_string += it->second;
- curl_slist* temp = curl_slist_append(header_list, header_string.c_str());
- if (temp == NULL) {
- LOGE("curl_slist_append failed");
- curl_slist_free_all(header_list);
- header_list = NULL;
- break;
- }
- header_list = temp;
- }
- if (header_list != NULL) {
- curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, header_list);
- }
- progress_callback_ = progress;
- curl_easy_setopt(curl_, CURLOPT_XFERINFOFUNCTION,
- HttpClient::curl_progress_callback);
- curl_easy_setopt(curl_, CURLOPT_XFERINFODATA, this);
- curl_easy_setopt(curl_, CURLOPT_NOPROGRESS, 0);
- if ((low_speed_limit_.bytes > 0) && (low_speed_limit_.seconds > 0)) {
- curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_LIMIT, low_speed_limit_.bytes);
- curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_TIME, low_speed_limit_.seconds);
- }
- #if 0
- // set basic auth if configured
- if (this->basicAuth.username.length() > 0) {
- std::string authString = std::string(this->basicAuth.username + ":" +
- this->basicAuth.password);
- curl_easy_setopt(this->curl_, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
- curl_easy_setopt(this->curl_, CURLOPT_USERPWD, authString.c_str());
- }
- #endif
- #if 0
- /** set user agent */
- curl_easy_setopt(this->curl_, CURLOPT_USERAGENT,
- this->GetUserAgent().c_str());
- #endif
- // set timeout
- if (connection_timeout_ > 0) {
- curl_easy_setopt(this->curl_, CURLOPT_CONNECTTIMEOUT_MS,
- connection_timeout_);
- // dont want to get a sig alarm on timeout
- curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);
- }
- if (timeout_ > 0) {
- curl_easy_setopt(this->curl_, CURLOPT_TIMEOUT_MS, timeout_);
- // dont want to get a sig alarm on timeout
- curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);
- }
- //允许重定向
- curl_easy_setopt(this->curl_, CURLOPT_FOLLOWLOCATION, 1L);
- curl_easy_setopt(this->curl_, CURLOPT_MAXREDIRS, static_cast<int64_t>(9));
- //多线程下使用超时禁用SIGNAL
- curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);
- //指定DNS解析规范为IPV4
- curl_easy_setopt(curl_, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
- //指定DNS
- curl_easy_setopt(curl_, CURLOPT_DNS_SERVERS,
- "1.2.4.8,210.2.4.8,114.114.114.114,119.29.29.29");
- // if provided, supply CA path
- if (!this->ca_info_file_path_.empty()) {
- curl_easy_setopt(curl_, CURLOPT_CAINFO, ca_info_file_path_.c_str());
- }
- if (!ssl_verify_) {
- curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
- curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L);
- }
- if (debug_) {
- curl_easy_setopt(curl_, CURLOPT_DEBUGFUNCTION, debug_trace);
- /* the DEBUGFUNCTION has no effect until we enable VERBOSE */
- curl_easy_setopt(curl_, CURLOPT_VERBOSE, 1L);
- }
- if (!save_path.empty()) {
- response.dst_file_ = fopen(save_path.c_str(), "wb");
- if (response.dst_file_ == NULL) {
- LOGE("open %s failed, %s", save_path.c_str(), strerror(errno));
- response.error_code_ = errno;
- response.error_message_ = strerror(errno);
- return response;
- }
- }
- #if 0
- // set cert file path
- if (!this->certPath.empty()) {
- curl_easy_setopt(this->curl_, CURLOPT_SSLCERT,
- this->certPath.c_str());
- }
- // set cert type
- if (!this->certType.empty()) {
- curl_easy_setopt(this->curl_, CURLOPT_SSLCERTTYPE,
- this->certType.c_str());
- }
- // set key file path
- if (!this->keyPath.empty()) {
- curl_easy_setopt(this->curl_, CURLOPT_SSLKEY,
- this->keyPath.c_str());
- }
- // set key password
- if (!this->keyPassword.empty()) {
- curl_easy_setopt(this->curl_, CURLOPT_KEYPASSWD,
- this->keyPassword.c_str());
- }
- // set web proxy address
- if (!this->uriProxy.empty()) {
- curl_easy_setopt(this->curl_, CURLOPT_PROXY,
- uriProxy.c_str());
- curl_easy_setopt(this->curl_, CURLOPT_HTTPPROXYTUNNEL,
- 1L);
- }
- #endif
- char error_buffer[CURL_ERROR_SIZE] = { 0 };
- curl_easy_setopt(curl_, CURLOPT_ERRORBUFFER, error_buffer);
- res = curl_easy_perform(curl_);
- if (res != CURLE_OK) {
- response.error_message_ = error_buffer;
- response.error_code_ = res;
- } else {
- int64_t http_code = 0;
- curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &http_code);
- response.status_code_ = static_cast<int>(http_code);
- }
- curl_easy_getinfo(curl_, CURLINFO_TOTAL_TIME,
- &response.total_time);
- curl_easy_getinfo(this->curl_, CURLINFO_NAMELOOKUP_TIME,
- &response.name_lookup_time);
- curl_easy_getinfo(this->curl_, CURLINFO_CONNECT_TIME,
- &response.connect_time);
- curl_easy_getinfo(this->curl_, CURLINFO_APPCONNECT_TIME,
- &response.app_connect_time);
- curl_easy_getinfo(this->curl_, CURLINFO_PRETRANSFER_TIME,
- &response.pre_transfer_time);
- curl_easy_getinfo(this->curl_, CURLINFO_STARTTRANSFER_TIME,
- &response.start_transfer_time);
- curl_easy_getinfo(this->curl_, CURLINFO_REDIRECT_TIME,
- &response.redirect_time);
- curl_easy_getinfo(this->curl_, CURLINFO_REDIRECT_COUNT,
- &response.redirect_count);
- if (response.dst_file_ != NULL) {
- fflush(response.dst_file_);
- fclose(response.dst_file_);
- sync();
- response.dst_file_ = NULL;
- }
- // free header list
- curl_slist_free_all(header_list);
- return response;
- }
- /**
- * @brief set custom Certificate Authority (CA) path
- *
- * @param caInfoFilePath - The path to a file holding the certificates used to
- * verify the peer with. See CURLOPT_CAINFO
- *
- */
- void
- HttpClient::SetCAInfoFilePath(const std::string& caInfoFilePath) {
- this->ca_info_file_path_ = caInfoFilePath;
- }
- #if 0
- /**
- * @brief set username and password for basic auth
- *
- * @param username
- * @param password
- *
- */
- void
- HttpClient::SetBasicAuth(const std::string& username,
- const std::string& password) {
- this->basicAuth.username = username;
- this->basicAuth.password = password;
- }
- #endif
- /**
- * @brief set certificate path
- *
- * @param path to certificate file
- *
- */
- //void
- //HttpClient::SetCertPath(const std::string& cert) {
- // this->certPath = cert;
- //}
- /**
- * @brief set certificate type
- *
- * @param certificate type (e.g. "PEM" or "DER")
- *
- */
- //void
- //HttpClient::SetCertType(const std::string& certType) {
- // this->certType = certType;
- //}
- /**
- * @brief set key path
- *
- * @param path to key file
- *
- */
- //void
- //HttpClient::SetKeyPath(const std::string& keyPath) {
- // this->keyPath = keyPath;
- //}
- /**
- * @brief set key password
- *
- * @param key password
- *
- */
- //void
- //HttpClient::SetKeyPassword(const std::string& keyPassword) {
- // this->keyPassword = keyPassword;
- //}
- /**
- * @brief set HTTP proxy address and port
- *
- * @param proxy address with port number
- *
- */
- //void
- //HttpClient::SetProxy(const std::string& uriProxy) {
- // std::string uriProxyUpper = uriProxy;
- // // check if the provided address is prefixed with "http"
- // std::transform(uriProxyUpper.begin(), uriProxyUpper.end(),
- // uriProxyUpper.begin(), ::toupper);
- //
- // if ((uriProxy.length() > 0) && (uriProxyUpper.compare(0, 4, "HTTP") != 0)) {
- // this->uriProxy = "http://" + uriProxy;
- // } else {
- // this->uriProxy = uriProxy;
- // }
- //}
- /**
- * @brief helper function to get called from the actual request methods to
- * prepare the curlHandle for transfer with generic options, perform the
- * request and record some stats from the last request and then reset the
- * handle with curl_easy_reset to its default state. This will keep things
- * like connections and session ID intact but makes sure you can change
- * parameters on the object for another request.
- *
- * @param uri URI to query
- * @param ret Reference to the Response struct that should be filled
- *
- * @return 0 on success and 1 on error
- */
- //Response
- //HttpClient::performCurlRequest(const std::string& uri, const std::string method_type) {}
- /**
- * @brief HTTP GET method
- *
- * @param url to query
- *
- * @return response struct
- */
- //Response
- //HttpClient::get(const std::string& url) {
- // return this->performCurlRequest(url);
- //}
- /**
- * @brief HTTP POST method
- *
- * @param url to query
- * @param data HTTP POST body
- *
- * @return response struct
- */
- //Response
- //HttpClient::post(const std::string& url,
- // const std::string& data) {
- // /** Now specify we want to POST data */
- // curl_easy_setopt(this->curlHandle, CURLOPT_POST, 1L);
- // /** set post fields */
- // curl_easy_setopt(this->curlHandle, CURLOPT_POSTFIELDS, data.c_str());
- // curl_easy_setopt(this->curlHandle, CURLOPT_POSTFIELDSIZE, data.size());
- //
- // return this->performCurlRequest(url);
- //}
- //Response
- //HttpClient::post(const std::string& url,
- // const std::map<std::string, std::string>& form_data) {
- //
- // struct curl_httppost *formpost = 0;
- // struct curl_httppost *lastptr = 0;
- // for (auto it = form_data.begin(); it != form_data.end(); ++it) {
- // curl_formadd(&formpost, &lastptr,
- // CURLFORM_PTRNAME, it->first.c_str(),
- // CURLFORM_PTRCONTENTS, it->second.data(),
- // CURLFORM_CONTENTSLENGTH, it->second.length(),
- // CURLFORM_END);
- // }
- //
- // /** Now specify we want to POST data */
- // curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPOST, formpost);
- //
- // Response response = this->performCurlRequest(url);
- // curl_formfree(formpost);
- // return response;
- //}
- HttpClient& HttpClient::SetConnectionTimeout(int ms) {
- connection_timeout_ = ms;
- return *this;
- }
- HttpClient& HttpClient::SetTimeout(int ms) {
- timeout_ = ms;
- return *this;
- }
- // trim from start
- static inline std::string <rim(std::string &s) { // NOLINT
- s.erase(s.begin(), std::find_if(s.begin(), s.end(),
- std::not1(std::ptr_fun<int, int>(std::isspace))));
- return s;
- }
- // trim from end
- static inline std::string &rtrim(std::string &s) { // NOLINT
- s.erase(std::find_if(s.rbegin(), s.rend(),
- std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
- return s;
- }
- // trim from both ends
- static inline std::string &trim(std::string &s) { // NOLINT
- return ltrim(rtrim(s));
- }
- size_t HttpClient::curl_response_write_callback(void* data, size_t size,
- size_t nmemb, void* userdata) {
- HttpResponse* response = (HttpResponse*) userdata;
- if (response->dst_file_ == NULL) {
- response->body_.append(reinterpret_cast<char*>(data), size * nmemb);
- return size * nmemb;
- }
- return fwrite(reinterpret_cast<char*>(data), 1, size * nmemb,
- response->dst_file_);
- }
- size_t HttpClient::header_callback(void *data, size_t size,
- size_t nmemb, void *userdata) {
- HttpResponse* r = (HttpResponse*)userdata;
- std::string header(reinterpret_cast<char*>(data), size*nmemb);
- //LOGD("Header Callback [%s]", trim(header).c_str());
- size_t seperator = header.find_first_of(':');
- if ( std::string::npos == seperator ) {
- // roll with non seperated headers...
- trim(header);
- if (0 == header.length()) {
- return (size * nmemb); // blank line;
- }
- r->header_[header] = "present";
- } else {
- std::string key = header.substr(0, seperator);
- trim(key);
- std::string value = header.substr(seperator + 1);
- trim(value);
- auto it = r->header_.find(key);
- if (it == r->header_.end()) {
- //LOGD("It's new Head Field %s", key.c_str());
- r->header_[key] = value;
- } else {
- //LOGD("Duplicate Head Field %s", key.c_str());
- }
- //LOGD("headers size %d", r->header_.size());
- // LOGD("HHHHHHHHHHHHHead%p %s %s", r, key.c_str(), value.c_str());
- }
- return (size * nmemb);
- }
- void HttpClient::SetSSLVerify(bool verify) {
- ssl_verify_ = verify;
- }
- void HttpClient::Abort() {
- aborted_ = true;
- Mutex::Autolock lock(do_mutex_);
- }
- int HttpClient::curl_progress_callback(void* data, curl_off_t dltotal,
- curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
- HttpClient* client = (HttpClient*)data;
- if (client->aborted_) {
- return -1;
- }
- if (client->progress_callback_ != NULL) {
- return client->progress_callback_(dltotal, dlnow, ultotal, ulnow);
- }
- return 0;
- }
- static void dump(const char *text, unsigned char *ptr, size_t size) {
- LOGD("%s, %10.10ld bytes (0x%8.8lx)\n",
- text, (long)size, (long)size);
- }
- int HttpClient::debug_trace(CURL *handle, curl_infotype type, char *data, ::size_t size,
- void *userp) {
- const char *text;
- (void)handle; /* prevent compiler warning */
- (void)userp;
- switch (type) {
- case CURLINFO_TEXT:
- LOGD("== Info: %s", data);
- default: /* in case a new one is introduced to shock us */
- return 0;
- case CURLINFO_HEADER_OUT:
- text = "=> Send header";
- break;
- case CURLINFO_DATA_OUT:
- text = "=> Send data";
- break;
- case CURLINFO_SSL_DATA_OUT:
- text = "=> Send SSL data";
- break;
- case CURLINFO_HEADER_IN:
- text = "<= Recv header";
- break;
- case CURLINFO_DATA_IN:
- text = "<= Recv data";
- break;
- case CURLINFO_SSL_DATA_IN:
- text = "<= Recv SSL data";
- break;
- }
- dump(text, (unsigned char *)data, size);
- return 0;
- }
- HttpClient& HttpClient::SetLowSpeedLimit(int bytes, int seconds) {
- low_speed_limit_.bytes = bytes;
- low_speed_limit_.seconds = seconds;
- return *this;
- }
- void HttpClient::SetDebug(bool debug) {
- debug_ = debug;
- }
- } /* namespace base */
|