libcurl.hpp
1 /*
2  * SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION.
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 
119  template <typename VAL>
120  void setopt(CURLoption option, VAL value)
121  {
122  CURLcode err = curl_easy_setopt(handle(), option, value);
123  if (err != CURLE_OK) {
124  std::stringstream ss;
125  ss << "curl_easy_setopt() error "
126  << "(" << curl_easy_strerror(err) << ")";
127  KVIKIO_FAIL(ss.str(), std::runtime_error);
128  }
129  }
130 
136  void perform();
137 
146  template <typename OUTPUT>
147  void getinfo(CURLINFO info, OUTPUT* output)
148  {
149  CURLcode err = curl_easy_getinfo(handle(), info, output);
150  if (err != CURLE_OK) {
151  std::stringstream ss;
152  ss << "curl_easy_getinfo() error "
153  << "(" << curl_easy_strerror(err) << ")";
154  KVIKIO_FAIL(ss.str(), std::runtime_error);
155  }
156  }
157 };
158 
159 namespace detail {
174 __attribute__((noinline)) inline std::string fix_conda_file_path_hack(std::string filename)
175 {
176  if (filename.data() != nullptr) { return std::string{filename.data()}; }
177  return std::string{};
178 }
179 } // namespace detail
180 
186 #define create_curl_handle() \
187  kvikio::CurlHandle(kvikio::LibCurl::instance().get_handle(), \
188  kvikio::detail::fix_conda_file_path_hack(__FILE__), \
189  KVIKIO_STRINGIFY(__LINE__))
190 
191 } // 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:120
CurlHandle(LibCurl::UniqueHandlePtr handle, std::string source_file, std::string source_line)
Construct a new curl handle.
void getinfo(CURLINFO info, OUTPUT *output)
Extract information from a curl handle.
Definition: libcurl.hpp:147
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:241
KvikIO namespace.
Definition: batch.hpp:16