1 //===-- llvm/Debuginfod/HTTPClient.cpp - HTTP client library ----*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 /// This file defines the implementation of the HTTPClient library for issuing
11 /// HTTP requests and handling the responses.
13 //===----------------------------------------------------------------------===//
15 #include "llvm/Debuginfod/HTTPClient.h"
16 #include "llvm/ADT/APInt.h"
17 #include "llvm/ADT/StringRef.h"
18 #include "llvm/Support/Errc.h"
19 #include "llvm/Support/Error.h"
20 #include "llvm/Support/MemoryBuffer.h"
21 #ifdef LLVM_ENABLE_CURL
22 #include <curl/curl.h>
27 HTTPRequest::HTTPRequest(StringRef Url
) { this->Url
= Url
.str(); }
29 bool operator==(const HTTPRequest
&A
, const HTTPRequest
&B
) {
30 return A
.Url
== B
.Url
&& A
.Method
== B
.Method
&&
31 A
.FollowRedirects
== B
.FollowRedirects
;
34 HTTPResponseHandler::~HTTPResponseHandler() = default;
36 bool HTTPClient::IsInitialized
= false;
38 class HTTPClientCleanup
{
40 ~HTTPClientCleanup() { HTTPClient::cleanup(); }
42 static const HTTPClientCleanup Cleanup
;
44 #ifdef LLVM_ENABLE_CURL
46 bool HTTPClient::isAvailable() { return true; }
48 void HTTPClient::initialize() {
50 curl_global_init(CURL_GLOBAL_ALL
);
55 void HTTPClient::cleanup() {
57 curl_global_cleanup();
58 IsInitialized
= false;
62 void HTTPClient::setTimeout(std::chrono::milliseconds Timeout
) {
63 if (Timeout
< std::chrono::milliseconds(0))
64 Timeout
= std::chrono::milliseconds(0);
65 curl_easy_setopt(Curl
, CURLOPT_TIMEOUT_MS
, Timeout
.count());
68 /// CurlHTTPRequest and the curl{Header,Write}Function are implementation
69 /// details used to work with Curl. Curl makes callbacks with a single
70 /// customizable pointer parameter.
71 struct CurlHTTPRequest
{
72 CurlHTTPRequest(HTTPResponseHandler
&Handler
) : Handler(Handler
) {}
73 void storeError(Error Err
) {
74 ErrorState
= joinErrors(std::move(Err
), std::move(ErrorState
));
76 HTTPResponseHandler
&Handler
;
77 llvm::Error ErrorState
= Error::success();
80 static size_t curlWriteFunction(char *Contents
, size_t Size
, size_t NMemb
,
81 CurlHTTPRequest
*CurlRequest
) {
84 CurlRequest
->Handler
.handleBodyChunk(StringRef(Contents
, Size
))) {
85 CurlRequest
->storeError(std::move(Err
));
91 HTTPClient::HTTPClient() {
92 assert(IsInitialized
&&
93 "Must call HTTPClient::initialize() at the beginning of main().");
96 Curl
= curl_easy_init();
97 assert(Curl
&& "Curl could not be initialized");
98 // Set the callback hooks.
99 curl_easy_setopt(Curl
, CURLOPT_WRITEFUNCTION
, curlWriteFunction
);
100 // Detect supported compressed encodings and accept all.
101 curl_easy_setopt(Curl
, CURLOPT_ACCEPT_ENCODING
, "");
104 HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl
); }
106 Error
HTTPClient::perform(const HTTPRequest
&Request
,
107 HTTPResponseHandler
&Handler
) {
108 if (Request
.Method
!= HTTPMethod::GET
)
109 return createStringError(errc::invalid_argument
,
110 "Unsupported CURL request method.");
112 SmallString
<128> Url
= Request
.Url
;
113 curl_easy_setopt(Curl
, CURLOPT_URL
, Url
.c_str());
114 curl_easy_setopt(Curl
, CURLOPT_FOLLOWLOCATION
, Request
.FollowRedirects
);
116 curl_slist
*Headers
= nullptr;
117 for (const std::string
&Header
: Request
.Headers
)
118 Headers
= curl_slist_append(Headers
, Header
.c_str());
119 curl_easy_setopt(Curl
, CURLOPT_HTTPHEADER
, Headers
);
121 CurlHTTPRequest
CurlRequest(Handler
);
122 curl_easy_setopt(Curl
, CURLOPT_WRITEDATA
, &CurlRequest
);
123 CURLcode CurlRes
= curl_easy_perform(Curl
);
124 curl_slist_free_all(Headers
);
125 if (CurlRes
!= CURLE_OK
)
126 return joinErrors(std::move(CurlRequest
.ErrorState
),
127 createStringError(errc::io_error
,
128 "curl_easy_perform() failed: %s\n",
129 curl_easy_strerror(CurlRes
)));
130 return std::move(CurlRequest
.ErrorState
);
133 unsigned HTTPClient::responseCode() {
135 curl_easy_getinfo(Curl
, CURLINFO_RESPONSE_CODE
, &Code
);
141 HTTPClient::HTTPClient() = default;
143 HTTPClient::~HTTPClient() = default;
145 bool HTTPClient::isAvailable() { return false; }
147 void HTTPClient::initialize() {}
149 void HTTPClient::cleanup() {}
151 void HTTPClient::setTimeout(std::chrono::milliseconds Timeout
) {}
153 Error
HTTPClient::perform(const HTTPRequest
&Request
,
154 HTTPResponseHandler
&Handler
) {
155 llvm_unreachable("No HTTP Client implementation available.");
158 unsigned HTTPClient::responseCode() {
159 llvm_unreachable("No HTTP Client implementation available.");