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
10*7917b3c6SDaniel Thornburgh /// This file defines the implementation of the HTTPClient library for issuing
11*7917b3c6SDaniel 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);
100d9941f74SNoah Shutty }
101d9941f74SNoah Shutty
~HTTPClient()102d9941f74SNoah Shutty HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); }
103d9941f74SNoah Shutty
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)104d9941f74SNoah Shutty Error HTTPClient::perform(const HTTPRequest &Request,
105d9941f74SNoah Shutty HTTPResponseHandler &Handler) {
106d9941f74SNoah Shutty if (Request.Method != HTTPMethod::GET)
107d9941f74SNoah Shutty return createStringError(errc::invalid_argument,
108d9941f74SNoah Shutty "Unsupported CURL request method.");
109d9941f74SNoah Shutty
110d9941f74SNoah Shutty SmallString<128> Url = Request.Url;
111d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());
112d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects);
113d9941f74SNoah Shutty
114d9941f74SNoah Shutty CurlHTTPRequest CurlRequest(Handler);
115d9941f74SNoah Shutty curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest);
116d9941f74SNoah Shutty CURLcode CurlRes = curl_easy_perform(Curl);
117d9941f74SNoah Shutty if (CurlRes != CURLE_OK)
118d9941f74SNoah Shutty return joinErrors(std::move(CurlRequest.ErrorState),
119d9941f74SNoah Shutty createStringError(errc::io_error,
120d9941f74SNoah Shutty "curl_easy_perform() failed: %s\n",
121d9941f74SNoah Shutty curl_easy_strerror(CurlRes)));
122d9941f74SNoah Shutty return std::move(CurlRequest.ErrorState);
123*7917b3c6SDaniel Thornburgh }
124d9941f74SNoah Shutty
responseCode()125*7917b3c6SDaniel Thornburgh unsigned HTTPClient::responseCode() {
126*7917b3c6SDaniel Thornburgh long Code = 0;
127d9941f74SNoah Shutty curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code);
128*7917b3c6SDaniel Thornburgh return Code;
129d9941f74SNoah Shutty }
130d9941f74SNoah Shutty
131d9941f74SNoah Shutty #else
132d9941f74SNoah Shutty
133d9941f74SNoah Shutty HTTPClient::HTTPClient() = default;
134d9941f74SNoah Shutty
135d9941f74SNoah Shutty HTTPClient::~HTTPClient() = default;
136d9941f74SNoah Shutty
isAvailable()137d9941f74SNoah Shutty bool HTTPClient::isAvailable() { return false; }
138d9941f74SNoah Shutty
initialize()139d9941f74SNoah Shutty void HTTPClient::initialize() {}
140d9941f74SNoah Shutty
cleanup()141d9941f74SNoah Shutty void HTTPClient::cleanup() {}
142d9941f74SNoah Shutty
setTimeout(std::chrono::milliseconds Timeout)143d9941f74SNoah Shutty void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {}
144d9941f74SNoah Shutty
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)145d9941f74SNoah Shutty Error HTTPClient::perform(const HTTPRequest &Request,
146d9941f74SNoah Shutty HTTPResponseHandler &Handler) {
147d9941f74SNoah Shutty llvm_unreachable("No HTTP Client implementation available.");
148d9941f74SNoah Shutty }
149d9941f74SNoah Shutty
responseCode()150*7917b3c6SDaniel Thornburgh unsigned HTTPClient::responseCode() {
151*7917b3c6SDaniel Thornburgh llvm_unreachable("No HTTP Client implementation available.");
152*7917b3c6SDaniel Thornburgh }
153*7917b3c6SDaniel Thornburgh
154d9941f74SNoah Shutty #endif
155