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