http_client.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  1. /*
  2. * client.cpp
  3. *
  4. * Created on: 2021年8月16日
  5. * Author: pengzc
  6. */
  7. #include "http_client.h"
  8. #include <string.h>
  9. #include <algorithm>
  10. #include <signal.h>
  11. #include <unistd.h>
  12. #include <fstream>
  13. #include "utils/Log.h"
  14. #include "manager/ConfigManager.h"
  15. namespace {
  16. class CURLGlobal {
  17. public:
  18. CURLGlobal() {
  19. CURLcode res = curl_global_init(CURL_GLOBAL_ALL);
  20. if (res != CURLE_OK) {
  21. LOGE("CURL curl_global_init failed");
  22. }
  23. //忽略SIGPIPE
  24. struct sigaction sa;
  25. sa.sa_handler = SIG_IGN;
  26. sa.sa_flags = 0;
  27. if (sigemptyset(&sa.sa_mask) == -1 ||
  28. sigaction(SIGPIPE, &sa, 0) == -1) {
  29. LOGE("failed ignore SIGPIPE");
  30. }
  31. }
  32. ~ CURLGlobal() {
  33. curl_global_cleanup();
  34. }
  35. };
  36. } /* anonymous namespace */
  37. namespace base {
  38. HttpClient::HttpClient() {
  39. static CURLGlobal global;
  40. curl_ = curl_easy_init();
  41. if (curl_ == NULL) {
  42. LOGE("curl_easy_init failed");
  43. }
  44. timeout_ = 0;
  45. connection_timeout_ = 0;
  46. ssl_verify_ = true;
  47. aborted_ = false;
  48. low_speed_limit_.bytes = 0;
  49. low_speed_limit_.seconds = 0;
  50. debug_ = false;
  51. ca_info_file_path_ = CONFIGMANAGER->getResFilePath("cacert.pem");
  52. }
  53. HttpClient::~HttpClient() {
  54. if (curl_) {
  55. curl_easy_cleanup(curl_);
  56. }
  57. }
  58. HttpResponse HttpClient::Do(const HttpRequest& request) {
  59. return Do(request, "", NULL);
  60. }
  61. HttpResponse HttpClient::Do(const HttpRequest& request, ProgressCallback progress) {
  62. return Do(request, "", progress);
  63. }
  64. HttpResponse HttpClient::Do(const HttpRequest& request, const std::string& save_path,
  65. ProgressCallback progress) {
  66. Mutex::Autolock lock(do_mutex_);
  67. if (curl_ == NULL) {
  68. return HttpResponse();
  69. }
  70. curl_easy_reset(curl_);
  71. aborted_ = false;
  72. HttpResponse response;
  73. std::string url = request.url_;
  74. std::string header_string;
  75. CURLcode res = CURLE_OK;
  76. /** set query URL */
  77. curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
  78. #if 0
  79. if (request.method_.compare("download") != 0) {
  80. /** set callback function */
  81. curl_easy_setopt(this->curl_, CURLOPT_WRITEFUNCTION,
  82. Helpers::write_callback);
  83. /** set data object to pass to callback function */
  84. curl_easy_setopt(this->curl_, CURLOPT_WRITEDATA, &ret);
  85. } else
  86. #endif
  87. /** set data callback */
  88. curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION,
  89. curl_response_write_callback);
  90. /** set data object to pass to callback function */
  91. curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &response);
  92. if (request.method_.compare("GET") == 0) {
  93. } else if (request.method_.compare("POST") == 0) {
  94. /** Now specify we want to POST data */
  95. curl_easy_setopt(this->curl_, CURLOPT_POST, 1L);
  96. if (!request.body_.empty()) {
  97. /** set post fields */
  98. curl_easy_setopt(this->curl_, CURLOPT_POSTFIELDS,
  99. request.body_.c_str());
  100. curl_easy_setopt(this->curl_, CURLOPT_POSTFIELDSIZE,
  101. request.body_.size());
  102. }
  103. if (!request.multiparts_.empty()) {
  104. struct curl_httppost *formpost = NULL;
  105. struct curl_httppost *lastptr = NULL;
  106. for (auto it = request.multiparts_.begin(); it != request.multiparts_.end(); ++it) {
  107. curl_formadd(&formpost, &lastptr,
  108. CURLFORM_PTRNAME, it->first.c_str(),
  109. CURLFORM_PTRCONTENTS, it->second.data(),
  110. CURLFORM_CONTENTSLENGTH, it->second.length(),
  111. CURLFORM_END);
  112. }
  113. /** Now specify we want to POST data */
  114. curl_easy_setopt(this->curl_, CURLOPT_HTTPPOST, formpost);
  115. }
  116. }
  117. #if 0
  118. /** set the header callback function */
  119. curl_easy_setopt(this->curl_, CURLOPT_HEADERFUNCTION, header_callback);
  120. /** callback object for headers */
  121. curl_easy_setopt(this->curl_, CURLOPT_HEADERDATA, &response);
  122. #endif
  123. /** set http headers */
  124. curl_slist* header_list = NULL;
  125. for (HttpRequest::HeaderFields::const_iterator it = request.header.begin();
  126. it != request.header.end(); ++it) {
  127. header_string = it->first;
  128. header_string += ": ";
  129. header_string += it->second;
  130. curl_slist* temp = curl_slist_append(header_list, header_string.c_str());
  131. if (temp == NULL) {
  132. LOGE("curl_slist_append failed");
  133. curl_slist_free_all(header_list);
  134. header_list = NULL;
  135. break;
  136. }
  137. header_list = temp;
  138. }
  139. if (header_list != NULL) {
  140. curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, header_list);
  141. }
  142. progress_callback_ = progress;
  143. curl_easy_setopt(curl_, CURLOPT_XFERINFOFUNCTION,
  144. HttpClient::curl_progress_callback);
  145. curl_easy_setopt(curl_, CURLOPT_XFERINFODATA, this);
  146. curl_easy_setopt(curl_, CURLOPT_NOPROGRESS, 0);
  147. if ((low_speed_limit_.bytes > 0) && (low_speed_limit_.seconds > 0)) {
  148. curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_LIMIT, low_speed_limit_.bytes);
  149. curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_TIME, low_speed_limit_.seconds);
  150. }
  151. #if 0
  152. // set basic auth if configured
  153. if (this->basicAuth.username.length() > 0) {
  154. std::string authString = std::string(this->basicAuth.username + ":" +
  155. this->basicAuth.password);
  156. curl_easy_setopt(this->curl_, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
  157. curl_easy_setopt(this->curl_, CURLOPT_USERPWD, authString.c_str());
  158. }
  159. #endif
  160. #if 0
  161. /** set user agent */
  162. curl_easy_setopt(this->curl_, CURLOPT_USERAGENT,
  163. this->GetUserAgent().c_str());
  164. #endif
  165. // set timeout
  166. if (connection_timeout_ > 0) {
  167. curl_easy_setopt(this->curl_, CURLOPT_CONNECTTIMEOUT_MS,
  168. connection_timeout_);
  169. // dont want to get a sig alarm on timeout
  170. curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);
  171. }
  172. if (timeout_ > 0) {
  173. curl_easy_setopt(this->curl_, CURLOPT_TIMEOUT_MS, timeout_);
  174. // dont want to get a sig alarm on timeout
  175. curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);
  176. }
  177. //允许重定向
  178. curl_easy_setopt(this->curl_, CURLOPT_FOLLOWLOCATION, 1L);
  179. curl_easy_setopt(this->curl_, CURLOPT_MAXREDIRS, static_cast<int64_t>(9));
  180. //多线程下使用超时禁用SIGNAL
  181. curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L);
  182. //指定DNS解析规范为IPV4
  183. curl_easy_setopt(curl_, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
  184. //指定DNS
  185. curl_easy_setopt(curl_, CURLOPT_DNS_SERVERS,
  186. "1.2.4.8,210.2.4.8,114.114.114.114,119.29.29.29");
  187. // if provided, supply CA path
  188. if (!this->ca_info_file_path_.empty()) {
  189. curl_easy_setopt(curl_, CURLOPT_CAINFO, ca_info_file_path_.c_str());
  190. }
  191. if (!ssl_verify_) {
  192. curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0L);
  193. curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0L);
  194. }
  195. if (debug_) {
  196. curl_easy_setopt(curl_, CURLOPT_DEBUGFUNCTION, debug_trace);
  197. /* the DEBUGFUNCTION has no effect until we enable VERBOSE */
  198. curl_easy_setopt(curl_, CURLOPT_VERBOSE, 1L);
  199. }
  200. if (!save_path.empty()) {
  201. response.dst_file_ = fopen(save_path.c_str(), "wb");
  202. if (response.dst_file_ == NULL) {
  203. LOGE("open %s failed, %s", save_path.c_str(), strerror(errno));
  204. response.error_code_ = errno;
  205. response.error_message_ = strerror(errno);
  206. return response;
  207. }
  208. }
  209. #if 0
  210. // set cert file path
  211. if (!this->certPath.empty()) {
  212. curl_easy_setopt(this->curl_, CURLOPT_SSLCERT,
  213. this->certPath.c_str());
  214. }
  215. // set cert type
  216. if (!this->certType.empty()) {
  217. curl_easy_setopt(this->curl_, CURLOPT_SSLCERTTYPE,
  218. this->certType.c_str());
  219. }
  220. // set key file path
  221. if (!this->keyPath.empty()) {
  222. curl_easy_setopt(this->curl_, CURLOPT_SSLKEY,
  223. this->keyPath.c_str());
  224. }
  225. // set key password
  226. if (!this->keyPassword.empty()) {
  227. curl_easy_setopt(this->curl_, CURLOPT_KEYPASSWD,
  228. this->keyPassword.c_str());
  229. }
  230. // set web proxy address
  231. if (!this->uriProxy.empty()) {
  232. curl_easy_setopt(this->curl_, CURLOPT_PROXY,
  233. uriProxy.c_str());
  234. curl_easy_setopt(this->curl_, CURLOPT_HTTPPROXYTUNNEL,
  235. 1L);
  236. }
  237. #endif
  238. char error_buffer[CURL_ERROR_SIZE] = { 0 };
  239. curl_easy_setopt(curl_, CURLOPT_ERRORBUFFER, error_buffer);
  240. res = curl_easy_perform(curl_);
  241. if (res != CURLE_OK) {
  242. response.error_message_ = error_buffer;
  243. response.error_code_ = res;
  244. } else {
  245. int64_t http_code = 0;
  246. curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &http_code);
  247. response.status_code_ = static_cast<int>(http_code);
  248. }
  249. curl_easy_getinfo(curl_, CURLINFO_TOTAL_TIME,
  250. &response.total_time);
  251. curl_easy_getinfo(this->curl_, CURLINFO_NAMELOOKUP_TIME,
  252. &response.name_lookup_time);
  253. curl_easy_getinfo(this->curl_, CURLINFO_CONNECT_TIME,
  254. &response.connect_time);
  255. curl_easy_getinfo(this->curl_, CURLINFO_APPCONNECT_TIME,
  256. &response.app_connect_time);
  257. curl_easy_getinfo(this->curl_, CURLINFO_PRETRANSFER_TIME,
  258. &response.pre_transfer_time);
  259. curl_easy_getinfo(this->curl_, CURLINFO_STARTTRANSFER_TIME,
  260. &response.start_transfer_time);
  261. curl_easy_getinfo(this->curl_, CURLINFO_REDIRECT_TIME,
  262. &response.redirect_time);
  263. curl_easy_getinfo(this->curl_, CURLINFO_REDIRECT_COUNT,
  264. &response.redirect_count);
  265. if (response.dst_file_ != NULL) {
  266. fflush(response.dst_file_);
  267. fclose(response.dst_file_);
  268. sync();
  269. response.dst_file_ = NULL;
  270. }
  271. // free header list
  272. curl_slist_free_all(header_list);
  273. return response;
  274. }
  275. /**
  276. * @brief set custom Certificate Authority (CA) path
  277. *
  278. * @param caInfoFilePath - The path to a file holding the certificates used to
  279. * verify the peer with. See CURLOPT_CAINFO
  280. *
  281. */
  282. void
  283. HttpClient::SetCAInfoFilePath(const std::string& caInfoFilePath) {
  284. this->ca_info_file_path_ = caInfoFilePath;
  285. }
  286. #if 0
  287. /**
  288. * @brief set username and password for basic auth
  289. *
  290. * @param username
  291. * @param password
  292. *
  293. */
  294. void
  295. HttpClient::SetBasicAuth(const std::string& username,
  296. const std::string& password) {
  297. this->basicAuth.username = username;
  298. this->basicAuth.password = password;
  299. }
  300. #endif
  301. /**
  302. * @brief set certificate path
  303. *
  304. * @param path to certificate file
  305. *
  306. */
  307. //void
  308. //HttpClient::SetCertPath(const std::string& cert) {
  309. // this->certPath = cert;
  310. //}
  311. /**
  312. * @brief set certificate type
  313. *
  314. * @param certificate type (e.g. "PEM" or "DER")
  315. *
  316. */
  317. //void
  318. //HttpClient::SetCertType(const std::string& certType) {
  319. // this->certType = certType;
  320. //}
  321. /**
  322. * @brief set key path
  323. *
  324. * @param path to key file
  325. *
  326. */
  327. //void
  328. //HttpClient::SetKeyPath(const std::string& keyPath) {
  329. // this->keyPath = keyPath;
  330. //}
  331. /**
  332. * @brief set key password
  333. *
  334. * @param key password
  335. *
  336. */
  337. //void
  338. //HttpClient::SetKeyPassword(const std::string& keyPassword) {
  339. // this->keyPassword = keyPassword;
  340. //}
  341. /**
  342. * @brief set HTTP proxy address and port
  343. *
  344. * @param proxy address with port number
  345. *
  346. */
  347. //void
  348. //HttpClient::SetProxy(const std::string& uriProxy) {
  349. // std::string uriProxyUpper = uriProxy;
  350. // // check if the provided address is prefixed with "http"
  351. // std::transform(uriProxyUpper.begin(), uriProxyUpper.end(),
  352. // uriProxyUpper.begin(), ::toupper);
  353. //
  354. // if ((uriProxy.length() > 0) && (uriProxyUpper.compare(0, 4, "HTTP") != 0)) {
  355. // this->uriProxy = "http://" + uriProxy;
  356. // } else {
  357. // this->uriProxy = uriProxy;
  358. // }
  359. //}
  360. /**
  361. * @brief helper function to get called from the actual request methods to
  362. * prepare the curlHandle for transfer with generic options, perform the
  363. * request and record some stats from the last request and then reset the
  364. * handle with curl_easy_reset to its default state. This will keep things
  365. * like connections and session ID intact but makes sure you can change
  366. * parameters on the object for another request.
  367. *
  368. * @param uri URI to query
  369. * @param ret Reference to the Response struct that should be filled
  370. *
  371. * @return 0 on success and 1 on error
  372. */
  373. //Response
  374. //HttpClient::performCurlRequest(const std::string& uri, const std::string method_type) {}
  375. /**
  376. * @brief HTTP GET method
  377. *
  378. * @param url to query
  379. *
  380. * @return response struct
  381. */
  382. //Response
  383. //HttpClient::get(const std::string& url) {
  384. // return this->performCurlRequest(url);
  385. //}
  386. /**
  387. * @brief HTTP POST method
  388. *
  389. * @param url to query
  390. * @param data HTTP POST body
  391. *
  392. * @return response struct
  393. */
  394. //Response
  395. //HttpClient::post(const std::string& url,
  396. // const std::string& data) {
  397. // /** Now specify we want to POST data */
  398. // curl_easy_setopt(this->curlHandle, CURLOPT_POST, 1L);
  399. // /** set post fields */
  400. // curl_easy_setopt(this->curlHandle, CURLOPT_POSTFIELDS, data.c_str());
  401. // curl_easy_setopt(this->curlHandle, CURLOPT_POSTFIELDSIZE, data.size());
  402. //
  403. // return this->performCurlRequest(url);
  404. //}
  405. //Response
  406. //HttpClient::post(const std::string& url,
  407. // const std::map<std::string, std::string>& form_data) {
  408. //
  409. // struct curl_httppost *formpost = 0;
  410. // struct curl_httppost *lastptr = 0;
  411. // for (auto it = form_data.begin(); it != form_data.end(); ++it) {
  412. // curl_formadd(&formpost, &lastptr,
  413. // CURLFORM_PTRNAME, it->first.c_str(),
  414. // CURLFORM_PTRCONTENTS, it->second.data(),
  415. // CURLFORM_CONTENTSLENGTH, it->second.length(),
  416. // CURLFORM_END);
  417. // }
  418. //
  419. // /** Now specify we want to POST data */
  420. // curl_easy_setopt(this->curlHandle, CURLOPT_HTTPPOST, formpost);
  421. //
  422. // Response response = this->performCurlRequest(url);
  423. // curl_formfree(formpost);
  424. // return response;
  425. //}
  426. HttpClient& HttpClient::SetConnectionTimeout(int ms) {
  427. connection_timeout_ = ms;
  428. return *this;
  429. }
  430. HttpClient& HttpClient::SetTimeout(int ms) {
  431. timeout_ = ms;
  432. return *this;
  433. }
  434. // trim from start
  435. static inline std::string &ltrim(std::string &s) { // NOLINT
  436. s.erase(s.begin(), std::find_if(s.begin(), s.end(),
  437. std::not1(std::ptr_fun<int, int>(std::isspace))));
  438. return s;
  439. }
  440. // trim from end
  441. static inline std::string &rtrim(std::string &s) { // NOLINT
  442. s.erase(std::find_if(s.rbegin(), s.rend(),
  443. std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
  444. return s;
  445. }
  446. // trim from both ends
  447. static inline std::string &trim(std::string &s) { // NOLINT
  448. return ltrim(rtrim(s));
  449. }
  450. size_t HttpClient::curl_response_write_callback(void* data, size_t size,
  451. size_t nmemb, void* userdata) {
  452. HttpResponse* response = (HttpResponse*) userdata;
  453. if (response->dst_file_ == NULL) {
  454. response->body_.append(reinterpret_cast<char*>(data), size * nmemb);
  455. return size * nmemb;
  456. }
  457. return fwrite(reinterpret_cast<char*>(data), 1, size * nmemb,
  458. response->dst_file_);
  459. }
  460. size_t HttpClient::header_callback(void *data, size_t size,
  461. size_t nmemb, void *userdata) {
  462. HttpResponse* r = (HttpResponse*)userdata;
  463. std::string header(reinterpret_cast<char*>(data), size*nmemb);
  464. //LOGD("Header Callback [%s]", trim(header).c_str());
  465. size_t seperator = header.find_first_of(':');
  466. if ( std::string::npos == seperator ) {
  467. // roll with non seperated headers...
  468. trim(header);
  469. if (0 == header.length()) {
  470. return (size * nmemb); // blank line;
  471. }
  472. r->header_[header] = "present";
  473. } else {
  474. std::string key = header.substr(0, seperator);
  475. trim(key);
  476. std::string value = header.substr(seperator + 1);
  477. trim(value);
  478. auto it = r->header_.find(key);
  479. if (it == r->header_.end()) {
  480. //LOGD("It's new Head Field %s", key.c_str());
  481. r->header_[key] = value;
  482. } else {
  483. //LOGD("Duplicate Head Field %s", key.c_str());
  484. }
  485. //LOGD("headers size %d", r->header_.size());
  486. // LOGD("HHHHHHHHHHHHHead%p %s %s", r, key.c_str(), value.c_str());
  487. }
  488. return (size * nmemb);
  489. }
  490. void HttpClient::SetSSLVerify(bool verify) {
  491. ssl_verify_ = verify;
  492. }
  493. void HttpClient::Abort() {
  494. aborted_ = true;
  495. Mutex::Autolock lock(do_mutex_);
  496. }
  497. int HttpClient::curl_progress_callback(void* data, curl_off_t dltotal,
  498. curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) {
  499. HttpClient* client = (HttpClient*)data;
  500. if (client->aborted_) {
  501. return -1;
  502. }
  503. if (client->progress_callback_ != NULL) {
  504. return client->progress_callback_(dltotal, dlnow, ultotal, ulnow);
  505. }
  506. return 0;
  507. }
  508. static void dump(const char *text, unsigned char *ptr, size_t size) {
  509. LOGD("%s, %10.10ld bytes (0x%8.8lx)\n",
  510. text, (long)size, (long)size);
  511. }
  512. int HttpClient::debug_trace(CURL *handle, curl_infotype type, char *data, ::size_t size,
  513. void *userp) {
  514. const char *text;
  515. (void)handle; /* prevent compiler warning */
  516. (void)userp;
  517. switch (type) {
  518. case CURLINFO_TEXT:
  519. LOGD("== Info: %s", data);
  520. default: /* in case a new one is introduced to shock us */
  521. return 0;
  522. case CURLINFO_HEADER_OUT:
  523. text = "=> Send header";
  524. break;
  525. case CURLINFO_DATA_OUT:
  526. text = "=> Send data";
  527. break;
  528. case CURLINFO_SSL_DATA_OUT:
  529. text = "=> Send SSL data";
  530. break;
  531. case CURLINFO_HEADER_IN:
  532. text = "<= Recv header";
  533. break;
  534. case CURLINFO_DATA_IN:
  535. text = "<= Recv data";
  536. break;
  537. case CURLINFO_SSL_DATA_IN:
  538. text = "<= Recv SSL data";
  539. break;
  540. }
  541. dump(text, (unsigned char *)data, size);
  542. return 0;
  543. }
  544. HttpClient& HttpClient::SetLowSpeedLimit(int bytes, int seconds) {
  545. low_speed_limit_.bytes = bytes;
  546. low_speed_limit_.seconds = seconds;
  547. return *this;
  548. }
  549. void HttpClient::SetDebug(bool debug) {
  550. debug_ = debug;
  551. }
  552. } /* namespace base */