My Project 3.5.5
C++ Distributed Hash Table
Loading...
Searching...
No Matches
dht_proxy_client.h
1/*
2 * Copyright (C) 2014-2025 Savoir-faire Linux Inc.
3 * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
4 * Adrien Béraud <adrien.beraud@savoirfairelinux.com>
5 * Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#pragma once
22
23#include <functional>
24#include <mutex>
25
26#include "callbacks.h"
27#include "def.h"
28#include "dht_interface.h"
29#include "proxy.h"
30#include "http.h"
31
32#include <restinio/all.hpp>
33#include <json/json.h>
34
35#include <chrono>
36#include <vector>
37#include <functional>
38
39namespace Json {
40class Value;
41}
42
43namespace http {
44class Resolver;
45class Request;
46}
47
48namespace dht {
49
50class OPENDHT_PUBLIC DhtProxyClient final : public DhtInterface {
51public:
52
53 DhtProxyClient();
54
55 explicit DhtProxyClient(
56 std::shared_ptr<crypto::Certificate> serverCA,
57 crypto::Identity clientIdentity,
58 std::function<void()> loopSignal,
59 const std::string& serverHost,
60 const std::string& userAgent = "",
61 const std::string& pushClientId = "",
62 const std::string& pushToken = "",
63 const std::string& pushTopic = "",
64 const std::string& pushPlatform = "",
65 std::shared_ptr<Logger> logger = {}
66 );
67
68 void setHeaderFields(http::Request& request);
69
70 virtual void setPushNotificationToken(const std::string& token) override;
71
72 virtual void setPushNotificationTopic(const std::string& topic) override {
73#ifdef OPENDHT_PUSH_NOTIFICATIONS
74 notificationTopic_ = topic;
75#else
76 (void) topic;
77#endif
78 }
79
80 virtual void setPushNotificationPlatform(const std::string& platform) override {
81#ifdef OPENDHT_PUSH_NOTIFICATIONS
82 platform_ = platform;
83#else
84 (void) platform;
85#endif
86 }
87
88 virtual ~DhtProxyClient();
89
93 inline const InfoHash& getNodeId() const override { return myid; }
94 void setOnPublicAddressChanged(PublicAddressChangedCb cb) override {
95 publicAddressChangedCb_ = std::move(cb);
96 }
97
101 NodeStatus getStatus(sa_family_t af) const override;
102 NodeStatus getStatus() const override {
103 return std::max(getStatus(AF_INET), getStatus(AF_INET6));
104 }
105
109 void shutdown(ShutdownCallback cb, bool) override;
110
117 bool isRunning(sa_family_t af = 0) const override;
118
129 virtual void get(const InfoHash& key, GetCallback cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override;
130 virtual void get(const InfoHash& key, GetCallback cb, DoneCallbackSimple donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
131 get(key, cb, bindDoneCb(std::move(donecb)), std::forward<Value::Filter>(f), std::forward<Where>(w));
132 }
133 virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallback donecb={}, Value::Filter&& f={}, Where&& w = {}) override {
134 get(key, bindGetCb(cb), std::move(donecb), std::forward<Value::Filter>(f), std::forward<Where>(w));
135 }
136 virtual void get(const InfoHash& key, GetCallbackSimple cb, DoneCallbackSimple donecb, Value::Filter&& f={}, Where&& w = {}) override {
137 get(key, bindGetCb(cb), bindDoneCb(std::move(donecb)), std::forward<Value::Filter>(f), std::forward<Where>(w));
138 }
139
147 void put(const InfoHash& key,
148 Sp<Value>,
149 DoneCallback cb=nullptr,
150 time_point created=time_point::max(),
151 bool permanent = false) override;
152 void put(const InfoHash& key,
153 const Sp<Value>& v,
154 DoneCallbackSimple cb,
155 time_point created=time_point::max(),
156 bool permanent = false) override
157 {
158 put(key, v, bindDoneCb(std::move(cb)), created, permanent);
159 }
160
161 void put(const InfoHash& key,
162 Value&& v,
163 DoneCallback cb=nullptr,
164 time_point created=time_point::max(),
165 bool permanent = false) override
166 {
167 put(key, std::make_shared<Value>(std::move(v)), std::move(cb), created, permanent);
168 }
169 void put(const InfoHash& key,
170 Value&& v,
171 DoneCallbackSimple cb,
172 time_point created=time_point::max(),
173 bool permanent = false) override
174 {
175 put(key, std::forward<Value>(v), bindDoneCb(std::move(cb)), created, permanent);
176 }
177
182 NodeStats getNodesStats(sa_family_t af) const override;
183
188 std::vector<SockAddr> getPublicAddress(sa_family_t family = 0) override;
189
197 virtual size_t listen(const InfoHash&, ValueCallback, Value::Filter={}, Where={}) override;
198
199 virtual size_t listen(const InfoHash& key, GetCallback cb, Value::Filter f={}, Where w={}) override {
200 return listen(key, [cb=std::move(cb)](const std::vector<Sp<Value>>& vals, bool expired){
201 if (not expired)
202 return cb(vals);
203 return true;
204 }, std::forward<Value::Filter>(f), std::forward<Where>(w));
205 }
206 virtual size_t listen(const InfoHash& key, GetCallbackSimple cb, Value::Filter f={}, Where w={}) override {
207 return listen(key, bindGetCb(std::move(cb)), std::forward<Value::Filter>(f), std::forward<Where>(w));
208 }
209 /*
210 * This function relies on the cache implementation.
211 * It means that there are no true cancel here, it keeps the caching in higher priority.
212 */
213 virtual bool cancelListen(const InfoHash& key, size_t token) override;
214
219 PushNotificationResult pushNotificationReceived(const std::map<std::string, std::string>& notification) override;
220
221 time_point periodic(const uint8_t*, size_t, SockAddr, const time_point& now) override;
222 time_point periodic(const uint8_t* buf, size_t buflen, const sockaddr* from, socklen_t fromlen, const time_point& now) override {
223 return periodic(buf, buflen, SockAddr(from, fromlen), now);
224 }
225
236 virtual void query(const InfoHash& /*key*/, QueryCallback /*cb*/, DoneCallback /*done_cb*/ = {}, Query&& /*q*/ = {}) override { }
237 virtual void query(const InfoHash& key, QueryCallback cb, DoneCallbackSimple done_cb = {}, Query&& q = {}) override {
238 query(key, cb, bindDoneCb(std::move(done_cb)), std::forward<Query>(q));
239 }
240
244 std::vector<Sp<Value>> getPut(const InfoHash&) const override;
245
249 Sp<Value> getPut(const InfoHash&, const Value::Id&) const override;
250
255 bool cancelPut(const InfoHash&, const Value::Id&) override;
256
257 void pingNode(SockAddr, DoneCallbackSimple&& /*cb*/={}) override { }
258
259 virtual void registerType(const ValueType& type) override {
260 types.registerType(type);
261 }
262 const ValueType& getType(ValueType::Id type_id) const override {
263 return types.getType(type_id);
264 }
265
266 std::vector<Sp<Value>> getLocal(const InfoHash& k, const Value::Filter& filter) const override;
267 Sp<Value> getLocalById(const InfoHash& k, Value::Id id) const override;
268
273 void insertNode(const InfoHash&, const SockAddr&) override { }
274 void insertNode(const NodeExport&) override { }
275 std::pair<size_t, size_t> getStoreSize() const override { return {}; }
276 std::vector<NodeExport> exportNodes() const override { return {}; }
277 std::vector<ValuesExport> exportValues() const override { return {}; }
278 void importValues(const std::vector<ValuesExport>&) override {}
279 std::string getStorageLog() const override { return {}; }
280 std::string getStorageLog(const InfoHash&) const override { return {}; }
281 std::string getRoutingTablesLog(sa_family_t) const override { return {}; }
282 std::string getSearchesLog(sa_family_t) const override { return {}; }
283 std::string getSearchLog(const InfoHash&, sa_family_t) const override { return {}; }
284 void dumpTables() const override {}
285 std::vector<unsigned> getNodeMessageStats(bool) override { return {}; }
286 void setStorageLimit(size_t) override {}
287 virtual size_t getStorageLimit() const override { return 0; }
288 void connectivityChanged(sa_family_t) override {
289 getProxyInfos();
290 }
291 void connectivityChanged() override {
292 getProxyInfos();
293 loopSignal_();
294 }
295
296 void listenKeepIdle(uint32_t seconds) {
297 listenKeepIdle_ = seconds;
298 }
299 inline uint32_t listenKeepIdle() { return listenKeepIdle_; }
300
301private:
305 void startProxy();
306 void stop();
307
312 struct InfoState;
313 void getProxyInfos();
314 void queryProxyInfo(const std::shared_ptr<InfoState>& infoState, const std::shared_ptr<http::Resolver>& resolver, sa_family_t family);
315 void onProxyInfos(const Json::Value& val, const sa_family_t family);
316 SockAddr parsePublicAddress(const Json::Value& val);
317
318 void opFailed();
319
320 void handleExpireListener(const asio::error_code &ec, const InfoHash& key);
321
322 struct Listener;
323 struct OperationState;
324 enum class ListenMethod {
325 LISTEN,
326 SUBSCRIBE,
327 RESUBSCRIBE,
328 };
329 using CacheValueCallback = std::function<bool(const std::vector<std::shared_ptr<Value>>& values, bool expired, system_clock::time_point)>;
330
334 void sendListen(const restinio::http_request_header_t& header, const CacheValueCallback& cb,
335 const Sp<OperationState>& opstate, Listener& listener, ListenMethod method = ListenMethod::LISTEN);
336 void handleResubscribe(const asio::error_code& ec, const InfoHash& key,
337 const size_t token, std::shared_ptr<OperationState> opstate);
338
339 void doPut(const InfoHash&, Sp<Value>, DoneCallbackSimple, time_point created, bool permanent);
340 void handleRefreshPut(const asio::error_code& ec, InfoHash key, Value::Id id);
341
345 void getConnectivityStatus();
349 void cancelAllListeners();
350
351 std::atomic_bool isDestroying_ {false};
352
353 std::string proxyUrl_;
354 dht::crypto::Identity clientIdentity_;
355 std::shared_ptr<dht::crypto::Certificate> serverCertificate_;
356 std::string userAgent_ {"OpenDHT"};
357 std::string pushClientId_;
358 std::string pushSessionId_;
359
360 mutable std::mutex lockCurrentProxyInfos_;
361 NodeStatus statusIpv4_ {NodeStatus::Disconnected};
362 NodeStatus statusIpv6_ {NodeStatus::Disconnected};
363 NodeStats stats4_ {};
364 NodeStats stats6_ {};
365 SockAddr localAddrv4_;
366 SockAddr localAddrv6_;
367 SockAddr publicAddressV4_;
368 SockAddr publicAddressV6_;
369 std::atomic_bool launchConnectedCbs_ {false};
370 PublicAddressChangedCb publicAddressChangedCb_ {};
371
372 InfoHash myid {};
373
374 // registred types
375 TypeStore types;
376
377 /*
378 * ASIO I/O Context for sockets in httpClient_
379 * Note: Each context is used in one thread only
380 */
381 asio::io_context httpContext_;
382 mutable std::mutex resolverLock_;
383 std::shared_ptr<http::Resolver> resolver_;
384
385 mutable std::mutex requestLock_;
386 std::map<unsigned, std::shared_ptr<http::Request>> requests_;
387 /*
388 * Thread for executing the http io_context.run() blocking call
389 */
390 std::thread httpClientThread_;
391
395 struct ProxySearch;
396
397 mutable std::mutex searchLock_;
398 size_t listenerToken_ {0};
399 std::map<InfoHash, ProxySearch> searches_;
400
404 uint32_t listenKeepIdle_ {120};
405
409 std::mutex lockCallbacks_;
410 std::vector<std::function<void()>> callbacks_;
411
412 Sp<InfoState> infoState_;
413
417 void handleProxyConfirm(const asio::error_code &ec);
418 std::unique_ptr<asio::steady_timer> nextProxyConfirmationTimer_;
419 std::unique_ptr<asio::steady_timer> listenerRestartTimer_;
420
424 void restartListeners(const asio::error_code &ec);
425
430 void resubscribe(const InfoHash& key, const size_t token, Listener& listener);
431
436 std::string deviceKey_ {};
437
441 std::string notificationTopic_ {};
442
446 std::string platform_
447#ifdef __ANDROID__
448 {"android"};
449#else
450#ifdef __APPLE__
451 {"ios"};
452#else
453 {};
454#endif
455#endif
456
457 const std::function<void()> loopSignal_;
458
459#ifdef OPENDHT_PUSH_NOTIFICATIONS
460 std::string fillBody(bool resubscribe);
461 void getPushRequest(Json::Value&) const;
462#endif // OPENDHT_PUSH_NOTIFICATIONS
463
464 Json::StreamWriterBuilder jsonBuilder_;
465 std::unique_ptr<Json::CharReader> jsonReader_;
466
467 std::shared_ptr<http::Request> buildRequest(const std::string& target = {});
468};
469
470}
bool cancelPut(const InfoHash &, const Value::Id &) override
void insertNode(const InfoHash &, const SockAddr &) override
NodeStatus getStatus(sa_family_t af) const override
Sp< Value > getPut(const InfoHash &, const Value::Id &) const override
virtual void query(const InfoHash &, QueryCallback, DoneCallback={}, Query &&={}) override
std::vector< Sp< Value > > getPut(const InfoHash &) const override
const InfoHash & getNodeId() const override
virtual void get(const InfoHash &key, GetCallback cb, DoneCallback donecb={}, Value::Filter &&f={}, Where &&w={}) override
virtual size_t listen(const InfoHash &, ValueCallback, Value::Filter={}, Where={}) override
bool isRunning(sa_family_t af=0) const override
Sp< Value > getLocalById(const InfoHash &k, Value::Id id) const override
PushNotificationResult pushNotificationReceived(const std::map< std::string, std::string > &notification) override
std::pair< size_t, size_t > getStoreSize() const override
void setStorageLimit(size_t) override
std::vector< NodeExport > exportNodes() const override
std::vector< Sp< Value > > getLocal(const InfoHash &k, const Value::Filter &filter) const override
std::vector< SockAddr > getPublicAddress(sa_family_t family=0) override
void connectivityChanged(sa_family_t) override
void put(const InfoHash &key, Sp< Value >, DoneCallback cb=nullptr, time_point created=time_point::max(), bool permanent=false) override
virtual size_t listen(const InfoHash &key, GetCallback cb, Value::Filter f={}, Where w={}) override
NodeStats getNodesStats(sa_family_t af) const override
void shutdown(ShutdownCallback cb, bool) override
NodeStatus
Definition callbacks.h:42
Describes a query destined to another peer.
Definition value.h:923
Serializable dht::Value filter.
Definition value.h:796