diff options
Diffstat (limited to 'libs/tdlib/td/td/telegram/net/DcOptionsSet.cpp')
-rw-r--r-- | libs/tdlib/td/td/telegram/net/DcOptionsSet.cpp | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/libs/tdlib/td/td/telegram/net/DcOptionsSet.cpp b/libs/tdlib/td/td/telegram/net/DcOptionsSet.cpp new file mode 100644 index 0000000000..4aefd3b7f7 --- /dev/null +++ b/libs/tdlib/td/td/telegram/net/DcOptionsSet.cpp @@ -0,0 +1,158 @@ +// +// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018 +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +#include "td/telegram/net/DcOptionsSet.h" + +#include "td/utils/format.h" +#include "td/utils/logging.h" + +#include <algorithm> +#include <set> +#include <utility> + +namespace td { + +void DcOptionsSet::add_dc_options(DcOptions dc_options) { + std::vector<DcOptionId> new_ordered_options; + for (auto &option : dc_options.dc_options) { + auto *info = register_dc_option(std::move(option)); + new_ordered_options.push_back(DcOptionId{info->pos}); + } + + std::set<DcOptionId> new_ordered_options_set(new_ordered_options.begin(), new_ordered_options.end()); + for (auto option_id : ordered_options_) { + if (!new_ordered_options_set.count(option_id)) { + new_ordered_options.push_back(option_id); + } + } + + ordered_options_ = std::move(new_ordered_options); + for (size_t i = 0; i < ordered_options_.size(); i++) { + options_[ordered_options_[i].pos]->order = i; + } +} + +DcOptions DcOptionsSet::get_dc_options() const { + DcOptions result; + for (auto id : ordered_options_) { + result.dc_options.push_back(options_[id.pos]->option); + } + return result; +} + +Result<DcOptionsSet::ConnectionInfo> DcOptionsSet::find_connection(DcId dc_id, bool allow_media_only, bool use_static) { + std::vector<ConnectionInfo> options; + std::vector<ConnectionInfo> static_options; + + for (auto &option_info : options_) { + auto &option = option_info->option; + if (option.get_dc_id() != dc_id) { + continue; + } + if (!option.is_valid()) { + LOG(INFO) << "Skip invalid DC option"; + continue; + } + if (!allow_media_only && option.is_media_only()) { + LOG(DEBUG) << "Skip media only option"; + continue; + } + + ConnectionInfo info; + info.option = &option; + info.order = option_info->order; + + OptionStat *option_stat = get_option_stat(option_info.get()); + + info.use_http = false; + info.stat = &option_stat->tcp_stat; + if (option.is_static()) { + static_options.push_back(info); + } else { + options.push_back(info); + } + + if (!option.is_obfuscated_tcp_only() && !option.is_static() && false) { // TODO fix HTTP-mode and enable it + info.use_http = true; + info.stat = &option_stat->http_stat; + options.push_back(info); + } + } + + if (use_static) { + if (!static_options.empty()) { + options = std::move(static_options); + } + } else { + if (options.empty()) { + options = std::move(static_options); + } + } + bool have_media_only = std::any_of(options.begin(), options.end(), [](auto &v) { return v.option->is_media_only(); }); + if (have_media_only) { + options.erase(std::remove_if(options.begin(), options.end(), [](auto &v) { return !v.option->is_media_only(); }), + options.end()); + } + + if (options.empty()) { + return Status::Error(PSLICE() << "No such connection: " << tag("dc_id", dc_id) + << tag("allow_media_only", allow_media_only) << tag("use_static", use_static)); + } + + auto last_error_at = std::min_element(options.begin(), options.end(), + [](const auto &a_option, const auto &b_option) { + return a_option.stat->error_at > b_option.stat->error_at; + }) + ->stat->error_at; + + auto result = *std::min_element(options.begin(), options.end(), [](const auto &a_option, const auto &b_option) { + auto &a = *a_option.stat; + auto &b = *b_option.stat; + auto a_state = a.state(); + auto b_state = b.state(); + if (a_state != b_state) { + return a_state < b_state; + } + if (a_state == Stat::Ok) { + if (a_option.order == b_option.order) { + return a_option.use_http < b_option.use_http; + } + return a_option.order < b_option.order; + } else if (a_state == Stat::Error) { + return a.error_at < b.error_at; + } + return a_option.order < b_option.order; + }); + result.should_check = !result.stat->is_ok() || result.use_http || last_error_at > Time::now_cached() - 10; + return result; +} + +void DcOptionsSet::reset() { + ordered_options_.clear(); +} + +DcOptionsSet::DcOptionInfo *DcOptionsSet::register_dc_option(DcOption &&option) { + auto info = std::make_unique<DcOptionInfo>(std::move(option), options_.size()); + init_option_stat(info.get()); + auto result = info.get(); + options_.push_back(std::move(info)); + return result; +} + +void DcOptionsSet::init_option_stat(DcOptionInfo *option_info) { + const auto &ip_address = option_info->option.get_ip_address(); + auto it_ok = option_to_stat_id_.insert(std::make_pair(ip_address, 0)); + if (it_ok.second) { + it_ok.first->second = option_stats_.create(std::make_unique<OptionStat>()); + } + option_info->stat_id = it_ok.first->second; +} + +DcOptionsSet::OptionStat *DcOptionsSet::get_option_stat(const DcOptionInfo *option_info) { + return option_stats_.get(option_info->stat_id)->get(); +} + +} // namespace td |