23#ifdef LLVM_ENABLE_CURL
35 return A.Url ==
B.Url &&
A.Method ==
B.Method &&
36 A.FollowRedirects ==
B.FollowRedirects &&
37 A.PinnedCertFingerprint ==
B.PinnedCertFingerprint;
50#ifdef LLVM_ENABLE_CURL
56 curl_global_init(CURL_GLOBAL_ALL);
63 curl_global_cleanup();
69 if (
Timeout < std::chrono::milliseconds(0))
70 Timeout = std::chrono::milliseconds(0);
71 curl_easy_setopt(Handle, CURLOPT_TIMEOUT_MS,
Timeout.count());
77struct CurlHTTPRequest {
78 CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {}
79 void storeError(
Error Err) {
80 ErrorState =
joinErrors(std::move(Err), std::move(ErrorState));
82 HTTPResponseHandler &Handler;
86static size_t curlWriteFunction(
char *Contents,
size_t Size,
size_t NMemb,
87 CurlHTTPRequest *CurlRequest) {
90 CurlRequest->Handler.handleBodyChunk(
StringRef(Contents,
Size))) {
91 CurlRequest->storeError(std::move(Err));
99 "Must call HTTPClient::initialize() at the beginning of main().");
102 Handle = curl_easy_init();
103 assert(Handle &&
"Curl could not be initialized");
105 curl_easy_setopt(Handle, CURLOPT_WRITEFUNCTION, curlWriteFunction);
107 curl_easy_setopt(Handle, CURLOPT_ACCEPT_ENCODING,
"");
116 "Unsupported CURL request method.");
118 SmallString<128> Url = Request.
Url;
119 curl_easy_setopt(Handle, CURLOPT_URL, Url.
c_str());
120 curl_easy_setopt(Handle, CURLOPT_FOLLOWLOCATION, Request.
FollowRedirects);
122 curl_slist *Headers =
nullptr;
123 for (
const std::string &Header : Request.
Headers)
124 Headers = curl_slist_append(Headers, Header.c_str());
125 curl_easy_setopt(Handle, CURLOPT_HTTPHEADER, Headers);
127 CurlHTTPRequest CurlRequest(Handler);
128 curl_easy_setopt(Handle, CURLOPT_WRITEDATA, &CurlRequest);
129 CURLcode CurlRes = curl_easy_perform(Handle);
130 curl_slist_free_all(Headers);
131 if (CurlRes != CURLE_OK)
132 return joinErrors(std::move(CurlRequest.ErrorState),
134 "curl_easy_perform() failed: %s\n",
135 curl_easy_strerror(CurlRes)));
136 return std::move(CurlRequest.ErrorState);
141 curl_easy_getinfo(Handle, CURLINFO_RESPONSE_CODE, &Code);
158struct WinHTTPSession {
159 HINTERNET SessionHandle =
nullptr;
160 HINTERNET ConnectHandle =
nullptr;
161 HINTERNET RequestHandle =
nullptr;
162 DWORD ResponseCode = 0;
166 WinHttpCloseHandle(RequestHandle);
168 WinHttpCloseHandle(ConnectHandle);
170 WinHttpCloseHandle(SessionHandle);
174bool parseURL(
StringRef Url, std::wstring &Host, std::wstring &Path,
175 INTERNET_PORT &Port,
bool &Secure) {
187 size_t SlashPos = Url.
find(
'/');
193 size_t ColonPos = HostPort.
find(
':');
204 Port =
static_cast<INTERNET_PORT
>(std::stoi(PortStr.
str()));
206 Port = Secure ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
233 WinHTTPSession *Session =
static_cast<WinHTTPSession *
>(Handle);
234 if (Session && Session->SessionHandle) {
236 WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_CONNECT_TIMEOUT,
237 &TimeoutMs,
sizeof(TimeoutMs));
238 WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_RECEIVE_TIMEOUT,
239 &TimeoutMs,
sizeof(TimeoutMs));
240 WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_SEND_TIMEOUT,
241 &TimeoutMs,
sizeof(TimeoutMs));
245static Error VerifyTLSCertWinHTTP(HINTERNET RequestHandle,
246 const std::string &PinnedFingerprint) {
250 if (!CryptStringToBinaryA(
251 PinnedFingerprint.c_str(), (DWORD)PinnedFingerprint.size(),
252 CRYPT_STRING_HEXRAW,
Expected, &ExpectedSize,
nullptr,
nullptr))
254 "Invalid certificate fingerprint format");
257 PCCERT_CONTEXT CertCtx =
nullptr;
258 DWORD CertCtxSize =
sizeof(CertCtx);
259 if (!WinHttpQueryOption(RequestHandle, WINHTTP_OPTION_SERVER_CERT_CONTEXT,
260 &CertCtx, &CertCtxSize))
262 "Failed to retrieve server certificate");
264 std::array<BYTE, 32> Actual;
265 DWORD ActualSize = Actual.size();
266 bool GotHash = CertGetCertificateContextProperty(
267 CertCtx, CERT_SHA256_HASH_PROP_ID, Actual.data(), &ActualSize);
268 CertFreeCertificateContext(CertCtx);
271 "Failed to compute certificate fingerprint");
273 if (memcmp(Actual.data(),
Expected, Actual.size()) != 0)
275 "Certificate fingerprint mismatch");
284 "Only GET requests are supported.");
285 for (
const std::string &Header : Request.
Headers)
286 if (Header.find(
"\r") != std::string::npos ||
287 Header.find(
"\n") != std::string::npos) {
289 "Unsafe request can lead to header injection.");
292 WinHTTPSession *Session =
static_cast<WinHTTPSession *
>(Handle);
295 std::wstring Host,
Path;
296 INTERNET_PORT Port = 0;
298 if (!parseURL(Request.
Url, Host, Path, Port, Secure))
300 "Invalid URL: " + Request.
Url);
303 Session->SessionHandle =
304 WinHttpOpen(L
"LLVM-HTTPClient/1.0", WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
305 WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0);
306 if (!Session->SessionHandle)
310 DWORD SecureProtocols =
311 WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 | WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3;
312 if (!WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_SECURE_PROTOCOLS,
313 &SecureProtocols,
sizeof(SecureProtocols)))
317 DWORD RedirectPolicy = WINHTTP_OPTION_REDIRECT_POLICY_DISALLOW_HTTPS_TO_HTTP;
319 RedirectPolicy = WINHTTP_OPTION_REDIRECT_POLICY_NEVER;
320 if (!WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_REDIRECT_POLICY,
321 &RedirectPolicy,
sizeof(RedirectPolicy)))
325 DWORD EnableHttp2 = WINHTTP_PROTOCOL_FLAG_HTTP2;
326 WinHttpSetOption(Session->SessionHandle, WINHTTP_OPTION_ENABLE_HTTP_PROTOCOL,
327 &EnableHttp2,
sizeof(EnableHttp2));
330 Session->ConnectHandle =
331 WinHttpConnect(Session->SessionHandle, Host.c_str(), Port, 0);
332 if (!Session->ConnectHandle) {
334 "Failed to connect to host: " + Request.
Url);
340 Flags |= WINHTTP_FLAG_SECURE;
342 Session->RequestHandle = WinHttpOpenRequest(
343 Session->ConnectHandle, L
"GET",
Path.c_str(),
nullptr, WINHTTP_NO_REFERER,
344 WINHTTP_DEFAULT_ACCEPT_TYPES, Flags);
345 if (!Session->RequestHandle)
348 DWORD SecurityFlags = 0;
351 DWORD EnableRevocationChecks = WINHTTP_ENABLE_SSL_REVOCATION;
352 if (!WinHttpSetOption(Session->RequestHandle, WINHTTP_OPTION_ENABLE_FEATURE,
353 &EnableRevocationChecks,
354 sizeof(EnableRevocationChecks)))
356 errc::io_error,
"Failed to enable certificate revocation checks");
362 SecurityFlags = (SecurityFlags | SECURITY_FLAG_IGNORE_UNKNOWN_CA);
363 if (!WinHttpSetOption(Session->RequestHandle, WINHTTP_OPTION_SECURITY_FLAGS,
364 &SecurityFlags,
sizeof(SecurityFlags)))
366 "Failed to enforce security flags");
370 for (
const std::string &Header : Request.
Headers) {
371 std::wstring WideHeader;
374 WinHttpAddRequestHeaders(Session->RequestHandle, WideHeader.c_str(),
375 static_cast<DWORD>(WideHeader.length()),
376 WINHTTP_ADDREQ_FLAG_ADD);
380 if (!WinHttpSendRequest(Session->RequestHandle, WINHTTP_NO_ADDITIONAL_HEADERS,
381 0,
nullptr, 0, 0, 0))
385 if (!WinHttpReceiveResponse(Session->RequestHandle,
nullptr))
389 if ((SecurityFlags & SECURITY_FLAG_IGNORE_UNKNOWN_CA) != 0)
390 if (
Error Err = VerifyTLSCertWinHTTP(Session->RequestHandle,
396 if (!WinHttpQueryHeaders(Session->RequestHandle,
397 WINHTTP_QUERY_STATUS_CODE |
398 WINHTTP_QUERY_FLAG_NUMBER,
399 WINHTTP_HEADER_NAME_BY_INDEX, &Session->ResponseCode,
401 Session->ResponseCode = 0;
404 DWORD BytesAvailable = 0;
405 while (WinHttpQueryDataAvailable(Session->RequestHandle, &BytesAvailable)) {
406 if (BytesAvailable == 0)
409 std::vector<char> Buffer(BytesAvailable);
411 if (!WinHttpReadData(Session->RequestHandle, Buffer.data(), BytesAvailable,
426 WinHTTPSession *Session =
static_cast<WinHTTPSession *
>(Handle);
427 return Session ? Session->ResponseCode : 0;
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
This file implements a class to represent arbitrary precision integral constant values and operations...
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
static GCRegistry::Add< OcamlGC > B("ocaml", "ocaml 3.10-compatible GC")
ManagedStatic< HTTPClientCleanup > Cleanup
This file contains the declarations of the HTTPClient library for issuing HTTP requests and handling ...
Lightweight error class with error context and mandatory checking.
static ErrorSuccess success()
Create a success value.
Tagged union holding either a T or a Error.
static bool isAvailable()
Returns true only if LLVM has been compiled with a working HTTPClient.
static bool IsInitialized
unsigned responseCode()
Returns the last received response code or zero if none.
static void initialize()
Must be called at the beginning of a program, while it is a single thread.
Error perform(const HTTPRequest &Request, HTTPResponseHandler &Handler)
Performs the Request, passing response data to the Handler.
void setTimeout(std::chrono::milliseconds Timeout)
Sets the timeout for the entire request, in milliseconds.
static void cleanup()
Must be called at the end of a program, while it is a single thread.
A handler for state updates occurring while an HTTPRequest is performed.
virtual Error handleBodyChunk(StringRef BodyChunk)=0
Processes an additional chunk of bytes of the HTTP response body.
ManagedStatic - This transparently changes the behavior of global statics to be lazily constructed on...
StringRef - Represent a constant reference to a string, i.e.
static constexpr size_t npos
std::string str() const
str - Get the contents as an std::string.
constexpr StringRef substr(size_t Start, size_t N=npos) const
Return a reference to the substring from [Start, Start + N).
bool starts_with(StringRef Prefix) const
Check if this string starts with the given Prefix.
StringRef drop_front(size_t N=1) const
Return a StringRef equal to 'this' but with the first N elements dropped.
size_t find(char C, size_t From=0) const
Search for the first character C in the string.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
NodeAddr< CodeNode * > Code
This is an optimization pass for GlobalISel generic memory operations.
Error createStringError(std::error_code EC, char const *Fmt, const Ts &... Vals)
Create formatted StringError object.
bool operator==(const AddressRangeValuePair &LHS, const AddressRangeValuePair &RHS)
Error joinErrors(Error E1, Error E2)
Concatenate errors.
@ Timeout
Reached timeout while waiting for the owner to release the lock.
LLVM_ABI bool ConvertUTF8toWide(unsigned WideCharWidth, llvm::StringRef Source, char *&ResultPtr, const UTF8 *&ErrorPtr)
Convert an UTF8 StringRef to UTF8, UTF16, or UTF32 depending on WideCharWidth.
A stateless description of an outbound HTTP request.
std::optional< std::string > PinnedCertFingerprint
SmallVector< std::string, 0 > Headers
HTTPRequest(StringRef Url)