1 #pragma once 2 3 /// @file 4 /// @brief Include for the @ref coeurl::Client 5 6 #include <atomic> 7 #include <functional> 8 #include <map> 9 #include <memory> 10 #include <mutex> 11 #include <string> 12 #include <thread> 13 #include <vector> 14 15 #include <curl/curl.h> 16 #include <event2/event.h> 17 #include <event2/event_struct.h> 18 #include <spdlog/logger.h> 19 20 #include "headers.hpp" 21 22 /// @namespace coeurl 23 /// @brief Namespace for all public @ref coeurl classes 24 namespace coeurl { 25 struct Request; 26 struct SockInfo; 27 28 //! Global information, common to all connections 29 struct Client { 30 //! construct a new client 31 Client(); 32 /// @brief cleans up a client 33 /// Implicitly closes the connection and blocks until all of them exited. 34 ~Client(); 35 36 //! Uncopyable 37 Client(Client const &) = delete; 38 //! Uncopyable 39 void operator=(Client const &) = delete; 40 //! Unmoveable 41 Client(Client &&) = delete; 42 //! Unmoveable 43 void operator=(Client &&) = delete; 44 45 //! Submit a manually created request 46 void submit_request(std::shared_ptr<Request> conn); 47 //! Stop all currently running requests 48 void shutdown(); 49 //! Stop the event loop. If you force close it, all pending requests are 50 //! cancelled. 51 void close(bool force = false); 52 53 /// @brief Make a simple GET request. 54 /// For more complicated requests, create it manually and call submit_request. 55 /// @param url The url to request. 56 /// @param callback The callback, which will be called after the request is 57 /// completed. The request will be passed as a parameter, which you can use 58 /// to access the response. 59 /// @param headers Headers to use for this request. Defaults to none. 60 /// @param max_redirects How many redirects to follow. Defaults to none. 61 void get(std::string url, std::function<void(const Request &)> callback, const Headers &headers = {}, 62 long max_redirects = 0); 63 64 /// @brief Make a simple DELETE request. 65 /// @param url The url to request. 66 /// @param callback The callback, which will be called after the request is 67 /// completed. The request will be passed as a parameter, which you can use 68 /// to access the response. 69 /// @param headers Headers to use for this request. Defaults to none. 70 /// @param max_redirects How many redirects to follow. Defaults to none. 71 void delete_(std::string url, std::function<void(const Request &)> callback, const Headers &headers = {}, 72 long max_redirects = 0); 73 74 /// @brief Make a simple DELETE request with a body. 75 /// @param url The url to request. 76 /// @param request_body The body to use with this request. 77 /// @param mimetype The mimetype of the @a request_body. 78 /// @param callback The callback, which will be called after the request is 79 /// completed. The request will be passed as a parameter, which you can use 80 /// to access the response. 81 /// @param headers Headers to use for this request. Defaults to none. 82 /// @param max_redirects How many redirects to follow. Defaults to none. 83 void delete_(std::string url, std::string request_body, std::string mimetype, 84 std::function<void(const Request &)> callback, const Headers &headers = {}, long max_redirects = 0); 85 86 /// @brief Make a simple HEAD request. 87 /// For more complicated requests, create it manually and call submit_request. 88 /// @param url The url to request. 89 /// @param callback The callback, which will be called after the request is 90 /// completed. The request will be passed as a parameter, which you can use 91 /// to access the response. 92 /// @param headers Headers to use for this request. Defaults to none. 93 /// @param max_redirects How many redirects to follow. Defaults to none. 94 void head(std::string url, std::function<void(const Request &)> callback, const Headers &headers = {}, 95 long max_redirects = 0); 96 97 /// @brief Make a simple OPTIONS request. 98 /// For more complicated requests, create it manually and call submit_request. 99 /// @param url The url to request. 100 /// @param callback The callback, which will be called after the request is 101 /// completed. The request will be passed as a parameter, which you can use 102 /// to access the response. 103 /// @param headers Headers to use for this request. Defaults to none. 104 /// @param max_redirects How many redirects to follow. Defaults to none. 105 void options(std::string url, std::function<void(const Request &)> callback, const Headers &headers = {}, 106 long max_redirects = 0); 107 108 /// @brief Make a simple PUT request with a body. 109 /// @param url The url to request. 110 /// @param request_body The body to use with this request. 111 /// @param mimetype The mimetype of the @a request_body. 112 /// @param callback The callback, which will be called after the request is 113 /// completed. The request will be passed as a parameter, which you can use 114 /// to access the response. 115 /// @param headers Headers to use for this request. Defaults to none. 116 /// @param max_redirects How many redirects to follow. Defaults to none. 117 void put(std::string url, std::string request_body, std::string mimetype, 118 std::function<void(const Request &)> callback, const Headers &headers = {}, long max_redirects = 0); 119 120 /// @brief Make a simple POST request with a body. 121 /// @param url The url to request. 122 /// @param request_body The body to use with this request. 123 /// @param mimetype The mimetype of the @a request_body. 124 /// @param callback The callback, which will be called after the request is 125 /// completed. The request will be passed as a parameter, which you can use 126 /// to access the response. 127 /// @param headers Headers to use for this request. Defaults to none. 128 /// @param max_redirects How many redirects to follow. Defaults to none. 129 void post(std::string url, std::string request_body, std::string mimetype, 130 std::function<void(const Request &)> callback, const Headers &headers = {}, long max_redirects = 0); 131 132 /// @brief Set a global @a logger. 133 /// @param logger The spdlog logger to use for logging. 134 /// 135 /// Set the logger while no requests are in flight, for example before 136 /// starting the first request. set_loggercoeurl::Client137 static void set_logger(std::shared_ptr<spdlog::logger> logger) { log = std::move(logger); } 138 //! Set whether to \a verify the https certificates or not. set_verify_peercoeurl::Client139 void set_verify_peer(bool verify) { this->verify_peer_ = verify; } 140 /// @brief Query whether certificate verification is enabled or not 141 /// @sa set_verify_peer does_verify_peercoeurl::Client142 bool does_verify_peer() { return this->verify_peer_; } 143 144 //! Timeout connection after the specified amount of seconds, if the server 145 //! stops sending acks. connection_timeoutcoeurl::Client146 void connection_timeout(long t) { connection_timeout_ = t; } 147 148 private: 149 // Call this to run the event loop. Will block until the client is shutdown. 150 void run(); 151 152 void remove_request(Request *r); 153 void check_multi_info(); 154 155 static void event_cb(evutil_socket_t fd, short kind, void *userp); 156 static void timer_cb(evutil_socket_t fd, short kind, void *userp); 157 static void add_pending_requests_cb(evutil_socket_t, short, void *userp); 158 static void stop_ev_loop_cb(evutil_socket_t, short, void *userp); 159 static void cancel_requests_cb(evutil_socket_t, short, void *userp); 160 161 static int multi_timer_cb(CURLM *multi, long timeout_ms, Client *g); 162 163 static int sock_cb(CURL *e, curl_socket_t s, int what, void *cbp, void *sockp); 164 void addsock(curl_socket_t s, int action); 165 void setsock(SockInfo *f, curl_socket_t s, int act); 166 void remsock(SockInfo *f); 167 168 static void mcode_or_die(const char *where, CURLMcode code); 169 170 static std::shared_ptr<spdlog::logger> log; 171 172 struct event_base *evbase{}; 173 struct event timer_event {}; 174 struct event add_request_timer {}; 175 struct event stop_event {}; 176 struct event cancel_requests_timer {}; 177 CURLM *multi{}; 178 int still_running = 0; 179 std::atomic<bool> stopped{false}; 180 bool verify_peer_ = true; 181 182 long connection_timeout_ = 0; 183 184 std::mutex pending_requests_mutex; 185 std::vector<std::shared_ptr<Request>> pending_requests; 186 std::mutex running_requests_mutex; 187 std::vector<std::shared_ptr<Request>> running_requests; 188 std::mutex stopped_mutex; 189 190 std::thread bg_thread; 191 192 friend Request; 193 }; 194 } // namespace coeurl 195