CurlClient.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. #include "CurlClient.h"
  2. #ifdef HTTPS_BACKEND_CURL
  3. #include <algorithm>
  4. #include <stdexcept>
  5. #include <sstream>
  6. #include <vector>
  7. typedef struct StringReader
  8. {
  9. const std::string *str;
  10. size_t pos;
  11. } StringReader;
  12. CurlClient::Curl::Curl()
  13. : handle(nullptr)
  14. , loaded(false)
  15. , global_cleanup(nullptr)
  16. , easy_init(nullptr)
  17. , easy_cleanup(nullptr)
  18. , easy_setopt(nullptr)
  19. , easy_perform(nullptr)
  20. , easy_getinfo(nullptr)
  21. , slist_append(nullptr)
  22. , slist_free_all(nullptr)
  23. {
  24. using namespace LibraryLoader;
  25. #ifdef _WIN32
  26. handle = OpenLibrary("libcurl.dll");
  27. #else
  28. handle = OpenLibrary("libcurl.so.4");
  29. #endif
  30. if (!handle)
  31. return;
  32. // Load symbols
  33. decltype(&curl_global_init) global_init = nullptr;
  34. if (!LoadSymbol(global_init, handle, "curl_global_init"))
  35. return;
  36. if (!LoadSymbol(global_cleanup, handle, "curl_global_cleanup"))
  37. return;
  38. if (!LoadSymbol(easy_init, handle, "curl_easy_init"))
  39. return;
  40. if (!LoadSymbol(easy_cleanup, handle, "curl_easy_cleanup"))
  41. return;
  42. if (!LoadSymbol(easy_setopt, handle, "curl_easy_setopt"))
  43. return;
  44. if (!LoadSymbol(easy_perform, handle, "curl_easy_perform"))
  45. return;
  46. if (!LoadSymbol(easy_getinfo, handle, "curl_easy_getinfo"))
  47. return;
  48. if (!LoadSymbol(slist_append, handle, "curl_slist_append"))
  49. return;
  50. if (!LoadSymbol(slist_free_all, handle, "curl_slist_free_all"))
  51. return;
  52. global_init(CURL_GLOBAL_DEFAULT);
  53. loaded = true;
  54. }
  55. CurlClient::Curl::~Curl()
  56. {
  57. if (loaded)
  58. global_cleanup();
  59. if (handle)
  60. LibraryLoader::CloseLibrary(handle);
  61. }
  62. static char toUppercase(char c)
  63. {
  64. int ch = (unsigned char) c;
  65. return toupper(ch);
  66. }
  67. static size_t stringReader(char *ptr, size_t size, size_t nmemb, StringReader *reader)
  68. {
  69. const char *data = reader->str->data();
  70. size_t len = reader->str->length();
  71. size_t maxCount = (len - reader->pos) / size;
  72. size_t desiredCount = std::min(maxCount, nmemb);
  73. size_t desiredBytes = desiredCount * size;
  74. std::copy(data + reader->pos, data + desiredBytes, ptr);
  75. reader->pos += desiredBytes;
  76. return desiredCount;
  77. }
  78. static size_t stringstreamWriter(char *ptr, size_t size, size_t nmemb, std::stringstream *ss)
  79. {
  80. size_t count = size*nmemb;
  81. ss->write(ptr, count);
  82. return count;
  83. }
  84. static size_t headerWriter(char *ptr, size_t size, size_t nmemb, std::map<std::string,std::string> *userdata)
  85. {
  86. std::map<std::string, std::string> &headers = *userdata;
  87. size_t count = size*nmemb;
  88. std::string line(ptr, count);
  89. size_t split = line.find(':');
  90. size_t newline = line.find('\r');
  91. if (newline == std::string::npos)
  92. newline = line.size();
  93. if (split != std::string::npos)
  94. headers[line.substr(0, split)] = line.substr(split+1, newline-split-1);
  95. return count;
  96. }
  97. bool CurlClient::valid() const
  98. {
  99. return curl.loaded;
  100. }
  101. HTTPSClient::Reply CurlClient::request(const HTTPSClient::Request &req)
  102. {
  103. Reply reply;
  104. reply.responseCode = 0;
  105. // Use sensible default header for later
  106. HTTPSClient::header_map newHeaders = req.headers;
  107. CURL *handle = curl.easy_init();
  108. if (!handle)
  109. throw std::runtime_error("Could not create curl request");
  110. curl.easy_setopt(handle, CURLOPT_URL, req.url.c_str());
  111. curl.easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
  112. curl.easy_setopt(handle, CURLOPT_CUSTOMREQUEST, req.method.c_str());
  113. StringReader reader {};
  114. if (req.postdata.size() > 0 && (req.method != "GET" && req.method != "HEAD"))
  115. {
  116. reader.str = &req.postdata;
  117. reader.pos = 0;
  118. curl.easy_setopt(handle, CURLOPT_UPLOAD, 1L);
  119. curl.easy_setopt(handle, CURLOPT_READFUNCTION, stringReader);
  120. curl.easy_setopt(handle, CURLOPT_READDATA, &reader);
  121. curl.easy_setopt(handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t) req.postdata.length());
  122. }
  123. if (req.method == "HEAD")
  124. curl.easy_setopt(handle, CURLOPT_NOBODY, 1L);
  125. // Curl doesn't copy memory, keep the strings around
  126. std::vector<std::string> lines;
  127. for (auto &header : newHeaders)
  128. {
  129. std::stringstream line;
  130. line << header.first << ": " << header.second;
  131. lines.push_back(line.str());
  132. }
  133. curl_slist *sendHeaders = nullptr;
  134. for (auto &line : lines)
  135. sendHeaders = curl.slist_append(sendHeaders, line.c_str());
  136. if (sendHeaders)
  137. curl.easy_setopt(handle, CURLOPT_HTTPHEADER, sendHeaders);
  138. std::stringstream body;
  139. curl.easy_setopt(handle, CURLOPT_WRITEFUNCTION, stringstreamWriter);
  140. curl.easy_setopt(handle, CURLOPT_WRITEDATA, &body);
  141. curl.easy_setopt(handle, CURLOPT_HEADERFUNCTION, headerWriter);
  142. curl.easy_setopt(handle, CURLOPT_HEADERDATA, &reply.headers);
  143. curl.easy_perform(handle);
  144. if (sendHeaders)
  145. curl.slist_free_all(sendHeaders);
  146. {
  147. long responseCode;
  148. curl.easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &responseCode);
  149. reply.responseCode = (int) responseCode;
  150. }
  151. reply.body = body.str();
  152. curl.easy_cleanup(handle);
  153. return reply;
  154. }
  155. CurlClient::Curl CurlClient::curl;
  156. #endif // HTTPS_BACKEND_CURL