mirror of
https://github.com/tdlight-team/tdlight-telegram-bot-api.git
synced 2024-12-04 18:02:58 +01:00
168 lines
5.8 KiB
C++
168 lines
5.8 KiB
C++
//
|
|
// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023
|
|
//
|
|
// 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 "telegram-bot-api/Query.h"
|
|
|
|
#include "telegram-bot-api/Stats.h"
|
|
|
|
#include "td/actor/actor.h"
|
|
|
|
#include "td/utils/format.h"
|
|
#include "td/utils/logging.h"
|
|
#include "td/utils/misc.h"
|
|
#include "td/utils/port/IPAddress.h"
|
|
#include "td/utils/SliceBuilder.h"
|
|
#include "td/utils/Time.h"
|
|
|
|
#include <numeric>
|
|
|
|
namespace telegram_bot_api {
|
|
|
|
td::FlatHashMap<td::string, td::unique_ptr<td::VirtuallyJsonable>> empty_parameters;
|
|
|
|
Query::Query(td::vector<td::BufferSlice> &&container, td::Slice token, bool is_user, bool is_test_dc, td::MutableSlice method,
|
|
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&args,
|
|
td::vector<std::pair<td::MutableSlice, td::MutableSlice>> &&headers, td::vector<td::HttpFile> &&files,
|
|
std::shared_ptr<SharedData> shared_data, const td::IPAddress &peer_ip_address, bool is_internal)
|
|
: state_(State::Query)
|
|
, shared_data_(shared_data)
|
|
, peer_ip_address_(peer_ip_address)
|
|
, container_(std::move(container))
|
|
, token_(token)
|
|
, is_user_(is_user)
|
|
, is_test_dc_(is_test_dc)
|
|
, method_(method)
|
|
, args_(std::move(args))
|
|
, headers_(std::move(headers))
|
|
, files_(std::move(files))
|
|
, is_internal_(is_internal) {
|
|
if (method_.empty()) {
|
|
method_ = arg("method");
|
|
}
|
|
td::to_lower_inplace(method_);
|
|
start_timestamp_ = td::Time::now();
|
|
LOG(INFO) << "Query " << this << ": " << *this;
|
|
if (shared_data_) {
|
|
shared_data_->query_count_.fetch_add(1, std::memory_order_relaxed);
|
|
if (method_ != "getupdates") {
|
|
shared_data_->query_list_size_.fetch_add(1, std::memory_order_relaxed);
|
|
shared_data_->query_list_.put(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
td::string Query::get_peer_ip_address() const {
|
|
if (peer_ip_address_.is_valid() && !peer_ip_address_.is_reserved()) { // external connection
|
|
return peer_ip_address_.get_ip_str().str();
|
|
} else {
|
|
// invalid peer IP address or connection from the local network
|
|
return get_header("x-real-ip").str();
|
|
}
|
|
}
|
|
|
|
td::int64 Query::query_size() const {
|
|
return std::accumulate(
|
|
container_.begin(), container_.end(), td::int64{0},
|
|
[](td::int64 acc, const td::BufferSlice &slice) { return static_cast<td::int64>(acc + slice.size()); });
|
|
}
|
|
|
|
td::int64 Query::files_size() const {
|
|
return std::accumulate(files_.begin(), files_.end(), td::int64{0},
|
|
[](td::int64 acc, const td::HttpFile &file) { return acc + file.size; });
|
|
}
|
|
|
|
td::int64 Query::files_max_size() const {
|
|
return std::accumulate(files_.begin(), files_.end(), td::int64{0},
|
|
[](td::int64 acc, const td::HttpFile &file) { return td::max(acc, file.size); });
|
|
}
|
|
|
|
void Query::set_stat_actor(td::ActorId<BotStatActor> stat_actor) {
|
|
stat_actor_ = stat_actor;
|
|
send_request_stat();
|
|
}
|
|
|
|
void Query::set_ok(td::BufferSlice result) {
|
|
CHECK(state_ == State::Query);
|
|
LOG(INFO) << "Query " << this << ": " << td::tag("method", method_) << td::tag("text", result.as_slice());
|
|
answer_ = std::move(result);
|
|
state_ = State::OK;
|
|
http_status_code_ = 200;
|
|
send_response_stat();
|
|
}
|
|
|
|
void Query::set_error(int http_status_code, td::BufferSlice result) {
|
|
LOG(INFO) << "Query " << this << ": " << td::tag("method", method_) << td::tag("code", http_status_code)
|
|
<< td::tag("text", result.as_slice());
|
|
CHECK(state_ == State::Query);
|
|
answer_ = std::move(result);
|
|
state_ = State::Error;
|
|
http_status_code_ = http_status_code;
|
|
send_response_stat();
|
|
}
|
|
|
|
void Query::set_retry_after_error(int retry_after) {
|
|
retry_after_ = retry_after;
|
|
|
|
td::FlatHashMap<td::string, td::unique_ptr<td::VirtuallyJsonable>> parameters;
|
|
parameters.emplace("retry_after", td::make_unique<td::VirtuallyJsonableLong>(retry_after));
|
|
set_error(429, td::json_encode<td::BufferSlice>(
|
|
JsonQueryError(429, PSLICE() << "Too Many Requests: retry after " << retry_after, parameters)));
|
|
}
|
|
|
|
td::StringBuilder &operator<<(td::StringBuilder &sb, const Query &query) {
|
|
auto padded_time =
|
|
td::lpad(PSTRING() << td::format::as_time(td::Time::now_cached() - query.start_timestamp()), 10, ' ');
|
|
sb << "[bot" << td::rpad(query.token().str(), 46, ' ') << "][time:" << padded_time << ']'
|
|
<< td::tag("method", td::lpad(query.method().str(), 25, ' '));
|
|
if (!query.args().empty()) {
|
|
sb << '{';
|
|
for (const auto &arg : query.args()) {
|
|
sb << '[';
|
|
if (arg.first.size() > 128) {
|
|
sb << '<' << arg.first.size() << '>' << td::oneline(arg.first.substr(0, 128)) << "...";
|
|
} else {
|
|
sb << td::oneline(arg.first);
|
|
}
|
|
sb << ':';
|
|
if (arg.second.size() > 4096) {
|
|
sb << '<' << arg.second.size() << '>' << td::oneline(arg.second.substr(0, 4096)) << "...";
|
|
} else {
|
|
sb << td::oneline(arg.second);
|
|
}
|
|
sb << ']';
|
|
}
|
|
sb << '}';
|
|
}
|
|
if (!query.files().empty()) {
|
|
sb << query.files();
|
|
}
|
|
return sb;
|
|
}
|
|
|
|
void Query::send_request_stat() const {
|
|
if (stat_actor_.empty()) {
|
|
return;
|
|
}
|
|
send_closure(stat_actor_, &BotStatActor::add_event<ServerBotStat::Request>,
|
|
ServerBotStat::Request{query_size(), file_count(), files_size(), files_max_size()}, td::Time::now());
|
|
}
|
|
|
|
void Query::send_response_stat() const {
|
|
auto now = td::Time::now();
|
|
if (now - start_timestamp_ >= 100.0 && !is_internal_) {
|
|
LOG(WARNING) << "Answer too old query with code " << http_status_code_ << " and answer size " << answer_.size()
|
|
<< ": " << *this;
|
|
}
|
|
|
|
if (stat_actor_.empty()) {
|
|
return;
|
|
}
|
|
send_closure(stat_actor_, &BotStatActor::add_event<ServerBotStat::Response>,
|
|
ServerBotStat::Response{state_ == State::OK, answer_.size(), file_count(), files_size()}, now);
|
|
}
|
|
|
|
} // namespace telegram_bot_api
|