CurlHttpClient.cc 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. /*
  2. * Copyright 1999-2019 Alibaba Cloud All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. #include "CurlHttpClient.h"
  17. #include "Utils.h"
  18. #include <cassert>
  19. #include <sstream>
  20. #include <string>
  21. #include <vector>
  22. #include <string.h>
  23. namespace AlibabaCloud {
  24. namespace {
  25. typedef struct {
  26. const char *data;
  27. const char *pos;
  28. const char *last;
  29. } uploadContext;
  30. static size_t readCallback(void *ptr, size_t size, size_t nmemb, void *stream) {
  31. uploadContext *ctx = (uploadContext *)stream;
  32. size_t len = 0;
  33. if (ctx->pos >= ctx->last) {
  34. return 0;
  35. }
  36. if ((size == 0) || (nmemb == 0) || (size * nmemb < 1)) {
  37. return 0;
  38. }
  39. len = ctx->last - ctx->pos;
  40. if (len > size * nmemb) {
  41. len = size * nmemb;
  42. }
  43. memcpy(ptr, ctx->pos, len);
  44. ctx->pos += len;
  45. return len;
  46. }
  47. size_t recvBody(char *ptr, size_t size, size_t nmemb, void *userdata) {
  48. std::ostringstream &out = *static_cast<std::ostringstream*>(userdata);
  49. out << std::string(ptr, nmemb*size);
  50. return nmemb * size;
  51. }
  52. size_t recvHeaders(char *buffer, size_t size, size_t nitems, void *userdata) {
  53. HttpResponse *response = static_cast<HttpResponse*>(userdata);
  54. std::string line(buffer);
  55. auto pos = line.find(':');
  56. if (pos != line.npos) {
  57. std::string name = line.substr(0, pos);
  58. std::string value = line.substr(pos + 2);
  59. size_t p = 0;
  60. if ((p = value.rfind('\r')) != value.npos)
  61. value[p] = '\0';
  62. response->setHeader(name, value);
  63. }
  64. return nitems * size;
  65. }
  66. void setCUrlProxy(CURL *curlHandle, const NetworkProxy &proxy) {
  67. if (proxy.type() == NetworkProxy::Type::None)
  68. return;
  69. long type;
  70. switch (proxy.type()) {
  71. case NetworkProxy::Type::Socks5:
  72. type = CURLPROXY_SOCKS5;
  73. break;
  74. case NetworkProxy::Type::Http:
  75. default:
  76. type = CURLPROXY_HTTP;
  77. break;
  78. }
  79. curl_easy_setopt(curlHandle, CURLOPT_PROXYTYPE, type);
  80. std::ostringstream out;
  81. out << proxy.hostName() << ":" << proxy.port();
  82. curl_easy_setopt(curlHandle, CURLOPT_PROXY, out.str().c_str());
  83. if (!proxy.user().empty()) {
  84. out.clear();
  85. out << proxy.user() << ":" << proxy.password();
  86. curl_easy_setopt(curlHandle, CURLOPT_PROXYUSERPWD, out.str().c_str());
  87. }
  88. }
  89. } // namespace
  90. CurlHttpClient::CurlHttpClient() :
  91. HttpClient(),
  92. curlHandle_(curl_easy_init()) {
  93. }
  94. CurlHttpClient::~CurlHttpClient() {
  95. curl_easy_cleanup(curlHandle_);
  96. }
  97. HttpClient::HttpResponseOutcome
  98. CurlHttpClient::makeRequest(const HttpRequest &request) {
  99. curl_easy_reset(curlHandle_);
  100. HttpResponse response(request);
  101. long connect_timeout = request.connectTimeout();
  102. long read_timeout = request.readTimeout();
  103. std::string url = request.url().toString();
  104. switch (request.method()) {
  105. case HttpRequest::Method::Get:
  106. break;
  107. case HttpRequest::Method::Post: {
  108. if (request.bodySize() > 0) {
  109. curl_easy_setopt(curlHandle_, CURLOPT_POSTFIELDS, request.body());
  110. } else {
  111. curl_easy_setopt(curlHandle_, CURLOPT_POSTFIELDS, "");
  112. }
  113. }
  114. break;
  115. case HttpRequest::Method::Put: {
  116. uploadContext* ctx = (uploadContext *)malloc(sizeof(uploadContext));
  117. // this is impossible, as the size is 24 Bytes
  118. if (ctx == nullptr) {
  119. return HttpResponseOutcome(
  120. Error("MemoryAllocateError",
  121. "There is not enough memory for http transfer."));
  122. }
  123. ctx->data = request.body();
  124. ctx->pos = request.body();
  125. ctx->last = ctx->pos + request.bodySize();
  126. curl_easy_setopt(curlHandle_, CURLOPT_READFUNCTION, readCallback);
  127. curl_easy_setopt(curlHandle_, CURLOPT_UPLOAD, 1L);
  128. curl_easy_setopt(curlHandle_, CURLOPT_READDATA, ctx);
  129. }
  130. break;
  131. case HttpRequest::Method::Delete: {
  132. curl_easy_setopt(curlHandle_, CURLOPT_CUSTOMREQUEST, "DELETE");
  133. if (request.bodySize() > 0) {
  134. curl_easy_setopt(curlHandle_, CURLOPT_POSTFIELDS, request.body());
  135. } else {
  136. curl_easy_setopt(curlHandle_, CURLOPT_POSTFIELDS, "");
  137. }
  138. }
  139. break;
  140. default:
  141. break;
  142. }
  143. curl_easy_setopt(curlHandle_, CURLOPT_URL, url.c_str());
  144. curl_easy_setopt(curlHandle_, CURLOPT_SSL_VERIFYPEER, 1L);
  145. curl_easy_setopt(curlHandle_, CURLOPT_SSL_VERIFYHOST, 2L);
  146. curl_easy_setopt(curlHandle_, CURLOPT_HEADERDATA, &response);
  147. curl_easy_setopt(curlHandle_, CURLOPT_HEADERFUNCTION, recvHeaders);
  148. if (connect_timeout > 0) {
  149. curl_easy_setopt(curlHandle_, CURLOPT_CONNECTTIMEOUT_MS, connect_timeout);
  150. }
  151. if (read_timeout > 0) {
  152. curl_easy_setopt(curlHandle_, CURLOPT_TIMEOUT_MS, read_timeout);
  153. }
  154. curl_slist *list = nullptr;
  155. auto headers = request.headers();
  156. for (const auto &p : headers) {
  157. std::string str = p.first;
  158. str.append(": ").append(p.second);
  159. list = curl_slist_append(list, str.c_str());
  160. }
  161. curl_easy_setopt(curlHandle_, CURLOPT_HTTPHEADER, list);
  162. std::ostringstream out;
  163. curl_easy_setopt(curlHandle_, CURLOPT_WRITEDATA, &out);
  164. curl_easy_setopt(curlHandle_, CURLOPT_WRITEFUNCTION, recvBody);
  165. setCUrlProxy(curlHandle_, proxy());
  166. if (GetEnv("DEBUG") == "sdk") {
  167. curl_easy_setopt(curlHandle_, CURLOPT_VERBOSE, 1L);
  168. }
  169. CURLcode res = curl_easy_perform(curlHandle_);
  170. switch (res) {
  171. case CURLE_OK: {
  172. long response_code;
  173. curl_easy_getinfo(curlHandle_, CURLINFO_RESPONSE_CODE, &response_code);
  174. response.setStatusCode(response_code);
  175. response.setBody(out.str().c_str(), out.str().length());
  176. return HttpResponseOutcome(response);
  177. }
  178. case CURLE_SSL_CONNECT_ERROR:
  179. return HttpResponseOutcome(
  180. Error("SSLConnectError",
  181. "A problem occurred somewhere in the SSL/TLS handshake."));
  182. case CURLE_OPERATION_TIMEDOUT:
  183. return HttpResponseOutcome(
  184. Error("OperationTimeoutError",
  185. "Timeout (connectTimeout: " +
  186. std::to_string(connect_timeout) + "ms, readTimeout: " +
  187. std::to_string(read_timeout) + "ms) when connect or read data: " +
  188. HttpMethodToString(request.method()) + " " + request.url().toString()));
  189. default: {
  190. return HttpResponseOutcome(
  191. Error("NetworkError",
  192. "Failed to connect to host or proxy: " +
  193. HttpMethodToString(request.method()) + " " + request.url().toString()));
  194. }
  195. }
  196. }
  197. } // namespace AlibabaCloud