Index: llvm/include/llvm/Support/CurlHTTPClient.h =================================================================== --- /dev/null +++ llvm/include/llvm/Support/CurlHTTPClient.h @@ -0,0 +1,44 @@ +//===-- llvm/Support/CURLClient.h - CURL 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 contains the declaration of the CurlHTTPRequest class. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_SUPPORT_CURL_HTTP_CLIENT_H +#define LLVM_SUPPORT_CURL_HTTP_CLIENT_H + +#include "llvm/Support/Error.h" +#include "llvm/Support/HTTPClient.h" + +typedef void CurlHandle; + +namespace llvm { + +class CurlHTTPClient : public HTTPClient { + template void curlSetOpt(T1 Option, T2 Parameter); + +protected: + CurlHandle *Curl = nullptr; + Error curlInit(); + +public: + using HTTPClient::HTTPClient; + + /// Performs the request + Error perform(const HTTPRequest Request, + HTTPResponseHandler &Handler) override; + + /// Cleans up curl handles. + ~CurlHTTPClient(); +}; + +} // end namespace llvm + +#endif // LLVM_SUPPORT_CURL_HTTP_CLIENT_H Index: llvm/lib/Support/CMakeLists.txt =================================================================== --- llvm/lib/Support/CMakeLists.txt +++ llvm/lib/Support/CMakeLists.txt @@ -74,6 +74,11 @@ set(system_libs ${system_libs} ${Z3_LIBRARIES}) endif() +# Link LibCURL if the user wants it +if (LLVM_ENABLE_CURL) + set(system_libs ${system_libs} ${CURL_LIBRARIES}) +endif () + # Override the C runtime allocator on Windows and embed it into LLVM tools & libraries if(LLVM_INTEGRATED_CRT_ALLOC) if (CMAKE_BUILD_TYPE AND NOT ${LLVM_USE_CRT_${uppercase_CMAKE_BUILD_TYPE}} MATCHES "^(MT|MTd)$") @@ -134,6 +139,7 @@ ConvertUTF.cpp ConvertUTFWrapper.cpp CrashRecoveryContext.cpp + CurlHTTPClient.cpp DataExtractor.cpp Debug.cpp DebugCounter.cpp Index: llvm/lib/Support/CurlHTTPClient.cpp =================================================================== --- /dev/null +++ llvm/lib/Support/CurlHTTPClient.cpp @@ -0,0 +1,92 @@ +//===-- llvm/Support/CURLClient.cpp - CURL 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 CurlHTTPClient class. +/// +//===----------------------------------------------------------------------===// + +#include "llvm/Support/CurlHTTPClient.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" +#include + +using namespace llvm; + +#define DEBUG_TYPE "CURLClient" + +/// CurlHTTPRequest and the curl{HeaderLine,BodyChunk}Hook functions are +/// implementation details used to work with curl. (Curl makes callbacks +/// with a single customizable pointer parameter.) +struct CurlHTTPRequest { + size_t Offset; + HTTPResponseHandler *Handler; +}; + +static size_t curlHeaderFunction(char *Contents, size_t Size, size_t NMemb, + CurlHTTPRequest *CurlRequest) { + assert(Size == 1 && "The Size passed by libCURL to CURLOPT_HEADERFUNCTION " + "should always be 1."); + return CurlRequest->Handler->handleHeaderLine(StringRef(Contents, NMemb)); +} + +static size_t curlWriteFunction(char *Contents, size_t Size, size_t NMemb, + CurlHTTPRequest *CurlRequest) { + Size *= NMemb; + size_t Offset = CurlRequest->Offset; + CurlRequest->Offset += Size; + return CurlRequest->Handler->handleBodyChunk(StringRef(Contents, Size), + Offset); +} + +template +void CurlHTTPClient::curlSetOpt(T1 Option, T2 Parameter) { + curl_easy_setopt(Curl, Option, Parameter); +} + +Error CurlHTTPClient::curlInit() { + if (Curl) + return Error::success(); + else if (!(Curl = curl_easy_init())) + return createStringError(errc::io_error, "Error initializing curl."); + + // Set the callback hooks on first initialization. + curlSetOpt(CURLOPT_WRITEFUNCTION, curlWriteFunction); + curlSetOpt(CURLOPT_HEADERFUNCTION, curlHeaderFunction); + return Error::success(); +} + +CurlHTTPClient::~CurlHTTPClient() { curl_easy_cleanup(Curl); } + +Error CurlHTTPClient::perform(const HTTPRequest Request, + HTTPResponseHandler &Handler) { + if (Request.Method != HTTPMethod::GET) + return createStringError(errc::invalid_argument, + "Unsupported CURL request method."); + if (Error Err = curlInit()) + return Err; + + SmallString<128> Url = Request.Url; + curlSetOpt(CURLOPT_URL, Url.c_str()); + curlSetOpt(CURLOPT_FOLLOWLOCATION, Request.FollowRedirects); + curlSetOpt(CURLOPT_WRITEDATA, &Handler); + curlSetOpt(CURLOPT_HEADERDATA, &Handler); + CURLcode CurlRes = curl_easy_perform(Curl); + if (CurlRes != CURLE_OK) + return createStringError(errc::io_error, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(CurlRes)); + + unsigned Code; + curl_easy_getinfo(Curl, CURLINFO_RESPONSE_CODE, &Code); + Handler.handleStatusCode(Code); + return Error::success(); +}