Compare commits

..
Author SHA1 Message Date
Cyrille Beraud b828060ac2 proxy: fix listener restart on connectivity change
When a device wakes up from Doze mode or switches networks (e.g. WiFi to 4G),
old TCP sockets may become silently dead. When connectivityChanged() is
called, the proxy client triggers a new HTTP request (getProxyInfos)
which succeeds since it uses a fresh socket, leading the client to
believe the connection is still UP.

Because the status doesn't change (Connected -> Connected),
launchConnectedCbs_ was not set, and restartListeners() was never invoked.
Consequently, push subscriptions and permanent puts were not renewed
on the proxy, causing the device to appear offline on the DHT.

This forces launchConnectedCbs_ to true on connectivityChanged, ensuring
that listeners and subscriptions are properly restarted.
2026-06-07 19:13:14 +02:00
Adrien Béraud 9a60329f66 proxy server: use token as request url 2026-06-03 20:00:34 -04:00
Adrien Béraud 0bb393ec36 http: allow to build Request by std::string url 2026-06-03 19:30:53 -04:00
vx32 947610003b http: allow to build Url from std::string, cleanup 2026-06-03 19:14:32 -04:00
Adrien Béraud dcbe158554 proxy server: allow url-encoded base64 for UnifiedPush 2026-06-03 19:12:40 -04:00
vx32 38e9a7fd61 base64: add support for url-encoded base64 2026-06-03 17:01:19 -04:00
7 changed files with 53 additions and 50 deletions

No files matched your search

+6 -1
View File
@@ -291,9 +291,14 @@ public:
virtual size_t getStorageLimit() const override { return 0; }
void setLocalStorageLimit(size_t) override {}
virtual size_t getLocalStorageLimit() const override { return STORAGE_LIMIT_UNLIMITED; }
void connectivityChanged(sa_family_t) override { getProxyInfos(); }
void connectivityChanged(sa_family_t) override
{
launchConnectedCbs_ = true;
getProxyInfos();
}
void connectivityChanged() override
{
launchConnectedCbs_ = true;
getProxyInfos();
loopSignal_();
}
+8 -14
View File
@@ -51,11 +51,11 @@ using ConnectHandlerCb = std::function<void(const asio::error_code& ec, const as
using ssl_socket_t = restinio::impl::tls_socket_t;
using socket_t = asio::ip::tcp::socket;
class OPENDHT_PUBLIC Url
struct OPENDHT_PUBLIC Url
{
public:
Url() = default;
Url(std::string_view url);
Url(std::string url);
std::string url;
std::string protocol {"http"};
std::string_view user;
@@ -149,7 +149,7 @@ public:
using ResolverCb
= std::function<void(const asio::error_code& ec, const std::vector<asio::ip::tcp::endpoint>& endpoints)>;
Resolver(asio::io_context& ctx, const std::string& url, std::shared_ptr<log::Logger> logger = {});
Resolver(asio::io_context& ctx, std::string url, std::shared_ptr<log::Logger> logger = {});
Resolver(asio::io_context& ctx,
std::string_view host,
std::string_view service,
@@ -161,10 +161,6 @@ public:
std::vector<asio::ip::tcp::endpoint> endpoints,
const bool ssl = false,
std::shared_ptr<log::Logger> logger = {});
Resolver(asio::io_context& ctx,
const std::string& url,
std::vector<asio::ip::tcp::endpoint> endpoints,
std::shared_ptr<log::Logger> logger = {});
~Resolver();
@@ -216,19 +212,19 @@ public:
// resolves implicitly
Request(asio::io_context& ctx,
const std::string& url,
std::string url,
const Json::Value& json,
OnJsonCb jsoncb,
std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb, std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, std::string url, OnJsonCb jsoncb, std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, std::string url, std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx,
std::string_view host,
std::string_view service,
const bool ssl = false,
std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<log::Logger> logger = {});
Request(asio::io_context& ctx, std::string url, OnDoneCb onDone, std::shared_ptr<log::Logger> logger = {});
// user defined resolver
Request(asio::io_context& ctx, std::shared_ptr<Resolver> resolver, sa_family_t family = AF_UNSPEC);
@@ -344,8 +340,6 @@ private:
dht::crypto::Identity client_identity_;
std::shared_ptr<dht::crypto::Certificate> server_ca_;
std::string service_;
std::string host_;
unsigned int id_;
static std::atomic_uint ids_;
+1 -11
View File
@@ -28,18 +28,8 @@ install(CODE [[
if(NOT _pip_check EQUAL 0)
message(WARNING "Skipping Python package install: pip not available for ${Python3_EXECUTABLE}.")
else()
set(_pip_install_args
install
--no-deps
--no-build-isolation
--break-system-packages
--prefix ${CMAKE_INSTALL_PREFIX})
if(DEFINED ENV{DESTDIR} AND NOT "$ENV{DESTDIR}" STREQUAL "")
list(APPEND _pip_install_args --root "$ENV{DESTDIR}")
endif()
list(APPEND _pip_install_args .)
execute_process(
COMMAND ${Python3_EXECUTABLE} -m pip ${_pip_install_args}
COMMAND ${Python3_EXECUTABLE} -m pip install --no-deps --break-system-packages .
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/python
OUTPUT_VARIABLE _pip_output
ERROR_VARIABLE _pip_error
+15
View File
@@ -36,4 +36,19 @@ base64_decode(std::string_view str)
return buffer;
}
std::vector<unsigned char>
base64_default_or_url_decode(std::string_view str)
{
std::vector<unsigned char> buffer(simdutf::maximal_binary_length_from_base64(str.data(), str.size()));
simdutf::result r = simdutf::base64_to_binary(str.data(),
str.size(),
(char*) buffer.data(),
simdutf::base64_options::base64_default_or_url);
if (r.error) {
throw std::invalid_argument(concat("Invalid base64 input: "sv, simdutf::error_to_string(r.error)));
} else {
buffer.resize(r.count);
}
return buffer;
}
} // namespace dht
+8 -1
View File
@@ -21,5 +21,12 @@ std::string base64_encode(const std::vector<unsigned char>& str);
* @return a base64-decoded buffer
*/
std::vector<unsigned char> base64_decode(std::string_view str);
/**
* Decode a buffer in base64 or url style base64 (including '-' '_' ).
*
* @param str the input buffer
* @return a base64-decoded buffer
*/
std::vector<unsigned char> base64_default_or_url_decode(std::string_view str);
} // namespace dht
} // namespace dht
+3 -10
View File
@@ -1205,14 +1205,7 @@ DhtProxyServer::sendPushNotification(
try {
std::shared_ptr<http::Request> request;
if (type == PushType::UnifiedPush) {
http::Url tokenUrl(token);
request = std::make_shared<http::Request>(io_context(),
concat(tokenUrl.protocol, "://"sv, tokenUrl.host),
tokenUrl.service,
tokenUrl.protocol.find("https") == 0,
logger_);
request->set_target(tokenUrl.target);
request->set_header_field(restinio::http_field_t::host, std::string(tokenUrl.host));
request = std::make_shared<http::Request>(io_context(), token, logger_);
} else {
request = std::make_shared<http::Request>(io_context(),
pushHostPort_.first,
@@ -1231,8 +1224,8 @@ DhtProxyServer::sendPushNotification(
Blob auth; ///< Auth secret
auto delim = topicView.find('|');
if (delim != std::string_view::npos) {
pubKey = base64_decode(topicView.substr(0, delim));
auth = base64_decode(topicView.substr(delim + 1));
pubKey = base64_default_or_url_decode(topicView.substr(0, delim));
auth = base64_default_or_url_decode(topicView.substr(delim + 1));
}
request->set_header_field(restinio::http_field_t::ttl, "86400");
+12 -13
View File
@@ -42,8 +42,8 @@ constexpr std::string_view ORIGIN_PROTOCOL {"//"sv};
constexpr const char HTTP_HEADER_DELIM[] = "\r\n\r\n";
constexpr unsigned MAX_REDIRECTS {5};
Url::Url(std::string_view url_str)
: url(url_str)
Url::Url(std::string url_str)
: url(std::move(url_str))
{
std::string_view u = url;
if (u.empty())
@@ -830,8 +830,8 @@ Connection::timeout(const std::chrono::seconds& timeout, HandlerCb cb)
// Resolver
Resolver::Resolver(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger)
: url_(url)
Resolver::Resolver(asio::io_context& ctx, std::string url, std::shared_ptr<dht::Logger> logger)
: url_(std::move(url))
, resolver_(ctx)
, destroyed_(std::make_shared<bool>(false))
, logger_(logger)
@@ -967,14 +967,14 @@ Resolver::resolve(std::string_view host, std::string_view service)
std::atomic_uint Request::ids_ {1};
Request::Request(asio::io_context& ctx,
const std::string& url,
std::string url,
const Json::Value& json,
OnJsonCb jsoncb,
std::shared_ptr<dht::Logger> logger)
: logger_(std::move(logger))
, id_(Request::ids_++)
, ctx_(ctx)
, resolver_(std::make_shared<Resolver>(ctx, url, logger))
, resolver_(std::make_shared<Resolver>(ctx, std::move(url), logger))
{
init_default_headers();
set_header_field(restinio::http_field_t::content_type, std::string(HTTP_HEADER_CONTENT_TYPE_JSON));
@@ -997,11 +997,11 @@ Request::Request(asio::io_context& ctx,
});
}
Request::Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb, std::shared_ptr<dht::Logger> logger)
Request::Request(asio::io_context& ctx, std::string url, OnJsonCb jsoncb, std::shared_ptr<dht::Logger> logger)
: logger_(std::move(logger))
, id_(Request::ids_++)
, ctx_(ctx)
, resolver_(std::make_shared<Resolver>(ctx, url, logger))
, resolver_(std::make_shared<Resolver>(ctx, std::move(url), logger))
{
init_default_headers();
set_header_field(restinio::http_field_t::accept, std::string(HTTP_HEADER_CONTENT_TYPE_JSON));
@@ -1022,20 +1022,20 @@ Request::Request(asio::io_context& ctx, const std::string& url, OnJsonCb jsoncb,
});
}
Request::Request(asio::io_context& ctx, const std::string& url, std::shared_ptr<dht::Logger> logger)
Request::Request(asio::io_context& ctx, std::string url, std::shared_ptr<dht::Logger> logger)
: logger_(logger)
, id_(Request::ids_++)
, ctx_(ctx)
, resolver_(std::make_shared<Resolver>(ctx, url, logger))
, resolver_(std::make_shared<Resolver>(ctx, std::move(url), logger))
{
init_default_headers();
}
Request::Request(asio::io_context& ctx, const std::string& url, OnDoneCb onDone, std::shared_ptr<dht::Logger> logger)
Request::Request(asio::io_context& ctx, std::string url, OnDoneCb onDone, std::shared_ptr<dht::Logger> logger)
: logger_(logger)
, id_(Request::ids_++)
, ctx_(ctx)
, resolver_(std::make_shared<Resolver>(ctx, url, logger))
, resolver_(std::make_shared<Resolver>(ctx, std::move(url), logger))
{
init_default_headers();
add_on_done_callback(std::move(onDone));
@@ -1086,7 +1086,6 @@ Request::Request(asio::io_context& ctx,
, family_(family)
, resolver_(resolver)
{
set_header_field(restinio::http_field_t::host, concat(get_url().host, ":", get_url().service));
set_target(Url(target).target);
}