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