xref: /llvm-project/llvm/lib/Debuginfod/HTTPClient.cpp (revision f2357daf12b29aee2ac4903a4431bf9a0dd06328)
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