diff --git a/td/telegram/net/ConnectionCreator.cpp b/td/telegram/net/ConnectionCreator.cpp index 4f95c523c..47ba57e77 100644 --- a/td/telegram/net/ConnectionCreator.cpp +++ b/td/telegram/net/ConnectionCreator.cpp @@ -1243,10 +1243,10 @@ void ConnectionCreator::start_up() { GetHostByNameActor::Options options; options.scheduler_id = G()->get_gc_scheduler_id(); - options.type = GetHostByNameActor::ResolveType::All; + options.types = {GetHostByNameActor::ResolveType::Google, GetHostByNameActor::ResolveType::Native}; options.ok_timeout = 5 * 60 - 1; options.error_timeout = 0; - get_host_by_name_actor_ = create_actor("GetHostByNameActor", options); + get_host_by_name_actor_ = create_actor("GetHostByNameActor", std::move(options)); ref_cnt_guard_ = create_reference(-1); diff --git a/tdnet/td/net/GetHostByNameActor.cpp b/tdnet/td/net/GetHostByNameActor.cpp index b431938d1..4c7829cb0 100644 --- a/tdnet/td/net/GetHostByNameActor.cpp +++ b/tdnet/td/net/GetHostByNameActor.cpp @@ -19,13 +19,13 @@ namespace detail { class GoogleDnsResolver : public Actor { public: - GoogleDnsResolver(std::string host, GetHostByNameActor::ResolveOptions options, Promise promise) - : host_(std::move(host)), options_(std::move(options)), promise_(std::move(promise)) { + GoogleDnsResolver(std::string host, bool prefer_ipv6, Promise promise) + : host_(std::move(host)), prefer_ipv6_(prefer_ipv6), promise_(std::move(promise)) { } private: std::string host_; - GetHostByNameActor::ResolveOptions options_; + bool prefer_ipv6_; Promise promise_; ActorOwn wget_; double begin_time_ = 0; @@ -37,11 +37,11 @@ class GoogleDnsResolver : public Actor { auto wget_promise = PromiseCreator::lambda([actor_id = actor_id(this)](Result r_http_query) { send_closure(actor_id, &GoogleDnsResolver::on_result, std::move(r_http_query)); }); - wget_ = create_actor("GoogleDnsResolver", std::move(wget_promise), - PSTRING() << "https://www.google.com/resolve?name=" << url_encode(host_) - << "&type=" << (options_.prefer_ipv6 ? 28 : 1), - std::vector>({{"Host", "dns.google.com"}}), timeout, ttl, - options_.prefer_ipv6, SslStream::VerifyPeer::Off); + wget_ = create_actor( + "GoogleDnsResolver", std::move(wget_promise), + PSTRING() << "https://www.google.com/resolve?name=" << url_encode(host_) << "&type=" << (prefer_ipv6_ ? 28 : 1), + std::vector>({{"Host", "dns.google.com"}}), timeout, ttl, prefer_ipv6_, + SslStream::VerifyPeer::Off); } static Result get_ip_address(Result r_http_query) { @@ -77,19 +77,19 @@ class GoogleDnsResolver : public Actor { class NativeDnsResolver : public Actor { public: - NativeDnsResolver(std::string host, GetHostByNameActor::ResolveOptions options, Promise promise) - : host_(std::move(host)), options_(std::move(options)), promise_(std::move(promise)) { + NativeDnsResolver(std::string host, bool prefer_ipv6, Promise promise) + : host_(std::move(host)), prefer_ipv6_(prefer_ipv6), promise_(std::move(promise)) { } private: std::string host_; - GetHostByNameActor::ResolveOptions options_; + bool prefer_ipv6_; Promise promise_; void start_up() override { IPAddress ip; auto begin_time = Time::now(); - auto status = ip.init_host_port(host_, 0, options_.prefer_ipv6); + auto status = ip.init_host_port(host_, 0, prefer_ipv6_); auto end_time = Time::now(); LOG(WARNING) << "Init host = " << host_ << " in " << end_time - begin_time << " seconds to " << ip; if (status.is_error()) { @@ -101,67 +101,10 @@ class NativeDnsResolver : public Actor { } }; -class DnsResolver : public Actor { - public: - DnsResolver(std::string host, GetHostByNameActor::ResolveOptions options, Promise promise) - : host_(std::move(host)), options_(std::move(options)), promise_(std::move(promise)) { - } - - private: - std::string host_; - GetHostByNameActor::ResolveOptions options_; - Promise promise_; - ActorOwn<> query_; - size_t pos_ = 0; - GetHostByNameActor::ResolveType types[2] = {GetHostByNameActor::ResolveType::Google, - GetHostByNameActor::ResolveType::Native}; - - void loop() override { - if (!query_.empty()) { - return; - } - if (pos_ == 2) { - promise_.set_error(Status::Error("Failed to resolve IP address")); - return stop(); - } - options_.type = types[pos_]; - pos_++; - query_ = GetHostByNameActor::resolve(host_, options_, - PromiseCreator::lambda([actor_id = actor_id(this)](Result res) { - send_closure(actor_id, &DnsResolver::on_result, std::move(res)); - })); - } - - void on_result(Result res) { - query_.reset(); - if (res.is_ok() || pos_ == 2) { - promise_.set_result(std::move(res)); - return stop(); - } - loop(); - } -}; - } // namespace detail -ActorOwn<> GetHostByNameActor::resolve(std::string host, ResolveOptions options, Promise promise) { - switch (options.type) { - case ResolveType::Native: - return ActorOwn<>(create_actor_on_scheduler( - "NativeDnsResolver", options.scheduler_id, std::move(host), options, std::move(promise))); - case ResolveType::Google: - return ActorOwn<>(create_actor_on_scheduler( - "GoogleDnsResolver", options.scheduler_id, std::move(host), options, std::move(promise))); - case ResolveType::All: - return ActorOwn<>(create_actor_on_scheduler("DnsResolver", options.scheduler_id, - std::move(host), options, std::move(promise))); - default: - UNREACHABLE(); - return ActorOwn<>(); - } -} - GetHostByNameActor::GetHostByNameActor(Options options) : options_(std::move(options)) { + CHECK(!options_.types.empty()); } void GetHostByNameActor::run(string host, int port, bool prefer_ipv6, Promise promise) { @@ -175,36 +118,54 @@ void GetHostByNameActor::run(string host, int port, bool prefer_ipv6, Promise res) mutable { - send_closure(actor_id, &GetHostByNameActor::on_result, std::move(host), prefer_ipv6, std::move(res)); - })); + run_query(std::move(host), prefer_ipv6, query); } } -void GetHostByNameActor::on_result(std::string host, bool prefer_ipv6, Result res) { - auto value_it = cache_[prefer_ipv6].find(host); - CHECK(value_it != cache_[prefer_ipv6].end()); - auto &value = value_it->second; +void GetHostByNameActor::run_query(std::string host, bool prefer_ipv6, Query &query) { + auto promise = PromiseCreator::lambda([actor_id = actor_id(this), host, prefer_ipv6](Result res) mutable { + send_closure(actor_id, &GetHostByNameActor::on_query_result, std::move(host), prefer_ipv6, std::move(res)); + }); + + CHECK(query.query.empty()); + CHECK(query.pos < options_.types.size()); + auto resolver_type = options_.types[query.pos++]; + query.query = [&] { + switch (resolver_type) { + case ResolveType::Native: + return ActorOwn<>(create_actor_on_scheduler( + "NativeDnsResolver", options_.scheduler_id, std::move(host), prefer_ipv6, std::move(promise))); + case ResolveType::Google: + return ActorOwn<>(create_actor_on_scheduler( + "GoogleDnsResolver", options_.scheduler_id, std::move(host), prefer_ipv6, std::move(promise))); + default: + UNREACHABLE(); + return ActorOwn<>(); + } + }(); +} + +void GetHostByNameActor::on_query_result(std::string host, bool prefer_ipv6, Result res) { auto query_it = active_queries_[prefer_ipv6].find(host); CHECK(query_it != active_queries_[prefer_ipv6].end()); auto &query = query_it->second; CHECK(!query.promises.empty()); CHECK(!query.query.empty()); + if (res.is_error() && query.pos < options_.types.size()) { + query.query.reset(); + return run_query(std::move(host), prefer_ipv6, query); + } + auto promises = std::move(query.promises); auto end_time = Time::now() + (res.is_ok() ? options_.ok_timeout : options_.error_timeout); - value = Value{std::move(res), end_time}; + auto value_it = cache_[prefer_ipv6].find(host); + CHECK(value_it != cache_[prefer_ipv6].end()); + value_it->second = Value{std::move(res), end_time}; active_queries_[prefer_ipv6].erase(query_it); for (auto &promise : promises) { - promise.second.set_result(value.get_ip_port(promise.first)); + promise.second.set_result(value_it->second.get_ip_port(promise.first)); } } diff --git a/tdnet/td/net/GetHostByNameActor.h b/tdnet/td/net/GetHostByNameActor.h index 859e8ee9a..4f44865e0 100644 --- a/tdnet/td/net/GetHostByNameActor.h +++ b/tdnet/td/net/GetHostByNameActor.h @@ -18,13 +18,13 @@ namespace td { class GetHostByNameActor final : public Actor { public: - enum class ResolveType { Native, Google, All }; + enum class ResolveType { Native, Google }; struct Options { static constexpr int32 DEFAULT_CACHE_TIME = 60 * 29; // 29 minutes static constexpr int32 DEFAULT_ERROR_CACHE_TIME = 60 * 5; // 5 minutes - ResolveType type{ResolveType::Native}; + vector types{ResolveType::Native}; int32 scheduler_id{-1}; int32 ok_timeout{DEFAULT_CACHE_TIME}; int32 error_timeout{DEFAULT_ERROR_CACHE_TIME}; @@ -34,15 +34,8 @@ class GetHostByNameActor final : public Actor { void run(std::string host, int port, bool prefer_ipv6, Promise promise); - struct ResolveOptions { - ResolveType type{ResolveType::Native}; - bool prefer_ipv6{false}; - int32 scheduler_id{-1}; - }; - static TD_WARN_UNUSED_RESULT ActorOwn<> resolve(std::string host, ResolveOptions options, Promise promise); - private: - void on_result(std::string host, bool prefer_ipv6, Result res); + void on_query_result(std::string host, bool prefer_ipv6, Result res); struct Value { Result ip; @@ -63,11 +56,14 @@ class GetHostByNameActor final : public Actor { struct Query { ActorOwn<> query; + size_t pos = 0; std::vector>> promises; }; std::unordered_map active_queries_[2]; Options options_; + + void run_query(std::string host, bool prefer_ipv6, Query &query); }; } // namespace td diff --git a/test/mtproto.cpp b/test/mtproto.cpp index 1304b000d..0ba0949e9 100644 --- a/test/mtproto.cpp +++ b/test/mtproto.cpp @@ -33,97 +33,53 @@ REGISTER_TESTS(mtproto); using namespace td; -TEST(Mtproto, GetHostByName) { - SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING)); - ConcurrentScheduler sched; - int threads_n = 0; - sched.init(threads_n); - - int cnt = 1; - { - auto guard = sched.get_main_guard(); - - auto run = [&](GetHostByNameActor::ResolveOptions options, string host) { - auto promise = PromiseCreator::lambda([&cnt, num = cnt, host](Result r_ip_address) { - if (r_ip_address.is_ok()) { - LOG(WARNING) << num << " " << host << " " << r_ip_address.ok(); - } else { - LOG(ERROR) << num << " " << host << " " << r_ip_address.error(); - } - if (--cnt == 0) { - Scheduler::instance()->finish(); - } - }); - cnt++; - GetHostByNameActor::resolve(host, options, std::move(promise)).release(); - }; - - std::vector hosts = {"127.0.0.2", "1.1.1.1", "localhost", "web.telegram.org", - "web.telegram.org", "москва.рф", ""}; - for (auto type : {GetHostByNameActor::ResolveType::Native, GetHostByNameActor::ResolveType::Google, - GetHostByNameActor::ResolveType::All}) { - for (auto host : hosts) { - for (auto prefer_ipv6 : {false, true}) { - GetHostByNameActor::ResolveOptions options; - options.type = type; - options.prefer_ipv6 = prefer_ipv6; - run(options, host); - } - } - } - } - cnt--; - sched.start(); - while (sched.run_main(10)) { - // empty - } - sched.finish(); -} - TEST(Mtproto, GetHostByNameActor) { SET_VERBOSITY_LEVEL(VERBOSITY_NAME(WARNING)); ConcurrentScheduler sched; - int threads_n = 0; + int threads_n = 1; sched.init(threads_n); int cnt = 1; + vector> actors; { auto guard = sched.get_main_guard(); - auto run = [&](GetHostByNameActor::Options options) { - auto host = create_actor("GetHostByNameActor", options); - auto host_id = host.get(); - auto promise = PromiseCreator::lambda([&, num = cnt](Result r_ip_address) { + auto run = [&](ActorId actor_id, string host, bool prefer_ipv6) { + auto promise = PromiseCreator::lambda([&cnt, &actors, num = cnt, host](Result r_ip_address) { if (r_ip_address.is_ok()) { - LOG(WARNING) << num << " " << r_ip_address.ok(); + LOG(WARNING) << num << " \"" << host << "\" " << r_ip_address.ok(); } else { - LOG(ERROR) << num << " " << r_ip_address.error(); + LOG(ERROR) << num << " \"" << host << "\" " << r_ip_address.error(); } if (--cnt == 0) { + actors.clear(); Scheduler::instance()->finish(); } }); cnt++; - send_closure(host_id, &GetHostByNameActor::run, "web.telegram.org", 443, false, std::move(promise)); - promise = PromiseCreator::lambda([&, num = cnt, host = std::move(host)](Result r_ip_address) { - if (r_ip_address.is_ok()) { - LOG(WARNING) << num << " " << r_ip_address.ok(); - } else { - LOG(ERROR) << num << " " << r_ip_address.error(); - } - if (--cnt == 0) { - Scheduler::instance()->finish(); - } - }); - cnt++; - send_closure(host_id, &GetHostByNameActor::run, "web.telegram.org", 443, false, std::move(promise)); + send_closure(actor_id, &GetHostByNameActor::run, host, 443, prefer_ipv6, std::move(promise)); }; - for (auto type : {GetHostByNameActor::ResolveType::Native, GetHostByNameActor::ResolveType::Google, - GetHostByNameActor::ResolveType::All}) { + std::vector hosts = { + "127.0.0.2", "1.1.1.1", "localhost", "web.telegram.org", "web.telegram.org", "москва.рф", "", "%", " ", "a"}; + for (auto types : {vector{GetHostByNameActor::ResolveType::Native}, + vector{GetHostByNameActor::ResolveType::Google}, + vector{GetHostByNameActor::ResolveType::Google, + GetHostByNameActor::ResolveType::Google, + GetHostByNameActor::ResolveType::Native}}) { GetHostByNameActor::Options options; - options.type = type; - run(options); + options.types = types; + options.scheduler_id = threads_n; + + auto actor = create_actor("GetHostByNameActor", std::move(options)); + auto actor_id = actor.get(); + actors.push_back(std::move(actor)); + + for (auto host : hosts) { + for (auto prefer_ipv6 : {false, true}) { + run(actor_id, host, prefer_ipv6); + } + } } } cnt--;