1*d9941f74SNoah Shutty //===-- llvm/Debuginfod/HTTPClient.cpp - HTTP client library ----*- C++ -*-===// 2*d9941f74SNoah Shutty // 3*d9941f74SNoah Shutty // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*d9941f74SNoah Shutty // See https://llvm.org/LICENSE.txt for license information. 5*d9941f74SNoah Shutty // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*d9941f74SNoah Shutty // 7*d9941f74SNoah Shutty //===----------------------------------------------------------------------===// 8*d9941f74SNoah Shutty /// 9*d9941f74SNoah Shutty /// \file 10*d9941f74SNoah Shutty /// 11*d9941f74SNoah Shutty /// This file defines the methods of the HTTPRequest, HTTPClient, and 12*d9941f74SNoah Shutty /// BufferedHTTPResponseHandler classes. 13*d9941f74SNoah Shutty /// 14*d9941f74SNoah Shutty //===----------------------------------------------------------------------===// 15*d9941f74SNoah Shutty 16*d9941f74SNoah Shutty #include "llvm/Debuginfod/HTTPClient.h" 17*d9941f74SNoah Shutty #include "llvm/ADT/APInt.h" 18*d9941f74SNoah Shutty #include "llvm/ADT/StringRef.h" 19*d9941f74SNoah Shutty #include "llvm/Support/Errc.h" 20*d9941f74SNoah Shutty #include "llvm/Support/Error.h" 21*d9941f74SNoah Shutty #include "llvm/Support/MemoryBuffer.h" 22*d9941f74SNoah Shutty #ifdef LLVM_ENABLE_CURL 23*d9941f74SNoah Shutty #include <curl/curl.h> 24*d9941f74SNoah Shutty #endif 25*d9941f74SNoah Shutty 26*d9941f74SNoah Shutty using namespace llvm; 27*d9941f74SNoah Shutty 28*d9941f74SNoah Shutty HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); } 29*d9941f74SNoah Shutty 30*d9941f74SNoah Shutty bool operator==(const HTTPRequest &A, const HTTPRequest &B) { 31*d9941f74SNoah Shutty return A.Url == B.Url && A.Method == B.Method && 32*d9941f74SNoah Shutty A.FollowRedirects == B.FollowRedirects; 33*d9941f74SNoah Shutty } 34*d9941f74SNoah Shutty 35*d9941f74SNoah Shutty HTTPResponseHandler::~HTTPResponseHandler() = default; 36*d9941f74SNoah Shutty 37*d9941f74SNoah Shutty static inline bool parseContentLengthHeader(StringRef LineRef, 38*d9941f74SNoah Shutty size_t &ContentLength) { 39*d9941f74SNoah Shutty // Content-Length is a mandatory header, and the only one we handle. 40*d9941f74SNoah Shutty return LineRef.consume_front("Content-Length: ") && 41*d9941f74SNoah Shutty to_integer(LineRef.trim(), ContentLength, 10); 42*d9941f74SNoah Shutty } 43*d9941f74SNoah Shutty 44*d9941f74SNoah Shutty Error BufferedHTTPResponseHandler::handleHeaderLine(StringRef HeaderLine) { 45*d9941f74SNoah Shutty if (ResponseBuffer.Body) 46*d9941f74SNoah Shutty return Error::success(); 47*d9941f74SNoah Shutty 48*d9941f74SNoah Shutty size_t ContentLength; 49*d9941f74SNoah Shutty if (parseContentLengthHeader(HeaderLine, ContentLength)) 50*d9941f74SNoah Shutty ResponseBuffer.Body = 51*d9941f74SNoah Shutty WritableMemoryBuffer::getNewUninitMemBuffer(ContentLength); 52*d9941f74SNoah Shutty 53*d9941f74SNoah Shutty return Error::success(); 54*d9941f74SNoah Shutty } 55*d9941f74SNoah Shutty 56*d9941f74SNoah Shutty Error BufferedHTTPResponseHandler::handleBodyChunk(StringRef BodyChunk) { 57*d9941f74SNoah Shutty if (!ResponseBuffer.Body) 58*d9941f74SNoah Shutty return createStringError(errc::io_error, 59*d9941f74SNoah Shutty "Unallocated response buffer. HTTP Body data " 60*d9941f74SNoah Shutty "received before Content-Length header."); 61*d9941f74SNoah Shutty if (Offset + BodyChunk.size() > ResponseBuffer.Body->getBufferSize()) 62*d9941f74SNoah Shutty return createStringError(errc::io_error, 63*d9941f74SNoah Shutty "Content size exceeds buffer size."); 64*d9941f74SNoah Shutty memcpy(ResponseBuffer.Body->getBufferStart() + Offset, BodyChunk.data(), 65*d9941f74SNoah Shutty BodyChunk.size()); 66*d9941f74SNoah Shutty Offset += BodyChunk.size(); 67*d9941f74SNoah Shutty return Error::success(); 68*d9941f74SNoah Shutty } 69*d9941f74SNoah Shutty 70*d9941f74SNoah Shutty Error BufferedHTTPResponseHandler::handleStatusCode(unsigned Code) { 71*d9941f74SNoah Shutty ResponseBuffer.Code = Code; 72*d9941f74SNoah Shutty return Error::success(); 73*d9941f74SNoah Shutty } 74*d9941f74SNoah Shutty 75*d9941f74SNoah Shutty Expected<HTTPResponseBuffer> HTTPClient::perform(const HTTPRequest &Request) { 76*d9941f74SNoah Shutty BufferedHTTPResponseHandler Handler; 77*d9941f74SNoah Shutty if (Error Err = perform(Request, Handler)) 78*d9941f74SNoah Shutty return std::move(Err); 79*d9941f74SNoah Shutty return std::move(Handler.ResponseBuffer); 80*d9941f74SNoah Shutty } 81*d9941f74SNoah Shutty 82*d9941f74SNoah Shutty Expected<HTTPResponseBuffer> HTTPClient::get(StringRef Url) { 83*d9941f74SNoah Shutty HTTPRequest Request(Url); 84*d9941f74SNoah Shutty return perform(Request); 85*d9941f74SNoah Shutty } 86*d9941f74SNoah Shutty 87*d9941f74SNoah Shutty #ifdef LLVM_ENABLE_CURL 88*d9941f74SNoah Shutty 89*d9941f74SNoah Shutty bool HTTPClient::isAvailable() { return true; } 90*d9941f74SNoah Shutty 91*d9941f74SNoah Shutty bool HTTPClient::IsInitialized = false; 92*d9941f74SNoah Shutty 93*d9941f74SNoah Shutty void HTTPClient::initialize() { 94*d9941f74SNoah Shutty if (!IsInitialized) { 95*d9941f74SNoah Shutty curl_global_init(CURL_GLOBAL_ALL); 96*d9941f74SNoah Shutty IsInitialized = true; 97*d9941f74SNoah Shutty } 98*d9941f74SNoah Shutty } 99*d9941f74SNoah Shutty 100*d9941f74SNoah Shutty void HTTPClient::cleanup() { 101*d9941f74SNoah Shutty if (IsInitialized) { 102*d9941f74SNoah Shutty curl_global_cleanup(); 103*d9941f74SNoah Shutty IsInitialized = false; 104*d9941f74SNoah Shutty } 105*d9941f74SNoah Shutty } 106*d9941f74SNoah Shutty 107*d9941f74SNoah Shutty void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) { 108*d9941f74SNoah Shutty if (Timeout < std::chrono::milliseconds(0)) 109*d9941f74SNoah Shutty Timeout = std::chrono::milliseconds(0); 110*d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_TIMEOUT_MS, Timeout.count()); 111*d9941f74SNoah Shutty } 112*d9941f74SNoah Shutty 113*d9941f74SNoah Shutty /// CurlHTTPRequest and the curl{Header,Write}Function are implementation 114*d9941f74SNoah Shutty /// details used to work with Curl. Curl makes callbacks with a single 115*d9941f74SNoah Shutty /// customizable pointer parameter. 116*d9941f74SNoah Shutty struct CurlHTTPRequest { 117*d9941f74SNoah Shutty CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {} 118*d9941f74SNoah Shutty void storeError(Error Err) { 119*d9941f74SNoah Shutty ErrorState = joinErrors(std::move(Err), std::move(ErrorState)); 120*d9941f74SNoah Shutty } 121*d9941f74SNoah Shutty HTTPResponseHandler &Handler; 122*d9941f74SNoah Shutty llvm::Error ErrorState = Error::success(); 123*d9941f74SNoah Shutty }; 124*d9941f74SNoah Shutty 125*d9941f74SNoah Shutty static size_t curlHeaderFunction(char *Contents, size_t Size, size_t NMemb, 126*d9941f74SNoah Shutty CurlHTTPRequest *CurlRequest) { 127*d9941f74SNoah Shutty assert(Size == 1 && "The Size passed by libCURL to CURLOPT_HEADERFUNCTION " 128*d9941f74SNoah Shutty "should always be 1."); 129*d9941f74SNoah Shutty if (Error Err = 130*d9941f74SNoah Shutty CurlRequest->Handler.handleHeaderLine(StringRef(Contents, NMemb))) { 131*d9941f74SNoah Shutty CurlRequest->storeError(std::move(Err)); 132*d9941f74SNoah Shutty return 0; 133*d9941f74SNoah Shutty } 134*d9941f74SNoah Shutty return NMemb; 135*d9941f74SNoah Shutty } 136*d9941f74SNoah Shutty 137*d9941f74SNoah Shutty static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb, 138*d9941f74SNoah Shutty CurlHTTPRequest *CurlRequest) { 139*d9941f74SNoah Shutty Size *= NMemb; 140*d9941f74SNoah Shutty if (Error Err = 141*d9941f74SNoah Shutty CurlRequest->Handler.handleBodyChunk(StringRef(Contents, Size))) { 142*d9941f74SNoah Shutty CurlRequest->storeError(std::move(Err)); 143*d9941f74SNoah Shutty return 0; 144*d9941f74SNoah Shutty } 145*d9941f74SNoah Shutty return Size; 146*d9941f74SNoah Shutty } 147*d9941f74SNoah Shutty 148*d9941f74SNoah Shutty HTTPClient::HTTPClient() { 149*d9941f74SNoah Shutty assert(IsInitialized && 150*d9941f74SNoah Shutty "Must call HTTPClient::initialize() at the beginning of main()."); 151*d9941f74SNoah Shutty if (Curl) 152*d9941f74SNoah Shutty return; 153*d9941f74SNoah Shutty assert((Curl = curl_easy_init()) && "Curl could not be initialized."); 154*d9941f74SNoah Shutty // Set the callback hooks. 155*d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, curlWriteFunction); 156*d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_HEADERFUNCTION, curlHeaderFunction); 157*d9941f74SNoah Shutty } 158*d9941f74SNoah Shutty 159*d9941f74SNoah Shutty HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); } 160*d9941f74SNoah Shutty 161*d9941f74SNoah Shutty Error HTTPClient::perform(const HTTPRequest &Request, 162*d9941f74SNoah Shutty HTTPResponseHandler &Handler) { 163*d9941f74SNoah Shutty if (Request.Method != HTTPMethod::GET) 164*d9941f74SNoah Shutty return createStringError(errc::invalid_argument, 165*d9941f74SNoah Shutty "Unsupported CURL request method."); 166*d9941f74SNoah Shutty 167*d9941f74SNoah Shutty SmallString<128> Url = Request.Url; 168*d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str()); 169*d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects); 170*d9941f74SNoah Shutty 171*d9941f74SNoah Shutty CurlHTTPRequest CurlRequest(Handler); 172*d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest); 173*d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_HEADERDATA, &CurlRequest); 174*d9941f74SNoah Shutty CURLcode CurlRes = curl_easy_perform(Curl); 175*d9941f74SNoah Shutty if (CurlRes != CURLE_OK) 176*d9941f74SNoah Shutty return joinErrors(std::move(CurlRequest.ErrorState), 177*d9941f74SNoah Shutty createStringError(errc::io_error, 178*d9941f74SNoah Shutty "curl_easy_perform() failed: %s\n", 179*d9941f74SNoah Shutty curl_easy_strerror(CurlRes))); 180*d9941f74SNoah Shutty if (CurlRequest.ErrorState) 181*d9941f74SNoah Shutty return std::move(CurlRequest.ErrorState); 182*d9941f74SNoah Shutty 183*d9941f74SNoah Shutty unsigned Code; 184*d9941f74SNoah Shutty curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code); 185*d9941f74SNoah Shutty if (Error Err = Handler.handleStatusCode(Code)) 186*d9941f74SNoah Shutty return joinErrors(std::move(CurlRequest.ErrorState), std::move(Err)); 187*d9941f74SNoah Shutty 188*d9941f74SNoah Shutty return std::move(CurlRequest.ErrorState); 189*d9941f74SNoah Shutty } 190*d9941f74SNoah Shutty 191*d9941f74SNoah Shutty #else 192*d9941f74SNoah Shutty 193*d9941f74SNoah Shutty HTTPClient::HTTPClient() = default; 194*d9941f74SNoah Shutty 195*d9941f74SNoah Shutty HTTPClient::~HTTPClient() = default; 196*d9941f74SNoah Shutty 197*d9941f74SNoah Shutty bool HTTPClient::isAvailable() { return false; } 198*d9941f74SNoah Shutty 199*d9941f74SNoah Shutty void HTTPClient::initialize() {} 200*d9941f74SNoah Shutty 201*d9941f74SNoah Shutty void HTTPClient::cleanup() {} 202*d9941f74SNoah Shutty 203*d9941f74SNoah Shutty void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {} 204*d9941f74SNoah Shutty 205*d9941f74SNoah Shutty Error HTTPClient::perform(const HTTPRequest &Request, 206*d9941f74SNoah Shutty HTTPResponseHandler &Handler) { 207*d9941f74SNoah Shutty llvm_unreachable("No HTTP Client implementation available."); 208*d9941f74SNoah Shutty } 209*d9941f74SNoah Shutty 210*d9941f74SNoah Shutty #endif 211