GetHostByName fixes and improvements.
GitOrigin-RevId: 531a53c4d559662be110d1ad6938ca3e52ccbc1e
This commit is contained in:
parent
2a0db51cd9
commit
83037ee014
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
namespace td {
|
namespace td {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
class GoogleDnsResolver : public Actor {
|
class GoogleDnsResolver : public Actor {
|
||||||
public:
|
public:
|
||||||
GoogleDnsResolver(std::string host, GetHostByNameActor::ResolveOptions options, Promise<IPAddress> promise)
|
GoogleDnsResolver(std::string host, GetHostByNameActor::ResolveOptions options, Promise<IPAddress> promise)
|
||||||
@ -25,40 +26,50 @@ class GoogleDnsResolver : public Actor {
|
|||||||
GetHostByNameActor::ResolveOptions options_;
|
GetHostByNameActor::ResolveOptions options_;
|
||||||
Promise<IPAddress> promise_;
|
Promise<IPAddress> promise_;
|
||||||
ActorOwn<Wget> wget_;
|
ActorOwn<Wget> wget_;
|
||||||
|
double begin_time_ = 0;
|
||||||
|
|
||||||
void start_up() override {
|
void start_up() override {
|
||||||
const int timeout = 10;
|
const int timeout = 10;
|
||||||
const int ttl = 3;
|
const int ttl = 3;
|
||||||
wget_ = create_actor<Wget>("Wget", create_result_handler(std::move(promise_)),
|
begin_time_ = Time::now();
|
||||||
|
auto wget_promise = PromiseCreator::lambda([actor_id = actor_id(this)](Result<HttpQueryPtr> r_http_query) {
|
||||||
|
send_closure(actor_id, &GoogleDnsResolver::on_result, std::move(r_http_query));
|
||||||
|
});
|
||||||
|
wget_ = create_actor<Wget>("GoogleDnsResolver", std::move(wget_promise),
|
||||||
PSTRING() << "https://www.google.com/resolve?name=" << url_encode(host_)
|
PSTRING() << "https://www.google.com/resolve?name=" << url_encode(host_)
|
||||||
<< "&type=" << (options_.prefer_ipv6 ? 28 : 1),
|
<< "&type=" << (options_.prefer_ipv6 ? 28 : 1),
|
||||||
std::vector<std::pair<string, string>>({{"Host", "dns.google.com"}}), timeout, ttl,
|
std::vector<std::pair<string, string>>({{"Host", "dns.google.com"}}), timeout, ttl,
|
||||||
options_.prefer_ipv6, SslStream::VerifyPeer::Off);
|
options_.prefer_ipv6, SslStream::VerifyPeer::Off);
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise<HttpQueryPtr> create_result_handler(Promise<IPAddress> promise) {
|
static Result<IPAddress> get_ip_address(Result<HttpQueryPtr> r_http_query) {
|
||||||
return PromiseCreator::lambda([promise = std::move(promise)](Result<HttpQueryPtr> r_http_query) mutable {
|
TRY_RESULT(http_query, std::move(r_http_query));
|
||||||
promise.set_result([&]() -> Result<IPAddress> {
|
TRY_RESULT(json_value, json_decode(http_query->content_));
|
||||||
TRY_RESULT(http_query, std::move(r_http_query));
|
if (json_value.type() != JsonValue::Type::Object) {
|
||||||
TRY_RESULT(json_value, json_decode(http_query->content_));
|
return Status::Error("Failed to parse DNS result: not an object");
|
||||||
if (json_value.type() != JsonValue::Type::Object) {
|
}
|
||||||
return Status::Error("Failed to parse dns result: not an object");
|
TRY_RESULT(answer, get_json_object_field(json_value.get_object(), "Answer", JsonValue::Type::Array, false));
|
||||||
}
|
auto &array = answer.get_array();
|
||||||
TRY_RESULT(answer, get_json_object_field(json_value.get_object(), "Answer", JsonValue::Type::Array, false));
|
if (array.size() == 0) {
|
||||||
auto &array = answer.get_array();
|
return Status::Error("Failed to parse DNS result: Answer is an empty array");
|
||||||
if (array.size() == 0) {
|
}
|
||||||
return Status::Error("Failed to parse dns result: Answer is an empty array");
|
if (array[0].type() != JsonValue::Type::Object) {
|
||||||
}
|
return Status::Error("Failed to parse DNS result: Answer[0] is not an object");
|
||||||
if (array[0].type() != JsonValue::Type::Object) {
|
}
|
||||||
return Status::Error("Failed to parse dns result: Answer[0] is not an object");
|
auto &answer_0 = array[0].get_object();
|
||||||
}
|
TRY_RESULT(ip_str, get_json_object_string_field(answer_0, "data", false));
|
||||||
auto &answer_0 = array[0].get_object();
|
IPAddress ip;
|
||||||
TRY_RESULT(ip_str, get_json_object_string_field(answer_0, "data", false));
|
TRY_STATUS(ip.init_host_port(ip_str, 0));
|
||||||
IPAddress ip;
|
return ip;
|
||||||
TRY_STATUS(ip.init_host_port(ip_str, 0));
|
}
|
||||||
return ip;
|
|
||||||
}());
|
void on_result(Result<HttpQueryPtr> r_http_query) {
|
||||||
});
|
auto end_time = Time::now();
|
||||||
|
auto result = get_ip_address(std::move(r_http_query));
|
||||||
|
LOG(WARNING) << "Init host = " << host_ << " in " << end_time - begin_time_ << " seconds to "
|
||||||
|
<< (result.is_ok() ? (PSLICE() << result.ok()) : CSlice("[invalid]"));
|
||||||
|
promise_.set_result(std::move(result));
|
||||||
|
stop();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -81,9 +92,9 @@ class NativeDnsResolver : public Actor {
|
|||||||
LOG(WARNING) << "Init host = " << host_ << " in " << end_time - begin_time << " seconds to " << ip;
|
LOG(WARNING) << "Init host = " << host_ << " in " << end_time - begin_time << " seconds to " << ip;
|
||||||
if (status.is_error()) {
|
if (status.is_error()) {
|
||||||
promise_.set_error(std::move(status));
|
promise_.set_error(std::move(status));
|
||||||
return;
|
} else {
|
||||||
|
promise_.set_value(std::move(ip));
|
||||||
}
|
}
|
||||||
promise_.set_value(std::move(ip));
|
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -108,7 +119,7 @@ class DnsResolver : public Actor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (pos_ == 2) {
|
if (pos_ == 2) {
|
||||||
promise_.set_error(Status::Error("Failed to resolve ip"));
|
promise_.set_error(Status::Error("Failed to resolve IP address"));
|
||||||
return stop();
|
return stop();
|
||||||
}
|
}
|
||||||
options_.type = types[pos_];
|
options_.type = types[pos_];
|
||||||
@ -130,8 +141,6 @@ class DnsResolver : public Actor {
|
|||||||
};
|
};
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
GetHostByNameActor::Options::Options() = default;
|
|
||||||
|
|
||||||
ActorOwn<> GetHostByNameActor::resolve(std::string host, ResolveOptions options, Promise<IPAddress> promise) {
|
ActorOwn<> GetHostByNameActor::resolve(std::string host, ResolveOptions options, Promise<IPAddress> promise) {
|
||||||
switch (options.type) {
|
switch (options.type) {
|
||||||
case ResolveType::Native:
|
case ResolveType::Native:
|
||||||
@ -153,15 +162,13 @@ GetHostByNameActor::GetHostByNameActor(Options options) : options_(options) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void GetHostByNameActor::on_result(std::string host, bool prefer_ipv6, Result<IPAddress> res) {
|
void GetHostByNameActor::on_result(std::string host, bool prefer_ipv6, Result<IPAddress> res) {
|
||||||
auto &value = cache_[prefer_ipv6].emplace(host, Value{{}, 0}).first->second;
|
auto value_it = cache_[prefer_ipv6].find(host);
|
||||||
|
CHECK(value_it != cache_[prefer_ipv6].end());
|
||||||
|
auto &value = value_it->second;
|
||||||
|
|
||||||
auto promises = std::move(value.promises);
|
auto promises = std::move(value.promises);
|
||||||
auto end_time = Time::now();
|
auto end_time = Time::now() + (res.is_ok() ? options_.ok_timeout : options_.error_timeout);
|
||||||
if (res.is_ok()) {
|
value = Value{std::move(res), end_time};
|
||||||
value = Value{res.move_as_ok(), end_time + options_.ok_timeout};
|
|
||||||
} else {
|
|
||||||
value = Value{res.move_as_error(), end_time + options_.error_timeout};
|
|
||||||
}
|
|
||||||
for (auto &promise : promises) {
|
for (auto &promise : promises) {
|
||||||
promise.second.set_result(value.get_ip_port(promise.first));
|
promise.second.set_result(value.get_ip_port(promise.first));
|
||||||
}
|
}
|
||||||
@ -176,6 +183,8 @@ void GetHostByNameActor::run(string host, int port, bool prefer_ipv6, Promise<IP
|
|||||||
|
|
||||||
value.promises.emplace_back(port, std::move(promise));
|
value.promises.emplace_back(port, std::move(promise));
|
||||||
if (value.query.empty()) {
|
if (value.query.empty()) {
|
||||||
|
CHECK(value.promises.size() == 1);
|
||||||
|
|
||||||
ResolveOptions options;
|
ResolveOptions options;
|
||||||
options.type = options_.type;
|
options.type = options_.type;
|
||||||
options.scheduler_id = options_.scheduler_id;
|
options.scheduler_id = options_.scheduler_id;
|
||||||
|
@ -19,20 +19,25 @@ namespace td {
|
|||||||
class GetHostByNameActor final : public Actor {
|
class GetHostByNameActor final : public Actor {
|
||||||
public:
|
public:
|
||||||
enum class ResolveType { Native, Google, All };
|
enum class ResolveType { Native, Google, All };
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
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};
|
ResolveType type{ResolveType::Native};
|
||||||
int scheduler_id{-1};
|
int32 scheduler_id{-1};
|
||||||
int32 ok_timeout{CACHE_TIME};
|
int32 ok_timeout{DEFAULT_CACHE_TIME};
|
||||||
int32 error_timeout{ERROR_CACHE_TIME};
|
int32 error_timeout{DEFAULT_ERROR_CACHE_TIME};
|
||||||
};
|
};
|
||||||
|
|
||||||
explicit GetHostByNameActor(Options options = {});
|
explicit GetHostByNameActor(Options options = {});
|
||||||
|
|
||||||
void run(std::string host, int port, bool prefer_ipv6, Promise<IPAddress> promise);
|
void run(std::string host, int port, bool prefer_ipv6, Promise<IPAddress> promise);
|
||||||
|
|
||||||
struct ResolveOptions {
|
struct ResolveOptions {
|
||||||
ResolveType type{ResolveType::Native};
|
ResolveType type{ResolveType::Native};
|
||||||
bool prefer_ipv6{false};
|
bool prefer_ipv6{false};
|
||||||
int scheduler_id{-1};
|
int32 scheduler_id{-1};
|
||||||
};
|
};
|
||||||
static TD_WARN_UNUSED_RESULT ActorOwn<> resolve(std::string host, ResolveOptions options, Promise<IPAddress> promise);
|
static TD_WARN_UNUSED_RESULT ActorOwn<> resolve(std::string host, ResolveOptions options, Promise<IPAddress> promise);
|
||||||
|
|
||||||
@ -56,8 +61,6 @@ class GetHostByNameActor final : public Actor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
std::unordered_map<string, Value> cache_[2];
|
std::unordered_map<string, Value> cache_[2];
|
||||||
static constexpr int32 CACHE_TIME = 60 * 29; // 29 minutes
|
|
||||||
static constexpr int32 ERROR_CACHE_TIME = 60 * 5; // 5 minutes
|
|
||||||
|
|
||||||
Options options_;
|
Options options_;
|
||||||
|
|
||||||
|
@ -345,8 +345,7 @@ Status IPAddress::init_ipv4_port(CSlice ipv4, int port) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Status IPAddress::init_host_port(CSlice host, int port, bool prefer_ipv6) {
|
Status IPAddress::init_host_port(CSlice host, int port, bool prefer_ipv6) {
|
||||||
auto str_port = to_string(port);
|
return init_host_port(host, PSLICE() << port, prefer_ipv6);
|
||||||
return init_host_port(host, str_port, prefer_ipv6);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) {
|
Status IPAddress::init_host_port(CSlice host, CSlice port, bool prefer_ipv6) {
|
||||||
|
@ -58,10 +58,11 @@ TEST(Mtproto, GetHostByName) {
|
|||||||
GetHostByNameActor::resolve(host, options, std::move(promise)).release();
|
GetHostByNameActor::resolve(host, options, std::move(promise)).release();
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<std::string> hosts = {"127.0.0.2", "1.1.1.1", "localhost", "web.telegram.org", "москва.рф"};
|
std::vector<std::string> hosts = {"127.0.0.2", "1.1.1.1", "localhost", "web.telegram.org",
|
||||||
for (auto host : hosts) {
|
"web.telegram.org", "москва.рф", ""};
|
||||||
for (auto type : {GetHostByNameActor::ResolveType::Native, GetHostByNameActor::ResolveType::Google,
|
for (auto type : {GetHostByNameActor::ResolveType::Native, GetHostByNameActor::ResolveType::Google,
|
||||||
GetHostByNameActor::ResolveType::All}) {
|
GetHostByNameActor::ResolveType::All}) {
|
||||||
|
for (auto host : hosts) {
|
||||||
for (auto prefer_ipv6 : {false, true}) {
|
for (auto prefer_ipv6 : {false, true}) {
|
||||||
GetHostByNameActor::ResolveOptions options;
|
GetHostByNameActor::ResolveOptions options;
|
||||||
options.type = type;
|
options.type = type;
|
||||||
|
Reference in New Issue
Block a user