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