1*d415bd75Srobert //===-- llvm/Debuginfod/HTTPClient.cpp - HTTP client library ----*- C++ -*-===//
2*d415bd75Srobert //
3*d415bd75Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*d415bd75Srobert // See https://llvm.org/LICENSE.txt for license information.
5*d415bd75Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*d415bd75Srobert //
7*d415bd75Srobert //===----------------------------------------------------------------------===//
8*d415bd75Srobert ///
9*d415bd75Srobert /// \file
10*d415bd75Srobert /// This file defines the implementation of the HTTPClient library for issuing
11*d415bd75Srobert /// HTTP requests and handling the responses.
12*d415bd75Srobert ///
13*d415bd75Srobert //===----------------------------------------------------------------------===//
14*d415bd75Srobert 
15*d415bd75Srobert #include "llvm/Debuginfod/HTTPClient.h"
16*d415bd75Srobert #include "llvm/ADT/APInt.h"
17*d415bd75Srobert #include "llvm/ADT/StringRef.h"
18*d415bd75Srobert #include "llvm/Support/Errc.h"
19*d415bd75Srobert #include "llvm/Support/Error.h"
20*d415bd75Srobert #include "llvm/Support/MemoryBuffer.h"
21*d415bd75Srobert #ifdef LLVM_ENABLE_CURL
22*d415bd75Srobert #include <curl/curl.h>
23*d415bd75Srobert #endif
24*d415bd75Srobert 
25*d415bd75Srobert using namespace llvm;
26*d415bd75Srobert 
HTTPRequest(StringRef Url)27*d415bd75Srobert HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); }
28*d415bd75Srobert 
operator ==(const HTTPRequest & A,const HTTPRequest & B)29*d415bd75Srobert bool operator==(const HTTPRequest &A, const HTTPRequest &B) {
30*d415bd75Srobert   return A.Url == B.Url && A.Method == B.Method &&
31*d415bd75Srobert          A.FollowRedirects == B.FollowRedirects;
32*d415bd75Srobert }
33*d415bd75Srobert 
34*d415bd75Srobert HTTPResponseHandler::~HTTPResponseHandler() = default;
35*d415bd75Srobert 
36*d415bd75Srobert bool HTTPClient::IsInitialized = false;
37*d415bd75Srobert 
38*d415bd75Srobert class HTTPClientCleanup {
39*d415bd75Srobert public:
~HTTPClientCleanup()40*d415bd75Srobert   ~HTTPClientCleanup() { HTTPClient::cleanup(); }
41*d415bd75Srobert };
42*d415bd75Srobert static const HTTPClientCleanup Cleanup;
43*d415bd75Srobert 
44*d415bd75Srobert #ifdef LLVM_ENABLE_CURL
45*d415bd75Srobert 
isAvailable()46*d415bd75Srobert bool HTTPClient::isAvailable() { return true; }
47*d415bd75Srobert 
initialize()48*d415bd75Srobert void HTTPClient::initialize() {
49*d415bd75Srobert   if (!IsInitialized) {
50*d415bd75Srobert     curl_global_init(CURL_GLOBAL_ALL);
51*d415bd75Srobert     IsInitialized = true;
52*d415bd75Srobert   }
53*d415bd75Srobert }
54*d415bd75Srobert 
cleanup()55*d415bd75Srobert void HTTPClient::cleanup() {
56*d415bd75Srobert   if (IsInitialized) {
57*d415bd75Srobert     curl_global_cleanup();
58*d415bd75Srobert     IsInitialized = false;
59*d415bd75Srobert   }
60*d415bd75Srobert }
61*d415bd75Srobert 
setTimeout(std::chrono::milliseconds Timeout)62*d415bd75Srobert void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {
63*d415bd75Srobert   if (Timeout < std::chrono::milliseconds(0))
64*d415bd75Srobert     Timeout = std::chrono::milliseconds(0);
65*d415bd75Srobert   curl_easy_setopt(Curl, CURLOPT_TIMEOUT_MS, Timeout.count());
66*d415bd75Srobert }
67*d415bd75Srobert 
68*d415bd75Srobert /// CurlHTTPRequest and the curl{Header,Write}Function are implementation
69*d415bd75Srobert /// details used to work with Curl. Curl makes callbacks with a single
70*d415bd75Srobert /// customizable pointer parameter.
71*d415bd75Srobert struct CurlHTTPRequest {
CurlHTTPRequestCurlHTTPRequest72*d415bd75Srobert   CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {}
storeErrorCurlHTTPRequest73*d415bd75Srobert   void storeError(Error Err) {
74*d415bd75Srobert     ErrorState = joinErrors(std::move(Err), std::move(ErrorState));
75*d415bd75Srobert   }
76*d415bd75Srobert   HTTPResponseHandler &Handler;
77*d415bd75Srobert   llvm::Error ErrorState = Error::success();
78*d415bd75Srobert };
79*d415bd75Srobert 
curlWriteFunction(char * Contents,size_t Size,size_t NMemb,CurlHTTPRequest * CurlRequest)80*d415bd75Srobert static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb,
81*d415bd75Srobert                                 CurlHTTPRequest *CurlRequest) {
82*d415bd75Srobert   Size *= NMemb;
83*d415bd75Srobert   if (Error Err =
84*d415bd75Srobert           CurlRequest->Handler.handleBodyChunk(StringRef(Contents, Size))) {
85*d415bd75Srobert     CurlRequest->storeError(std::move(Err));
86*d415bd75Srobert     return 0;
87*d415bd75Srobert   }
88*d415bd75Srobert   return Size;
89*d415bd75Srobert }
90*d415bd75Srobert 
HTTPClient()91*d415bd75Srobert HTTPClient::HTTPClient() {
92*d415bd75Srobert   assert(IsInitialized &&
93*d415bd75Srobert          "Must call HTTPClient::initialize() at the beginning of main().");
94*d415bd75Srobert   if (Curl)
95*d415bd75Srobert     return;
96*d415bd75Srobert   Curl = curl_easy_init();
97*d415bd75Srobert   assert(Curl && "Curl could not be initialized");
98*d415bd75Srobert   // Set the callback hooks.
99*d415bd75Srobert   curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);
100*d415bd75Srobert }
101*d415bd75Srobert 
~HTTPClient()102*d415bd75Srobert HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); }
103*d415bd75Srobert 
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)104*d415bd75Srobert Error HTTPClient::perform(const HTTPRequest &Request,
105*d415bd75Srobert                           HTTPResponseHandler &Handler) {
106*d415bd75Srobert   if (Request.Method != HTTPMethod::GET)
107*d415bd75Srobert     return createStringError(errc::invalid_argument,
108*d415bd75Srobert                              "Unsupported CURL request method.");
109*d415bd75Srobert 
110*d415bd75Srobert   SmallString<128> Url = Request.Url;
111*d415bd75Srobert   curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());
112*d415bd75Srobert   curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects);
113*d415bd75Srobert 
114*d415bd75Srobert   curl_slist *Headers = nullptr;
115*d415bd75Srobert   for (const std::string &Header : Request.Headers)
116*d415bd75Srobert     Headers = curl_slist_append(Headers, Header.c_str());
117*d415bd75Srobert   curl_easy_setopt(Curl, CURLOPT_HTTPHEADER, Headers);
118*d415bd75Srobert 
119*d415bd75Srobert   CurlHTTPRequest CurlRequest(Handler);
120*d415bd75Srobert   curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest);
121*d415bd75Srobert   CURLcode CurlRes = curl_easy_perform(Curl);
122*d415bd75Srobert   curl_slist_free_all(Headers);
123*d415bd75Srobert   if (CurlRes != CURLE_OK)
124*d415bd75Srobert     return joinErrors(std::move(CurlRequest.ErrorState),
125*d415bd75Srobert                       createStringError(errc::io_error,
126*d415bd75Srobert                                         "curl_easy_perform() failed: %s\n",
127*d415bd75Srobert                                         curl_easy_strerror(CurlRes)));
128*d415bd75Srobert   return std::move(CurlRequest.ErrorState);
129*d415bd75Srobert }
130*d415bd75Srobert 
responseCode()131*d415bd75Srobert unsigned HTTPClient::responseCode() {
132*d415bd75Srobert   long Code = 0;
133*d415bd75Srobert   curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code);
134*d415bd75Srobert   return Code;
135*d415bd75Srobert }
136*d415bd75Srobert 
137*d415bd75Srobert #else
138*d415bd75Srobert 
139*d415bd75Srobert HTTPClient::HTTPClient() = default;
140*d415bd75Srobert 
141*d415bd75Srobert HTTPClient::~HTTPClient() = default;
142*d415bd75Srobert 
isAvailable()143*d415bd75Srobert bool HTTPClient::isAvailable() { return false; }
144*d415bd75Srobert 
initialize()145*d415bd75Srobert void HTTPClient::initialize() {}
146*d415bd75Srobert 
cleanup()147*d415bd75Srobert void HTTPClient::cleanup() {}
148*d415bd75Srobert 
setTimeout(std::chrono::milliseconds Timeout)149*d415bd75Srobert void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {}
150*d415bd75Srobert 
perform(const HTTPRequest & Request,HTTPResponseHandler & Handler)151*d415bd75Srobert Error HTTPClient::perform(const HTTPRequest &Request,
152*d415bd75Srobert                           HTTPResponseHandler &Handler) {
153*d415bd75Srobert   llvm_unreachable("No HTTP Client implementation available.");
154*d415bd75Srobert }
155*d415bd75Srobert 
responseCode()156*d415bd75Srobert unsigned HTTPClient::responseCode() {
157*d415bd75Srobert   llvm_unreachable("No HTTP Client implementation available.");
158*d415bd75Srobert }
159*d415bd75Srobert 
160*d415bd75Srobert #endif
161