My Project 2.4.4
C++ Distributed Hash Table
Loading...
Searching...
No Matches
http.h
1/*
2 * Copyright (C) 2014-2022 Savoir-faire Linux Inc.
3 * Author: Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <https://www.gnu.org/licenses/>.
17 */
18
19#pragma once
20
21#include "def.h"
22#include "infohash.h"
23#include "crypto.h"
24
25// some libraries may try to redefine snprintf
26// but restinio will use it in std namespace
27#ifdef _MSC_VER
28# undef snprintf
29# define snprintf snprintf
30#endif
31
32#include <asio/ssl/context.hpp>
33#include <restinio/http_headers.hpp>
34#include <restinio/message_builders.hpp>
35
36#include <memory>
37#include <queue>
38#include <mutex>
39
40namespace Json {
41class Value;
42}
43
44extern "C" {
45struct http_parser;
46struct http_parser_settings;
47}
48
49namespace restinio {
50namespace impl {
51class tls_socket_t;
52}
53}
54
55namespace dht {
56struct Logger;
57
58namespace crypto {
59struct Certificate;
60}
61
62namespace http {
63
64using HandlerCb = std::function<void(const asio::error_code& ec)>;
65using BytesHandlerCb = std::function<void(const asio::error_code& ec, size_t bytes)>;
66using ConnectHandlerCb = std::function<void(const asio::error_code& ec,
67 const asio::ip::tcp::endpoint& endpoint)>;
68
69using ssl_socket_t = restinio::impl::tls_socket_t;
70using socket_t = asio::ip::tcp::socket;
71
72class OPENDHT_PUBLIC Url
73{
74public:
75 Url() = default;
76 Url(const std::string& url);
77 std::string url;
78 std::string protocol {"http"};
79 std::string host;
80 std::string service;
81 std::string target;
82 std::string query;
83 std::string fragment;
84
85 std::string toString() const;
86};
87
88class OPENDHT_PUBLIC Connection : public std::enable_shared_from_this<Connection>
89{
90public:
91 Connection(asio::io_context& ctx, const bool ssl = true, std::shared_ptr<dht::Logger> l = {});
92 Connection(asio::io_context& ctx, std::shared_ptr<dht::crypto::Certificate> server_ca,
93 const dht::crypto::Identity& identity, std::shared_ptr<dht::Logger> l = {});
95
96 inline unsigned int id() const { return id_; };
97 bool is_open() const;
98 bool is_ssl() const;
99 void checkOcsp(bool check = true) { checkOcsp_ = check; }
100
101 void set_ssl_verification(const std::string& hostname, const asio::ssl::verify_mode verify_mode);
102
103 asio::streambuf& input();
104 std::istream& data() { return istream_; }
105
106 std::string read_bytes(size_t bytes = 0);
107 std::string read_until(const char delim);
108
109 void async_connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, ConnectHandlerCb);
110 void async_handshake(HandlerCb cb);
111 void async_write(BytesHandlerCb cb);
112 void async_read_until(const char* delim, BytesHandlerCb cb);
113 void async_read_until(char delim, BytesHandlerCb cb);
114 void async_read(size_t bytes, BytesHandlerCb cb);
115 void async_read_some(size_t bytes, BytesHandlerCb cb);
116
117 void timeout(const std::chrono::seconds timeout, HandlerCb cb = {});
118 void close();
119
120private:
121
122 template<typename T>
123 T wrapCallabck(T cb) const {
124 return [t=shared_from_this(),cb=std::move(cb)](auto ...params) {
125 cb(params...);
126 };
127 }
128
129 mutable std::mutex mutex_;
130
131 unsigned int id_;
132 static std::atomic_uint ids_;
133
134 asio::io_context& ctx_;
135 std::unique_ptr<socket_t> socket_;
136 std::shared_ptr<asio::ssl::context> ssl_ctx_;
137 std::unique_ptr<ssl_socket_t> ssl_socket_;
138
139 asio::ip::tcp::endpoint endpoint_;
140
141 asio::streambuf write_buf_;
142 asio::streambuf read_buf_;
143 std::istream istream_;
144
145 std::unique_ptr<asio::steady_timer> timeout_timer_;
146 std::shared_ptr<dht::Logger> logger_;
147 bool checkOcsp_ {false};
148};
149
154{
155 ListenerSession() = default;
156 dht::InfoHash hash;
157 std::future<size_t> token;
158 std::shared_ptr<restinio::response_builder_t<restinio::chunked_output_t>> response;
159};
160
161/* @class Resolver
162 * @brief The purpose is to only resolve once to avoid mutliple dns requests per operation.
163 */
164class OPENDHT_PUBLIC Resolver
165{
166public:
167 using ResolverCb = std::function<void(const asio::error_code& ec,
168 const std::vector<asio::ip::tcp::endpoint>& endpoints)>;
169
170 Resolver(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger = {});
171 Resolver(asio::io_context& ctx, const std::string& host, const std::string& service,
172 const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
173
174 // use already resolved endpoints with classes using this resolver
175 Resolver(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint> endpoints,
176 const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
177 Resolver(asio::io_context& ctx, const std::string& url, std::vector<asio::ip::tcp::endpoint> endpoints,
178 std::shared_ptr<dht::Logger> logger = {});
179
180 ~Resolver();
181
182 inline const Url& get_url() const {
183 return url_;
184 }
185
186 void add_callback(ResolverCb cb, sa_family_t family = AF_UNSPEC);
187
188 std::shared_ptr<Logger> getLogger() const {
189 return logger_;
190 }
191
192private:
193 void resolve(const std::string& host, const std::string& service);
194
195 mutable std::mutex mutex_;
196
197 Url url_;
198 asio::error_code ec_;
199 asio::ip::tcp::resolver resolver_;
200 std::shared_ptr<bool> destroyed_;
201 std::vector<asio::ip::tcp::endpoint> endpoints_;
202
203 bool completed_ {false};
204 std::queue<ResolverCb> cbs_;
205
206 std::shared_ptr<dht::Logger> logger_;
207};
208
209class Request;
210
212{
213 unsigned status_code {0};
214 std::map<std::string, std::string> headers;
215 std::string body;
216 bool aborted {false};
217 std::weak_ptr<Request> request;
218};
219
220class OPENDHT_PUBLIC Request : public std::enable_shared_from_this<Request>
221{
222public:
223 enum class State {
224 CREATED,
225 SENDING,
226 HEADER_RECEIVED,
227 RECEIVING,
228 DONE
229 };
230 using OnStatusCb = std::function<void(unsigned status_code)>;
231 using OnDataCb = std::function<void(const char* at, size_t length)>;
232 using OnStateChangeCb = std::function<void(State state, const Response& response)>;
233 using OnJsonCb = std::function<void(Json::Value value, const Response& response)>;
234 using OnDoneCb = std::function<void(const Response& response)>;
235
236 // resolves implicitly
237 Request(asio::io_context& ctx, const std::string& url, const Json::Value& json, OnJsonCb jsoncb,
238 std::shared_ptr<dht::Logger> logger = {});
239 Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb,
240 std::shared_ptr<dht::Logger> logger = {});
241
242 Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger = {});
243 Request(asio::io_context& ctx, const std::string& host, const std::string& service,
244 const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
245 Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<dht::Logger> logger = {});
246
247 // user defined resolver
248 Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, sa_family_t family = AF_UNSPEC);
249 Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, const std::string& target, sa_family_t family = AF_UNSPEC);
250
251 // user defined resolved endpoints
252 Request(asio::io_context& ctx, std::vector<asio::ip::tcp::endpoint>&& endpoints,
253 const bool ssl = false, std::shared_ptr<dht::Logger> logger = {});
254
255 ~Request();
256
257 inline unsigned int id() const { return id_; };
258 void set_connection(std::shared_ptr<Connection> connection);
259 std::shared_ptr<Connection> get_connection() const;
260 inline const Url& get_url() const {
261 return resolver_->get_url();
262 };
263
265 std::shared_ptr<Request> getPrevious() const {
266 return prev_.lock();
267 }
268
269 inline std::string& to_string() {
270 return request_;
271 }
272
273 void set_certificate_authority(std::shared_ptr<dht::crypto::Certificate> certificate);
274 void set_identity(const dht::crypto::Identity& identity);
275 void set_logger(std::shared_ptr<dht::Logger> logger);
276
280 void set_header(restinio::http_request_header_t header);
281 void set_method(restinio::http_method_id_t method);
282 void set_target(std::string target);
283 void set_header_field(restinio::http_field_t field, std::string value);
284 void set_connection_type(restinio::http_connection_header_t connection);
285 void set_body(std::string body);
286 void set_auth(const std::string& username, const std::string& password);
287
288 void add_on_status_callback(OnStatusCb cb);
289 void add_on_body_callback(OnDataCb cb);
290 void add_on_state_change_callback(OnStateChangeCb cb);
291 void add_on_done_callback(OnDoneCb cb);
292
293 void send();
294
296 const Response& await();
297
301 void cancel();
302 void terminate(const asio::error_code& ec);
303
304private:
305 using OnCompleteCb = std::function<void()>;
306
307 struct Callbacks {
308 OnStatusCb on_status;
309 OnDataCb on_header_field;
310 OnDataCb on_header_value;
311 OnDataCb on_body;
312 OnStateChangeCb on_state_change;
313 };
314
315 static std::string getRelativePath(const Url& origin, const std::string& path);
316
317 void notify_state_change(State state);
318
319 void build();
320
321 void init_default_headers();
325 void init_parser();
326
327 void connect(std::vector<asio::ip::tcp::endpoint>&& endpoints, HandlerCb cb = {});
328
329 void post();
330
331 void handle_request(const asio::error_code& ec);
332 void handle_response(const asio::error_code& ec, size_t bytes);
333
334 void onHeadersComplete();
335 void onBody(const char* at, size_t length);
336 void onComplete();
337
338 mutable std::mutex mutex_;
339
340 std::shared_ptr<dht::Logger> logger_;
341
342 restinio::http_request_header_t header_;
343 std::map<restinio::http_field_t, std::string> headers_;
344 restinio::http_connection_header_t connection_type_ {restinio::http_connection_header_t::close};
345 std::string body_;
346
347 Callbacks cbs_;
348 State state_;
349
350 dht::crypto::Identity client_identity_;
351 std::shared_ptr<dht::crypto::Certificate> server_ca_;
352 std::string service_;
353 std::string host_;
354
355 unsigned int id_;
356 static std::atomic_uint ids_;
357 asio::io_context& ctx_;
358 sa_family_t family_ = AF_UNSPEC;
359 std::shared_ptr<Connection> conn_;
360 std::shared_ptr<Resolver> resolver_;
361
362 Response response_ {};
363 std::string request_;
364 std::atomic<bool> finishing_ {false};
365 std::unique_ptr<http_parser> parser_;
366 std::unique_ptr<http_parser_settings> parser_s_;
367
368 // Next request in case of redirect following
369 std::shared_ptr<Request> next_;
370 std::weak_ptr<Request> prev_;
371 unsigned num_redirect {0};
372 bool follow_redirect {true};
373};
374
375} // namespace http
376} // namespace dht
377
std::shared_ptr< Request > getPrevious() const
Definition: http.h:265
const Response & await()
void set_header(restinio::http_request_header_t header)
Definition: callbacks.h:35