CurlClient.cpp 5.4 KB

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