mirror of
https://github.com/MaaAssistantArknights/MaaAssistantArknights.git
synced 2026-07-20 02:55:38 +08:00
Merge pull request #1767 from MaaAssistantArknights/perf/assthttp
perf: AsstHttp 的各种优化
This commit is contained in:
@@ -10,23 +10,26 @@
|
||||
|
||||
namespace asst::http
|
||||
{
|
||||
class Response : public std::string
|
||||
class Response
|
||||
{
|
||||
private:
|
||||
static const inline std::unordered_set<std::string_view> _accepted_protocol_version = { "HTTP/1.1",
|
||||
"HTTP/2.0" };
|
||||
std::string m_raw_data;
|
||||
std::string m_last_error;
|
||||
unsigned m_status_code = 0;
|
||||
std::string_view m_protocol_version;
|
||||
std::string_view m_status_code_info;
|
||||
std::string_view m_data;
|
||||
std::unordered_map<std::string, std::string_view> m_headers;
|
||||
bool _analyze_status_line(std::string_view status_line)
|
||||
std::string_view m_body;
|
||||
std::unordered_map<std::string_view, std::string_view> m_headers;
|
||||
|
||||
bool _analyze_status_line(const std::string_view& status_line)
|
||||
{
|
||||
size_t _word_count = 0;
|
||||
for (const auto& word : utils::string_split(status_line, " ")) {
|
||||
++_word_count;
|
||||
if (_word_count == 1) {
|
||||
static const std::unordered_set<std::string_view> accepted_protocol_version = { "HTTP/1.1" };
|
||||
if (!accepted_protocol_version.contains(word)) {
|
||||
if (!_accepted_protocol_version.contains(word)) {
|
||||
m_last_error = "unsupported protocol version`" + std::string(word) + "`";
|
||||
return false;
|
||||
}
|
||||
@@ -54,16 +57,18 @@ namespace asst::http
|
||||
m_last_error = "status line too short";
|
||||
return false;
|
||||
}
|
||||
bool _analyze_headers_line(std::string_view status_line)
|
||||
bool _analyze_headers_line(const std::string_view& status_line)
|
||||
{
|
||||
size_t _colon_pos = status_line.find(':');
|
||||
if (_colon_pos == status_line.npos) {
|
||||
m_last_error = "connot decode header `" + std::string(status_line) + "`";
|
||||
m_last_error = "cannot decode header `" + std::string(status_line) + "`";
|
||||
return false;
|
||||
}
|
||||
m_headers[utils::view_cast<std::string>(status_line.substr(0, _colon_pos) | views::transform([](char c) {
|
||||
return static_cast<char>(std::tolower(c));
|
||||
}))] =
|
||||
// 把 key 转换为小写
|
||||
for (auto& c : status_line.substr(0, _colon_pos)) {
|
||||
const_cast<char&>(c) = static_cast<char&&>(std::tolower(c));
|
||||
}
|
||||
m_headers[status_line.substr(0, _colon_pos)] =
|
||||
status_line.substr(_colon_pos + 1 + status_line.substr(_colon_pos + 1).find_first_not_of(' '));
|
||||
return true;
|
||||
}
|
||||
@@ -71,10 +76,11 @@ namespace asst::http
|
||||
public:
|
||||
template <typename... Args>
|
||||
requires std::is_constructible_v<std::string, Args...>
|
||||
Response(Args&&... args) : std::string(std::forward<Args>(args)...)
|
||||
Response(Args&&... args) : m_raw_data(std::forward<Args>(args)...)
|
||||
{
|
||||
std::string_view this_view = *this;
|
||||
std::string_view this_view = m_raw_data;
|
||||
bool _is_status_line = true;
|
||||
bool _is_data_line = false;
|
||||
for (const auto& line : utils::string_split(this_view, "\r\n")) {
|
||||
// status
|
||||
if (_is_status_line) {
|
||||
@@ -84,24 +90,40 @@ namespace asst::http
|
||||
_is_status_line = false;
|
||||
}
|
||||
// headers
|
||||
else if (!line.empty()) {
|
||||
if (!_analyze_headers_line(line)) {
|
||||
return;
|
||||
else if (!_is_data_line) {
|
||||
if (!line.empty()) {
|
||||
if (!_analyze_headers_line(line)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
_is_data_line = true; // 当前行是空行,则下一行开始都是 data 段
|
||||
}
|
||||
}
|
||||
// data
|
||||
else {
|
||||
m_data = std::string_view(line.begin(), this_view.end());
|
||||
m_body = std::string_view(line.begin(), this_view.end());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned status_code() const { return m_status_code; }
|
||||
std::string_view protocol_version() const { return m_protocol_version; }
|
||||
std::string_view status_code_info() const { return m_status_code_info; }
|
||||
std::string_view data() const { return m_data; }
|
||||
std::optional<std::string_view> find_header(const std::string& key) const
|
||||
Response() = default;
|
||||
~Response() noexcept = default;
|
||||
|
||||
Response(Response&&) noexcept = default;
|
||||
Response& operator=(Response&&) noexcept = default;
|
||||
|
||||
// string_view 类型的成员在复制时,其指向的 string 可能被析构
|
||||
// 虽然应该有更高效的复制方式,不过在这里还是直接调用 Args&&... 的构造函数
|
||||
Response(const Response&) = delete;
|
||||
Response& operator=(const Response&) = delete;
|
||||
|
||||
unsigned status_code() const noexcept { return m_status_code; }
|
||||
std::string_view protocol_version() const noexcept { return m_protocol_version; }
|
||||
std::string_view status_code_info() const noexcept { return m_status_code_info; }
|
||||
std::string_view body() const noexcept { return m_body; }
|
||||
std::optional<std::string_view> find_header(const std::string_view& key) const
|
||||
{
|
||||
if (auto iter = m_headers.find(key); iter != m_headers.cend()) {
|
||||
return iter->second;
|
||||
@@ -110,13 +132,14 @@ namespace asst::http
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
const auto& headers() const { return m_headers; }
|
||||
const auto& headers() const noexcept { return m_headers; }
|
||||
const std::string& get_last_error() const noexcept { return m_last_error; }
|
||||
operator std::string() const noexcept { return m_raw_data; }
|
||||
|
||||
bool success() const { return m_status_code == 200; }
|
||||
bool status_2xx() const { return m_status_code >= 200 && m_status_code < 300; }
|
||||
bool status_3xx() const { return m_status_code >= 300 && m_status_code < 400; }
|
||||
bool status_4xx() const { return m_status_code >= 400 && m_status_code < 500; }
|
||||
bool status_5xx() const { return m_status_code >= 500 && m_status_code < 600; }
|
||||
bool success() const noexcept { return m_status_code == 200; }
|
||||
bool status_2xx() const noexcept { return m_status_code >= 200 && m_status_code < 300; }
|
||||
bool status_3xx() const noexcept { return m_status_code >= 300 && m_status_code < 400; }
|
||||
bool status_4xx() const noexcept { return m_status_code >= 400 && m_status_code < 500; }
|
||||
bool status_5xx() const noexcept { return m_status_code >= 500 && m_status_code < 600; }
|
||||
};
|
||||
} // namespace asst::http
|
||||
|
||||
@@ -31,13 +31,6 @@ namespace asst::utils
|
||||
template <typename... Unused>
|
||||
constexpr bool always_false = false;
|
||||
|
||||
template <typename dst_t, typename src_t>
|
||||
requires ranges::range<src_t> && ranges::range<dst_t>
|
||||
dst_t view_cast(src_t&& src)
|
||||
{
|
||||
return dst_t(src.begin(), src.end());
|
||||
}
|
||||
|
||||
template <typename char_t = char>
|
||||
using pair_of_string_view = std::pair<std::basic_string_view<char_t>, std::basic_string_view<char_t>>;
|
||||
|
||||
@@ -149,7 +142,7 @@ namespace asst::utils
|
||||
|
||||
constexpr _iterator& operator++()
|
||||
{
|
||||
const auto _end = _pre->_rng.cend();
|
||||
auto _end = _pre->_rng.cend();
|
||||
|
||||
if (_cur = _nxt.cbegin(); _cur == _end) {
|
||||
_trailing_empty = false;
|
||||
@@ -200,6 +193,13 @@ namespace asst::utils
|
||||
: _rng(std::forward<rng_t>(rang)), _pat(std::forward<pat_t>(patt))
|
||||
{}
|
||||
|
||||
template <typename rng_t>
|
||||
constexpr string_split_view(rng_t&& rang, char_t&& elem)
|
||||
{
|
||||
// 摆烂辣!解决不了临时变量导致 _pat 悬垂的问题
|
||||
ASST_STATIC_ASSERT_FALSE("please use `views::split` instead ^_^", rng_t);
|
||||
}
|
||||
|
||||
template <typename rng_t>
|
||||
constexpr string_split_view(rng_t&& rang, const char_t& elem) noexcept
|
||||
: _rng(std::forward<rng_t>(rang)), _pat(&elem, 1)
|
||||
|
||||
@@ -736,7 +736,7 @@ void asst::AutoRecruitTask::upload_to_yituliu(const json::value& details)
|
||||
|
||||
m_report_yituliu_task_ptr->set_report_type(ReportType::YituliuBigData)
|
||||
.set_body(body.to_string())
|
||||
.set_retry_times(1)
|
||||
.set_retry_times(0)
|
||||
.run();
|
||||
}
|
||||
|
||||
|
||||
@@ -145,14 +145,11 @@ namespace asst
|
||||
}
|
||||
|
||||
template <typename Stream, typename T, typename Enable = void>
|
||||
struct has_stream_insertion_operator : std::false_type
|
||||
{};
|
||||
static constexpr bool has_stream_insertion_operator = false;
|
||||
|
||||
template <typename Stream, typename T>
|
||||
struct has_stream_insertion_operator<Stream, T,
|
||||
std::void_t<decltype(std::declval<Stream&>() << std::declval<T>())>>
|
||||
: std::true_type
|
||||
{};
|
||||
static constexpr bool has_stream_insertion_operator<
|
||||
Stream, T, std::void_t<decltype(std::declval<Stream&>() << std::declval<T>())>> = true;
|
||||
|
||||
template <bool ToAnsi, typename Stream>
|
||||
static Stream& stream_put(Stream& s, std::filesystem::path&& v)
|
||||
@@ -170,7 +167,7 @@ namespace asst
|
||||
s << std::string(std::forward<T>(v));
|
||||
return s;
|
||||
}
|
||||
else if constexpr (has_stream_insertion_operator<Stream, T>::value) {
|
||||
else if constexpr (has_stream_insertion_operator<Stream, T>) {
|
||||
s << std::forward<T>(v);
|
||||
return s;
|
||||
}
|
||||
|
||||
@@ -112,13 +112,18 @@ asst::http::Response asst::ReportDataTask::report(const std::string& subtask, co
|
||||
else if (!retry_cond(response)) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
Log.trace("retrying... | why:", response.get_last_error(), "status_code:", response.status_code());
|
||||
}
|
||||
}
|
||||
|
||||
cb_info["why"] = "上报失败";
|
||||
cb_info["details"] = json::object { { "error", response.get_last_error() },
|
||||
{ "status_code", response.status_code() },
|
||||
{ "status_code_info", std::string(response.status_code_info()) },
|
||||
{ "response", std::string(response.data()) } };
|
||||
cb_info["details"] = json::object {
|
||||
{ "error", response.get_last_error() },
|
||||
{ "status_code", response.status_code() },
|
||||
{ "status_code_info", std::string(response.status_code_info()) },
|
||||
{ "response", std::string(response.body()) },
|
||||
};
|
||||
|
||||
callback(AsstMsg::SubTaskError, cb_info);
|
||||
|
||||
@@ -127,22 +132,24 @@ asst::http::Response asst::ReportDataTask::report(const std::string& subtask, co
|
||||
|
||||
asst::http::Response asst::ReportDataTask::escape_and_request(const std::string& format)
|
||||
{
|
||||
LogTraceFunction;
|
||||
|
||||
std::string body_escape = utils::string_replace_all(m_body, { { "\"", "\\\"" } });
|
||||
std::string body_escape = utils::string_replace_all(m_body, "\"", "\\\"");
|
||||
|
||||
#ifdef _WIN32
|
||||
std::string body_escapes = utils::utf8_to_unicode_escape(body_escape);
|
||||
#else
|
||||
std::string body_escapes = body_escape;
|
||||
body_escape = utils::utf8_to_unicode_escape(body_escape);
|
||||
#endif
|
||||
|
||||
std::string cmd_line =
|
||||
utils::string_replace_all(format, { { "[body]", body_escapes }, { "[extra]", m_extra_param } });
|
||||
utils::string_replace_all(format, { { "[body]", body_escape }, { "[extra]", m_extra_param } });
|
||||
|
||||
Log.info("request:\n", cmd_line);
|
||||
http::Response response = utils::callcmd(cmd_line);
|
||||
Log.info("response:\n", response);
|
||||
Log.info("request:\n" + cmd_line);
|
||||
std::string response = utils::callcmd(cmd_line);
|
||||
Log.info("response:\n" + response);
|
||||
|
||||
// Log.info("response:\n" + utils::string_replace_all(response, {
|
||||
// { "\n", " [LF]\n" },
|
||||
// { "\r [LF]\n", " [CRLF]\n" },
|
||||
// { "\r", " [CR]\n" },
|
||||
// }));
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user