HTTPClient.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. //===-- llvm/Debuginfod/HTTPClient.cpp - HTTP client library ----*- C++ -*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. ///
  9. /// \file
  10. ///
  11. /// This file defines the methods of the HTTPRequest, HTTPClient, and
  12. /// BufferedHTTPResponseHandler classes.
  13. ///
  14. //===----------------------------------------------------------------------===//
  15. #include "llvm/Debuginfod/HTTPClient.h"
  16. #include "llvm/ADT/APInt.h"
  17. #include "llvm/ADT/StringRef.h"
  18. #include "llvm/Support/Errc.h"
  19. #include "llvm/Support/Error.h"
  20. #include "llvm/Support/MemoryBuffer.h"
  21. #ifdef LLVM_ENABLE_CURL
  22. #error #include <curl/curl.h>
  23. #endif
  24. using namespace llvm;
  25. HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); }
  26. bool operator==(const HTTPRequest &A, const HTTPRequest &B) {
  27. return A.Url == B.Url && A.Method == B.Method &&
  28. A.FollowRedirects == B.FollowRedirects;
  29. }
  30. HTTPResponseHandler::~HTTPResponseHandler() = default;
  31. static inline bool parseContentLengthHeader(StringRef LineRef,
  32. size_t &ContentLength) {
  33. // Content-Length is a mandatory header, and the only one we handle.
  34. return LineRef.consume_front("Content-Length: ") &&
  35. to_integer(LineRef.trim(), ContentLength, 10);
  36. }
  37. Error BufferedHTTPResponseHandler::handleHeaderLine(StringRef HeaderLine) {
  38. if (ResponseBuffer.Body)
  39. return Error::success();
  40. size_t ContentLength;
  41. if (parseContentLengthHeader(HeaderLine, ContentLength))
  42. ResponseBuffer.Body =
  43. WritableMemoryBuffer::getNewUninitMemBuffer(ContentLength);
  44. return Error::success();
  45. }
  46. Error BufferedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) {
  47. if (!ResponseBuffer.Body)
  48. return createStringError(errc::io_error,
  49. "Unallocated response buffer. HTTP Body data "
  50. "received before Content-Length header.");
  51. if (Offset + BodyChunk.size() > ResponseBuffer.Body->getBufferSize())
  52. return createStringError(errc::io_error,
  53. "Content size exceeds buffer size.");
  54. memcpy(ResponseBuffer.Body->getBufferStart() + Offset, BodyChunk.data(),
  55. BodyChunk.size());
  56. Offset += BodyChunk.size();
  57. return Error::success();
  58. }
  59. Error BufferedHTTPResponseHandler::handleStatusCode(unsigned Code) {
  60. ResponseBuffer.Code = Code;
  61. return Error::success();
  62. }
  63. bool HTTPClient::IsInitialized = false;
  64. class HTTPClientCleanup {
  65. public:
  66. ~HTTPClientCleanup() { HTTPClient::cleanup(); }
  67. };
  68. static const HTTPClientCleanup Cleanup;
  69. Expected<HTTPResponseBuffer> HTTPClient::perform(const HTTPRequest &Request) {
  70. BufferedHTTPResponseHandler Handler;
  71. if (Error Err = perform(Request, Handler))
  72. return std::move(Err);
  73. return std::move(Handler.ResponseBuffer);
  74. }
  75. Expected<HTTPResponseBuffer> HTTPClient::get(StringRef Url) {
  76. HTTPRequest Request(Url);
  77. return perform(Request);
  78. }
  79. #ifdef LLVM_ENABLE_CURL
  80. bool HTTPClient::isAvailable() { return true; }
  81. void HTTPClient::initialize() {
  82. if (!IsInitialized) {
  83. curl_global_init(CURL_GLOBAL_ALL);
  84. IsInitialized = true;
  85. }
  86. }
  87. void HTTPClient::cleanup() {
  88. if (IsInitialized) {
  89. curl_global_cleanup();
  90. IsInitialized = false;
  91. }
  92. }
  93. void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {
  94. if (Timeout < std::chrono::milliseconds(0))
  95. Timeout = std::chrono::milliseconds(0);
  96. curl_easy_setopt(Curl, CURLOPT_TIMEOUT_MS, Timeout.count());
  97. }
  98. /// CurlHTTPRequest and the curl{Header,Write}Function are implementation
  99. /// details used to work with Curl. Curl makes callbacks with a single
  100. /// customizable pointer parameter.
  101. struct CurlHTTPRequest {
  102. CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {}
  103. void storeError(Error Err) {
  104. ErrorState = joinErrors(std::move(Err), std::move(ErrorState));
  105. }
  106. HTTPResponseHandler &Handler;
  107. llvm::Error ErrorState = Error::success();
  108. };
  109. static size_t curlHeaderFunction(char *Contents, size_t Size, size_t NMemb,
  110. CurlHTTPRequest *CurlRequest) {
  111. assert(Size == 1 && "The Size passed by libCURL to CURLOPT_HEADERFUNCTION "
  112. "should always be 1.");
  113. if (Error Err =
  114. CurlRequest->Handler.handleHeaderLine(StringRef(Contents, NMemb))) {
  115. CurlRequest->storeError(std::move(Err));
  116. return 0;
  117. }
  118. return NMemb;
  119. }
  120. static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb,
  121. CurlHTTPRequest *CurlRequest) {
  122. Size *= NMemb;
  123. if (Error Err =
  124. CurlRequest->Handler.handleBodyChunk(StringRef(Contents, Size))) {
  125. CurlRequest->storeError(std::move(Err));
  126. return 0;
  127. }
  128. return Size;
  129. }
  130. HTTPClient::HTTPClient() {
  131. assert(IsInitialized &&
  132. "Must call HTTPClient::initialize() at the beginning of main().");
  133. if (Curl)
  134. return;
  135. Curl = curl_easy_init();
  136. assert(Curl && "Curl could not be initialized");
  137. // Set the callback hooks.
  138. curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);
  139. curl_easy_setopt(Curl, CURLOPT_HEADERFUNCTION, curlHeaderFunction);
  140. }
  141. HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); }
  142. Error HTTPClient::perform(const HTTPRequest &Request,
  143. HTTPResponseHandler &Handler) {
  144. if (Request.Method != HTTPMethod::GET)
  145. return createStringError(errc::invalid_argument,
  146. "Unsupported CURL request method.");
  147. SmallString<128> Url = Request.Url;
  148. curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());
  149. curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects);
  150. CurlHTTPRequest CurlRequest(Handler);
  151. curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest);
  152. curl_easy_setopt(Curl, CURLOPT_HEADERDATA, &CurlRequest);
  153. CURLcode CurlRes = curl_easy_perform(Curl);
  154. if (CurlRes != CURLE_OK)
  155. return joinErrors(std::move(CurlRequest.ErrorState),
  156. createStringError(errc::io_error,
  157. "curl_easy_perform() failed: %s\n",
  158. curl_easy_strerror(CurlRes)));
  159. if (CurlRequest.ErrorState)
  160. return std::move(CurlRequest.ErrorState);
  161. unsigned Code;
  162. curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code);
  163. if (Error Err = Handler.handleStatusCode(Code))
  164. return joinErrors(std::move(CurlRequest.ErrorState), std::move(Err));
  165. return std::move(CurlRequest.ErrorState);
  166. }
  167. #else
  168. HTTPClient::HTTPClient() = default;
  169. HTTPClient::~HTTPClient() = default;
  170. bool HTTPClient::isAvailable() { return false; }
  171. void HTTPClient::initialize() {}
  172. void HTTPClient::cleanup() {}
  173. void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {}
  174. Error HTTPClient::perform(const HTTPRequest &Request,
  175. HTTPResponseHandler &Handler) {
  176. llvm_unreachable("No HTTP Client implementation available.");
  177. }
  178. #endif