HTTPClient.cpp 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  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. /// This file defines the implementation of the HTTPClient library for issuing
  11. /// HTTP requests and handling the responses.
  12. ///
  13. //===----------------------------------------------------------------------===//
  14. #include "llvm/Debuginfod/HTTPClient.h"
  15. #include "llvm/ADT/APInt.h"
  16. #include "llvm/ADT/StringRef.h"
  17. #include "llvm/Support/Errc.h"
  18. #include "llvm/Support/Error.h"
  19. #include "llvm/Support/MemoryBuffer.h"
  20. #ifdef LLVM_ENABLE_CURL
  21. #error #include <curl/curl.h>
  22. #endif
  23. using namespace llvm;
  24. HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); }
  25. bool operator==(const HTTPRequest &A, const HTTPRequest &B) {
  26. return A.Url == B.Url && A.Method == B.Method &&
  27. A.FollowRedirects == B.FollowRedirects;
  28. }
  29. HTTPResponseHandler::~HTTPResponseHandler() = default;
  30. bool HTTPClient::IsInitialized = false;
  31. class HTTPClientCleanup {
  32. public:
  33. ~HTTPClientCleanup() { HTTPClient::cleanup(); }
  34. };
  35. static const HTTPClientCleanup Cleanup;
  36. #ifdef LLVM_ENABLE_CURL
  37. bool HTTPClient::isAvailable() { return true; }
  38. void HTTPClient::initialize() {
  39. if (!IsInitialized) {
  40. curl_global_init(CURL_GLOBAL_ALL);
  41. IsInitialized = true;
  42. }
  43. }
  44. void HTTPClient::cleanup() {
  45. if (IsInitialized) {
  46. curl_global_cleanup();
  47. IsInitialized = false;
  48. }
  49. }
  50. void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {
  51. if (Timeout < std::chrono::milliseconds(0))
  52. Timeout = std::chrono::milliseconds(0);
  53. curl_easy_setopt(Curl, CURLOPT_TIMEOUT_MS, Timeout.count());
  54. }
  55. /// CurlHTTPRequest and the curl{Header,Write}Function are implementation
  56. /// details used to work with Curl. Curl makes callbacks with a single
  57. /// customizable pointer parameter.
  58. struct CurlHTTPRequest {
  59. CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {}
  60. void storeError(Error Err) {
  61. ErrorState = joinErrors(std::move(Err), std::move(ErrorState));
  62. }
  63. HTTPResponseHandler &Handler;
  64. llvm::Error ErrorState = Error::success();
  65. };
  66. static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb,
  67. CurlHTTPRequest *CurlRequest) {
  68. Size *= NMemb;
  69. if (Error Err =
  70. CurlRequest->Handler.handleBodyChunk(StringRef(Contents, Size))) {
  71. CurlRequest->storeError(std::move(Err));
  72. return 0;
  73. }
  74. return Size;
  75. }
  76. HTTPClient::HTTPClient() {
  77. assert(IsInitialized &&
  78. "Must call HTTPClient::initialize() at the beginning of main().");
  79. if (Curl)
  80. return;
  81. Curl = curl_easy_init();
  82. assert(Curl && "Curl could not be initialized");
  83. // Set the callback hooks.
  84. curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);
  85. }
  86. HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); }
  87. Error HTTPClient::perform(const HTTPRequest &Request,
  88. HTTPResponseHandler &Handler) {
  89. if (Request.Method != HTTPMethod::GET)
  90. return createStringError(errc::invalid_argument,
  91. "Unsupported CURL request method.");
  92. SmallString<128> Url = Request.Url;
  93. curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());
  94. curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects);
  95. curl_slist *Headers = nullptr;
  96. for (const std::string &Header : Request.Headers)
  97. Headers = curl_slist_append(Headers, Header.c_str());
  98. curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers);
  99. CurlHTTPRequest CurlRequest(Handler);
  100. curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest);
  101. CURLcode CurlRes = curl_easy_perform(Curl);
  102. curl_slist_free_all(Headers);
  103. if (CurlRes != CURLE_OK)
  104. return joinErrors(std::move(CurlRequest.ErrorState),
  105. createStringError(errc::io_error,
  106. "curl_easy_perform() failed: %s\n",
  107. curl_easy_strerror(CurlRes)));
  108. return std::move(CurlRequest.ErrorState);
  109. }
  110. unsigned HTTPClient::responseCode() {
  111. long Code = 0;
  112. curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code);
  113. return Code;
  114. }
  115. #else
  116. HTTPClient::HTTPClient() = default;
  117. HTTPClient::~HTTPClient() = default;
  118. bool HTTPClient::isAvailable() { return false; }
  119. void HTTPClient::initialize() {}
  120. void HTTPClient::cleanup() {}
  121. void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {}
  122. Error HTTPClient::perform(const HTTPRequest &Request,
  123. HTTPResponseHandler &Handler) {
  124. llvm_unreachable("No HTTP Client implementation available.");
  125. }
  126. unsigned HTTPClient::responseCode() {
  127. llvm_unreachable("No HTTP Client implementation available.");
  128. }
  129. #endif