perf: intern the announcer module's URL strings (#2085)

* perf: intern the announcer module's internal URLs
This commit is contained in:
Charles Kerr 2021-11-02 18:00:01 -05:00 committed by GitHub
parent 790b0bb2b5
commit 9dca5f2086
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 280 additions and 265 deletions

View File

@ -15,6 +15,8 @@
#include <string>
#include "transmission.h" /* SHA_DIGEST_LENGTH */
#include "quark.h"
#include "utils.h"
/***
**** SCRAPE
@ -34,7 +36,7 @@ auto inline constexpr TR_MULTISCRAPE_MAX = 60;
struct tr_scrape_request
{
/* the scrape URL */
char const* url;
tr_quark scrape_url;
/* the name to use when deep logging is enabled */
char log_name[128];
@ -81,7 +83,7 @@ struct tr_scrape_response
struct tr_scrape_response_row rows[TR_MULTISCRAPE_MAX];
/* the raw scrape url */
std::string url;
tr_quark scrape_url;
/* human-readable error string on failure, or nullptr */
std::string errmsg;
@ -149,7 +151,7 @@ struct tr_announce_request
uint64_t leftUntilComplete;
/* the tracker's announce URL */
char* url;
tr_quark announce_url;
/* key generated by and returned from an http tracker.
* see tr_announce_response.tracker_id_str */
@ -234,3 +236,16 @@ void tr_tracker_udp_announce(
void* user_data);
void tr_tracker_udp_start_shutdown(tr_session* session);
tr_quark tr_announcerGetKey(tr_parsed_url_t const& parsed);
inline tr_quark tr_announcerGetKey(std::string_view url)
{
auto const parsed = tr_urlParseTracker(url);
return parsed ? tr_announcerGetKey(*parsed) : TR_KEY_NONE;
}
inline tr_quark tr_announcerGetKey(tr_quark url)
{
return tr_announcerGetKey(tr_quark_get_string_view(url));
}

View File

@ -41,16 +41,16 @@ static char const* get_event_string(tr_announce_request const* req)
static char* announce_url_new(tr_session const* session, tr_announce_request const* req)
{
evbuffer* const buf = evbuffer_new();
char escaped_info_hash[SHA_DIGEST_LENGTH * 3 + 1];
auto const announce_sv = tr_quark_get_string_view(req->announce_url);
char escaped_info_hash[SHA_DIGEST_LENGTH * 3 + 1];
tr_http_escape_sha1(escaped_info_hash, req->info_hash);
auto* const buf = evbuffer_new();
evbuffer_expand(buf, 1024);
evbuffer_add_printf(
buf,
"%s"
"%" TR_PRIsv
"%c"
"info_hash=%s"
"&peer_id=%" TR_PRIsv
@ -62,8 +62,8 @@ static char* announce_url_new(tr_session const* session, tr_announce_request con
"&key=%x"
"&compact=1"
"&supportcrypto=1",
req->url,
strchr(req->url, '?') != nullptr ? '&' : '?',
TR_PRIsv_ARG(announce_sv),
announce_sv.find('?') == announce_sv.npos ? '?' : '&',
escaped_info_hash,
TR_PRIsv_ARG(req->peer_id),
req->port,
@ -323,8 +323,6 @@ void tr_tracker_http_announce(
tr_announce_response_func response_func,
void* response_func_user_data)
{
char* const url = announce_url_new(session, request);
auto* const d = tr_new0(announce_data, 1);
d->response.seeders = -1;
d->response.leechers = -1;
@ -334,9 +332,9 @@ void tr_tracker_http_announce(
memcpy(d->response.info_hash, request->info_hash, SHA_DIGEST_LENGTH);
tr_strlcpy(d->log_name, request->log_name, sizeof(d->log_name));
char* const url = announce_url_new(session, request);
dbgmsg(request->log_name, "Sending announce to libcurl: \"%s\"", url);
tr_webRun(session, url, on_announce_done, d);
tr_free(url);
}
@ -380,7 +378,9 @@ static void on_scrape_done(
tr_scrape_response* response = &data->response;
response->did_connect = did_connect;
response->did_timeout = did_timeout;
dbgmsg(data->log_name, "Got scrape response for \"%s\"", response->url.c_str());
auto const scrape_url_sv = tr_quark_get_string_view(response->scrape_url);
dbgmsg(data->log_name, "Got scrape response for \"%" TR_PRIsv "\"", TR_PRIsv_ARG(scrape_url_sv));
if (response_code != HTTP_OK)
{
@ -447,6 +447,7 @@ static void on_scrape_done(
{
struct tr_scrape_response_row* row = &response->rows[j];
// TODO(ckerr): ugh, interning info dict hashes is awful
if (memcmp(tr_quark_get_string(key), row->info_hash, SHA_DIGEST_LENGTH) == 0)
{
if (tr_variantDictFindInt(val, TR_KEY_complete, &intVal))
@ -484,11 +485,12 @@ static void on_scrape_done(
static char* scrape_url_new(tr_scrape_request const* req)
{
struct evbuffer* const buf = evbuffer_new();
auto const sv = tr_quark_get_string_view(req->scrape_url);
evbuffer_add_printf(buf, "%s", req->url);
char delimiter = strchr(req->url, '?') != nullptr ? '&' : '?';
auto* const buf = evbuffer_new();
evbuffer_add(buf, std::data(sv), std::size(sv));
char delimiter = sv.find('?') == sv.npos ? '?' : '&';
for (int i = 0; i < req->info_hash_count; ++i)
{
char str[SHA_DIGEST_LENGTH * 3 + 1];
@ -509,7 +511,7 @@ void tr_tracker_http_scrape(
char* url = scrape_url_new(request);
auto* d = new scrape_data{};
d->response.url = request->url;
d->response.scrape_url = request->scrape_url;
d->response_func = response_func;
d->response_func_user_data = response_func_user_data;
d->response.row_count = request->info_hash_count;

View File

@ -29,7 +29,7 @@
#include "tr-udp.h"
#include "utils.h"
#define dbgmsg(name, ...) tr_logAddDeepNamed((name).c_str(), __VA_ARGS__)
#define dbgmsg(key, ...) tr_logAddDeepNamed(tr_quark_get_string(key), __VA_ARGS__)
/****
*****
@ -190,7 +190,7 @@ static struct tau_scrape_request* tau_scrape_request_new(
req->transaction_id = transaction_id;
req->callback = callback;
req->user_data = user_data;
req->response.url = in->url;
req->response.scrape_url = in->scrape_url;
req->response.row_count = in->info_hash_count;
req->payload.assign(payload_begin, payload_end);
@ -416,8 +416,8 @@ struct tau_tracker
{
tr_session* const session;
std::string const key;
std::string const host;
tr_quark const key;
tr_quark const host;
int const port;
struct evdns_getaddrinfo_request* dns_request = nullptr;
@ -434,10 +434,10 @@ struct tau_tracker
tr_ptrArray announces = {};
tr_ptrArray scrapes = {};
tau_tracker(tr_session* session_in, std::string key_in, std::string host_in, int port_in)
tau_tracker(tr_session* session_in, tr_quark key_in, tr_quark host_in, int port_in)
: session{ session_in }
, key{ std::move(key_in) }
, host{ std::move(host_in) }
, key{ key_in }
, host{ host_in }
, port{ port_in }
{
}
@ -681,7 +681,7 @@ static void tau_tracker_upkeep_ex(struct tau_tracker* tracker, bool timeout_reqs
dbgmsg(tracker->host, "Trying a new DNS lookup");
tracker->dns_request = evdns_getaddrinfo(
tracker->session->evdns_base,
tracker->host.c_str(),
tr_quark_get_string(tracker->host),
nullptr,
&hints,
tau_tracker_on_dns,
@ -759,35 +759,33 @@ static struct tr_announcer_udp* announcer_udp_get(tr_session* session)
/* Finds the tau_tracker struct that corresponds to this url.
If it doesn't exist yet, create one. */
static struct tau_tracker* tau_session_get_tracker(struct tr_announcer_udp* tau, char const* url)
static tau_tracker* tau_session_get_tracker(tr_announcer_udp* tau, tr_quark announce_url)
{
/* see if we've already got a tracker that matches this host + port */
auto port = int{};
char* host = nullptr;
tr_urlParse(url, TR_BAD_SIZE, nullptr, &host, &port, nullptr);
char* const key = tr_strdup_printf("%s:%d", host, port);
// build a lookup key for this tracker
auto const announce_sv = tr_quark_get_string_view(announce_url);
auto parsed = tr_urlParseTracker(announce_sv);
TR_ASSERT(parsed);
if (!parsed)
{
return nullptr;
}
tau_tracker* tracker = nullptr;
for (int i = 0, n = tr_ptrArraySize(&tau->trackers); tracker == nullptr && i < n; ++i)
// see if we already have it
// TODO: replace tr_ptrArray
auto const key = tr_announcerGetKey(*parsed);
for (int i = 0, n = tr_ptrArraySize(&tau->trackers); i < n; ++i)
{
auto* tmp = static_cast<struct tau_tracker*>(tr_ptrArrayNth(&tau->trackers, i));
if (tmp->key == key)
{
tracker = tmp;
return tmp;
}
}
/* if we don't have a match, build a new tracker */
if (tracker == nullptr)
{
tracker = new tau_tracker{ tau->session, key, host, port };
tr_ptrArrayAppend(&tau->trackers, tracker);
dbgmsg(tracker->key, "New tau_tracker created");
}
tr_free(key);
tr_free(host);
// we don't have it -- build a new one
auto* const tracker = new tau_tracker{ tau->session, key, tr_quark_new(parsed->host), parsed->port };
tr_ptrArrayAppend(&tau->trackers, tracker);
dbgmsg(tracker->key, "New tau_tracker created");
return tracker;
}
@ -954,9 +952,14 @@ void tr_tracker_udp_announce(
tr_announce_response_func response_func,
void* user_data)
{
struct tr_announcer_udp* tau = announcer_udp_get(session);
struct tau_tracker* tracker = tau_session_get_tracker(tau, request->url);
struct tau_announce_request* r = tau_announce_request_new(request, response_func, user_data);
tr_announcer_udp* tau = announcer_udp_get(session);
tau_tracker* tracker = tau_session_get_tracker(tau, request->announce_url);
if (tracker == nullptr)
{
return;
}
tau_announce_request* r = tau_announce_request_new(request, response_func, user_data);
tr_ptrArrayAppend(&tracker->announces, r);
tau_tracker_upkeep_ex(tracker, false);
}
@ -967,9 +970,14 @@ void tr_tracker_udp_scrape(
tr_scrape_response_func response_func,
void* user_data)
{
struct tr_announcer_udp* tau = announcer_udp_get(session);
struct tau_tracker* tracker = tau_session_get_tracker(tau, request->url);
struct tau_scrape_request* r = tau_scrape_request_new(request, response_func, user_data);
tr_announcer_udp* tau = announcer_udp_get(session);
tau_tracker* tracker = tau_session_get_tracker(tau, request->scrape_url);
if (tracker == nullptr)
{
return;
}
tau_scrape_request* r = tau_scrape_request_new(request, response_func, user_data);
tr_ptrArrayAppend(&tracker->scrapes, r);
tau_tracker_upkeep_ex(tracker, false);
}

View File

@ -12,9 +12,9 @@
#include <cstdio>
#include <cstdlib> /* qsort() */
#include <cstring> /* strcmp(), memcpy(), strncmp() */
#include <map>
#include <set>
#include <string_view>
#include <unordered_map>
#include <vector>
#include <event2/buffer.h>
@ -33,6 +33,8 @@
#include "tr-assert.h"
#include "utils.h"
using namespace std::literals;
struct tr_tier;
static void tier_build_log_name(struct tr_tier const* tier, char* buf, size_t buflen);
@ -113,7 +115,12 @@ struct StopsCompare
}
// tertiary key: the tracker's announce url
return tr_strcmp0(a->url, b->url);
if (a->announce_url != b->announce_url)
{
return a->announce_url < b->announce_url ? -1 : 1;
}
return 0;
}
bool operator()(tr_announce_request const* a, tr_announce_request const* b) const
@ -130,12 +137,12 @@ struct StopsCompare
struct tr_scrape_info
{
std::string const url;
tr_quark const scrape_url;
int multiscrape_max;
tr_scrape_info(std::string url_in, int const multiscrape_max_in)
: url{ std::move(url_in) }
tr_scrape_info(tr_quark scrape_url_in, int const multiscrape_max_in)
: scrape_url{ scrape_url_in }
, multiscrape_max{ multiscrape_max_in }
{
}
@ -147,7 +154,7 @@ struct tr_scrape_info
struct tr_announcer
{
std::set<tr_announce_request*, StopsCompare> stops;
std::map<std::string, tr_scrape_info> scrape_info;
std::unordered_map<tr_quark, tr_scrape_info> scrape_info;
tr_session* session;
struct event* upkeepTimer;
@ -155,18 +162,16 @@ struct tr_announcer
time_t tauUpkeepAt;
};
static struct tr_scrape_info* tr_announcerGetScrapeInfo(struct tr_announcer* announcer, std::string const& url)
static tr_scrape_info* tr_announcerGetScrapeInfo(tr_announcer* announcer, tr_quark url)
{
struct tr_scrape_info* info = nullptr;
if (!std::empty(url))
if (url == TR_KEY_NONE)
{
auto& scrapes = announcer->scrape_info;
auto const it = scrapes.try_emplace(url, url, TR_MULTISCRAPE_MAX);
info = &it.first->second;
return nullptr;
}
return info;
auto& scrapes = announcer->scrape_info;
auto const it = scrapes.try_emplace(url, url, TR_MULTISCRAPE_MAX);
return &it.first->second;
}
static void onUpkeepTimer(evutil_socket_t fd, short what, void* vannouncer);
@ -208,8 +213,8 @@ void tr_announcerClose(tr_session* session)
/* a row in tr_tier's list of trackers */
struct tr_tracker
{
char* key;
char* announce;
tr_quark key;
tr_quark announce_url;
struct tr_scrape_info* scrape_info;
char* tracker_id_str;
@ -224,31 +229,20 @@ struct tr_tracker
uint32_t id;
};
/* format: host+':'+ port */
static char* getKey(char const* url)
// format: `${host}:${port}`
tr_quark tr_announcerGetKey(tr_parsed_url_t const& parsed)
{
char* scheme = nullptr;
char* host = nullptr;
int port = 0;
tr_urlParse(url, TR_BAD_SIZE, &scheme, &host, &port, nullptr);
char* const ret = tr_strdup_printf(
"%s://%s:%d",
scheme != nullptr ? scheme : "invalid",
host != nullptr ? host : "invalid",
port);
tr_free(host);
tr_free(scheme);
return ret;
std::string buf;
tr_buildBuf(buf, parsed.host, ":"sv, parsed.portstr);
return tr_quark_new(buf);
}
static void trackerConstruct(tr_announcer* announcer, tr_tracker* tracker, tr_tracker_info const* inf)
{
memset(tracker, 0, sizeof(tr_tracker));
tracker->key = getKey(inf->announce);
tracker->announce = tr_strdup(inf->announce);
tracker->scrape_info = inf->scrape == nullptr ? nullptr : tr_announcerGetScrapeInfo(announcer, inf->scrape);
tracker->key = tr_announcerGetKey(inf->announce);
tracker->announce_url = tr_quark_new(tr_strvstrip(inf->announce));
tracker->scrape_info = inf->scrape == nullptr ? nullptr : tr_announcerGetScrapeInfo(announcer, tr_quark_new(inf->scrape));
tracker->id = inf->id;
tracker->seederCount = -1;
tracker->leecherCount = -1;
@ -258,8 +252,6 @@ static void trackerConstruct(tr_announcer* announcer, tr_tracker* tracker, tr_tr
static void trackerDestruct(tr_tracker* tracker)
{
tr_free(tracker->tracker_id_str);
tr_free(tracker->announce);
tr_free(tracker->key);
}
/***
@ -361,12 +353,11 @@ static void tierDestruct(tr_tier* tier)
static void tier_build_log_name(tr_tier const* tier, char* buf, size_t buflen)
{
tr_snprintf(
buf,
buflen,
"[%s---%s]",
(tier != nullptr && tier->tor != nullptr) ? tr_torrentName(tier->tor) : "?",
(tier != nullptr && tier->currentTracker != nullptr) ? tier->currentTracker->key : "?");
auto const* const name = tier != nullptr && tier->tor != nullptr ? tr_torrentName(tier->tor) : "?";
auto const key_sv = tier != nullptr && tier->currentTracker != nullptr ?
tr_quark_get_string_view(tier->currentTracker->key) :
"?"sv;
tr_snprintf(buf, buflen, "[%s---%" TR_PRIsv "]", name, TR_PRIsv_ARG(key_sv));
}
static void tierIncrementTracker(tr_tier* tier)
@ -476,7 +467,7 @@ static void publishMessage(tr_tier* tier, char const* msg, TrackerEventType type
if (tier->currentTracker != nullptr)
{
event.tracker = tier->currentTracker->announce;
event.announce_url = tier->currentTracker->announce_url;
}
(*tiers->callback)(tier->tor, &event, tiers->callbackData);
@ -532,117 +523,95 @@ static void publishPeersPex(tr_tier* tier, int seeders, int leechers, tr_pex con
****
***/
struct ann_tracker_info
struct AnnTrackerInfo
{
tr_tracker_info info;
char* scheme;
char* host;
char* path;
int port;
};
/* primary key: tier
* secondary key: udp comes before http */
static int filter_trackers_compare_func(void const* va, void const* vb)
{
auto* a = static_cast<struct ann_tracker_info const*>(va);
auto* b = static_cast<struct ann_tracker_info const*>(vb);
if (a->info.tier != b->info.tier)
AnnTrackerInfo(tr_tracker_info info_in, tr_parsed_url_t url_in)
: info{ info_in }
, url{ url_in }
{
return a->info.tier - b->info.tier;
}
return -strcmp(a->scheme, b->scheme);
}
tr_tracker_info info;
tr_parsed_url_t url;
/* primary key: tier
* secondary key: udp comes before http */
int compare(AnnTrackerInfo const& that) const // <=>
{
if (this->info.tier != that.info.tier)
{
return this->info.tier - that.info.tier;
}
return -this->url.scheme.compare(that.url.scheme);
}
bool operator<(AnnTrackerInfo const& that) const // less than
{
return this->compare(that) < 0;
}
};
/**
* Massages the incoming list of trackers into something we can use.
*/
static tr_tracker_info* filter_trackers(tr_tracker_info const* input, int input_count, int* setme_count)
{
int n = 0;
struct ann_tracker_info* tmp = tr_new0(struct ann_tracker_info, input_count);
auto tmp = std::vector<AnnTrackerInfo>{};
tmp.reserve(input_count);
/* build a list of valid trackers */
for (int i = 0; i < input_count; ++i)
// build a list of valid trackers
for (auto const *walk = input, *const end = walk + input_count; walk != end; ++walk)
{
if (tr_urlIsValidTracker(input[i].announce))
auto const parsed = tr_urlParseTracker(walk->announce);
if (!parsed)
{
int port = 0;
char* scheme = nullptr;
char* host = nullptr;
char* path = nullptr;
tr_urlParse(input[i].announce, TR_BAD_SIZE, &scheme, &host, &port, &path);
/* weed out one common source of duplicates:
* "http://tracker/announce" +
* "http://tracker:80/announce"
*/
bool is_duplicate = false;
for (int j = 0; !is_duplicate && j < n; ++j)
{
is_duplicate = tmp[j].port == port && strcmp(tmp[j].scheme, scheme) == 0 && strcmp(tmp[j].host, host) == 0 &&
strcmp(tmp[j].path, path) == 0;
}
if (is_duplicate)
{
tr_free(path);
tr_free(host);
tr_free(scheme);
continue;
}
tmp[n].info = input[i];
tmp[n].scheme = scheme;
tmp[n].host = host;
tmp[n].port = port;
tmp[n].path = path;
n++;
continue;
}
// weed out implicit-vs-explicit port duplicates e.g.
// "http://tracker/announce" + "http://tracker:80/announce"
auto const is_same = [&parsed](auto const& item)
{
return item.url.scheme == parsed->scheme && item.url.host == parsed->host && item.url.port == parsed->port &&
item.url.path == parsed->path;
};
if (std::any_of(std::begin(tmp), std::end(tmp), is_same))
{
continue;
}
tmp.emplace_back(*walk, *parsed);
}
/* if two announce URLs differ only by scheme, put them in the same tier.
* (note: this can leave gaps in the `tier' values, but since the calling
* function doesn't care, there's no point in removing the gaps...) */
for (int i = 0; i < n; ++i)
// if two announce URLs differ only by scheme, put them in the same tier.
// (note: this can leave gaps in the `tier' values, but since the calling
// function doesn't care, there's no point in removing the gaps...)
for (size_t i = 0, n = std::size(tmp); i < n; ++i)
{
for (int j = i + 1; j < n; ++j)
for (size_t j = i + 1; j < n; ++j)
{
if (tmp[i].info.tier != tmp[j].info.tier && tmp[i].port == tmp[j].port &&
tr_strcmp0(tmp[i].host, tmp[j].host) == 0 && tr_strcmp0(tmp[i].path, tmp[j].path) == 0)
auto const& a = tmp[i];
auto& b = tmp[j];
if ((a.info.tier != b.info.tier) && (a.url.port == b.url.port) && (a.url.host == b.url.host) &&
(a.url.path == b.url.path))
{
tmp[j].info.tier = tmp[i].info.tier;
b.info.tier = a.info.tier;
}
}
}
/* sort them, for two reasons:
* (1) unjumble the tiers from the previous step
* (2) move the UDP trackers to the front of each tier */
qsort(tmp, n, sizeof(struct ann_tracker_info), filter_trackers_compare_func);
// sort them, for two reasons:
// 1. unjumble the tiers from the previous step
// 2. move the UDP trackers to the front of each tier
std::sort(std::begin(tmp), std::end(tmp));
/* build the output */
// build the output
auto const n = std::size(tmp);
*setme_count = n;
struct tr_tracker_info* const ret = tr_new0(tr_tracker_info, n);
for (int i = 0; i < n; ++i)
{
ret[i] = tmp[i].info;
}
/* cleanup */
for (int i = 0; i < n; ++i)
{
tr_free(tmp[i].path);
tr_free(tmp[i].host);
tr_free(tmp[i].scheme);
}
tr_free(tmp);
auto* const ret = tr_new0(tr_tracker_info, n);
std::transform(std::begin(tmp), std::end(tmp), ret, [](auto const& item) { return item.info; });
return ret;
}
@ -932,7 +901,7 @@ static tr_announce_request* announce_request_new(
{
tr_announce_request* req = tr_new0(tr_announce_request, 1);
req->port = tr_sessionGetPublicPeerPort(announcer->session);
req->url = tr_strdup(tier->currentTracker->announce);
req->announce_url = tier->currentTracker->announce_url;
req->tracker_id_str = tr_strdup(tier->currentTracker->tracker_id_str);
memcpy(req->info_hash, tor->info.hash, SHA_DIGEST_LENGTH);
req->peer_id = tr_torrentGetPeerId(tor);
@ -1035,8 +1004,9 @@ static void on_announce_error(tr_tier* tier, char const* err, tr_announce_event
/* schedule a reannounce */
int const interval = getRetryInterval(tier->currentTracker);
dbgmsg(tier, "Announce error: %s (Retrying in %d seconds)", err, interval);
tr_logAddTorInfo(tier->tor, "Announce error: %s (Retrying in %d seconds)", err, interval);
auto const* const key_cstr = tr_quark_get_string(tier->currentTracker->key);
dbgmsg(tier, "Tracker '%s' announce error: %s (Retrying in %d seconds)", key_cstr, err, interval);
tr_logAddTorInfo(tier->tor, "Tracker '%s' announce error: %s (Retrying in %d seconds)", key_cstr, err, interval);
tier_announce_event_push(tier, e, tr_time() + interval);
}
@ -1227,7 +1197,6 @@ static void on_announce_done(tr_announce_response const* response, void* vdata)
static void announce_request_free(tr_announce_request* req)
{
tr_free(req->tracker_id_str);
tr_free(req->url);
tr_free(req);
}
@ -1249,17 +1218,18 @@ static void announce_request_delegate(
#endif
if (strncmp(request->url, "http", 4) == 0)
auto const announce_sv = tr_quark_get_string_view(request->announce_url);
if (announce_sv.find("http://"sv) == 0 || announce_sv.find("https://"sv) == 0)
{
tr_tracker_http_announce(session, request, callback, callback_data);
}
else if (strncmp(request->url, "udp://", 6) == 0)
else if (announce_sv.find("udp://"sv) == 0)
{
tr_tracker_udp_announce(session, request, callback, callback_data);
}
else
{
tr_logAddError("Unsupported url: %s", request->url);
tr_logAddError("Unsupported url: %" TR_PRIsv, TR_PRIsv_ARG(announce_sv));
}
announce_request_free(request);
@ -1332,13 +1302,14 @@ static void on_scrape_error(tr_session const* session, tr_tier* tier, char const
/* schedule a rescrape */
int const interval = getRetryInterval(tier->currentTracker);
dbgmsg(tier, "Scrape error: %s (Retrying in %zu seconds)", errmsg, (size_t)interval);
tr_logAddTorInfo(tier->tor, "Scrape error: %s (Retrying in %zu seconds)", errmsg, (size_t)interval);
auto const* const key_cstr = tr_quark_get_string(tier->currentTracker->key);
dbgmsg(tier, "Tracker '%s' scrape error: %s (Retrying in %zu seconds)", key_cstr, errmsg, (size_t)interval);
tr_logAddTorInfo(tier->tor, "Tracker '%s' error: %s (Retrying in %zu seconds)", key_cstr, errmsg, (size_t)interval);
tier->lastScrapeSucceeded = false;
tier->scrapeAt = get_next_scrape_time(session, tier, interval);
}
static tr_tier* find_tier(tr_torrent* tor, std::string const& scrape)
static tr_tier* find_tier(tr_torrent* tor, tr_quark scrape_url)
{
struct tr_torrent_tiers* tt = tor->tiers;
@ -1346,7 +1317,7 @@ static tr_tier* find_tier(tr_torrent* tor, std::string const& scrape)
{
tr_tracker const* const tracker = tt->tiers[i].currentTracker;
if (tracker != nullptr && tracker->scrape_info != nullptr && tracker->scrape_info->url == scrape)
if (tracker != nullptr && tracker->scrape_info != nullptr && tracker->scrape_info->scrape_url == scrape_url)
{
return &tt->tiers[i];
}
@ -1355,6 +1326,42 @@ static tr_tier* find_tier(tr_torrent* tor, std::string const& scrape)
return nullptr;
}
static void checkMultiscrapeMax(tr_announcer* announcer, tr_scrape_response const* response)
{
if (!multiscrape_too_big(response->errmsg))
{
return;
}
auto const& url = response->scrape_url;
struct tr_scrape_info* const scrape_info = tr_announcerGetScrapeInfo(announcer, url);
if (scrape_info == nullptr)
{
return;
}
// Lower the max only if it hasn't already lowered for a similar
// error. So if N parallel multiscrapes all have the same `max`
// and error out, lower the value once for that batch, not N times.
int& multiscrape_max = scrape_info->multiscrape_max;
if (multiscrape_max < response->row_count)
{
return;
}
int const n = std::max(1, int{ multiscrape_max - TrMultiscrapeStep });
if (multiscrape_max != n)
{
// don't log the full URL, since that might have a personal announce id
// (note: we know 'parsed' will be successful since this url has a scrape_info)
auto const parsed = *tr_urlParse(tr_quark_get_string_view(url));
auto clean_url = std::string{};
tr_buildBuf(clean_url, parsed.scheme, "://"sv, parsed.host, ":"sv, parsed.portstr);
tr_logAddNamedInfo(clean_url.c_str(), "Reducing multiscrape max to %d", n);
multiscrape_max = n;
}
}
static void on_scrape_done(tr_scrape_response const* response, void* vsession)
{
time_t const now = tr_time();
@ -1368,13 +1375,16 @@ static void on_scrape_done(tr_scrape_response const* response, void* vsession)
if (tor != nullptr)
{
tr_tier* tier = find_tier(tor, response->url);
tr_tier* tier = find_tier(tor, response->scrape_url);
if (tier != nullptr)
{
auto const scrape_url_sv = tr_quark_get_string_view(response->scrape_url);
dbgmsg(
tier,
"scraped url:%s -- "
"scraped url:%" TR_PRIsv
" -- "
"did_connect:%d "
"did_timeout:%d "
"seeders:%d "
@ -1383,7 +1393,7 @@ static void on_scrape_done(tr_scrape_response const* response, void* vsession)
"downloaders:%d "
"min_request_interval:%d "
"err:%s ",
response->url.c_str(),
TR_PRIsv_ARG(scrape_url_sv),
(int)response->did_connect,
(int)response->did_timeout,
row->seeders,
@ -1448,41 +1458,7 @@ static void on_scrape_done(tr_scrape_response const* response, void* vsession)
}
}
/* Maybe reduce the number of torrents in a multiscrape req */
if (multiscrape_too_big(response->errmsg))
{
auto const& url = response->url;
struct tr_scrape_info* const scrape_info = tr_announcerGetScrapeInfo(announcer, url);
if (scrape_info != nullptr)
{
int* multiscrape_max = &scrape_info->multiscrape_max;
/* Lower the max only if it hasn't already lowered for a similar error.
For example if N parallel multiscrapes all have the same `max` and
error out, lower the value once for that batch, not N times. */
if (*multiscrape_max >= response->row_count)
{
int const n = std::max(1, int{ *multiscrape_max - TrMultiscrapeStep });
if (*multiscrape_max != n)
{
char* scheme = nullptr;
char* host = nullptr;
auto port = int{};
if (tr_urlParse(std::data(url), std::size(url), &scheme, &host, &port, nullptr))
{
/* don't log the full URL, since that might have a personal announce id */
char* sanitized_url = tr_strdup_printf("%s://%s:%d", scheme, host, port);
tr_logAddNamedInfo(sanitized_url, "Reducing multiscrape max to %d", n);
tr_free(sanitized_url);
tr_free(host);
tr_free(scheme);
}
*multiscrape_max = n;
}
}
}
}
checkMultiscrapeMax(announcer, response);
}
static void scrape_request_delegate(
@ -1493,17 +1469,19 @@ static void scrape_request_delegate(
{
tr_session* session = announcer->session;
if (strncmp(request->url, "http", 4) == 0)
auto const scrape_sv = tr_quark_get_string_view(request->scrape_url);
if (scrape_sv.find("http://"sv) == 0 || scrape_sv.find("https://"sv) == 0)
{
tr_tracker_http_scrape(session, request, callback, callback_data);
}
else if (strncmp(request->url, "udp://", 6) == 0)
else if (scrape_sv.find("udp://"sv) == 0)
{
tr_tracker_udp_scrape(session, request, callback, callback_data);
}
else
{
tr_logAddError("Unsupported url: %s", request->url);
tr_logAddError("Unsupported url: %" TR_PRIsv, TR_PRIsv_ARG(scrape_sv));
}
}
@ -1532,7 +1510,7 @@ static void multiscrape(tr_announcer* announcer, std::vector<tr_tier*> const& ti
continue;
}
if (scrape_info->url != req->url)
if (scrape_info->scrape_url != req->scrape_url)
{
continue;
}
@ -1547,7 +1525,7 @@ static void multiscrape(tr_announcer* announcer, std::vector<tr_tier*> const& ti
if (!found && request_count < MaxScrapesPerUpkeep)
{
tr_scrape_request* req = &requests[request_count++];
req->url = scrape_info->url.c_str();
req->scrape_url = scrape_info->scrape_url;
tier_build_log_name(tier, req->log_name, sizeof(req->log_name));
memcpy(req->info_hash[req->info_hash_count++], hash, SHA_DIGEST_LENGTH);
@ -1748,21 +1726,12 @@ tr_tracker_stat* tr_announcerStats(tr_torrent const* torrent, int* setmeTrackerC
tr_tracker_stat* st = &ret[out++];
st->id = tracker->id;
tr_strlcpy(st->host, tracker->key, sizeof(st->host));
tr_strlcpy(st->announce, tracker->announce, sizeof(st->announce));
st->host = tr_quark_get_string(tracker->key);
st->announce = tr_quark_get_string(tracker->announce_url);
st->tier = i;
st->isBackup = tracker != tier->currentTracker;
st->lastScrapeStartTime = tier->lastScrapeStartTime;
if (tracker->scrape_info != nullptr)
{
tr_strlcpy(st->scrape, tracker->scrape_info->url.c_str(), sizeof(st->scrape));
}
else
{
st->scrape[0] = '\0';
}
st->scrape = tracker->scrape_info == nullptr ? "" : tr_quark_get_string(tracker->scrape_info->scrape_url);
st->seederCount = tracker->seederCount;
st->leecherCount = tracker->leecherCount;
st->downloadCount = tracker->downloadCount;
@ -1850,7 +1819,7 @@ static void copy_tier_attributes_impl(struct tr_tier* tgt, int trackerIndex, tr_
{
/* sanity clause */
TR_ASSERT(trackerIndex < tgt->tracker_count);
TR_ASSERT(tr_strcmp0(tgt->trackers[trackerIndex].announce, src->currentTracker->announce) == 0);
TR_ASSERT(tgt->trackers[trackerIndex].announce_url == src->currentTracker->announce_url);
tr_tier const keep = *tgt;
@ -1883,7 +1852,7 @@ static void copy_tier_attributes(struct tr_torrent_tiers* tt, tr_tier const* src
{
for (int j = 0; !found && j < tt->tiers[i].tracker_count; ++j)
{
if (tr_strcmp0(src->currentTracker->announce, tt->tiers[i].trackers[j].announce) == 0)
if (src->currentTracker->announce_url == tt->tiers[i].trackers[j].announce_url)
{
found = true;
copy_tier_attributes_impl(&tt->tiers[i], j, src);

View File

@ -13,6 +13,7 @@
#endif
#include "transmission.h"
#include "quark.h"
struct tr_announcer;
struct tr_torrent_tiers;
@ -40,7 +41,7 @@ struct tr_tracker_event
/* for TR_TRACKER_WARNING and TR_TRACKER_ERROR */
char const* text;
char const* tracker;
tr_quark announce_url;
/* for TR_TRACKER_PEERS */
struct tr_pex const* pex;

View File

@ -507,7 +507,7 @@ void tr_torrentSetLocalError(tr_torrent* tor, char const* fmt, ...)
va_start(ap, fmt);
tor->error = TR_STAT_LOCAL_ERROR;
tor->errorTracker[0] = '\0';
tor->error_announce_url = TR_KEY_NONE;
evutil_vsnprintf(tor->errorString, sizeof(tor->errorString), fmt, ap);
va_end(ap);
@ -522,8 +522,8 @@ void tr_torrentSetLocalError(tr_torrent* tor, char const* fmt, ...)
static constexpr void tr_torrentClearError(tr_torrent* tor)
{
tor->error = TR_STAT_OK;
tor->error_announce_url = TR_KEY_NONE;
tor->errorString[0] = '\0';
tor->errorTracker[0] = '\0';
}
static void onTrackerResponse(tr_torrent* tor, tr_tracker_event const* event, void* /*user_data*/)
@ -546,13 +546,13 @@ static void onTrackerResponse(tr_torrent* tor, tr_tracker_event const* event, vo
case TR_TRACKER_WARNING:
tr_logAddTorErr(tor, _("Tracker warning: \"%s\""), event->text);
tor->error = TR_STAT_TRACKER_WARNING;
tr_strlcpy(tor->errorTracker, event->tracker, sizeof(tor->errorTracker));
tor->error_announce_url = event->announce_url;
tr_strlcpy(tor->errorString, event->text, sizeof(tor->errorString));
break;
case TR_TRACKER_ERROR:
tor->error = TR_STAT_TRACKER_ERROR;
tr_strlcpy(tor->errorTracker, event->tracker, sizeof(tor->errorTracker));
tor->error_announce_url = event->announce_url;
tr_strlcpy(tor->errorString, event->text, sizeof(tor->errorString));
break;
@ -2689,7 +2689,7 @@ bool tr_torrentSetAnnounceList(tr_torrent* tor, tr_tracker_info const* trackers_
for (int i = 0; clear && i < trackerCount; ++i)
{
if (strcmp(trackers[i].announce, tor->errorTracker) == 0)
if (strcmp(trackers[i].announce, tr_quark_get_string(tor->error_announce_url)) == 0)
{
clear = false;
}

View File

@ -23,6 +23,7 @@
#include "bitfield.h"
#include "completion.h" /* tr_completion */
#include "file.h"
#include "quark.h"
#include "session.h" /* tr_sessionLock(), tr_sessionUnlock() */
#include "tr-assert.h"
#include "tr-macros.h"
@ -140,7 +141,7 @@ struct tr_torrent
tr_stat_errtype error;
char errorString[128];
char errorTracker[128];
tr_quark error_announce_url;
/// DND

View File

@ -1438,14 +1438,15 @@ struct tr_tracker_stat
/* whether or not we've ever scraped to this tracker */
bool hasScraped;
/* human-readable string identifying the tracker */
char host[1024];
/* human-readable string identifying the tracker.
* 'host' is a slight misnomer; the current format ist `$host:$port` */
char const* host;
/* the full announce URL */
char announce[1024];
char const* announce;
/* the full scrape URL */
char scrape[1024];
char const* scrape;
/* Transmission uses one tracker per tier,
* and the others are kept as backups */

View File

@ -876,6 +876,8 @@ static bool urlCharsAreValid(std::string_view url)
std::optional<tr_parsed_url_t> tr_urlParse(std::string_view url)
{
url = tr_strvstrip(url);
if (!urlCharsAreValid(url))
{
return {};
@ -918,11 +920,21 @@ std::optional<tr_parsed_url_t> tr_urlParse(std::string_view url)
return tr_parsed_url_t{ scheme, host, path, portstr, port };
}
bool tr_urlIsValidTracker(std::string_view url)
static bool tr_isValidTrackerScheme(std::string_view scheme)
{
auto constexpr Schemes = std::array<std::string_view, 3>{ "http"sv, "https"sv, "udp"sv };
return std::find(std::begin(Schemes), std::end(Schemes), scheme) != std::end(Schemes);
}
std::optional<tr_parsed_url_t> tr_urlParseTracker(std::string_view url)
{
auto const parsed = tr_urlParse(url);
return parsed && std::find(std::begin(Schemes), std::end(Schemes), parsed->scheme) != std::end(Schemes);
return parsed && tr_isValidTrackerScheme(parsed->scheme) ? *parsed : std::optional<tr_parsed_url_t>{};
}
bool tr_urlIsValidTracker(std::string_view url)
{
return !!tr_urlParseTracker(url);
}
bool tr_urlIsValid(std::string_view url)

View File

@ -193,11 +193,12 @@ void tr_free_ptrv(void* const* p);
*/
void* tr_memdup(void const* src, size_t byteCount);
#define tr_new(struct_type, n_structs) ((struct_type*)tr_malloc(sizeof(struct_type) * (size_t)(n_structs)))
#define tr_new(struct_type, n_structs) (static_cast<struct_type*>(tr_malloc(sizeof(struct_type) * (size_t)(n_structs))))
#define tr_new0(struct_type, n_structs) ((struct_type*)tr_malloc0(sizeof(struct_type) * (size_t)(n_structs)))
#define tr_new0(struct_type, n_structs) (static_cast<struct_type*>(tr_malloc0(sizeof(struct_type) * (size_t)(n_structs))))
#define tr_renew(struct_type, mem, n_structs) ((struct_type*)tr_realloc((mem), sizeof(struct_type) * (size_t)(n_structs)))
#define tr_renew(struct_type, mem, n_structs) \
(static_cast<struct_type*>(tr_realloc((mem), sizeof(struct_type) * (size_t)(n_structs))))
/**
* @brief make a newly-allocated copy of a substring
@ -286,6 +287,7 @@ bool tr_urlIsValidTracker(std::string_view url);
/** @brief return true if the url is a [ http, https, ftp, sftp ] url that Transmission understands */
bool tr_urlIsValid(std::string_view url);
// TODO: move this to types.h
struct tr_parsed_url_t
{
std::string_view scheme;
@ -297,6 +299,10 @@ struct tr_parsed_url_t
std::optional<tr_parsed_url_t> tr_urlParse(std::string_view url);
// like tr_urlParse(), but with the added constraint that 'scheme'
// must be one we that Transmission supports for announce and scrape
std::optional<tr_parsed_url_t> tr_urlParseTracker(std::string_view url);
/** @brief parse a URL into its component parts
@return True on success or false if an error occurred */
bool tr_urlParse(char const* url, size_t url_len, char** setme_scheme, char** setme_host, int* setme_port, char** setme_path)