xref: /freebsd-src/contrib/llvm-project/llvm/lib/Debuginfod/HTTPClient.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
10eae32dcSDimitry Andric //===-- llvm/Debuginfod/HTTPClient.cpp - HTTP client library ----*- C++ -*-===//
20eae32dcSDimitry Andric //
30eae32dcSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40eae32dcSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50eae32dcSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60eae32dcSDimitry Andric //
70eae32dcSDimitry Andric //===----------------------------------------------------------------------===//
80eae32dcSDimitry Andric ///
90eae32dcSDimitry Andric /// \file
1081ad6265SDimitry Andric /// This file defines the implementation of the HTTPClient library for issuing
1181ad6265SDimitry Andric /// HTTP requests and handling the responses.
120eae32dcSDimitry Andric ///
130eae32dcSDimitry Andric //===----------------------------------------------------------------------===//
140eae32dcSDimitry Andric 
150eae32dcSDimitry Andric #include "llvm/Debuginfod/HTTPClient.h"
160eae32dcSDimitry Andric #include "llvm/ADT/APInt.h"
170eae32dcSDimitry Andric #include "llvm/ADT/StringRef.h"
180eae32dcSDimitry Andric #include "llvm/Support/Errc.h"
190eae32dcSDimitry Andric #include "llvm/Support/Error.h"
200eae32dcSDimitry Andric #include "llvm/Support/MemoryBuffer.h"
210eae32dcSDimitry Andric #ifdef LLVM_ENABLE_CURL
220eae32dcSDimitry Andric #include <curl/curl.h>
230eae32dcSDimitry Andric #endif
240eae32dcSDimitry Andric 
250eae32dcSDimitry Andric using namespace llvm;
260eae32dcSDimitry Andric 
HTTPRequest(StringRef Url)270eae32dcSDimitry Andric HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); }
280eae32dcSDimitry Andric 
operator ==(const HTTPRequest & A,const HTTPRequest & B)290eae32dcSDimitry Andric bool operator==(const HTTPRequest &A, const HTTPRequest &B) {
300eae32dcSDimitry Andric   return A.Url == B.Url && A.Method == B.Method &&
310eae32dcSDimitry Andric          A.FollowRedirects == B.FollowRedirects;
320eae32dcSDimitry Andric }
330eae32dcSDimitry Andric 
340eae32dcSDimitry Andric HTTPResponseHandler::~HTTPResponseHandler() = default;
350eae32dcSDimitry Andric 
360eae32dcSDimitry Andric bool HTTPClient::IsInitialized = false;
370eae32dcSDimitry Andric 
380eae32dcSDimitry Andric class HTTPClientCleanup {
390eae32dcSDimitry Andric public:
~HTTPClientCleanup()400eae32dcSDimitry Andric   ~HTTPClientCleanup() { HTTPClient::cleanup(); }
410eae32dcSDimitry Andric };
420eae32dcSDimitry Andric static const HTTPClientCleanup Cleanup;
430eae32dcSDimitry Andric 
440eae32dcSDimitry Andric #ifdef LLVM_ENABLE_CURL
450eae32dcSDimitry Andric 
isAvailable()460eae32dcSDimitry Andric bool HTTPClient::isAvailable() { return true; }
470eae32dcSDimitry Andric 
initialize()480eae32dcSDimitry Andric void HTTPClient::initialize() {
490eae32dcSDimitry Andric   if (!IsInitialized) {
500eae32dcSDimitry Andric     curl_global_init(CURL_GLOBAL_ALL);
510eae32dcSDimitry Andric     IsInitialized = true;
520eae32dcSDimitry Andric   }
530eae32dcSDimitry Andric }
540eae32dcSDimitry Andric 
cleanup()550eae32dcSDimitry Andric void HTTPClient::cleanup() {
560eae32dcSDimitry Andric   if (IsInitialized) {
570eae32dcSDimitry Andric     curl_global_cleanup();
580eae32dcSDimitry Andric     IsInitialized = false;
590eae32dcSDimitry Andric   }
600eae32dcSDimitry Andric }
610eae32dcSDimitry Andric 
setTimeout(std::chrono::milliseconds Timeout)620eae32dcSDimitry Andric void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {
630eae32dcSDimitry Andric   if (Timeout < std::chrono::milliseconds(0))
640eae32dcSDimitry Andric     Timeout = std::chrono::milliseconds(0);
650eae32dcSDimitry Andric   curl_easy_setopt(Curl, CURLOPT_TIMEOUT_MS, Timeout.count());
660eae32dcSDimitry Andric }
670eae32dcSDimitry Andric 
680eae32dcSDimitry Andric /// CurlHTTPRequest and the curl{Header,Write}Function are implementation
690eae32dcSDimitry Andric /// details used to work with Curl. Curl makes callbacks with a single
700eae32dcSDimitry Andric /// customizable pointer parameter.
710eae32dcSDimitry Andric struct CurlHTTPRequest {
CurlHTTPRequestCurlHTTPRequest720eae32dcSDimitry Andric   CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {}
storeErrorCurlHTTPRequest730eae32dcSDimitry Andric   void storeError(Error Err) {
740eae32dcSDimitry Andric     ErrorState = joinErrors(std::move(Err), std::move(ErrorState));
750eae32dcSDimitry Andric   }
760eae32dcSDimitry Andric   HTTPResponseHandler &Handler;
770eae32dcSDimitry Andric   llvm::Error ErrorState = Error::success();
780eae32dcSDimitry Andric };
790eae32dcSDimitry Andric 
curlWriteFunction(char * Contents,size_t Size,size_t NMemb,CurlHTTPRequest * CurlRequest)800eae32dcSDimitry Andric static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb,
810eae32dcSDimitry Andric                                 CurlHTTPRequest *CurlRequest) {
820eae32dcSDimitry Andric   Size *= NMemb;
830eae32dcSDimitry Andric   if (Error Err =
840eae32dcSDimitry Andric           CurlRequest->Handler.handleBodyChunk(StringRef(Contents, Size))) {
850eae32dcSDimitry Andric     CurlRequest->storeError(std::move(Err));
860eae32dcSDimitry Andric     return 0;
870eae32dcSDimitry Andric   }
880eae32dcSDimitry Andric   return Size;
890eae32dcSDimitry Andric }
900eae32dcSDimitry Andric 
HTTPClient()910eae32dcSDimitry Andric HTTPClient::HTTPClient() {
920eae32dcSDimitry Andric   assert(IsInitialized &&
930eae32dcSDimitry Andric          "Must call HTTPClient::initialize() at the beginning of main().");
940eae32dcSDimitry Andric   if (Curl)
950eae32dcSDimitry Andric     return;
961838bd0fSDimitry Andric   Curl = curl_easy_init();
971838bd0fSDimitry Andric   assert(Curl && "Curl could not be initialized");
980eae32dcSDimitry Andric   // Set the callback hooks.
990eae32dcSDimitry Andric   curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);
100*5f757f3fSDimitry Andric   // Detect supported compressed encodings and accept all.
101*5f757f3fSDimitry Andric   curl_easy_setopt(Curl, CURLOPT_ACCEPT_ENCODING, "");
1020eae32dcSDimitry Andric }
1030eae32dcSDimitry Andric 
~HTTPClient()1040eae32dcSDimitry Andric HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); }
1050eae32dcSDimitry Andric 
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)1060eae32dcSDimitry Andric Error HTTPClient::perform(const HTTPRequest &Request,
1070eae32dcSDimitry Andric                           HTTPResponseHandler &Handler) {
1080eae32dcSDimitry Andric   if (Request.Method != HTTPMethod::GET)
1090eae32dcSDimitry Andric     return createStringError(errc::invalid_argument,
1100eae32dcSDimitry Andric                              "Unsupported CURL request method.");
1110eae32dcSDimitry Andric 
1120eae32dcSDimitry Andric   SmallString<128> Url = Request.Url;
1130eae32dcSDimitry Andric   curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());
1140eae32dcSDimitry Andric   curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects);
1150eae32dcSDimitry Andric 
116bdd1243dSDimitry Andric   curl_slist *Headers = nullptr;
117bdd1243dSDimitry Andric   for (const std::string &Header : Request.Headers)
118bdd1243dSDimitry Andric     Headers = curl_slist_append(Headers, Header.c_str());
119bdd1243dSDimitry Andric   curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers);
120bdd1243dSDimitry Andric 
1210eae32dcSDimitry Andric   CurlHTTPRequest CurlRequest(Handler);
1220eae32dcSDimitry Andric   curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest);
1230eae32dcSDimitry Andric   CURLcode CurlRes = curl_easy_perform(Curl);
124bdd1243dSDimitry Andric   curl_slist_free_all(Headers);
1250eae32dcSDimitry Andric   if (CurlRes != CURLE_OK)
1260eae32dcSDimitry Andric     return joinErrors(std::move(CurlRequest.ErrorState),
1270eae32dcSDimitry Andric                       createStringError(errc::io_error,
1280eae32dcSDimitry Andric                                         "curl_easy_perform() failed: %s\n",
1290eae32dcSDimitry Andric                                         curl_easy_strerror(CurlRes)));
1300eae32dcSDimitry Andric   return std::move(CurlRequest.ErrorState);
13181ad6265SDimitry Andric }
1320eae32dcSDimitry Andric 
responseCode()13381ad6265SDimitry Andric unsigned HTTPClient::responseCode() {
13481ad6265SDimitry Andric   long Code = 0;
1350eae32dcSDimitry Andric   curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code);
13681ad6265SDimitry Andric   return Code;
1370eae32dcSDimitry Andric }
1380eae32dcSDimitry Andric 
1390eae32dcSDimitry Andric #else
1400eae32dcSDimitry Andric 
1410eae32dcSDimitry Andric HTTPClient::HTTPClient() = default;
1420eae32dcSDimitry Andric 
1430eae32dcSDimitry Andric HTTPClient::~HTTPClient() = default;
1440eae32dcSDimitry Andric 
isAvailable()1450eae32dcSDimitry Andric bool HTTPClient::isAvailable() { return false; }
1460eae32dcSDimitry Andric 
initialize()1470eae32dcSDimitry Andric void HTTPClient::initialize() {}
1480eae32dcSDimitry Andric 
cleanup()1490eae32dcSDimitry Andric void HTTPClient::cleanup() {}
1500eae32dcSDimitry Andric 
setTimeout(std::chrono::milliseconds Timeout)1510eae32dcSDimitry Andric void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {}
1520eae32dcSDimitry Andric 
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)1530eae32dcSDimitry Andric Error HTTPClient::perform(const HTTPRequest &Request,
1540eae32dcSDimitry Andric                           HTTPResponseHandler &Handler) {
1550eae32dcSDimitry Andric   llvm_unreachable("No HTTP Client implementation available.");
1560eae32dcSDimitry Andric }
1570eae32dcSDimitry Andric 
responseCode()15881ad6265SDimitry Andric unsigned HTTPClient::responseCode() {
15981ad6265SDimitry Andric   llvm_unreachable("No HTTP Client implementation available.");
16081ad6265SDimitry Andric }
16181ad6265SDimitry Andric 
1620eae32dcSDimitry Andric #endif
163