163 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// This file defines the implementation of the HTTPClient library for issuing11/// HTTP requests and handling the responses.12///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_CURL22#include <curl/curl.h>23#endif24 25using namespace llvm;26 27HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); }28 29bool operator==(const HTTPRequest &A, const HTTPRequest &B) {30 return A.Url == B.Url && A.Method == B.Method &&31 A.FollowRedirects == B.FollowRedirects;32}33 34HTTPResponseHandler::~HTTPResponseHandler() = default;35 36bool HTTPClient::IsInitialized = false;37 38class HTTPClientCleanup {39public:40 ~HTTPClientCleanup() { HTTPClient::cleanup(); }41};42static const HTTPClientCleanup Cleanup;43 44#ifdef LLVM_ENABLE_CURL45 46bool HTTPClient::isAvailable() { return true; }47 48void HTTPClient::initialize() {49 if (!IsInitialized) {50 curl_global_init(CURL_GLOBAL_ALL);51 IsInitialized = true;52 }53}54 55void HTTPClient::cleanup() {56 if (IsInitialized) {57 curl_global_cleanup();58 IsInitialized = false;59 }60}61 62void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {63 if (Timeout < std::chrono::milliseconds(0))64 Timeout = std::chrono::milliseconds(0);65 curl_easy_setopt(Curl, CURLOPT_TIMEOUT_MS, Timeout.count());66}67 68/// CurlHTTPRequest and the curl{Header,Write}Function are implementation69/// details used to work with Curl. Curl makes callbacks with a single70/// customizable pointer parameter.71struct CurlHTTPRequest {72 CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {}73 void storeError(Error Err) {74 ErrorState = joinErrors(std::move(Err), std::move(ErrorState));75 }76 HTTPResponseHandler &Handler;77 llvm::Error ErrorState = Error::success();78};79 80static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb,81 CurlHTTPRequest *CurlRequest) {82 Size *= NMemb;83 if (Error Err =84 CurlRequest->Handler.handleBodyChunk(StringRef(Contents, Size))) {85 CurlRequest->storeError(std::move(Err));86 return 0;87 }88 return Size;89}90 91HTTPClient::HTTPClient() {92 assert(IsInitialized &&93 "Must call HTTPClient::initialize() at the beginning of main().");94 if (Curl)95 return;96 Curl = curl_easy_init();97 assert(Curl && "Curl could not be initialized");98 // Set the callback hooks.99 curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);100 // Detect supported compressed encodings and accept all.101 curl_easy_setopt(Curl, CURLOPT_ACCEPT_ENCODING, "");102}103 104HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); }105 106Error HTTPClient::perform(const HTTPRequest &Request,107 HTTPResponseHandler &Handler) {108 if (Request.Method != HTTPMethod::GET)109 return createStringError(errc::invalid_argument,110 "Unsupported CURL request method.");111 112 SmallString<128> Url = Request.Url;113 curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());114 curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects);115 116 curl_slist *Headers = nullptr;117 for (const std::string &Header : Request.Headers)118 Headers = curl_slist_append(Headers, Header.c_str());119 curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers);120 121 CurlHTTPRequest CurlRequest(Handler);122 curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest);123 CURLcode CurlRes = curl_easy_perform(Curl);124 curl_slist_free_all(Headers);125 if (CurlRes != CURLE_OK)126 return joinErrors(std::move(CurlRequest.ErrorState),127 createStringError(errc::io_error,128 "curl_easy_perform() failed: %s\n",129 curl_easy_strerror(CurlRes)));130 return std::move(CurlRequest.ErrorState);131}132 133unsigned HTTPClient::responseCode() {134 long Code = 0;135 curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code);136 return Code;137}138 139#else140 141HTTPClient::HTTPClient() = default;142 143HTTPClient::~HTTPClient() = default;144 145bool HTTPClient::isAvailable() { return false; }146 147void HTTPClient::initialize() {}148 149void HTTPClient::cleanup() {}150 151void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {}152 153Error HTTPClient::perform(const HTTPRequest &Request,154 HTTPResponseHandler &Handler) {155 llvm_unreachable("No HTTP Client implementation available.");156}157 158unsigned HTTPClient::responseCode() {159 llvm_unreachable("No HTTP Client implementation available.");160}161 162#endif163