mirror of
https://github.com/transmission/transmission
synced 2025-03-13 07:33:02 +00:00
refactor: use the new benc parser in announcer-http (#2529)
This commit is contained in:
parent
b998032305
commit
7f60738cce
4 changed files with 195 additions and 186 deletions
|
@ -19,6 +19,7 @@
|
|||
#include "transmission.h"
|
||||
|
||||
#include "announcer-common.h"
|
||||
#include "benc.h"
|
||||
#include "crypto-utils.h"
|
||||
#include "error.h"
|
||||
#include "log.h"
|
||||
|
@ -28,7 +29,6 @@
|
|||
#include "torrent.h"
|
||||
#include "trevent.h" /* tr_runInEventThread() */
|
||||
#include "utils.h"
|
||||
#include "variant.h"
|
||||
#include "web-utils.h"
|
||||
#include "web.h"
|
||||
|
||||
|
@ -113,67 +113,15 @@ static std::string announce_url_new(tr_session const* session, tr_announce_reque
|
|||
unsigned char const* const ipv6 = tr_globalIPv6(session);
|
||||
if (ipv6 != nullptr)
|
||||
{
|
||||
char ipv6_readable[INET6_ADDRSTRLEN];
|
||||
evutil_inet_ntop(AF_INET6, ipv6, ipv6_readable, INET6_ADDRSTRLEN);
|
||||
auto ipv6_readable = std::array<char, INET6_ADDRSTRLEN>{};
|
||||
evutil_inet_ntop(AF_INET6, ipv6, std::data(ipv6_readable), std::size(ipv6_readable));
|
||||
evbuffer_add_printf(buf, "&ipv6=");
|
||||
tr_http_escape(buf, ipv6_readable, true);
|
||||
tr_http_escape(buf, std::data(ipv6_readable), true);
|
||||
}
|
||||
|
||||
return evbuffer_free_to_str(buf);
|
||||
}
|
||||
|
||||
static auto listToPex(tr_variant* peerList)
|
||||
{
|
||||
size_t n = 0;
|
||||
size_t const len = tr_variantListSize(peerList);
|
||||
auto pex = std::vector<tr_pex>(len);
|
||||
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
{
|
||||
tr_variant* const peer = tr_variantListChild(peerList, i);
|
||||
|
||||
if (peer == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto ip = std::string_view{};
|
||||
if (!tr_variantDictFindStrView(peer, TR_KEY_ip, &ip))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto addr = tr_address{};
|
||||
if (!tr_address_from_string(&addr, ip))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
auto port = int64_t{};
|
||||
if (!tr_variantDictFindInt(peer, TR_KEY_port, &port))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (port < 0 || port > USHRT_MAX)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!tr_address_is_valid_for_peers(&addr, port))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
pex[n].addr = addr;
|
||||
pex[n].port = htons((uint16_t)port);
|
||||
++n;
|
||||
}
|
||||
|
||||
pex.resize(n);
|
||||
return pex;
|
||||
}
|
||||
|
||||
struct announce_data
|
||||
{
|
||||
tr_announce_response response;
|
||||
|
@ -220,79 +168,115 @@ static void verboseLog(std::string_view description, tr_direction direction, std
|
|||
out << std::endl << "[b64]"sv << direction_sv << tr_base64_encode(message) << std::endl;
|
||||
}
|
||||
|
||||
void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::string_view msg)
|
||||
static auto constexpr MaxBencDepth = 8;
|
||||
|
||||
void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::string_view benc)
|
||||
{
|
||||
verboseLog("Announce response:", TR_DOWN, msg);
|
||||
verboseLog("Announce response:", TR_DOWN, benc);
|
||||
|
||||
auto benc = tr_variant{};
|
||||
auto const variant_loaded = tr_variantFromBuf(&benc, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, msg);
|
||||
if (!variant_loaded)
|
||||
struct AnnounceHandler final : public transmission::benc::BasicHandler<MaxBencDepth>
|
||||
{
|
||||
return;
|
||||
}
|
||||
using BasicHandler = transmission::benc::BasicHandler<MaxBencDepth>;
|
||||
|
||||
if (tr_variantIsDict(&benc))
|
||||
{
|
||||
auto i = int64_t{};
|
||||
auto sv = std::string_view{};
|
||||
tr_variant* tmp = nullptr;
|
||||
tr_announce_response& response_;
|
||||
std::optional<size_t> row_;
|
||||
tr_pex pex_ = {};
|
||||
|
||||
if (tr_variantDictFindStrView(&benc, TR_KEY_failure_reason, &sv))
|
||||
explicit AnnounceHandler(tr_announce_response& response)
|
||||
: response_{ response }
|
||||
{
|
||||
response.errmsg = sv;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindStrView(&benc, TR_KEY_warning_message, &sv))
|
||||
bool StartDict() override
|
||||
{
|
||||
response.warning = sv;
|
||||
BasicHandler::StartDict();
|
||||
|
||||
pex_ = {};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(&benc, TR_KEY_interval, &i))
|
||||
bool EndDict() override
|
||||
{
|
||||
response.interval = i;
|
||||
BasicHandler::EndDict();
|
||||
|
||||
if (tr_address_is_valid_for_peers(&pex_.addr, pex_.port))
|
||||
{
|
||||
response_.pex.push_back(pex_);
|
||||
pex_ = {};
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(&benc, TR_KEY_min_interval, &i))
|
||||
bool Int64(int64_t value) override
|
||||
{
|
||||
response.min_interval = i;
|
||||
auto const key = currentKey();
|
||||
|
||||
if (key == "interval")
|
||||
{
|
||||
response_.interval = value;
|
||||
}
|
||||
else if (key == "min interval"sv)
|
||||
{
|
||||
response_.min_interval = value;
|
||||
}
|
||||
else if (key == "complete"sv)
|
||||
{
|
||||
response_.seeders = value;
|
||||
}
|
||||
else if (key == "incomplete"sv)
|
||||
{
|
||||
response_.leechers = value;
|
||||
}
|
||||
else if (key == "downloaded"sv)
|
||||
{
|
||||
response_.downloads = value;
|
||||
}
|
||||
else if (key == "port"sv)
|
||||
{
|
||||
pex_.port = htons(uint16_t(value));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindStrView(&benc, TR_KEY_tracker_id, &sv))
|
||||
bool String(std::string_view value) override
|
||||
{
|
||||
response.tracker_id = sv;
|
||||
}
|
||||
auto const key = currentKey();
|
||||
|
||||
if (tr_variantDictFindInt(&benc, TR_KEY_complete, &i))
|
||||
{
|
||||
response.seeders = i;
|
||||
}
|
||||
if (key == "failure reason"sv)
|
||||
{
|
||||
response_.errmsg = value;
|
||||
}
|
||||
else if (key == "warning message"sv)
|
||||
{
|
||||
response_.warning = value;
|
||||
}
|
||||
else if (key == "tracker id"sv)
|
||||
{
|
||||
response_.tracker_id = value;
|
||||
}
|
||||
else if (key == "peers"sv)
|
||||
{
|
||||
response_.pex = tr_peerMgrCompactToPex(std::data(value), std::size(value), nullptr, 0);
|
||||
}
|
||||
else if (key == "peers6"sv)
|
||||
{
|
||||
response_.pex6 = tr_peerMgrCompact6ToPex(std::data(value), std::size(value), nullptr, 0);
|
||||
}
|
||||
else if (key == "ip")
|
||||
{
|
||||
tr_address_from_string(&pex_.addr, value);
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(&benc, TR_KEY_incomplete, &i))
|
||||
{
|
||||
response.leechers = i;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
if (tr_variantDictFindInt(&benc, TR_KEY_downloaded, &i))
|
||||
{
|
||||
response.downloads = i;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindStrView(&benc, TR_KEY_peers6, &sv))
|
||||
{
|
||||
response.pex6 = tr_peerMgrCompact6ToPex(std::data(sv), std::size(sv), nullptr, 0);
|
||||
}
|
||||
|
||||
if (tr_variantDictFindStrView(&benc, TR_KEY_peers, &sv))
|
||||
{
|
||||
response.pex = tr_peerMgrCompactToPex(std::data(sv), std::size(sv), nullptr, 0);
|
||||
}
|
||||
else if (tr_variantDictFindList(&benc, TR_KEY_peers, &tmp))
|
||||
{
|
||||
response.pex = listToPex(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
tr_variantFree(&benc);
|
||||
auto stack = transmission::benc::ParserStack<MaxBencDepth>{};
|
||||
auto handler = AnnounceHandler{ response };
|
||||
transmission::benc::parse(benc, stack, handler);
|
||||
}
|
||||
|
||||
static void on_announce_done(
|
||||
|
@ -376,75 +360,86 @@ static void on_scrape_done_eventthread(void* vdata)
|
|||
delete data;
|
||||
}
|
||||
|
||||
void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::string_view msg)
|
||||
void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::string_view benc)
|
||||
{
|
||||
verboseLog("Scrape response:", TR_DOWN, msg);
|
||||
verboseLog("Scrape response:", TR_DOWN, benc);
|
||||
|
||||
auto top = tr_variant{};
|
||||
auto const variant_loaded = tr_variantFromBuf(&top, TR_VARIANT_PARSE_BENC | TR_VARIANT_PARSE_INPLACE, msg);
|
||||
if (!variant_loaded)
|
||||
struct ScrapeHandler final : public transmission::benc::BasicHandler<MaxBencDepth>
|
||||
{
|
||||
return;
|
||||
}
|
||||
using BasicHandler = transmission::benc::BasicHandler<MaxBencDepth>;
|
||||
|
||||
if (auto sv = std::string_view{}; tr_variantDictFindStrView(&top, TR_KEY_failure_reason, &sv))
|
||||
{
|
||||
response.errmsg = sv;
|
||||
}
|
||||
tr_scrape_response& response_;
|
||||
std::optional<size_t> row_;
|
||||
|
||||
tr_variant* flags = nullptr;
|
||||
auto intVal = int64_t{};
|
||||
if (tr_variantDictFindDict(&top, TR_KEY_flags, &flags) &&
|
||||
tr_variantDictFindInt(flags, TR_KEY_min_request_interval, &intVal))
|
||||
{
|
||||
response.min_request_interval = intVal;
|
||||
}
|
||||
|
||||
tr_variant* files = nullptr;
|
||||
if (tr_variantDictFindDict(&top, TR_KEY_files, &files))
|
||||
{
|
||||
auto key = tr_quark{};
|
||||
tr_variant* val = nullptr;
|
||||
|
||||
for (int i = 0; tr_variantDictChild(files, i, &key, &val); ++i)
|
||||
explicit ScrapeHandler(tr_scrape_response& response)
|
||||
: response_{ response }
|
||||
{
|
||||
/* populate the corresponding row in our response array */
|
||||
for (int j = 0; j < response.row_count; ++j)
|
||||
}
|
||||
|
||||
bool Key(std::string_view value) override
|
||||
{
|
||||
BasicHandler::Key(value);
|
||||
|
||||
auto needle = tr_sha1_digest_t{};
|
||||
if (depth() == 2 && key(1) == "files"sv && std::size(value) == std::size(needle))
|
||||
{
|
||||
struct tr_scrape_response_row* row = &response.rows[j];
|
||||
std::copy_n(reinterpret_cast<std::byte const*>(std::data(value)), std::size(value), std::data(needle));
|
||||
auto const it = std::find_if(
|
||||
std::begin(response_.rows),
|
||||
std::end(response_.rows),
|
||||
[needle](auto const& row) { return row.info_hash == needle; });
|
||||
|
||||
// TODO(ckerr): ugh, interning info dict hashes is awful
|
||||
auto const& hash = row->info_hash;
|
||||
auto const key_sv = tr_quark_get_string_view(key);
|
||||
if (std::size(hash) == std::size(key_sv) && memcmp(std::data(hash), std::data(key_sv), std::size(hash)) == 0)
|
||||
if (it == std::end(response_.rows))
|
||||
{
|
||||
if (tr_variantDictFindInt(val, TR_KEY_complete, &intVal))
|
||||
{
|
||||
row->seeders = intVal;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(val, TR_KEY_incomplete, &intVal))
|
||||
{
|
||||
row->leechers = intVal;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(val, TR_KEY_downloaded, &intVal))
|
||||
{
|
||||
row->downloads = intVal;
|
||||
}
|
||||
|
||||
if (tr_variantDictFindInt(val, TR_KEY_downloaders, &intVal))
|
||||
{
|
||||
row->downloaders = intVal;
|
||||
}
|
||||
|
||||
break;
|
||||
row_.reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
row_ = std::distance(std::begin(response_.rows), it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tr_variantFree(&top);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Int64(int64_t value) override
|
||||
{
|
||||
if (row_ && currentKey() == "complete"sv)
|
||||
{
|
||||
response_.rows[*row_].seeders = value;
|
||||
}
|
||||
else if (row_ && currentKey() == "downloaded"sv)
|
||||
{
|
||||
response_.rows[*row_].downloads = value;
|
||||
}
|
||||
else if (row_ && currentKey() == "incomplete"sv)
|
||||
{
|
||||
response_.rows[*row_].leechers = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool String(std::string_view value) override
|
||||
{
|
||||
if (depth() == 1 && currentKey() == "failure reason"sv)
|
||||
{
|
||||
response_.errmsg = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
auto stack = transmission::benc::ParserStack<MaxBencDepth>{};
|
||||
auto handler = ScrapeHandler{ response };
|
||||
tr_error* error = nullptr;
|
||||
transmission::benc::parse(benc, stack, handler, nullptr, &error);
|
||||
if (error != nullptr)
|
||||
{
|
||||
std::cerr << error->message << std::endl;
|
||||
tr_error_clear(&error);
|
||||
}
|
||||
}
|
||||
|
||||
static void on_scrape_done(
|
||||
|
|
|
@ -59,35 +59,63 @@ struct BasicHandler : public Handler
|
|||
|
||||
bool StartDict() override
|
||||
{
|
||||
keys.emplace_back();
|
||||
push();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Key(std::string_view key) override
|
||||
{
|
||||
keys.back() = key;
|
||||
keys_[depth_] = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EndDict() override
|
||||
{
|
||||
keys.resize(keys.size() - 1);
|
||||
pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StartArray() override
|
||||
{
|
||||
keys.emplace_back();
|
||||
push();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EndArray() override
|
||||
{
|
||||
keys.resize(keys.size() - 1);
|
||||
pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::array<std::string_view, MaxDepth> keys;
|
||||
auto key(size_t i) const
|
||||
{
|
||||
return keys_[i];
|
||||
}
|
||||
|
||||
auto depth() const
|
||||
{
|
||||
return depth_;
|
||||
}
|
||||
|
||||
auto currentKey() const
|
||||
{
|
||||
return key(depth());
|
||||
}
|
||||
|
||||
private:
|
||||
void push()
|
||||
{
|
||||
++depth_;
|
||||
keys_[depth_] = {};
|
||||
}
|
||||
|
||||
void pop()
|
||||
{
|
||||
--depth_;
|
||||
}
|
||||
|
||||
size_t depth_ = 0;
|
||||
std::array<std::string_view, MaxDepth> keys_;
|
||||
};
|
||||
|
||||
template<std::size_t MaxDepth>
|
||||
|
@ -125,7 +153,7 @@ struct ParserStack
|
|||
return stack[depth];
|
||||
}
|
||||
|
||||
bool expectingDictKey() const
|
||||
[[nodiscard]] bool expectingDictKey() const
|
||||
{
|
||||
return depth > 0 && stack[depth].parent_type == ParentType::Dict && (stack[depth].n_children_walked % 2) == 0;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ using namespace std::literals;
|
|||
namespace
|
||||
{
|
||||
|
||||
auto constexpr my_static = std::array<std::string_view, 390>{ ""sv,
|
||||
auto constexpr my_static = std::array<std::string_view, 383>{ ""sv,
|
||||
"activeTorrentCount"sv,
|
||||
"activity-date"sv,
|
||||
"activityDate"sv,
|
||||
|
@ -108,7 +108,6 @@ auto constexpr my_static = std::array<std::string_view, 390>{ ""sv,
|
|||
"errorString"sv,
|
||||
"eta"sv,
|
||||
"etaIdle"sv,
|
||||
"failure reason"sv,
|
||||
"fields"sv,
|
||||
"file-count"sv,
|
||||
"fileStats"sv,
|
||||
|
@ -150,8 +149,6 @@ auto constexpr my_static = std::array<std::string_view, 390>{ ""sv,
|
|||
"incomplete-dir-enabled"sv,
|
||||
"info"sv,
|
||||
"inhibit-desktop-hibernation"sv,
|
||||
"interval"sv,
|
||||
"ip"sv,
|
||||
"ipv4"sv,
|
||||
"ipv6"sv,
|
||||
"isBackup"sv,
|
||||
|
@ -198,7 +195,6 @@ auto constexpr my_static = std::array<std::string_view, 390>{ ""sv,
|
|||
"metadata_size"sv,
|
||||
"metainfo"sv,
|
||||
"method"sv,
|
||||
"min interval"sv,
|
||||
"min_request_interval"sv,
|
||||
"move"sv,
|
||||
"msg_type"sv,
|
||||
|
@ -230,7 +226,6 @@ auto constexpr my_static = std::array<std::string_view, 390>{ ""sv,
|
|||
"peers"sv,
|
||||
"peers2"sv,
|
||||
"peers2-6"sv,
|
||||
"peers6"sv,
|
||||
"peersConnected"sv,
|
||||
"peersFrom"sv,
|
||||
"peersGettingFromUs"sv,
|
||||
|
@ -370,7 +365,6 @@ auto constexpr my_static = std::array<std::string_view, 390>{ ""sv,
|
|||
"torrents"sv,
|
||||
"totalSize"sv,
|
||||
"total_size"sv,
|
||||
"tracker id"sv,
|
||||
"trackerAdd"sv,
|
||||
"trackerRemove"sv,
|
||||
"trackerReplace"sv,
|
||||
|
@ -403,7 +397,6 @@ auto constexpr my_static = std::array<std::string_view, 390>{ ""sv,
|
|||
"v"sv,
|
||||
"version"sv,
|
||||
"wanted"sv,
|
||||
"warning message"sv,
|
||||
"watch-dir"sv,
|
||||
"watch-dir-enabled"sv,
|
||||
"webseeds"sv,
|
||||
|
|
|
@ -111,7 +111,6 @@ enum
|
|||
TR_KEY_errorString,
|
||||
TR_KEY_eta,
|
||||
TR_KEY_etaIdle,
|
||||
TR_KEY_failure_reason,
|
||||
TR_KEY_fields,
|
||||
TR_KEY_file_count,
|
||||
TR_KEY_fileStats,
|
||||
|
@ -153,8 +152,6 @@ enum
|
|||
TR_KEY_incomplete_dir_enabled,
|
||||
TR_KEY_info,
|
||||
TR_KEY_inhibit_desktop_hibernation,
|
||||
TR_KEY_interval,
|
||||
TR_KEY_ip,
|
||||
TR_KEY_ipv4,
|
||||
TR_KEY_ipv6,
|
||||
TR_KEY_isBackup,
|
||||
|
@ -201,7 +198,6 @@ enum
|
|||
TR_KEY_metadata_size,
|
||||
TR_KEY_metainfo,
|
||||
TR_KEY_method,
|
||||
TR_KEY_min_interval,
|
||||
TR_KEY_min_request_interval,
|
||||
TR_KEY_move,
|
||||
TR_KEY_msg_type,
|
||||
|
@ -233,7 +229,6 @@ enum
|
|||
TR_KEY_peers,
|
||||
TR_KEY_peers2,
|
||||
TR_KEY_peers2_6,
|
||||
TR_KEY_peers6,
|
||||
TR_KEY_peersConnected,
|
||||
TR_KEY_peersFrom,
|
||||
TR_KEY_peersGettingFromUs,
|
||||
|
@ -373,7 +368,6 @@ enum
|
|||
TR_KEY_torrents,
|
||||
TR_KEY_totalSize,
|
||||
TR_KEY_total_size,
|
||||
TR_KEY_tracker_id,
|
||||
TR_KEY_trackerAdd,
|
||||
TR_KEY_trackerRemove,
|
||||
TR_KEY_trackerReplace,
|
||||
|
@ -406,7 +400,6 @@ enum
|
|||
TR_KEY_v,
|
||||
TR_KEY_version,
|
||||
TR_KEY_wanted,
|
||||
TR_KEY_warning_message,
|
||||
TR_KEY_watch_dir,
|
||||
TR_KEY_watch_dir_enabled,
|
||||
TR_KEY_webseeds,
|
||||
|
|
Loading…
Add table
Reference in a new issue