2023-11-01 21:11:11 +00:00
|
|
|
// This file Copyright © Mnemosyne LLC.
|
2022-02-07 16:25:02 +00:00
|
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
2022-01-20 18:27:56 +00:00
|
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
|
|
// License text can be found in the licenses/ folder.
|
2008-06-07 21:26:41 +00:00
|
|
|
|
2021-09-19 20:41:35 +00:00
|
|
|
#include <algorithm>
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <cstdint> // uint64_t, uint32_t
|
|
|
|
#include <ctime>
|
2022-03-28 22:13:32 +00:00
|
|
|
#include <iterator>
|
2022-06-20 04:08:58 +00:00
|
|
|
#include <memory>
|
2022-06-29 01:48:39 +00:00
|
|
|
#include <numeric> // std::accumulate()
|
2021-09-27 19:28:11 +00:00
|
|
|
#include <set>
|
2022-01-13 02:13:58 +00:00
|
|
|
#include <string>
|
2021-12-15 21:25:42 +00:00
|
|
|
#include <string_view>
|
2022-08-17 16:08:36 +00:00
|
|
|
#include <utility>
|
2008-06-07 21:26:41 +00:00
|
|
|
|
2023-07-08 15:24:03 +00:00
|
|
|
#include <event2/buffer.h>
|
|
|
|
|
2023-04-16 20:34:19 +00:00
|
|
|
#include <fmt/core.h>
|
2022-03-30 19:59:13 +00:00
|
|
|
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/transmission.h"
|
|
|
|
|
|
|
|
#include "libtransmission/bandwidth.h"
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/bitfield.h"
|
|
|
|
#include "libtransmission/block-info.h"
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/cache.h"
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/peer-common.h"
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/peer-mgr.h"
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/session.h"
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/timer.h"
|
|
|
|
#include "libtransmission/torrent.h"
|
2023-07-08 15:24:03 +00:00
|
|
|
#include "libtransmission/tr-assert.h"
|
|
|
|
#include "libtransmission/tr-macros.h"
|
2023-11-03 17:03:26 +00:00
|
|
|
#include "libtransmission/tr-strbuf.h"
|
2023-04-14 19:33:23 +00:00
|
|
|
#include "libtransmission/utils-ev.h"
|
|
|
|
#include "libtransmission/utils.h"
|
|
|
|
#include "libtransmission/web-utils.h"
|
|
|
|
#include "libtransmission/web.h"
|
|
|
|
#include "libtransmission/webseed.h"
|
2008-06-07 21:26:41 +00:00
|
|
|
|
2023-11-03 17:03:26 +00:00
|
|
|
struct evbuffer;
|
|
|
|
|
2022-02-12 22:52:40 +00:00
|
|
|
using namespace std::literals;
|
2023-11-10 23:12:24 +00:00
|
|
|
using namespace libtransmission::Values;
|
2022-02-12 22:52:40 +00:00
|
|
|
|
2021-10-07 13:33:55 +00:00
|
|
|
namespace
|
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
class tr_webseed_impl;
|
2022-02-14 02:09:56 +00:00
|
|
|
|
|
|
|
class tr_webseed_task
|
2008-06-07 21:26:41 +00:00
|
|
|
{
|
2022-02-14 02:09:56 +00:00
|
|
|
public:
|
2024-04-02 14:31:20 +00:00
|
|
|
tr_webseed_task(tr_torrent const& tor, tr_webseed_impl* webseed_in, tr_block_span_t blocks_in)
|
|
|
|
: blocks{ blocks_in }
|
|
|
|
, webseed_{ webseed_in }
|
|
|
|
, session_{ tor.session }
|
|
|
|
, end_byte_{ tor.block_loc(blocks.end - 1).byte + tor.block_size(blocks.end - 1) }
|
|
|
|
, loc_{ tor.block_loc(blocks.begin) }
|
2022-02-14 02:09:56 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
evbuffer_add_cb(content_.get(), on_buffer_got_data, this);
|
2022-02-14 02:09:56 +00:00
|
|
|
}
|
2021-10-07 13:33:55 +00:00
|
|
|
|
2022-02-14 02:09:56 +00:00
|
|
|
[[nodiscard]] auto* content() const
|
|
|
|
{
|
|
|
|
return content_.get();
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void request_next_chunk();
|
|
|
|
|
|
|
|
bool dead = false;
|
2022-02-23 13:38:18 +00:00
|
|
|
tr_block_span_t const blocks;
|
2024-04-02 14:31:20 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
void use_fetched_blocks();
|
|
|
|
|
|
|
|
static void on_partial_data_fetched(tr_web::FetchResponse const& web_response);
|
|
|
|
static void on_buffer_got_data(evbuffer* /*buf*/, evbuffer_cb_info const* info, void* vtask);
|
|
|
|
|
|
|
|
tr_webseed_impl* const webseed_;
|
|
|
|
tr_session* const session_;
|
|
|
|
uint64_t const end_byte_;
|
2022-02-23 13:38:18 +00:00
|
|
|
|
|
|
|
// the current position in the task; i.e., the next block to save
|
2024-04-02 14:31:20 +00:00
|
|
|
tr_block_info::Location loc_;
|
2022-02-14 02:09:56 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
libtransmission::evhelpers::evbuffer_unique_ptr const content_{ evbuffer_new() };
|
2022-02-14 02:09:56 +00:00
|
|
|
};
|
2021-10-07 13:33:55 +00:00
|
|
|
|
2022-02-12 22:52:40 +00:00
|
|
|
/**
|
|
|
|
* Manages how many web tasks should be running at a time.
|
|
|
|
*
|
|
|
|
* - When all is well, allow multiple tasks running in parallel.
|
|
|
|
* - If we get an error, throttle down to only one at a time
|
|
|
|
* until we get piece data.
|
|
|
|
* - If we have too many errors in a row, put the peer in timeout
|
|
|
|
* and don't allow _any_ connections for awhile.
|
|
|
|
*/
|
|
|
|
class ConnectionLimiter
|
|
|
|
{
|
|
|
|
public:
|
2024-04-02 14:31:20 +00:00
|
|
|
constexpr void task_started() noexcept
|
2022-02-12 22:52:40 +00:00
|
|
|
{
|
|
|
|
++n_tasks;
|
|
|
|
}
|
2021-10-07 13:33:55 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void task_finished(bool success)
|
2022-02-12 22:52:40 +00:00
|
|
|
{
|
|
|
|
if (!success)
|
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
task_failed();
|
2022-02-12 22:52:40 +00:00
|
|
|
}
|
2021-10-07 13:33:55 +00:00
|
|
|
|
2022-02-12 22:52:40 +00:00
|
|
|
TR_ASSERT(n_tasks > 0);
|
|
|
|
--n_tasks;
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
constexpr void got_data() noexcept
|
2022-02-12 22:52:40 +00:00
|
|
|
{
|
|
|
|
TR_ASSERT(n_tasks > 0);
|
|
|
|
n_consecutive_failures = 0;
|
|
|
|
paused_until = 0;
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
[[nodiscard]] size_t slots_available() const noexcept
|
2022-02-12 22:52:40 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
if (is_paused())
|
2022-02-12 22:52:40 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const max = max_connections();
|
2022-02-12 22:52:40 +00:00
|
|
|
if (n_tasks >= max)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return max - n_tasks;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2024-04-02 14:31:20 +00:00
|
|
|
[[nodiscard]] bool is_paused() const noexcept
|
2022-02-12 22:52:40 +00:00
|
|
|
{
|
|
|
|
return paused_until > tr_time();
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
[[nodiscard]] constexpr size_t max_connections() const noexcept
|
2022-02-12 22:52:40 +00:00
|
|
|
{
|
|
|
|
return n_consecutive_failures > 0 ? 1 : MaxConnections;
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void task_failed()
|
2022-02-12 22:52:40 +00:00
|
|
|
{
|
|
|
|
TR_ASSERT(n_tasks > 0);
|
|
|
|
|
|
|
|
if (++n_consecutive_failures >= MaxConsecutiveFailures)
|
|
|
|
{
|
|
|
|
paused_until = tr_time() + TimeoutIntervalSecs;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
static auto constexpr TimeoutIntervalSecs = time_t{ 120 };
|
|
|
|
static auto constexpr MaxConnections = size_t{ 4 };
|
|
|
|
static auto constexpr MaxConsecutiveFailures = MaxConnections;
|
2022-02-12 22:52:40 +00:00
|
|
|
|
|
|
|
size_t n_tasks = 0;
|
|
|
|
size_t n_consecutive_failures = 0;
|
|
|
|
time_t paused_until = 0;
|
|
|
|
};
|
2021-10-07 13:33:55 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
class tr_webseed_impl final : public tr_webseed
|
2011-01-06 01:00:21 +00:00
|
|
|
{
|
2021-10-07 13:33:55 +00:00
|
|
|
public:
|
2024-03-16 00:52:09 +00:00
|
|
|
struct RequestLimit
|
|
|
|
{
|
|
|
|
// How many spans those blocks could be in.
|
|
|
|
// This is for webseeds, which make parallel requests.
|
|
|
|
size_t max_spans = 0;
|
|
|
|
|
|
|
|
// How many blocks we could request.
|
|
|
|
size_t max_blocks = 0;
|
|
|
|
};
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
tr_webseed_impl(tr_torrent& tor_in, std::string_view url, tr_peer_callback_webseed callback_in, void* callback_data_in)
|
|
|
|
: tr_webseed{ tor_in }
|
|
|
|
, tor{ tor_in }
|
2021-10-07 13:33:55 +00:00
|
|
|
, base_url{ url }
|
2024-04-02 14:31:20 +00:00
|
|
|
, idle_timer_{ session->timerMaker().create([this]() { on_idle(); }) }
|
|
|
|
, have_{ tor_in.piece_count() }
|
|
|
|
, bandwidth_{ &tor_in.bandwidth() }
|
|
|
|
, callback_{ callback_in }
|
|
|
|
, callback_data_{ callback_data_in }
|
2021-10-07 13:33:55 +00:00
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
have_.set_has_all();
|
2023-05-06 04:11:05 +00:00
|
|
|
idle_timer_->start_repeating(IdleTimerInterval);
|
2021-10-07 13:33:55 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
tr_webseed_impl(tr_webseed_impl&&) = delete;
|
|
|
|
tr_webseed_impl(tr_webseed_impl const&) = delete;
|
|
|
|
tr_webseed_impl& operator=(tr_webseed_impl&&) = delete;
|
|
|
|
tr_webseed_impl& operator=(tr_webseed_impl const&) = delete;
|
2022-08-03 17:03:28 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
~tr_webseed_impl() override
|
2021-10-07 13:33:55 +00:00
|
|
|
{
|
|
|
|
// flag all the pending tasks as dead
|
|
|
|
std::for_each(std::begin(tasks), std::end(tasks), [](auto* task) { task->dead = true; });
|
|
|
|
tasks.clear();
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
[[nodiscard]] Speed get_piece_speed(uint64_t now, tr_direction dir) const override
|
2022-02-23 13:38:18 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
return dir == TR_DOWN ? bandwidth_.get_piece_speed(now, dir) : Speed{};
|
2022-02-23 13:38:18 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
[[nodiscard]] tr_webseed_view get_view() const override
|
2021-10-07 13:33:55 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const is_downloading = !std::empty(tasks);
|
|
|
|
auto const speed = get_piece_speed(tr_time_msec(), TR_DOWN);
|
|
|
|
return { base_url.c_str(), is_downloading, speed.base_quantity() };
|
2021-10-07 13:33:55 +00:00
|
|
|
}
|
|
|
|
|
2024-03-16 00:52:09 +00:00
|
|
|
[[nodiscard]] TR_CONSTEXPR20 size_t active_req_count(tr_direction dir) const noexcept override
|
2022-06-29 01:48:39 +00:00
|
|
|
{
|
|
|
|
if (dir == TR_CLIENT_TO_PEER) // blocks we've requested
|
|
|
|
{
|
|
|
|
return std::accumulate(
|
|
|
|
std::begin(tasks),
|
|
|
|
std::end(tasks),
|
|
|
|
size_t{},
|
|
|
|
[](size_t sum, auto const* task) { return sum + (task->blocks.end - task->blocks.begin); });
|
|
|
|
}
|
|
|
|
|
|
|
|
// webseed will never request blocks from us
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2022-12-08 22:44:19 +00:00
|
|
|
[[nodiscard]] std::string display_name() const override
|
2022-06-02 02:33:33 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
if (auto const parsed = tr_urlParse(base_url))
|
2022-06-02 02:33:33 +00:00
|
|
|
{
|
2023-11-21 15:02:03 +00:00
|
|
|
return fmt::format("{:s}:{:d}", parsed->host, parsed->port);
|
2022-06-02 02:33:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return base_url;
|
|
|
|
}
|
|
|
|
|
2022-11-23 05:26:10 +00:00
|
|
|
[[nodiscard]] tr_bitfield const& has() const noexcept override
|
2022-06-27 19:12:31 +00:00
|
|
|
{
|
2022-11-23 05:26:10 +00:00
|
|
|
return have_;
|
2022-06-27 19:12:31 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void got_piece_data(uint32_t n_bytes)
|
2022-02-23 13:38:18 +00:00
|
|
|
{
|
2023-04-23 01:25:55 +00:00
|
|
|
bandwidth_.notify_bandwidth_consumed(TR_DOWN, n_bytes, true, tr_time_msec());
|
2022-09-01 21:37:11 +00:00
|
|
|
publish(tr_peer_event::GotPieceData(n_bytes));
|
2024-04-02 14:31:20 +00:00
|
|
|
connection_limiter.got_data();
|
2022-02-23 13:38:18 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void publish_rejection(tr_block_span_t block_span)
|
2022-02-23 13:38:18 +00:00
|
|
|
{
|
|
|
|
for (auto block = block_span.begin; block < block_span.end; ++block)
|
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
publish(tr_peer_event::GotRejected(tor.block_info(), block));
|
2022-02-23 13:38:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-03-16 00:52:09 +00:00
|
|
|
void request_blocks(tr_block_span_t const* block_spans, size_t n_spans) override
|
2022-07-02 19:42:16 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
if (!tor.is_running() || tor.is_done())
|
2022-07-02 19:42:16 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto const *span = block_spans, *end = span + n_spans; span != end; ++span)
|
|
|
|
{
|
|
|
|
auto* const task = new tr_webseed_task{ tor, this, *span };
|
|
|
|
tasks.insert(task);
|
2024-04-02 14:31:20 +00:00
|
|
|
task->request_next_chunk();
|
2022-07-02 19:42:16 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
tr_peerMgrClientSentRequests(&tor, this, *span);
|
2022-07-02 19:42:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void on_idle()
|
|
|
|
{
|
|
|
|
auto const [max_spans, max_blocks] = max_available_reqs();
|
|
|
|
if (max_spans == 0 || max_blocks == 0)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prefer to request large, contiguous chunks from webseeds.
|
|
|
|
// The actual value of '64' is arbitrary here; we could probably
|
|
|
|
// be smarter about this.
|
|
|
|
auto spans = tr_peerMgrGetNextRequests(&tor, this, max_blocks);
|
|
|
|
if (std::size(spans) > max_spans)
|
|
|
|
{
|
|
|
|
spans.resize(max_spans);
|
|
|
|
}
|
|
|
|
request_blocks(std::data(spans), std::size(spans));
|
|
|
|
}
|
|
|
|
|
2024-03-16 00:52:09 +00:00
|
|
|
[[nodiscard]] RequestLimit max_available_reqs() const noexcept
|
2022-07-04 18:03:32 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const n_slots = connection_limiter.slots_available();
|
2022-07-04 18:03:32 +00:00
|
|
|
if (n_slots == 0)
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
if (!tor.is_running() || tor.is_done())
|
2022-07-04 18:03:32 +00:00
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
// Prefer to request large, contiguous chunks from webseeds.
|
|
|
|
// The actual value of '64' is arbitrary here;
|
|
|
|
// we could probably be smarter about this.
|
2024-03-16 00:52:09 +00:00
|
|
|
static auto constexpr PreferredBlocksPerTask = size_t{ 64 };
|
2022-07-04 18:03:32 +00:00
|
|
|
return { n_slots, n_slots * PreferredBlocksPerTask };
|
|
|
|
}
|
|
|
|
|
2022-09-01 21:37:11 +00:00
|
|
|
void publish(tr_peer_event const& peer_event)
|
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
if (callback_ != nullptr)
|
2022-09-01 21:37:11 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
(*callback_)(this, peer_event, callback_data_);
|
2022-09-01 21:37:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
tr_torrent& tor;
|
2021-10-07 13:33:55 +00:00
|
|
|
std::string const base_url;
|
|
|
|
|
2022-02-12 22:52:40 +00:00
|
|
|
ConnectionLimiter connection_limiter;
|
2021-09-27 19:28:11 +00:00
|
|
|
std::set<tr_webseed_task*> tasks;
|
2011-07-10 15:24:51 +00:00
|
|
|
|
2022-02-14 02:09:56 +00:00
|
|
|
private:
|
2022-08-11 17:28:37 +00:00
|
|
|
static auto constexpr IdleTimerInterval = 2s;
|
2022-11-23 05:26:10 +00:00
|
|
|
|
|
|
|
std::unique_ptr<libtransmission::Timer> const idle_timer_;
|
|
|
|
|
|
|
|
tr_bitfield have_;
|
|
|
|
|
|
|
|
tr_bandwidth bandwidth_;
|
2024-04-02 14:31:20 +00:00
|
|
|
|
|
|
|
tr_peer_callback_webseed const callback_;
|
|
|
|
void* const callback_data_;
|
2022-02-14 02:09:56 +00:00
|
|
|
};
|
2021-12-07 18:11:28 +00:00
|
|
|
|
2023-01-22 19:21:30 +00:00
|
|
|
// ---
|
2008-06-07 21:26:41 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void tr_webseed_task::use_fetched_blocks()
|
2011-07-10 15:24:51 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const lock = session_->unique_lock();
|
2011-07-10 15:24:51 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const& tor = webseed_->tor;
|
2011-07-10 15:24:51 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
for (auto* const buf = content();;)
|
2022-02-23 13:38:18 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const block_size = tor.block_size(loc_.block);
|
2022-02-23 13:38:18 +00:00
|
|
|
if (evbuffer_get_length(buf) < block_size)
|
2011-07-10 15:24:51 +00:00
|
|
|
{
|
2022-02-23 13:38:18 +00:00
|
|
|
break;
|
2015-07-05 07:54:46 +00:00
|
|
|
}
|
2011-07-10 15:24:51 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
if (tor.has_block(loc_.block))
|
2022-02-23 13:38:18 +00:00
|
|
|
{
|
|
|
|
evbuffer_drain(buf, block_size);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
auto block_buf = new Cache::BlockData(block_size);
|
|
|
|
evbuffer_remove(buf, std::data(*block_buf), std::size(*block_buf));
|
|
|
|
session_->run_in_session_thread(
|
|
|
|
[session = session_, tor_id = tor.id(), block = loc_.block, block_buf, webseed = webseed_]()
|
|
|
|
{
|
|
|
|
auto data = std::unique_ptr<Cache::BlockData>{ block_buf };
|
|
|
|
if (auto const* const torrent = tr_torrentFindFromId(session, tor_id); torrent != nullptr)
|
|
|
|
{
|
|
|
|
session->cache->write_block(tor_id, block, std::move(data));
|
|
|
|
webseed->publish(tr_peer_event::GotBlock(torrent->block_info(), block));
|
|
|
|
}
|
|
|
|
});
|
2022-02-23 13:38:18 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
loc_ = tor.byte_loc(loc_.byte + block_size);
|
2022-02-23 13:38:18 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
TR_ASSERT(loc_.byte <= end_byte_);
|
|
|
|
TR_ASSERT(loc_.byte == end_byte_ || loc_.block_offset == 0);
|
2022-02-23 13:38:18 +00:00
|
|
|
}
|
2011-07-10 15:24:51 +00:00
|
|
|
}
|
|
|
|
|
2023-01-22 19:21:30 +00:00
|
|
|
// ---
|
2011-09-12 21:46:15 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void tr_webseed_task::on_buffer_got_data(evbuffer* /*buf*/, evbuffer_cb_info const* info, void* vtask)
|
2008-06-07 21:26:41 +00:00
|
|
|
{
|
2017-04-20 16:02:19 +00:00
|
|
|
size_t const n_added = info->n_added;
|
2022-02-14 02:09:56 +00:00
|
|
|
auto* const task = static_cast<tr_webseed_task*>(vtask);
|
2022-02-23 13:38:18 +00:00
|
|
|
if (n_added == 0 || task->dead)
|
2011-01-06 01:00:21 +00:00
|
|
|
{
|
2022-02-23 13:38:18 +00:00
|
|
|
return;
|
|
|
|
}
|
2017-04-19 12:04:45 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const lock = task->session_->unique_lock();
|
|
|
|
task->webseed_->got_piece_data(n_added);
|
2008-06-07 21:26:41 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void tr_webseed_task::on_partial_data_fetched(tr_web::FetchResponse const& web_response)
|
2008-06-07 21:26:41 +00:00
|
|
|
{
|
2023-11-23 05:02:21 +00:00
|
|
|
auto const& [status, body, primary_ip, did_connect, did_timeout, vtask] = web_response;
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const success = status == 206;
|
2022-02-16 18:33:50 +00:00
|
|
|
|
2022-02-23 13:38:18 +00:00
|
|
|
auto* const task = static_cast<tr_webseed_task*>(vtask);
|
2013-02-04 16:23:33 +00:00
|
|
|
|
2022-02-23 13:38:18 +00:00
|
|
|
if (task->dead)
|
2013-02-04 16:23:33 +00:00
|
|
|
{
|
2022-02-23 13:38:18 +00:00
|
|
|
delete task;
|
2017-04-19 12:04:45 +00:00
|
|
|
return;
|
2013-02-04 16:23:33 +00:00
|
|
|
}
|
2008-11-08 22:24:07 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
auto* const webseed = task->webseed_;
|
|
|
|
webseed->connection_limiter.task_finished(success);
|
2011-07-10 15:24:51 +00:00
|
|
|
|
2022-02-23 13:38:18 +00:00
|
|
|
if (!success)
|
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
webseed->publish_rejection({ task->loc_.block, task->blocks.end });
|
2022-02-23 13:38:18 +00:00
|
|
|
webseed->tasks.erase(task);
|
|
|
|
delete task;
|
|
|
|
return;
|
|
|
|
}
|
2011-07-10 15:24:51 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
task->use_fetched_blocks();
|
2022-05-18 20:49:40 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
if (task->loc_.byte < task->end_byte_)
|
2022-02-23 13:38:18 +00:00
|
|
|
{
|
|
|
|
// Request finished successfully but there's still data missing.
|
|
|
|
// That means we've reached the end of a file and need to request
|
|
|
|
// the next one
|
2024-04-02 14:31:20 +00:00
|
|
|
task->request_next_chunk();
|
2022-02-23 13:38:18 +00:00
|
|
|
return;
|
2008-11-08 22:24:07 +00:00
|
|
|
}
|
2022-02-23 13:38:18 +00:00
|
|
|
|
2022-04-24 17:46:44 +00:00
|
|
|
TR_ASSERT(evbuffer_get_length(task->content()) == 0);
|
2024-04-02 14:31:20 +00:00
|
|
|
TR_ASSERT(task->loc_.byte == task->end_byte_);
|
2022-02-23 13:38:18 +00:00
|
|
|
webseed->tasks.erase(task);
|
|
|
|
delete task;
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
webseed->on_idle();
|
2008-06-07 21:26:41 +00:00
|
|
|
}
|
|
|
|
|
2022-03-28 22:13:32 +00:00
|
|
|
template<typename OutputIt>
|
2024-04-02 14:31:20 +00:00
|
|
|
void makeUrl(tr_webseed_impl const* const webseed, std::string_view name, OutputIt out)
|
2008-06-07 21:26:41 +00:00
|
|
|
{
|
2022-09-06 17:52:58 +00:00
|
|
|
auto const& url = webseed->base_url;
|
2022-03-28 22:13:32 +00:00
|
|
|
|
|
|
|
out = std::copy(std::begin(url), std::end(url), out);
|
2011-01-06 01:00:21 +00:00
|
|
|
|
2023-06-30 14:49:58 +00:00
|
|
|
if (tr_strv_ends_with(url, "/"sv) && !std::empty(name))
|
2017-04-19 12:04:45 +00:00
|
|
|
{
|
2022-08-21 13:43:09 +00:00
|
|
|
tr_urlPercentEncode(out, name, false);
|
2017-04-19 12:04:45 +00:00
|
|
|
}
|
2011-01-06 01:00:21 +00:00
|
|
|
}
|
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
void tr_webseed_task::request_next_chunk()
|
2011-01-06 01:00:21 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const& tor = webseed_->tor;
|
2013-02-04 16:23:33 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const downloaded_loc = tor.byte_loc(loc_.byte + evbuffer_get_length(content()));
|
2013-02-04 16:23:33 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
auto const [file_index, file_offset] = tor.file_offset(downloaded_loc);
|
|
|
|
auto const left_in_file = tor.file_size(file_index) - file_offset;
|
|
|
|
auto const left_in_task = end_byte_ - downloaded_loc.byte;
|
2022-02-23 13:38:18 +00:00
|
|
|
auto const this_chunk = std::min(left_in_file, left_in_task);
|
2022-04-24 17:46:44 +00:00
|
|
|
TR_ASSERT(this_chunk > 0U);
|
2013-04-13 20:25:28 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
webseed_->connection_limiter.task_started();
|
2022-02-25 20:45:00 +00:00
|
|
|
|
2022-03-28 22:13:32 +00:00
|
|
|
auto url = tr_urlbuf{};
|
2024-04-02 14:31:20 +00:00
|
|
|
makeUrl(webseed_, tor.file_subpath(file_index), std::back_inserter(url));
|
|
|
|
auto options = tr_web::FetchOptions{ url.sv(), on_partial_data_fetched, this };
|
2023-11-21 15:02:03 +00:00
|
|
|
options.range = fmt::format("{:d}-{:d}", file_offset, file_offset + this_chunk - 1);
|
2024-04-02 14:31:20 +00:00
|
|
|
options.speed_limit_tag = tor.id();
|
|
|
|
options.buffer = content();
|
|
|
|
tor.session->fetch(std::move(options));
|
2008-06-07 21:26:41 +00:00
|
|
|
}
|
|
|
|
|
2022-02-14 02:09:56 +00:00
|
|
|
} // namespace
|
|
|
|
|
2023-01-22 19:21:30 +00:00
|
|
|
// ---
|
2008-06-10 02:36:52 +00:00
|
|
|
|
2024-04-02 14:31:20 +00:00
|
|
|
std::unique_ptr<tr_webseed> tr_webseed::create(
|
|
|
|
tr_torrent& torrent,
|
|
|
|
std::string_view url,
|
|
|
|
tr_peer_callback_webseed callback,
|
|
|
|
void* callback_data)
|
2011-01-06 01:00:21 +00:00
|
|
|
{
|
2024-04-02 14:31:20 +00:00
|
|
|
return std::make_unique<tr_webseed_impl>(torrent, url, callback, callback_data);
|
2008-06-07 21:26:41 +00:00
|
|
|
}
|