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 10*81ad6265SDimitry Andric /// This file defines the implementation of the HTTPClient library for issuing 11*81ad6265SDimitry 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 270eae32dcSDimitry Andric HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); } 280eae32dcSDimitry Andric 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: 400eae32dcSDimitry Andric ~HTTPClientCleanup() { HTTPClient::cleanup(); } 410eae32dcSDimitry Andric }; 420eae32dcSDimitry Andric static const HTTPClientCleanup Cleanup; 430eae32dcSDimitry Andric 440eae32dcSDimitry Andric #ifdef LLVM_ENABLE_CURL 450eae32dcSDimitry Andric 460eae32dcSDimitry Andric bool HTTPClient::isAvailable() { return true; } 470eae32dcSDimitry Andric 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 550eae32dcSDimitry Andric void HTTPClient::cleanup() { 560eae32dcSDimitry Andric if (IsInitialized) { 570eae32dcSDimitry Andric curl_global_cleanup(); 580eae32dcSDimitry Andric IsInitialized = false; 590eae32dcSDimitry Andric } 600eae32dcSDimitry Andric } 610eae32dcSDimitry Andric 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 { 720eae32dcSDimitry Andric CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {} 730eae32dcSDimitry 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 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 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); 1000eae32dcSDimitry Andric } 1010eae32dcSDimitry Andric 1020eae32dcSDimitry Andric HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); } 1030eae32dcSDimitry Andric 1040eae32dcSDimitry Andric Error HTTPClient::perform(const HTTPRequest &Request, 1050eae32dcSDimitry Andric HTTPResponseHandler &Handler) { 1060eae32dcSDimitry Andric if (Request.Method != HTTPMethod::GET) 1070eae32dcSDimitry Andric return createStringError(errc::invalid_argument, 1080eae32dcSDimitry Andric "Unsupported CURL request method."); 1090eae32dcSDimitry Andric 1100eae32dcSDimitry Andric SmallString<128> Url = Request.Url; 1110eae32dcSDimitry Andric curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str()); 1120eae32dcSDimitry Andric curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects); 1130eae32dcSDimitry Andric 1140eae32dcSDimitry Andric CurlHTTPRequest CurlRequest(Handler); 1150eae32dcSDimitry Andric curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest); 1160eae32dcSDimitry Andric CURLcode CurlRes = curl_easy_perform(Curl); 1170eae32dcSDimitry Andric if (CurlRes != CURLE_OK) 1180eae32dcSDimitry Andric return joinErrors(std::move(CurlRequest.ErrorState), 1190eae32dcSDimitry Andric createStringError(errc::io_error, 1200eae32dcSDimitry Andric "curl_easy_perform() failed: %s\n", 1210eae32dcSDimitry Andric curl_easy_strerror(CurlRes))); 1220eae32dcSDimitry Andric return std::move(CurlRequest.ErrorState); 123*81ad6265SDimitry Andric } 1240eae32dcSDimitry Andric 125*81ad6265SDimitry Andric unsigned HTTPClient::responseCode() { 126*81ad6265SDimitry Andric long Code = 0; 1270eae32dcSDimitry Andric curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code); 128*81ad6265SDimitry Andric return Code; 1290eae32dcSDimitry Andric } 1300eae32dcSDimitry Andric 1310eae32dcSDimitry Andric #else 1320eae32dcSDimitry Andric 1330eae32dcSDimitry Andric HTTPClient::HTTPClient() = default; 1340eae32dcSDimitry Andric 1350eae32dcSDimitry Andric HTTPClient::~HTTPClient() = default; 1360eae32dcSDimitry Andric 1370eae32dcSDimitry Andric bool HTTPClient::isAvailable() { return false; } 1380eae32dcSDimitry Andric 1390eae32dcSDimitry Andric void HTTPClient::initialize() {} 1400eae32dcSDimitry Andric 1410eae32dcSDimitry Andric void HTTPClient::cleanup() {} 1420eae32dcSDimitry Andric 1430eae32dcSDimitry Andric void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {} 1440eae32dcSDimitry Andric 1450eae32dcSDimitry Andric Error HTTPClient::perform(const HTTPRequest &Request, 1460eae32dcSDimitry Andric HTTPResponseHandler &Handler) { 1470eae32dcSDimitry Andric llvm_unreachable("No HTTP Client implementation available."); 1480eae32dcSDimitry Andric } 1490eae32dcSDimitry Andric 150*81ad6265SDimitry Andric unsigned HTTPClient::responseCode() { 151*81ad6265SDimitry Andric llvm_unreachable("No HTTP Client implementation available."); 152*81ad6265SDimitry Andric } 153*81ad6265SDimitry Andric 1540eae32dcSDimitry Andric #endif 155