perf: intern the announcer module's URL strings (#2085)
* perf: intern the announcer module's internal URLs
This commit is contained in:
parent
790b0bb2b5
commit
9dca5f2086
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue