88#include < unordered_set>
99#include < vector>
1010
11+ ; // <- libclang bug workaround
12+
1113#ifdef USE_STANDALONE_ASIO
1214#include < asio.hpp>
1315#include < asio/steady_timer.hpp>
@@ -33,14 +35,11 @@ namespace SimpleWeb {
3335#endif
3436
3537namespace SimpleWeb {
36- template <class socket_type >
37- class Client ;
38-
39- template <class socket_type >
4038 class ClientBase {
4139 public:
40+ class Response ;
4241 class Content : public std ::istream {
43- friend class ClientBase <socket_type> ;
42+ friend class Response ;
4443
4544 public:
4645 std::size_t size () noexcept {
@@ -64,8 +63,10 @@ namespace SimpleWeb {
6463 };
6564
6665 class Response {
67- friend class ClientBase <socket_type>;
68- friend class Client <socket_type>;
66+ template <typename SocketType>
67+ friend class ClientTemplate ;
68+ template <typename SocketType>
69+ friend class Client ;
6970
7071 asio::streambuf streambuf;
7172
@@ -80,7 +81,7 @@ namespace SimpleWeb {
8081 };
8182
8283 class Config {
83- friend class ClientBase <socket_type> ;
84+ friend class ClientBase ;
8485
8586 private:
8687 Config () noexcept {}
@@ -97,67 +98,49 @@ namespace SimpleWeb {
9798 std::string proxy_server;
9899 };
99100
100- protected:
101- class Connection : public std ::enable_shared_from_this<Connection> {
102- public:
103- template <typename ... Args>
104- Connection (std::shared_ptr<ScopeRunner> handler_runner, long timeout, Args &&... args) noexcept
105- : handler_runner(std::move(handler_runner)), timeout(timeout), socket(new socket_type(std::forward<Args>(args)...)) {}
106-
107- std::shared_ptr<ScopeRunner> handler_runner;
108- long timeout;
109-
110- std::unique_ptr<socket_type> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
111- bool in_use = false ;
112- bool attempt_reconnect = true ;
113-
114- std::unique_ptr<asio::steady_timer> timer;
115-
116- void set_timeout (long seconds = 0 ) noexcept {
117- if (seconds == 0 )
118- seconds = timeout;
119- if (seconds == 0 ) {
120- timer = nullptr ;
121- return ;
122- }
123- timer = std::unique_ptr<asio::steady_timer>(new asio::steady_timer (socket->get_io_service ()));
124- timer->expires_from_now (std::chrono::seconds (seconds));
125- auto self = this ->shared_from_this ();
126- timer->async_wait ([self](const error_code &ec) {
127- if (!ec) {
128- error_code ec;
129- self->socket ->lowest_layer ().cancel (ec);
130- }
131- });
132- }
133-
134- void cancel_timeout () noexcept {
135- if (timer) {
136- error_code ec;
137- timer->cancel (ec);
138- }
139- }
140- };
141-
142- class Session {
143- public:
144- Session (std::size_t max_response_streambuf_size, std::shared_ptr<Connection> connection, std::unique_ptr<asio::streambuf> request_streambuf) noexcept
145- : connection(std::move(connection)), request_streambuf(std::move(request_streambuf)), response(new Response(max_response_streambuf_size)) {}
146-
147- std::shared_ptr<Connection> connection;
148- std::unique_ptr<asio::streambuf> request_streambuf;
149- std::shared_ptr<Response> response;
150- std::function<void (const std::shared_ptr<Connection> &, const error_code &)> callback;
151- };
152-
153- public:
154101 // / Set before calling request
155102 Config config;
156103
157104 // / If you have your own asio::io_service, store its pointer here before calling request().
158105 // / When using asynchronous requests, running the io_service is up to the programmer.
159106 std::shared_ptr<asio::io_service> io_service;
160107
108+ // / Asynchronous request where setting and/or running Client's io_service is required.
109+ // / Do not use concurrently with the synchronous request functions.
110+ virtual void request (const std::string &method, const std::string &path, string_view content, const CaseInsensitiveMultimap &header,
111+ std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback_) = 0;
112+ // / Asynchronous request where setting and/or running Client's io_service is required.
113+ // / Do not use concurrently with the synchronous request functions.
114+ virtual void request (const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header,
115+ std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback_) = 0;
116+
117+ // / Asynchronous request where setting and/or running Client's io_service is required.
118+ // / Do not use concurrently with the synchronous request functions.
119+ void request (const std::string &method, const std::string &path, string_view content,
120+ std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback) {
121+ request (method, path, content, CaseInsensitiveMultimap (), std::move (request_callback));
122+ }
123+
124+ // / Asynchronous request where setting and/or running Client's io_service is required.
125+ // / Do not use concurrently with the synchronous request functions.
126+ void request (const std::string &method, const std::string &path,
127+ std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback) {
128+ request (method, path, std::string (), CaseInsensitiveMultimap (), std::move (request_callback));
129+ }
130+
131+ // / Asynchronous request where setting and/or running Client's io_service is required.
132+ // / Do not use concurrently with the synchronous request functions.
133+ void request (const std::string &method, std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback) {
134+ request (method, std::string (" /" ), std::string (), CaseInsensitiveMultimap (), std::move (request_callback));
135+ }
136+
137+ // / Asynchronous request where setting and/or running Client's io_service is required.
138+ // / Do not use concurrently with the synchronous request functions.
139+ void request (const std::string &method, const std::string &path, std::istream &content,
140+ std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback) {
141+ request (method, path, content, CaseInsensitiveMultimap (), std::move (request_callback));
142+ }
143+
161144 // / Convenience function to perform synchronous request. The io_service is run within this function.
162145 // / If reusing the io_service for other tasks, use the asynchronous request functions instead.
163146 // / Do not use concurrently with the asynchronous request functions.
@@ -218,10 +201,78 @@ namespace SimpleWeb {
218201 return response;
219202 }
220203
204+ // / Close connections
205+ virtual void stop () noexcept = 0;
206+
207+ virtual ~ClientBase () {}
208+
209+ protected:
210+ std::size_t concurrent_synchronous_requests = 0 ;
211+ std::mutex concurrent_synchronous_requests_mutex;
212+ };
213+
214+ template <typename SocketType>
215+ class ClientTemplate : public ClientBase {
216+ protected:
217+ class Connection : public std ::enable_shared_from_this<Connection> {
218+ public:
219+ template <typename ... Args>
220+ Connection (std::shared_ptr<ScopeRunner> handler_runner, long timeout, Args &&... args) noexcept
221+ : handler_runner(std::move(handler_runner)), timeout(timeout), socket(new SocketType(std::forward<Args>(args)...)) {}
222+
223+ std::shared_ptr<ScopeRunner> handler_runner;
224+ long timeout;
225+
226+ std::unique_ptr<SocketType> socket; // Socket must be unique_ptr since asio::ssl::stream<asio::ip::tcp::socket> is not movable
227+ bool in_use = false ;
228+ bool attempt_reconnect = true ;
229+
230+ std::unique_ptr<asio::steady_timer> timer;
231+
232+ void set_timeout (long seconds = 0 ) noexcept {
233+ if (seconds == 0 )
234+ seconds = timeout;
235+ if (seconds == 0 ) {
236+ timer = nullptr ;
237+ return ;
238+ }
239+ timer = std::unique_ptr<asio::steady_timer>(new asio::steady_timer (socket->get_io_service ()));
240+ timer->expires_from_now (std::chrono::seconds (seconds));
241+ auto self = this ->shared_from_this ();
242+ timer->async_wait ([self](const error_code &ec) {
243+ if (!ec) {
244+ error_code ec;
245+ self->socket ->lowest_layer ().cancel (ec);
246+ }
247+ });
248+ }
249+
250+ void cancel_timeout () noexcept {
251+ if (timer) {
252+ error_code ec;
253+ timer->cancel (ec);
254+ }
255+ }
256+ };
257+
258+ class Session {
259+ public:
260+ Session (std::size_t max_response_streambuf_size, std::shared_ptr<Connection> connection, std::unique_ptr<asio::streambuf> request_streambuf) noexcept
261+ : connection(std::move(connection)), request_streambuf(std::move(request_streambuf)), response(new Response(max_response_streambuf_size)) {}
262+
263+ std::shared_ptr<Connection> connection;
264+ std::unique_ptr<asio::streambuf> request_streambuf;
265+ std::shared_ptr<Response> response;
266+ std::function<void (const std::shared_ptr<Connection> &, const error_code &)> callback;
267+ };
268+
269+ public:
270+ using ClientBase::request;
271+
221272 // / Asynchronous request where setting and/or running Client's io_service is required.
222273 // / Do not use concurrently with the synchronous request functions.
223274 void request (const std::string &method, const std::string &path, string_view content, const CaseInsensitiveMultimap &header,
224- std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback_) {
275+ std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback_) override {
225276 auto session = std::make_shared<Session>(config.max_response_streambuf_size , get_connection (), create_request_header (method, path, header));
226277 auto response = session->response ;
227278 auto request_callback = std::make_shared<std::function<void (std::shared_ptr<Response>, const error_code &)>>(std::move (request_callback_));
@@ -268,25 +319,8 @@ namespace SimpleWeb {
268319
269320 // / Asynchronous request where setting and/or running Client's io_service is required.
270321 // / Do not use concurrently with the synchronous request functions.
271- void request (const std::string &method, const std::string &path, string_view content,
272- std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback) {
273- request (method, path, content, CaseInsensitiveMultimap (), std::move (request_callback));
274- }
275-
276- // / Asynchronous request where setting and/or running Client's io_service is required.
277- void request (const std::string &method, const std::string &path,
278- std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback) {
279- request (method, path, std::string (), CaseInsensitiveMultimap (), std::move (request_callback));
280- }
281-
282- // / Asynchronous request where setting and/or running Client's io_service is required.
283- void request (const std::string &method, std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback) {
284- request (method, std::string (" /" ), std::string (), CaseInsensitiveMultimap (), std::move (request_callback));
285- }
286-
287- // / Asynchronous request where setting and/or running Client's io_service is required.
288322 void request (const std::string &method, const std::string &path, std::istream &content, const CaseInsensitiveMultimap &header,
289- std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback_) {
323+ std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback_) override {
290324 auto session = std::make_shared<Session>(config.max_response_streambuf_size , get_connection (), create_request_header (method, path, header));
291325 auto response = session->response ;
292326 auto request_callback = std::make_shared<std::function<void (std::shared_ptr<Response>, const error_code &)>>(std::move (request_callback_));
@@ -335,14 +369,8 @@ namespace SimpleWeb {
335369 connect (session);
336370 }
337371
338- // / Asynchronous request where setting and/or running Client's io_service is required.
339- void request (const std::string &method, const std::string &path, std::istream &content,
340- std::function<void (std::shared_ptr<Response>, const error_code &)> &&request_callback) {
341- request (method, path, content, CaseInsensitiveMultimap (), std::move (request_callback));
342- }
343-
344372 // / Close connections
345- void stop () noexcept {
373+ void stop () noexcept override {
346374 std::unique_lock<std::mutex> lock (connections_mutex);
347375 for (auto it = connections.begin (); it != connections.end ();) {
348376 error_code ec;
@@ -351,7 +379,7 @@ namespace SimpleWeb {
351379 }
352380 }
353381
354- virtual ~ClientBase () noexcept {
382+ ~ClientTemplate () {
355383 handler_runner->stop ();
356384 stop ();
357385 }
@@ -370,10 +398,7 @@ namespace SimpleWeb {
370398
371399 std::shared_ptr<ScopeRunner> handler_runner;
372400
373- std::size_t concurrent_synchronous_requests = 0 ;
374- std::mutex concurrent_synchronous_requests_mutex;
375-
376- ClientBase (const std::string &host_port, unsigned short default_port) noexcept : default_port(default_port), handler_runner(new ScopeRunner()) {
401+ ClientTemplate (const std::string &host_port, unsigned short default_port) noexcept : default_port(default_port), handler_runner(new ScopeRunner()) {
377402 auto parsed_host_port = parse_host_port (host_port, default_port);
378403 host = parsed_host_port.first ;
379404 port = parsed_host_port.second ;
@@ -420,7 +445,7 @@ namespace SimpleWeb {
420445 auto corrected_path = path;
421446 if (corrected_path == " " )
422447 corrected_path = " /" ;
423- if (!config.proxy_server .empty () && std::is_same<socket_type , asio::ip::tcp::socket>::value)
448+ if (!config.proxy_server .empty () && std::is_same<SocketType , asio::ip::tcp::socket>::value)
424449 corrected_path = " http://" + host + ' :' + std::to_string (port) + corrected_path;
425450
426451 std::unique_ptr<asio::streambuf> streambuf (new asio::streambuf ());
@@ -638,15 +663,15 @@ namespace SimpleWeb {
638663 }
639664 };
640665
641- template <class socket_type >
642- class Client : public ClientBase <socket_type> {};
666+ template <typename = void >
667+ class Client : public ClientBase {};
643668
644669 using HTTP = asio::ip::tcp::socket;
645670
646671 template <>
647- class Client <HTTP> : public ClientBase <HTTP> {
672+ class Client <HTTP> : public ClientTemplate <HTTP> {
648673 public:
649- Client (const std::string &server_port_path) noexcept : ClientBase <HTTP>::ClientBase (server_port_path, 80 ) {}
674+ Client (const std::string &server_port_path) noexcept : ClientTemplate <HTTP>::ClientTemplate (server_port_path, 80 ) {}
650675
651676 protected:
652677 std::shared_ptr<Connection> create_connection () noexcept override {
0 commit comments