libcurl.hpp
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 #pragma once
6 
7 #ifndef KVIKIO_LIBCURL_FOUND
8 #error \
9  "cannot include the remote IO API, please build KvikIO with libcurl (-DKvikIO_REMOTE_SUPPORT=ON)"
10 #endif
11 
12 #include <functional>
13 #include <memory>
14 #include <mutex>
15 #include <sstream>
16 #include <string>
17 #include <vector>
18 
19 #include <curl/curl.h>
20 
21 #include <kvikio/error.hpp>
22 
23 namespace kvikio {
24 
42 class LibCurl {
43  public:
44  // We hold a unique pointer to the raw curl handle and set `curl_easy_cleanup` as its Deleter.
45  using UniqueHandlePtr = std::unique_ptr<CURL, std::function<decltype(curl_easy_cleanup)>>;
46 
47  private:
48  std::mutex _mutex{};
49  // Curl handles free to be used.
50  std::vector<UniqueHandlePtr> _free_curl_handles{};
51 
52  LibCurl();
53  ~LibCurl() noexcept;
54 
55  public:
56  static LibCurl& instance();
57 
61  UniqueHandlePtr get_free_handle();
62 
66  UniqueHandlePtr get_handle();
67 
71  void retain_handle(UniqueHandlePtr handle);
72 };
73 
80 class CurlHandle {
81  private:
82  char _errbuf[CURL_ERROR_SIZE];
83  LibCurl::UniqueHandlePtr _handle;
84 
85  public:
95  CurlHandle(LibCurl::UniqueHandlePtr handle, std::string source_file, std::string source_line);
96  ~CurlHandle() noexcept;
97 
101  CurlHandle(CurlHandle const&) = delete;
102  CurlHandle& operator=(CurlHandle const&) = delete;
103  CurlHandle(CurlHandle&& o) = delete;
104  CurlHandle& operator=(CurlHandle&& o) = delete;
105 
109  CURL* handle() noexcept;
110 
120  [[nodiscard]] std::string error_message() const;
121 
125  void clear_error_message() noexcept;
126 
135  template <typename VAL>
136  void setopt(CURLoption option, VAL value)
137  {
138  CURLcode err = curl_easy_setopt(handle(), option, value);
139  if (err != CURLE_OK) {
140  std::stringstream ss;
141  ss << "curl_easy_setopt() error "
142  << "(" << curl_easy_strerror(err) << ")";
143  KVIKIO_FAIL(ss.str(), std::runtime_error);
144  }
145  }
146 
158  void perform();
159 
169  void perform(std::function<void()> const& on_retry);
170 
179  template <typename OUTPUT>
180  void getinfo(CURLINFO info, OUTPUT* output)
181  {
182  CURLcode err = curl_easy_getinfo(handle(), info, output);
183  if (err != CURLE_OK) {
184  std::stringstream ss;
185  ss << "curl_easy_getinfo() error "
186  << "(" << curl_easy_strerror(err) << ")";
187  KVIKIO_FAIL(ss.str(), std::runtime_error);
188  }
189  }
190 };
191 
192 namespace detail {
207 __attribute__((noinline)) inline std::string fix_conda_file_path_hack(std::string filename)
208 {
209  if (filename.data() != nullptr) { return std::string{filename.data()}; }
210  return std::string{};
211 }
212 } // namespace detail
213 
219 #define create_curl_handle() \
220  kvikio::CurlHandle(kvikio::LibCurl::instance().get_handle(), \
221  kvikio::detail::fix_conda_file_path_hack(__FILE__), \
222  KVIKIO_STRINGIFY(__LINE__))
223 
224 } // namespace kvikio
Representation of a curl easy handle pointer and its operations.
Definition: libcurl.hpp:80
void setopt(CURLoption option, VAL value)
Set option for the curl handle.
Definition: libcurl.hpp:136
CurlHandle(LibCurl::UniqueHandlePtr handle, std::string source_file, std::string source_line)
Construct a new curl handle.
void clear_error_message() noexcept
Discard the recorded error message.
void perform(std::function< void()> const &on_retry)
Perform a blocking network transfer, and if the transfer fails, execute an on_retry callback to roll ...
std::string error_message() const
Get the most recent error message libcurl recorded for this handle.
void getinfo(CURLINFO info, OUTPUT *output)
Extract information from a curl handle.
Definition: libcurl.hpp:180
void perform()
Perform a blocking network transfer using previously set options.
CURL * handle() noexcept
Get the underlying curl easy handle pointer.
Singleton class to initialize and cleanup the global state of libcurl.
Definition: libcurl.hpp:42
UniqueHandlePtr get_free_handle()
Returns a free curl handle if available.
void retain_handle(UniqueHandlePtr handle)
Retain a curl handle for later use.
UniqueHandlePtr get_handle()
Returns a curl handle, create a new handle if none is available.
#define KVIKIO_FAIL(...)
Indicates that an erroneous code path has been taken.
Definition: error.hpp:187
KvikIO namespace.
Definition: batch.hpp:16