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
107917b3c6SDaniel Thornburgh /// This file defines the implementation of the HTTPClient library for issuing
117917b3c6SDaniel Thornburgh /// HTTP requests and handling the responses.
12d9941f74SNoah Shutty ///
13d9941f74SNoah Shutty //===----------------------------------------------------------------------===//
14d9941f74SNoah Shutty
15d9941f74SNoah Shutty #include "llvm/Debuginfod/HTTPClient.h"
16d9941f74SNoah Shutty #include "llvm/ADT/APInt.h"
17d9941f74SNoah Shutty #include "llvm/ADT/StringRef.h"
18d9941f74SNoah Shutty #include "llvm/Support/Errc.h"
19d9941f74SNoah Shutty #include "llvm/Support/Error.h"
20d9941f74SNoah Shutty #include "llvm/Support/MemoryBuffer.h"
21d9941f74SNoah Shutty #ifdef LLVM_ENABLE_CURL
22d9941f74SNoah Shutty #include <curl/curl.h>
23d9941f74SNoah Shutty #endif
24d9941f74SNoah Shutty
25d9941f74SNoah Shutty using namespace llvm;
26d9941f74SNoah Shutty
HTTPRequest(StringRef Url)27d9941f74SNoah Shutty HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); }
28d9941f74SNoah Shutty
operator ==(const HTTPRequest & A,const HTTPRequest & B)29d9941f74SNoah Shutty bool operator==(const HTTPRequest &A, const HTTPRequest &B) {
30d9941f74SNoah Shutty return A.Url == B.Url && A.Method == B.Method &&
31d9941f74SNoah Shutty A.FollowRedirects == B.FollowRedirects;
32d9941f74SNoah Shutty }
33d9941f74SNoah Shutty
34d9941f74SNoah Shutty HTTPResponseHandler::~HTTPResponseHandler() = default;
35d9941f74SNoah Shutty
3634491ca7SNoah Shutty bool HTTPClient::IsInitialized = false;
3734491ca7SNoah Shutty
3834491ca7SNoah Shutty class HTTPClientCleanup {
3934491ca7SNoah Shutty public:
~HTTPClientCleanup()4034491ca7SNoah Shutty ~HTTPClientCleanup() { HTTPClient::cleanup(); }
4134491ca7SNoah Shutty };
4234491ca7SNoah Shutty static const HTTPClientCleanup Cleanup;
4334491ca7SNoah Shutty
44d9941f74SNoah Shutty #ifdef LLVM_ENABLE_CURL
45d9941f74SNoah Shutty
isAvailable()46d9941f74SNoah Shutty bool HTTPClient::isAvailable() { return true; }
47d9941f74SNoah Shutty
initialize()48d9941f74SNoah Shutty void HTTPClient::initialize() {
49d9941f74SNoah Shutty if (!IsInitialized) {
50d9941f74SNoah Shutty curl_global_init(CURL_GLOBAL_ALL);
51d9941f74SNoah Shutty IsInitialized = true;
52d9941f74SNoah Shutty }
53d9941f74SNoah Shutty }
54d9941f74SNoah Shutty
cleanup()55d9941f74SNoah Shutty void HTTPClient::cleanup() {
56d9941f74SNoah Shutty if (IsInitialized) {
57d9941f74SNoah Shutty curl_global_cleanup();
58d9941f74SNoah Shutty IsInitialized = false;
59d9941f74SNoah Shutty }
60d9941f74SNoah Shutty }
61d9941f74SNoah Shutty
setTimeout(std::chrono::milliseconds Timeout)62d9941f74SNoah Shutty void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {
63d9941f74SNoah Shutty if (Timeout < std::chrono::milliseconds(0))
64d9941f74SNoah Shutty Timeout = std::chrono::milliseconds(0);
65d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_TIMEOUT_MS, Timeout.count());
66d9941f74SNoah Shutty }
67d9941f74SNoah Shutty
68d9941f74SNoah Shutty /// CurlHTTPRequest and the curl{Header,Write}Function are implementation
69d9941f74SNoah Shutty /// details used to work with Curl. Curl makes callbacks with a single
70d9941f74SNoah Shutty /// customizable pointer parameter.
71d9941f74SNoah Shutty struct CurlHTTPRequest {
CurlHTTPRequestCurlHTTPRequest72d9941f74SNoah Shutty CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {}
storeErrorCurlHTTPRequest73d9941f74SNoah Shutty void storeError(Error Err) {
74d9941f74SNoah Shutty ErrorState = joinErrors(std::move(Err), std::move(ErrorState));
75d9941f74SNoah Shutty }
76d9941f74SNoah Shutty HTTPResponseHandler &Handler;
77d9941f74SNoah Shutty llvm::Error ErrorState = Error::success();
78d9941f74SNoah Shutty };
79d9941f74SNoah Shutty
curlWriteFunction(char * Contents,size_t Size,size_t NMemb,CurlHTTPRequest * CurlRequest)80d9941f74SNoah Shutty static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb,
81d9941f74SNoah Shutty CurlHTTPRequest *CurlRequest) {
82d9941f74SNoah Shutty Size *= NMemb;
83d9941f74SNoah Shutty if (Error Err =
84d9941f74SNoah Shutty CurlRequest->Handler.handleBodyChunk(StringRef(Contents, Size))) {
85d9941f74SNoah Shutty CurlRequest->storeError(std::move(Err));
86d9941f74SNoah Shutty return 0;
87d9941f74SNoah Shutty }
88d9941f74SNoah Shutty return Size;
89d9941f74SNoah Shutty }
90d9941f74SNoah Shutty
HTTPClient()91d9941f74SNoah Shutty HTTPClient::HTTPClient() {
92d9941f74SNoah Shutty assert(IsInitialized &&
93d9941f74SNoah Shutty "Must call HTTPClient::initialize() at the beginning of main().");
94d9941f74SNoah Shutty if (Curl)
95d9941f74SNoah Shutty return;
963dd2d4c0SFangrui Song Curl = curl_easy_init();
973dd2d4c0SFangrui Song assert(Curl && "Curl could not be initialized");
98d9941f74SNoah Shutty // Set the callback hooks.
99d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);
100*f2357dafSDaniel Thornburgh // Detect supported compressed encodings and accept all.
101*f2357dafSDaniel Thornburgh curl_easy_setopt(Curl, CURLOPT_ACCEPT_ENCODING, "");
102d9941f74SNoah Shutty }
103d9941f74SNoah Shutty
~HTTPClient()104d9941f74SNoah Shutty HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); }
105d9941f74SNoah Shutty
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)106d9941f74SNoah Shutty Error HTTPClient::perform(const HTTPRequest &Request,
107d9941f74SNoah Shutty HTTPResponseHandler &Handler) {
108d9941f74SNoah Shutty if (Request.Method != HTTPMethod::GET)
109d9941f74SNoah Shutty return createStringError(errc::invalid_argument,
110d9941f74SNoah Shutty "Unsupported CURL request method.");
111d9941f74SNoah Shutty
112d9941f74SNoah Shutty SmallString<128> Url = Request.Url;
113d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());
114d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects);
115d9941f74SNoah Shutty
11632a02a9cSDaniel Thornburgh curl_slist *Headers = nullptr;
11732a02a9cSDaniel Thornburgh for (const std::string &Header : Request.Headers)
11832a02a9cSDaniel Thornburgh Headers = curl_slist_append(Headers, Header.c_str());
11932a02a9cSDaniel Thornburgh curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers);
12032a02a9cSDaniel Thornburgh
121d9941f74SNoah Shutty CurlHTTPRequest CurlRequest(Handler);
122d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest);
123d9941f74SNoah Shutty CURLcode CurlRes = curl_easy_perform(Curl);
12432a02a9cSDaniel Thornburgh curl_slist_free_all(Headers);
125d9941f74SNoah Shutty if (CurlRes != CURLE_OK)
126d9941f74SNoah Shutty return joinErrors(std::move(CurlRequest.ErrorState),
127d9941f74SNoah Shutty createStringError(errc::io_error,
128d9941f74SNoah Shutty "curl_easy_perform() failed: %s\n",
129d9941f74SNoah Shutty curl_easy_strerror(CurlRes)));
130d9941f74SNoah Shutty return std::move(CurlRequest.ErrorState);
1317917b3c6SDaniel Thornburgh }
132d9941f74SNoah Shutty
responseCode()1337917b3c6SDaniel Thornburgh unsigned HTTPClient::responseCode() {
1347917b3c6SDaniel Thornburgh long Code = 0;
135d9941f74SNoah Shutty curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code);
1367917b3c6SDaniel Thornburgh return Code;
137d9941f74SNoah Shutty }
138d9941f74SNoah Shutty
139d9941f74SNoah Shutty #else
140d9941f74SNoah Shutty
141d9941f74SNoah Shutty HTTPClient::HTTPClient() = default;
142d9941f74SNoah Shutty
143d9941f74SNoah Shutty HTTPClient::~HTTPClient() = default;
144d9941f74SNoah Shutty
isAvailable()145d9941f74SNoah Shutty bool HTTPClient::isAvailable() { return false; }
146d9941f74SNoah Shutty
initialize()147d9941f74SNoah Shutty void HTTPClient::initialize() {}
148d9941f74SNoah Shutty
cleanup()149d9941f74SNoah Shutty void HTTPClient::cleanup() {}
150d9941f74SNoah Shutty
setTimeout(std::chrono::milliseconds Timeout)151d9941f74SNoah Shutty void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {}
152d9941f74SNoah Shutty
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)153d9941f74SNoah Shutty Error HTTPClient::perform(const HTTPRequest &Request,
154d9941f74SNoah Shutty HTTPResponseHandler &Handler) {
155d9941f74SNoah Shutty llvm_unreachable("No HTTP Client implementation available.");
156d9941f74SNoah Shutty }
157d9941f74SNoah Shutty
responseCode()1587917b3c6SDaniel Thornburgh unsigned HTTPClient::responseCode() {
1597917b3c6SDaniel Thornburgh llvm_unreachable("No HTTP Client implementation available.");
1607917b3c6SDaniel Thornburgh }
1617917b3c6SDaniel Thornburgh
162d9941f74SNoah Shutty #endif
163