155 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C++
		
	
	
	
//===-- llvm/Debuginfod/HTTPClient.cpp - HTTP client library ----*- C++ -*-===//
 | 
						|
//
 | 
						|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | 
						|
// See https://llvm.org/LICENSE.txt for license information.
 | 
						|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | 
						|
//
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
///
 | 
						|
/// \file
 | 
						|
/// This file defines the implementation of the HTTPClient library for issuing
 | 
						|
/// HTTP requests and handling the responses.
 | 
						|
///
 | 
						|
//===----------------------------------------------------------------------===//
 | 
						|
 | 
						|
#include "llvm/Debuginfod/HTTPClient.h"
 | 
						|
#include "llvm/ADT/APInt.h"
 | 
						|
#include "llvm/ADT/StringRef.h"
 | 
						|
#include "llvm/Support/Errc.h"
 | 
						|
#include "llvm/Support/Error.h"
 | 
						|
#include "llvm/Support/MemoryBuffer.h"
 | 
						|
#ifdef LLVM_ENABLE_CURL
 | 
						|
#include <curl/curl.h>
 | 
						|
#endif
 | 
						|
 | 
						|
using namespace llvm;
 | 
						|
 | 
						|
HTTPRequest::HTTPRequest(StringRef Url) { this->Url = Url.str(); }
 | 
						|
 | 
						|
bool operator==(const HTTPRequest &A, const HTTPRequest &B) {
 | 
						|
  return A.Url == B.Url && A.Method == B.Method &&
 | 
						|
         A.FollowRedirects == B.FollowRedirects;
 | 
						|
}
 | 
						|
 | 
						|
HTTPResponseHandler::~HTTPResponseHandler() = default;
 | 
						|
 | 
						|
bool HTTPClient::IsInitialized = false;
 | 
						|
 | 
						|
class HTTPClientCleanup {
 | 
						|
public:
 | 
						|
  ~HTTPClientCleanup() { HTTPClient::cleanup(); }
 | 
						|
};
 | 
						|
static const HTTPClientCleanup Cleanup;
 | 
						|
 | 
						|
#ifdef LLVM_ENABLE_CURL
 | 
						|
 | 
						|
bool HTTPClient::isAvailable() { return true; }
 | 
						|
 | 
						|
void HTTPClient::initialize() {
 | 
						|
  if (!IsInitialized) {
 | 
						|
    curl_global_init(CURL_GLOBAL_ALL);
 | 
						|
    IsInitialized = true;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void HTTPClient::cleanup() {
 | 
						|
  if (IsInitialized) {
 | 
						|
    curl_global_cleanup();
 | 
						|
    IsInitialized = false;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {
 | 
						|
  if (Timeout < std::chrono::milliseconds(0))
 | 
						|
    Timeout = std::chrono::milliseconds(0);
 | 
						|
  curl_easy_setopt(Curl, CURLOPT_TIMEOUT_MS, Timeout.count());
 | 
						|
}
 | 
						|
 | 
						|
/// CurlHTTPRequest and the curl{Header,Write}Function are implementation
 | 
						|
/// details used to work with Curl. Curl makes callbacks with a single
 | 
						|
/// customizable pointer parameter.
 | 
						|
struct CurlHTTPRequest {
 | 
						|
  CurlHTTPRequest(HTTPResponseHandler &Handler) : Handler(Handler) {}
 | 
						|
  void storeError(Error Err) {
 | 
						|
    ErrorState = joinErrors(std::move(Err), std::move(ErrorState));
 | 
						|
  }
 | 
						|
  HTTPResponseHandler &Handler;
 | 
						|
  llvm::Error ErrorState = Error::success();
 | 
						|
};
 | 
						|
 | 
						|
static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb,
 | 
						|
                                CurlHTTPRequest *CurlRequest) {
 | 
						|
  Size *= NMemb;
 | 
						|
  if (Error Err =
 | 
						|
          CurlRequest->Handler.handleBodyChunk(StringRef(Contents, Size))) {
 | 
						|
    CurlRequest->storeError(std::move(Err));
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  return Size;
 | 
						|
}
 | 
						|
 | 
						|
HTTPClient::HTTPClient() {
 | 
						|
  assert(IsInitialized &&
 | 
						|
         "Must call HTTPClient::initialize() at the beginning of main().");
 | 
						|
  if (Curl)
 | 
						|
    return;
 | 
						|
  Curl = curl_easy_init();
 | 
						|
  assert(Curl && "Curl could not be initialized");
 | 
						|
  // Set the callback hooks.
 | 
						|
  curl_easy_setopt(Curl, CURLOPT_WRITEFUNCTION, curlWriteFunction);
 | 
						|
}
 | 
						|
 | 
						|
HTTPClient::~HTTPClient() { curl_easy_cleanup(Curl); }
 | 
						|
 | 
						|
Error HTTPClient::perform(const HTTPRequest &Request,
 | 
						|
                          HTTPResponseHandler &Handler) {
 | 
						|
  if (Request.Method != HTTPMethod::GET)
 | 
						|
    return createStringError(errc::invalid_argument,
 | 
						|
                             "Unsupported CURL request method.");
 | 
						|
 | 
						|
  SmallString<128> Url = Request.Url;
 | 
						|
  curl_easy_setopt(Curl, CURLOPT_URL, Url.c_str());
 | 
						|
  curl_easy_setopt(Curl, CURLOPT_FOLLOWLOCATION, Request.FollowRedirects);
 | 
						|
 | 
						|
  CurlHTTPRequest CurlRequest(Handler);
 | 
						|
  curl_easy_setopt(Curl, CURLOPT_WRITEDATA, &CurlRequest);
 | 
						|
  CURLcode CurlRes = curl_easy_perform(Curl);
 | 
						|
  if (CurlRes != CURLE_OK)
 | 
						|
    return joinErrors(std::move(CurlRequest.ErrorState),
 | 
						|
                      createStringError(errc::io_error,
 | 
						|
                                        "curl_easy_perform() failed: %s\n",
 | 
						|
                                        curl_easy_strerror(CurlRes)));
 | 
						|
  return std::move(CurlRequest.ErrorState);
 | 
						|
}
 | 
						|
 | 
						|
unsigned HTTPClient::responseCode() {
 | 
						|
  long Code = 0;
 | 
						|
  curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code);
 | 
						|
  return Code;
 | 
						|
}
 | 
						|
 | 
						|
#else
 | 
						|
 | 
						|
HTTPClient::HTTPClient() = default;
 | 
						|
 | 
						|
HTTPClient::~HTTPClient() = default;
 | 
						|
 | 
						|
bool HTTPClient::isAvailable() { return false; }
 | 
						|
 | 
						|
void HTTPClient::initialize() {}
 | 
						|
 | 
						|
void HTTPClient::cleanup() {}
 | 
						|
 | 
						|
void HTTPClient::setTimeout(std::chrono::milliseconds Timeout) {}
 | 
						|
 | 
						|
Error HTTPClient::perform(const HTTPRequest &Request,
 | 
						|
                          HTTPResponseHandler &Handler) {
 | 
						|
  llvm_unreachable("No HTTP Client implementation available.");
 | 
						|
}
 | 
						|
 | 
						|
unsigned HTTPClient::responseCode() {
 | 
						|
  llvm_unreachable("No HTTP Client implementation available.");
 | 
						|
}
 | 
						|
 | 
						|
#endif
 |