xref: /src/contrib/llvm-project/llvm/lib/Debuginfod/HTTPClient.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
177fc4c14SDimitry Andric //===-- llvm/Debuginfod/HTTPClient.cpp - HTTP client library ----*- C++ -*-===//
277fc4c14SDimitry Andric //
377fc4c14SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
477fc4c14SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
577fc4c14SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
677fc4c14SDimitry Andric //
777fc4c14SDimitry Andric //===----------------------------------------------------------------------===//
877fc4c14SDimitry Andric ///
977fc4c14SDimitry Andric /// \file
10145449b1SDimitry Andric /// This file defines the implementation of the HTTPClient library for issuing
11145449b1SDimitry Andric /// HTTP requests and handling the responses.
1277fc4c14SDimitry Andric ///
1377fc4c14SDimitry Andric //===----------------------------------------------------------------------===//
1477fc4c14SDimitry Andric 
1577fc4c14SDimitry Andric #include "llvm/Debuginfod/HTTPClient.h"
1677fc4c14SDimitry Andric #include "llvm/ADT/APInt.h"
1777fc4c14SDimitry Andric #include "llvm/ADT/StringRef.h"
1877fc4c14SDimitry Andric #include "llvm/Support/Errc.h"
1977fc4c14SDimitry Andric #include "llvm/Support/Error.h"
2077fc4c14SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
2177fc4c14SDimitry Andric #ifdef LLVM_ENABLE_CURL
2277fc4c14SDimitry Andric #include <curl/curl.h>
2377fc4c14SDimitry Andric #endif
2477fc4c14SDimitry Andric 
2577fc4c14SDimitry Andric using namespace llvm;
2677fc4c14SDimitry Andric 
HTTPRequest(StringRef Url)2777fc4c14SDimitry Andric HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); }
2877fc4c14SDimitry Andric 
operator ==(const HTTPRequest & A,const HTTPRequest & B)2977fc4c14SDimitry Andric bool operator==(const HTTPRequest &A, const HTTPRequest &B) {
3077fc4c14SDimitry Andric   return A.Url == B.Url && A.Method == B.Method &&
3177fc4c14SDimitry Andric          A.FollowRedirects == B.FollowRedirects;
3277fc4c14SDimitry Andric }
3377fc4c14SDimitry Andric 
3477fc4c14SDimitry Andric HTTPResponseHandler::~HTTPResponseHandler() = default;
3577fc4c14SDimitry Andric 
3677fc4c14SDimitry Andric bool HTTPClient::IsInitialized = false;
3777fc4c14SDimitry Andric 
3877fc4c14SDimitry Andric class HTTPClientCleanup {
3977fc4c14SDimitry Andric public:
~HTTPClientCleanup()4077fc4c14SDimitry Andric   ~HTTPClientCleanup() { HTTPClient::cleanup(); }
4177fc4c14SDimitry Andric };
4277fc4c14SDimitry Andric static const HTTPClientCleanup Cleanup;
4377fc4c14SDimitry Andric 
4477fc4c14SDimitry Andric #ifdef LLVM_ENABLE_CURL
4577fc4c14SDimitry Andric 
isAvailable()4677fc4c14SDimitry Andric bool HTTPClient::isAvailable() { return true; }
4777fc4c14SDimitry Andric 
initialize()4877fc4c14SDimitry Andric void HTTPClient::initialize() {
4977fc4c14SDimitry Andric   if (!IsInitialized) {
5077fc4c14SDimitry Andric     curl_global_init(CURL_GLOBAL_ALL);
5177fc4c14SDimitry Andric     IsInitialized = true;
5277fc4c14SDimitry Andric   }
5377fc4c14SDimitry Andric }
5477fc4c14SDimitry Andric 
cleanup()5577fc4c14SDimitry Andric void HTTPClient::cleanup() {
5677fc4c14SDimitry Andric   if (IsInitialized) {
5777fc4c14SDimitry Andric     curl_global_cleanup();
5877fc4c14SDimitry Andric     IsInitialized = false;
5977fc4c14SDimitry Andric   }
6077fc4c14SDimitry Andric }
6177fc4c14SDimitry Andric 
setTimeout(std::chrono::milliseconds Timeout)6277fc4c14SDimitry Andric void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {
6377fc4c14SDimitry Andric   if (Timeout < std::chrono::milliseconds(0))
6477fc4c14SDimitry Andric     Timeout = std::chrono::milliseconds(0);
6577fc4c14SDimitry Andric   curl_easy_setopt(Curl, CURLOPT_TIMEOUT_MS, Timeout.count());
6677fc4c14SDimitry Andric }
6777fc4c14SDimitry Andric 
6877fc4c14SDimitry Andric /// CurlHTTPRequest and the curl{Header,Write}Function are implementation
6977fc4c14SDimitry Andric /// details used to work with Curl. Curl makes callbacks with a single
7077fc4c14SDimitry Andric /// customizable pointer parameter.
7177fc4c14SDimitry Andric struct CurlHTTPRequest {
CurlHTTPRequestCurlHTTPRequest7277fc4c14SDimitry Andric   CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {}
storeErrorCurlHTTPRequest7377fc4c14SDimitry Andric   void storeError(Error Err) {
7477fc4c14SDimitry Andric     ErrorState = joinErrors(std::move(Err), std::move(ErrorState));
7577fc4c14SDimitry Andric   }
7677fc4c14SDimitry Andric   HTTPResponseHandler &Handler;
7777fc4c14SDimitry Andric   llvm::Error ErrorState = Error::success();
7877fc4c14SDimitry Andric };
7977fc4c14SDimitry Andric 
curlWriteFunction(char * Contents,size_t Size,size_t NMemb,CurlHTTPRequest * CurlRequest)8077fc4c14SDimitry Andric static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb,
8177fc4c14SDimitry Andric                                 CurlHTTPRequest *CurlRequest) {
8277fc4c14SDimitry Andric   Size *= NMemb;
8377fc4c14SDimitry Andric   if (Error Err =
8477fc4c14SDimitry Andric           CurlRequest->Handler.handleBodyChunk(StringRef(Contents, Size))) {
8577fc4c14SDimitry Andric     CurlRequest->storeError(std::move(Err));
8677fc4c14SDimitry Andric     return 0;
8777fc4c14SDimitry Andric   }
8877fc4c14SDimitry Andric   return Size;
8977fc4c14SDimitry Andric }
9077fc4c14SDimitry Andric 
HTTPClient()9177fc4c14SDimitry Andric HTTPClient::HTTPClient() {
9277fc4c14SDimitry Andric   assert(IsInitialized &&
9377fc4c14SDimitry Andric          "Must call HTTPClient::initialize() at the beginning of main().");
9477fc4c14SDimitry Andric   if (Curl)
9577fc4c14SDimitry Andric     return;
96145449b1SDimitry Andric   Curl = curl_easy_init();
97145449b1SDimitry Andric   assert(Curl && "Curl could not be initialized");
9877fc4c14SDimitry Andric   // Set the callback hooks.
9977fc4c14SDimitry Andric   curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);
100b1c73532SDimitry Andric   // Detect supported compressed encodings and accept all.
101b1c73532SDimitry Andric   curl_easy_setopt(Curl, CURLOPT_ACCEPT_ENCODING, "");
10277fc4c14SDimitry Andric }
10377fc4c14SDimitry Andric 
~HTTPClient()10477fc4c14SDimitry Andric HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); }
10577fc4c14SDimitry Andric 
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)10677fc4c14SDimitry Andric Error HTTPClient::perform(const HTTPRequest &Request,
10777fc4c14SDimitry Andric                           HTTPResponseHandler &Handler) {
10877fc4c14SDimitry Andric   if (Request.Method != HTTPMethod::GET)
10977fc4c14SDimitry Andric     return createStringError(errc::invalid_argument,
11077fc4c14SDimitry Andric                              "Unsupported CURL request method.");
11177fc4c14SDimitry Andric 
11277fc4c14SDimitry Andric   SmallString<128> Url = Request.Url;
11377fc4c14SDimitry Andric   curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());
11477fc4c14SDimitry Andric   curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects);
11577fc4c14SDimitry Andric 
116e3b55780SDimitry Andric   curl_slist *Headers = nullptr;
117e3b55780SDimitry Andric   for (const std::string &Header : Request.Headers)
118e3b55780SDimitry Andric     Headers = curl_slist_append(Headers, Header.c_str());
119e3b55780SDimitry Andric   curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers);
120e3b55780SDimitry Andric 
12177fc4c14SDimitry Andric   CurlHTTPRequest CurlRequest(Handler);
12277fc4c14SDimitry Andric   curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest);
12377fc4c14SDimitry Andric   CURLcode CurlRes = curl_easy_perform(Curl);
124e3b55780SDimitry Andric   curl_slist_free_all(Headers);
12577fc4c14SDimitry Andric   if (CurlRes != CURLE_OK)
12677fc4c14SDimitry Andric     return joinErrors(std::move(CurlRequest.ErrorState),
12777fc4c14SDimitry Andric                       createStringError(errc::io_error,
12877fc4c14SDimitry Andric                                         "curl_easy_perform() failed: %s\n",
12977fc4c14SDimitry Andric                                         curl_easy_strerror(CurlRes)));
13077fc4c14SDimitry Andric   return std::move(CurlRequest.ErrorState);
131145449b1SDimitry Andric }
13277fc4c14SDimitry Andric 
responseCode()133145449b1SDimitry Andric unsigned HTTPClient::responseCode() {
134145449b1SDimitry Andric   long Code = 0;
13577fc4c14SDimitry Andric   curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code);
136145449b1SDimitry Andric   return Code;
13777fc4c14SDimitry Andric }
13877fc4c14SDimitry Andric 
13977fc4c14SDimitry Andric #else
14077fc4c14SDimitry Andric 
14177fc4c14SDimitry Andric HTTPClient::HTTPClient() = default;
14277fc4c14SDimitry Andric 
14377fc4c14SDimitry Andric HTTPClient::~HTTPClient() = default;
14477fc4c14SDimitry Andric 
isAvailable()14577fc4c14SDimitry Andric bool HTTPClient::isAvailable() { return false; }
14677fc4c14SDimitry Andric 
initialize()14777fc4c14SDimitry Andric void HTTPClient::initialize() {}
14877fc4c14SDimitry Andric 
cleanup()14977fc4c14SDimitry Andric void HTTPClient::cleanup() {}
15077fc4c14SDimitry Andric 
setTimeout(std::chrono::milliseconds Timeout)15177fc4c14SDimitry Andric void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {}
15277fc4c14SDimitry Andric 
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)15377fc4c14SDimitry Andric Error HTTPClient::perform(const HTTPRequest &Request,
15477fc4c14SDimitry Andric                           HTTPResponseHandler &Handler) {
15577fc4c14SDimitry Andric   llvm_unreachable("No HTTP Client implementation available.");
15677fc4c14SDimitry Andric }
15777fc4c14SDimitry Andric 
responseCode()158145449b1SDimitry Andric unsigned HTTPClient::responseCode() {
159145449b1SDimitry Andric   llvm_unreachable("No HTTP Client implementation available.");
160145449b1SDimitry Andric }
161145449b1SDimitry Andric 
16277fc4c14SDimitry Andric #endif
163