1 /* 2 * Copyright 2022 Google LLC 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #ifndef FCP_CLIENT_HTTP_CURL_CURL_API_H_ 17 #define FCP_CLIENT_HTTP_CURL_CURL_API_H_ 18 19 #include <memory> 20 #include <string> 21 #include <type_traits> 22 23 #include "absl/synchronization/mutex.h" 24 #include "curl/curl.h" 25 26 namespace fcp::client::http::curl { 27 // An RAII wrapper around the libcurl easy handle, which works with one request. 28 // The class is not thread-safe. 29 class CurlEasyHandle { 30 public: 31 ~CurlEasyHandle(); 32 CurlEasyHandle(const CurlEasyHandle&) = delete; 33 CurlEasyHandle& operator=(const CurlEasyHandle&) = delete; 34 35 CURLcode GetInfo(CURLINFO info, curl_off_t* value) const; 36 37 template <typename T, typename = std::enable_if_t<std::is_trivial_v<T>>> SetOpt(CURLoption option,T value)38 CURLcode SetOpt(CURLoption option, T value) { 39 return curl_easy_setopt(easy_handle_, option, value); 40 } SetOpt(CURLoption option,const std::string & value)41 CURLcode SetOpt(CURLoption option, const std::string& value) { 42 return SetOpt(option, value.c_str()); 43 } 44 45 // Converts the curl code into a human-readable form. 46 ABSL_MUST_USE_RESULT static std::string StrError(CURLcode code); 47 48 // Returns the underlying curl handle. 49 ABSL_MUST_USE_RESULT CURL* GetEasyHandle() const; 50 51 private: 52 friend class CurlApi; 53 CurlEasyHandle(); 54 CURL* const easy_handle_; 55 }; 56 57 // An RAII wrapper around the libcurl multi handle, which is a bundle of easy 58 // requests that performs them in parallel in one thread. The class is not 59 // thread-safe. 60 class CurlMultiHandle { 61 public: 62 ~CurlMultiHandle(); 63 CurlMultiHandle(const CurlMultiHandle&) = delete; 64 CurlMultiHandle& operator=(const CurlMultiHandle&) = delete; 65 66 // Fetches the next message in the message queue. 67 CURLMsg* InfoRead(int* msgs_in_queue); 68 69 // Add a new request to the bundle. 70 CURLMcode AddEasyHandle(CurlEasyHandle* easy_handle); 71 72 // Remove the requests from the bundle. It will cancel an unfinished request, 73 // but it will not delete the easy handle. 74 CURLMcode RemoveEasyHandle(CurlEasyHandle* easy_handle); 75 76 // Performs all active tasks and returns. 77 CURLMcode Perform(int* num_running_handles); 78 79 // Waits for a new job 80 CURLMcode Poll(curl_waitfd extra_fds[], unsigned int extra_nfds, 81 int timeout_ms, int* numfds); 82 83 // Converts the curl code into a human-readable form. 84 ABSL_MUST_USE_RESULT static std::string StrError(CURLMcode code); 85 86 private: 87 friend class CurlApi; 88 CurlMultiHandle(); 89 CURLM* const multi_handle_; 90 }; 91 92 // An RAII wrapper around global initialization for libcurl. It forces the user 93 // to create it first, so the initialization can be made, on which handles 94 // depend. The class needs to be created only once, and its methods are 95 // thread-safe. 96 class CurlApi { 97 public: 98 CurlApi(); 99 ~CurlApi(); 100 CurlApi(const CurlApi&) = delete; 101 CurlApi& operator=(const CurlApi&) = delete; 102 103 ABSL_MUST_USE_RESULT std::unique_ptr<CurlEasyHandle> CreateEasyHandle() const; 104 ABSL_MUST_USE_RESULT std::unique_ptr<CurlMultiHandle> CreateMultiHandle() 105 const; 106 107 private: 108 mutable absl::Mutex mutex_; 109 }; 110 111 } // namespace fcp::client::http::curl 112 113 #endif // FCP_CLIENT_HTTP_CURL_CURL_API_H_ 114