| relevance 4 | ../test/test_dht.cpp:1266 | pass in the actual salt as a parameter |
pass in the actual salt as a parameter../test/test_dht.cpp:1266
key_desc_t const desc_error[] =
{
{ "e", bdecode_node::list_t, 2, 0 },
{ "y", bdecode_node::string_t, 1, 0},
};
bdecode_node desc_error_keys[2];
// ==== get / put mutable items ===
span<char const> itemv;
signature sig;
char buffer[1200];
sequence_number seq(4);
public_key pk;
secret_key sk;
get_test_keypair(pk, sk);
for (int with_salt = 0; with_salt < 2; ++with_salt)
{
seq = sequence_number(4);
std::printf("\nTEST GET/PUT%s \ngenerating ed25519 keys\n\n"
, with_salt ? " with-salt" : " no-salt");
std::array<char, 32> seed = ed25519_create_seed();
std::tie(pk, sk) = ed25519_create_keypair(seed);
std::printf("pub: %s priv: %s\n"
, aux::to_hex(pk.bytes).c_str()
, aux::to_hex(sk.bytes).c_str());
std::string salt;
if (with_salt) salt = "foobar";
hasher h(pk.bytes);
if (with_salt) h.update(salt);
sha1_hash target_id = h.final();
std::printf("target_id: %s\n"
, aux::to_hex(target_id).c_str());
send_dht_request(t.dht_node, "get", t.source, &response
, msg_args().target(target_id));
key_desc_t const desc[] =
{
{ "r", bdecode_node::dict_t, 0, key_desc_t::parse_children },
{ "id", bdecode_node::string_t, 20, 0},
{ "token", bdecode_node::string_t, 0, 0},
{ "ip", bdecode_node::string_t, 0, key_desc_t::optional | key_desc_t::last_child},
| ||
| relevance 4 | ../test/test_dht.cpp:2154 | pass in th actual salt as the argument |
pass in th actual salt as the argument../test/test_dht.cpp:2154 }
}
g_got_peers.clear();
}
} // anonymous namespace
TORRENT_TEST(get_peers_v4)
{
test_get_peers(rand_v4);
}
TORRENT_TEST(get_peers_v6)
{
if (supports_ipv6())
test_get_peers(rand_v6);
}
namespace {
void test_mutable_get(address(&rand_addr)(), bool const with_salt)
{
dht_test_setup t(udp::endpoint(rand_addr(), 20));
public_key pk;
secret_key sk;
get_test_keypair(pk, sk);
char buffer[1200];
sequence_number seq(4);
span<char const> itemv;
bdecode_node response;
std::string salt;
if (with_salt) salt = "foobar";
// mutable get
g_sent_packets.clear();
udp::endpoint const initial_node(rand_addr(), 1234);
dht::node_id const initial_node_id = to_hash("1111111111222222222233333333334444444444");
t.dht_node.m_table.add_node(node_entry{initial_node_id, initial_node, 10, true});
g_put_item.assign(items[0].ent, salt, seq, pk, sk);
t.dht_node.put_item(pk, std::string()
, std::bind(&put_mutable_item_cb, _1, _2, 0)
, put_mutable_item_data_cb);
TEST_EQUAL(g_sent_packets.size(), 1);
| ||
| relevance 3 | ../test/test_dht.cpp:118 | make the mock_socket hold a reference to the list of where to record packets instead of having a global variable |
make the mock_socket hold a reference to the list of where to record
packets instead of having a global variable../test/test_dht.cpp:118}
void add_and_replace(node_id& dst, node_id const& add)
{
bool carry = false;
for (int k = 19; k >= 0; --k)
{
int sum = dst[k] + add[k] + (carry ? 1 : 0);
dst[k] = sum & 255;
carry = sum > 255;
}
}
void node_push_back(std::vector<node_entry>* nv, node_entry const& n)
{
nv->push_back(n);
}
void nop_node() {}
std::list<std::pair<udp::endpoint, entry>> g_sent_packets;
struct mock_socket final : socket_manager
{
bool has_quota() override { return true; }
bool send_packet(aux::listen_socket_handle const&, entry& msg, udp::endpoint const& ep) override
{
| ||
| relevance 3 | ../test/test_dht.cpp:127 | ideally the mock_socket would contain this queue of packets, to make tests independent |
ideally the mock_socket would contain this queue of packets, to
make tests independent../test/test_dht.cpp:127 int sum = dst[k] + add[k] + (carry ? 1 : 0);
dst[k] = sum & 255;
carry = sum > 255;
}
}
void node_push_back(std::vector<node_entry>* nv, node_entry const& n)
{
nv->push_back(n);
}
void nop_node() {}
std::list<std::pair<udp::endpoint, entry>> g_sent_packets;
struct mock_socket final : socket_manager
{
bool has_quota() override { return true; }
bool send_packet(aux::listen_socket_handle const&, entry& msg, udp::endpoint const& ep) override
{
g_sent_packets.push_back(std::make_pair(ep, msg));
return true;
}
};
std::shared_ptr<aux::listen_socket_t> dummy_listen_socket(udp::endpoint src)
{
auto ret = std::make_shared<aux::listen_socket_t>();
ret->local_endpoint = tcp::endpoint(src.address(), src.port());
ret->external_address.cast_vote(src.address()
, aux::session_interface::source_dht, rand_v4());
return ret;
}
std::shared_ptr<aux::listen_socket_t> dummy_listen_socket4()
{
auto ret = std::make_shared<aux::listen_socket_t>();
ret->local_endpoint = tcp::endpoint(addr4("192.168.4.1"), 6881);
ret->external_address.cast_vote(addr4("236.0.0.1")
, aux::session_interface::source_dht, rand_v4());
return ret;
}
std::shared_ptr<aux::listen_socket_t> dummy_listen_socket6()
{
auto ret = std::make_shared<aux::listen_socket_t>();
ret->local_endpoint = tcp::endpoint(addr6("2002::1"), 6881);
ret->external_address.cast_vote(addr6("2002::1")
, aux::session_interface::source_dht, rand_v6());
return ret;
}
| ||
| relevance 3 | ../test/test_dht.cpp:1223 | split this up into smaller tests |
split this up into smaller tests../test/test_dht.cpp:1223 }
lt::aux::array<node_entry, 9> build_nodes(sha1_hash target)
{
return lt::aux::array<node_entry, 9>(
std::array<node_entry, 9> {
{ { target, udp::endpoint(addr4("1.1.1.1"), 1231), 10, true}
, { target, udp::endpoint(addr4("2.2.2.2"), 1232), 10, true}
, { target, udp::endpoint(addr4("3.3.3.3"), 1233), 10, true}
, { target, udp::endpoint(addr4("4.4.4.4"), 1234), 10, true}
, { target, udp::endpoint(addr4("5.5.5.5"), 1235), 10, true}
, { target, udp::endpoint(addr4("6.6.6.6"), 1236), 10, true}
, { target, udp::endpoint(addr4("7.7.7.7"), 1237), 10, true}
, { target, udp::endpoint(addr4("8.8.8.8"), 1238), 10, true}
, { target, udp::endpoint(addr4("9.9.9.9"), 1239), 10, true} }
});
}
span<char const> const empty_salt;
void test_put(address(&rand_addr)())
{
dht_test_setup t(udp::endpoint(rand_addr(), 20));
bdecode_node response;
bool ret;
// ====== put ======
init_rand_address();
udp::endpoint eps[1000];
for (int i = 0; i < 1000; ++i)
eps[i] = udp::endpoint(rand_addr(), std::uint16_t(random(16534) + 1));
announce_immutable_items(t.dht_node, eps, items, sizeof(items)/sizeof(items[0]));
key_desc_t const desc2[] =
{
{ "y", bdecode_node::string_t, 1, 0 }
};
bdecode_node desc2_keys[1];
key_desc_t const desc_error[] =
{
{ "e", bdecode_node::list_t, 2, 0 },
{ "y", bdecode_node::string_t, 1, 0},
};
bdecode_node desc_error_keys[2];
| ||
| relevance 3 | ../test/test_dht.cpp:2623 | use dht_test_setup class to simplify the node setup |
use dht_test_setup class to simplify the node setup../test/test_dht.cpp:2623 args.nodes({nodes[8]});
send_dht_response(t.dht_node, response, nodes[i].ep(), args);
g_sent_packets.erase(packet);
// once we've sent the response from the farthest node, we're done
if (i == 0) break;
}
TEST_EQUAL(g_put_count, 1);
// k nodes should now have outstanding put requests
TEST_EQUAL(g_sent_packets.size(), 8);
g_sent_packets.clear();
g_put_item.clear();
g_put_count = 0;
}
TORRENT_TEST(dht_dual_stack)
{
auto sett = test_settings();
mock_socket s;
auto sock4 = dummy_listen_socket4();
auto sock6 = dummy_listen_socket6();
obs observer;
counters cnt;
node* node4p = nullptr, *node6p = nullptr;
auto get_foreign_node = [&](node_id const&, std::string const& family)
{
if (family == "n4") return node4p;
if (family == "n6") return node6p;
TEST_CHECK(false);
return static_cast<node*>(nullptr);
};
std::unique_ptr<dht_storage_interface> dht_storage(dht_default_storage_constructor(sett));
dht_storage->update_node_ids({node_id(nullptr)});
dht::node node4(sock4, &s, sett, node_id(nullptr), &observer, cnt, get_foreign_node, *dht_storage);
dht::node node6(sock6, &s, sett, node_id(nullptr), &observer, cnt, get_foreign_node, *dht_storage);
node4p = &node4;
node6p = &node6;
// DHT should be running on port 48199 now
bdecode_node response;
char error_string[200];
bool ret;
node_id id = to_hash("3123456789abcdef01232456789abcdef0123456");
node4.m_table.node_seen(id, udp::endpoint(addr("4.4.4.4"), 4440), 10);
node6.m_table.node_seen(id, udp::endpoint(addr("4::4"), 4441), 10);
// v4 node requesting v6 nodes
| ||
| relevance 3 | ../test/test_dht.cpp:3168 | use dht_test_setup class to simplify the node setup |
use dht_test_setup class to simplify the node setup../test/test_dht.cpp:3168
bdecode_node response;
send_dht_request(t.dht_node, "ping", t.source, &response);
dht::key_desc_t const pong_desc[] = {
{ "y", bdecode_node::string_t, 1, 0 },
{ "t", bdecode_node::string_t, 2, 0 },
{ "r", bdecode_node::dict_t, 0, key_desc_t::parse_children },
{ "id", bdecode_node::string_t, 20, key_desc_t::last_child },
};
bdecode_node pong_keys[4];
bool ret = dht::verify_message(response, pong_desc, pong_keys, t.error_string);
TEST_CHECK(ret);
if (!ret) return;
TEST_EQUAL(node_id(pong_keys[3].string_ptr()), t.dht_node.nid());
}
TORRENT_TEST(read_only_node)
{
auto sett = test_settings();
sett.set_bool(settings_pack::dht_read_only, true);
mock_socket s;
auto ls = dummy_listen_socket4();
obs observer;
counters cnt;
std::unique_ptr<dht_storage_interface> dht_storage(dht_default_storage_constructor(sett));
dht_storage->update_node_ids({node_id(nullptr)});
dht::node node(ls, &s, sett, node_id(nullptr), &observer, cnt, get_foreign_node_stub, *dht_storage);
udp::endpoint source(addr("10.0.0.1"), 20);
bdecode_node response;
msg_args args;
// for incoming requests, read_only node won't response.
send_dht_request(node, "ping", source, &response, args, "10", false);
TEST_EQUAL(response.type(), bdecode_node::none_t);
args.target(sha1_hash("01010101010101010101"));
send_dht_request(node, "get", source, &response, args, "10", false);
TEST_EQUAL(response.type(), bdecode_node::none_t);
// also, the sender shouldn't be added to routing table.
TEST_EQUAL(std::get<0>(node.size()), 0);
// for outgoing requests, read_only node will add 'ro' key (value == 1)
// in top-level of request.
bdecode_node parsed[7];
char error_string[200];
udp::endpoint initial_node(addr("4.4.4.4"), 1234);
dht::node_id const initial_node_id = to_hash("1111111111222222222233333333334444444444");
| ||
| relevance 3 | ../test/test_dht.cpp:3313 | use dht_test_setup class to simplify the node setup |
use dht_test_setup class to simplify the node setup../test/test_dht.cpp:3313 node_id const response_id = generate_next();
entry response;
response["y"] = "r";
response["t"] = g_sent_packets.front().second["t"].string();
response["r"]["id"] = response_id.to_string();
node_id nid = node_id::max();
TEST_EQUAL(incoming_rpc_message(rpc, response, wrong_port, &nid), false);
TEST_EQUAL(nid, node_id::max());
incoming_rpc_message(rpc, response, source, &nid);
TEST_EQUAL(nid, response_id);
}
#ifndef TORRENT_DISABLE_LOGGING
// these tests rely on logging being enabled
TORRENT_TEST(invalid_error_msg)
{
auto sett = test_settings();
mock_socket s;
auto ls = dummy_listen_socket4();
obs observer;
counters cnt;
std::unique_ptr<dht_storage_interface> dht_storage(dht_default_storage_constructor(sett));
dht_storage->update_node_ids({node_id(nullptr)});
dht::node node(ls, &s, sett, node_id(nullptr), &observer, cnt, get_foreign_node_stub, *dht_storage);
udp::endpoint source(addr("10.0.0.1"), 20);
entry e;
e["y"] = "e";
e["e"].string() = "Malformed Error";
char msg_buf[1500];
int size = bencode(msg_buf, e);
bdecode_node decoded;
error_code ec;
bdecode(msg_buf, msg_buf + size, decoded, ec);
if (ec) std::printf("bdecode failed: %s\n", ec.message().c_str());
dht::msg m(decoded, source);
node.incoming(node.m_sock, m);
bool found = false;
for (auto const& log : observer.m_log)
{
if (log.find("INCOMING ERROR") != std::string::npos
&& log.find("(malformed)") != std::string::npos)
found = true;
| ||
| relevance 3 | ../test/test_dht.cpp:3406 | use dht_test_setup class to simplify the node setup |
use dht_test_setup class to simplify the node setup../test/test_dht.cpp:3406 TEST_CHECK(algo->num_sorted_results() == 0);
auto results = algo->results();
TEST_CHECK(results.size() == eps.size());
for (std::size_t i = 0; i < eps.size(); ++i)
TEST_CHECK(eps[i] == results[i]->target_ep());
// setting the node ID, regardless of what we set it to, should cause this
// observer to become sorted. i.e. be moved to the beginning of the result
// list.
results[5]->set_id(node_id("abababababababababab"));
TEST_CHECK(algo->num_sorted_results() == 1);
results = algo->results();
TEST_CHECK(results.size() == eps.size());
TEST_CHECK(eps[5] == results[0]->target_ep());
algo->done();
}
TORRENT_TEST(rpc_invalid_error_msg)
{
auto sett = test_settings();
mock_socket s;
auto ls = dummy_listen_socket4();
obs observer;
counters cnt;
dht::routing_table table(node_id(), udp::v4(), 8, sett, &observer);
dht::rpc_manager rpc(node_id(), sett, table, ls, &s, &observer);
std::unique_ptr<dht_storage_interface> dht_storage(dht_default_storage_constructor(sett));
dht_storage->update_node_ids({node_id(nullptr)});
dht::node node(ls, &s, sett, node_id(nullptr), &observer, cnt, get_foreign_node_stub, *dht_storage);
udp::endpoint source(addr("10.0.0.1"), 20);
// we need this to create an entry for this transaction ID, otherwise the
// incoming message will just be dropped
entry req;
req["y"] = "q";
req["q"] = "bogus_query";
req["t"] = "\0\0\0\0";
g_sent_packets.clear();
auto algo = std::make_shared<dht::traversal_algorithm>(node, node_id());
auto o = rpc.allocate_observer<null_observer>(std::move(algo), source, node_id());
#if TORRENT_USE_ASSERTS
o->m_in_constructor = false;
#endif
o->flags |= observer::flag_queried;
rpc.invoke(req, source, o);
| ||
| relevance 3 | ../src/ut_metadata.cpp:268 | use the aux::write_* functions and the span here instead, it will fit better with send_buffer() |
use the aux::write_* functions and the span here instead, it
will fit better with send_buffer()../src/ut_metadata.cpp:268 int metadata_piece_size = 0;
if (m_torrent.valid_metadata())
e["total_size"] = m_tp.metadata().size();
if (type == msg_t::piece)
{
TORRENT_ASSERT(piece >= 0 && piece < (m_tp.metadata().size() + 16 * 1024 - 1) / (16 * 1024));
TORRENT_ASSERT(m_pc.associated_torrent().lock()->valid_metadata());
TORRENT_ASSERT(m_torrent.valid_metadata());
int const offset = piece * 16 * 1024;
metadata = m_tp.metadata().data() + offset;
metadata_piece_size = std::min(
int(m_tp.metadata().size()) - offset, 16 * 1024);
TORRENT_ASSERT(metadata_piece_size > 0);
TORRENT_ASSERT(offset >= 0);
TORRENT_ASSERT(offset + metadata_piece_size <= m_tp.metadata().size());
}
char msg[200];
char* header = msg;
char* p = &msg[6];
int const len = bencode(p, e);
int const total_size = 2 + len + metadata_piece_size;
namespace io = aux;
io::write_uint32(total_size, header);
io::write_uint8(bt_peer_connection::msg_extended, header);
io::write_uint8(m_message_index, header);
m_pc.send_buffer({msg, len + 6});
| ||
| relevance 3 | ../src/torrent.cpp:461 | we could probably get away with just saving a few fields here |
we could probably get away with just saving a few fields here../src/torrent.cpp:461 // --- V2 HASHES ---
if (m_torrent_file->is_valid() && m_torrent_file->info_hashes().has_v2())
{
if (!p.merkle_trees.empty())
load_merkle_trees(
std::move(p.merkle_trees)
, std::move(p.merkle_tree_mask)
, std::move(p.verified_leaf_hashes));
// we really don't want to store extra copies of the trees
TORRENT_ASSERT(p.merkle_trees.empty());
}
if (valid_metadata())
{
inc_stats_counter(counters::num_total_pieces_added
, m_torrent_file->num_pieces());
}
m_add_torrent_params = std::make_unique<add_torrent_params>(std::move(p));
// seed m_active_timer/m_seeding_timer/m_finished_timer's live state
// to match reality as constructed (m_paused/m_seed_mode etc. are
// all settled by now); every subsequent transition maintains it
// from here via update_state_timers()
update_state_timers();
}
void torrent::load_merkle_trees(
aux::vector<std::vector<sha256_hash>, file_index_t> trees_import
, aux::vector<std::vector<bool>, file_index_t> mask
, aux::vector<std::vector<bool>, file_index_t> verified)
{
auto const& fs = m_torrent_file->orig_files();
std::vector<bool> const empty_verified;
for (file_index_t i{0}; i < fs.end_file(); ++i)
{
if (fs.pad_file_at(i) || fs.file_size(i) == 0)
continue;
if (i >= trees_import.end_index()) break;
std::vector<bool> const& verified_bitmask = (i >= verified.end_index()) ? empty_verified : verified[i];
if (i < mask.end_index() && !mask[i].empty())
{
m_merkle_trees[i].load_sparse_tree(trees_import[i], mask[i], verified_bitmask);
}
else
{
m_merkle_trees[i].load_tree(trees_import[i], verified_bitmask);
| ||
| relevance 3 | ../src/torrent.cpp:766 | assert there are no outstanding async operations on this torrent |
assert there are no outstanding async operations on this
torrent../src/torrent.cpp:766#endif
if (!m_ses.dht()) return false;
if (m_torrent_file->is_valid() && !m_files_checked) return false;
if (!m_announce_to_dht) return false;
if (m_paused) return false;
// don't announce private torrents
if (m_torrent_file->is_valid() && m_torrent_file->priv()) return false;
if (m_trackers.empty()) return true;
if (!settings().get_bool(settings_pack::use_dht_as_fallback)) return true;
return std::none_of(m_trackers.begin(), m_trackers.end()
, [](aux::announce_entry const& tr) { return bool(tr.verified); });
}
#endif
torrent::~torrent()
{
#if TORRENT_USE_ASSERTS
// by the time a torrent is destroyed it must already have been
// removed from every session_impl::m_torrent_lists entry, otherwise
// the list is left with a dangling pointer.
for (auto const& l : m_links)
{
TORRENT_ASSERT(!l.in_list());
}
#endif
// The invariant can't be maintained here, since the torrent
// is being destructed, all weak references to it have been
// reset, which means that all its peers already have an
// invalidated torrent pointer (so it cannot be verified to be correct)
// i.e. the invariant can only be maintained if all connections have
// been closed by the time the torrent is destructed. And they are
// supposed to be closed. So we can still do the invariant check.
// however, the torrent object may be destructed from the main
// thread when shutting down, if the disk cache has references to it.
// this means that the invariant check that this is called from the
// network thread cannot be maintained
TORRENT_ASSERT(m_peer_class == peer_class_t{0});
TORRENT_ASSERT(m_connections.empty());
// just in case, make sure the session accounting is kept right
for (auto p : m_connections)
m_ses.close_connection(p);
}
| ||
| relevance 3 | ../src/torrent.cpp:1401 | there's some duplication between this function and peer_connection::incoming_piece(). is there a way to merge something? |
there's some duplication between this function and
peer_connection::incoming_piece(). is there a way to merge something?../src/torrent.cpp:1401 void torrent::add_piece_async(piece_index_t const piece
, std::vector<char> data, add_piece_flags_t const flags)
{
TORRENT_ASSERT(is_single_thread());
TORRENT_ASSERT_PRECOND(piece < torrent_file().end_piece());
TORRENT_ASSERT_PRECOND(piece >= piece_index_t{0});
TORRENT_ASSERT_PRECOND(data.size() == std::size_t(m_torrent_file->piece_size_for_req(piece)));
// make sure the piece index is correct
if (piece >= torrent_file().end_piece())
return;
// make sure the piece size is correct
if (data.size() != std::size_t(m_torrent_file->piece_size_for_req(piece)))
return;
add_piece(piece, data.data(), flags);
}
void torrent::add_piece(piece_index_t const piece, char const* data
, add_piece_flags_t const flags)
{
TORRENT_ASSERT(is_single_thread());
// make sure the piece index is correct
if (piece >= torrent_file().end_piece())
return;
int const piece_size = m_torrent_file->piece_size_for_req(piece);
int const blocks_in_piece = (piece_size + block_size() - 1) / block_size();
if (m_deleted) return;
// avoid crash trying to access the picker when there is none
if (m_have_all && !has_picker()) return;
// we don't support clobbering the piece picker while checking the
// files. We may end up having the same piece multiple times
TORRENT_ASSERT_PRECOND(state() != torrent_status::checking_files
&& state() != torrent_status::checking_resume_data);
if (state() == torrent_status::checking_files
|| state() == torrent_status::checking_resume_data)
return;
need_picker();
if (picker().have_piece(piece)
&& !(flags & torrent_handle::overwrite_existing))
return;
| ||
| relevance 3 | ../src/torrent.cpp:4073 | this could probably be pulled out into a free function |
this could probably be pulled out into a free function../src/torrent.cpp:4073 std::int64_t calc_bytes(file_storage const& fs, piece_count const& pc)
{
// it's an impossible combination to have 0 pieces, but still have one of them be the last piece
TORRENT_ASSERT(!(pc.num_pieces == 0 && pc.last_piece == true));
// if we have 0 pieces, we can't have any pad blocks either
TORRENT_ASSERT(!(pc.num_pieces == 0 && pc.pad_bytes > 0));
// if we have all pieces, we must also have the last one
TORRENT_ASSERT(!(pc.num_pieces == fs.num_pieces() && pc.last_piece == false));
// every block should not be a pad block
TORRENT_ASSERT(pc.pad_bytes <= std::int64_t(pc.num_pieces) * fs.piece_length());
return std::int64_t(pc.num_pieces) * fs.piece_length()
- (pc.last_piece ? fs.piece_length() - fs.piece_size(fs.last_piece()) : 0)
- std::int64_t(pc.pad_bytes);
}
// fills in total_wanted, total_wanted_done and total_done
void torrent::bytes_done(torrent_status& st, status_flags_t const flags) const
{
INVARIANT_CHECK;
st.total_done = 0;
st.total_wanted_done = 0;
st.total_wanted = m_size_on_disk;
st.total = m_size_on_disk;
TORRENT_ASSERT(st.total_wanted <= m_torrent_file->total_size());
TORRENT_ASSERT(st.total_wanted >= 0);
TORRENT_ASSERT(!valid_metadata() || m_torrent_file->num_pieces() > 0);
if (!valid_metadata()) return;
if (m_seed_mode || is_seed())
{
// once we're a seed and remove the piece picker, we stop tracking
// piece- and file priority. We consider everything as being
// "wanted"
st.total_done = m_size_on_disk;
st.total_wanted_done = m_size_on_disk;
TORRENT_ASSERT(st.total_wanted <= st.total_done);
TORRENT_ASSERT(st.total_wanted_done <= st.total_wanted);
TORRENT_ASSERT(st.total_done <= m_torrent_file->total_size());
return;
}
else if (!has_picker())
return;
TORRENT_ASSERT(has_picker());
| ||
| relevance 3 | ../src/torrent.cpp:5051 | should this alert have an error code in it? |
should this alert have an error code in it?../src/torrent.cpp:5051 on_remove_peers();
TORRENT_ASSERT(m_connections.empty());
// post a message to the main thread to destruct
// the torrent object from there
if (m_storage)
{
try {
m_ses.disk_thread().async_stop_torrent(m_storage
, std::bind(&torrent::on_torrent_aborted, shared_from_this()));
}
catch (std::exception const& e)
{
TORRENT_UNUSED(e);
m_storage.reset();
#ifndef TORRENT_DISABLE_LOGGING
debug_log("Failed to flush disk cache: %s", e.what());
#endif
// clients may rely on this alert to be posted, so it's probably a
// good idea to post it here, even though we failed
if (alerts().should_post<cache_flushed_alert>())
alerts().emplace_alert<cache_flushed_alert>(get_handle());
}
m_ses.deferred_submit_jobs();
}
else
{
if (alerts().should_post<cache_flushed_alert>())
alerts().emplace_alert<cache_flushed_alert>(get_handle());
alerts().emplace_alert<torrent_removed_alert>(get_handle()
, info_hash(), get_userdata());
}
| ||
| relevance 3 | ../src/torrent.cpp:5110 | this should return optional<>. piece index -1 should not be allowed |
this should return optional<>. piece index -1 should not be
allowed../src/torrent.cpp:5110 }
#ifndef TORRENT_DISABLE_SUPERSEEDING
void torrent::set_super_seeding(bool const on)
{
if (on == m_super_seeding) return;
m_super_seeding = on;
set_need_save_resume(torrent_handle::if_state_changed);
state_updated();
if (m_super_seeding) return;
// disable super seeding for all peers
for (auto pc : *this)
{
pc->superseed_piece(piece_index_t(-1), piece_index_t(-1));
}
}
piece_index_t torrent::get_piece_to_super_seed(typed_bitfield<piece_index_t> const& bits)
{
// return a piece with low availability that is not in
// the bitfield and that is not currently being super
// seeded by any peer
TORRENT_ASSERT(m_super_seeding);
// do a linear search from the first piece
int min_availability = 9999;
std::vector<piece_index_t> avail_vec;
for (auto const i : m_torrent_file->piece_range())
{
if (bits[i]) continue;
int availability = 0;
for (auto pc : *this)
{
if (pc->super_seeded_piece(i))
{
// avoid super-seeding the same piece to more than one
// peer if we can avoid it. Do this by artificially
// increase the availability
availability = 999;
break;
}
if (pc->has_piece(i)) ++availability;
}
if (availability > min_availability) continue;
if (availability == min_availability)
{
avail_vec.push_back(i);
| ||
| relevance 3 | ../src/session_handle.cpp:695 | expose the sequence_number, public_key, secret_key and signature types to the client |
expose the sequence_number, public_key, secret_key and signature
types to the client../src/session_handle.cpp:695 void session_handle::dht_get_item(sha1_hash const& target)
{
#ifndef TORRENT_DISABLE_DHT
async_call(&session_impl::dht_get_immutable_item, target);
#else
TORRENT_UNUSED(target);
#endif
}
void session_handle::dht_get_item(std::array<char, 32> key
, std::string salt)
{
#ifndef TORRENT_DISABLE_DHT
async_call(&session_impl::dht_get_mutable_item, key, salt);
#else
TORRENT_UNUSED(key);
TORRENT_UNUSED(salt);
#endif
}
sha1_hash session_handle::dht_put_item(entry data)
{
std::vector<char> buf;
bencode(std::back_inserter(buf), data);
sha1_hash const ret = hasher(buf).final();
#ifndef TORRENT_DISABLE_DHT
async_call(&session_impl::dht_put_immutable_item, data, ret);
#endif
return ret;
}
void session_handle::dht_put_item(std::array<char, 32> key
, std::function<void(entry&, std::array<char,64>&
, std::int64_t&, std::string const&)> cb
, std::string salt)
{
#ifndef TORRENT_DISABLE_DHT
async_call(&session_impl::dht_put_mutable_item, key, cb, salt);
#else
TORRENT_UNUSED(key);
TORRENT_UNUSED(cb);
TORRENT_UNUSED(salt);
#endif
}
void session_handle::dht_get_peers(sha1_hash const& info_hash)
{
#ifndef TORRENT_DISABLE_DHT
async_call(&session_impl::dht_get_peers, info_hash);
#else
| ||
| relevance 3 | ../src/session_impl.cpp:1152 | closing the udp sockets here means that the uTP connections cannot be closed gracefully |
closing the udp sockets here means that
the uTP connections cannot be closed gracefully../src/session_impl.cpp:1152#ifndef TORRENT_DISABLE_LOGGING
session_log(" aborting all connections (%d)", int(m_connections.size()));
#endif
// abort all connections
for (auto i = m_connections.begin(); i != m_connections.end();)
{
peer_connection* p = (*i).get();
++i;
p->disconnect(errors::stopping_torrent, operation_t::bittorrent);
}
// close the listen sockets
for (auto const& l : m_listen_sockets)
{
if (l->sock)
{
l->sock->close(ec);
TORRENT_ASSERT(!ec);
}
if (l->udp_sock)
{
l->udp_sock->sock.close();
}
}
// we need to give all the sockets an opportunity to actually have their handlers
// called and cancelled before we continue the shutdown. This is a bit
// complicated, if there are no "undead" peers, it's safe to resume the
// shutdown, but if there are, we have to wait for them to be cleared out
// first. In session_impl::on_tick() we check them periodically. If we're
// shutting down and we remove the last one, we'll initiate
// shutdown_stage2 from there.
if (m_undead_peers.empty())
{
post(m_io_context, make_handler([this] { abort_stage2(); }
, m_abort_handler_storage, *this));
}
}
void session_impl::abort_stage2() noexcept
{
m_download_rate.close();
m_upload_rate.close();
// it's OK to detach the threads here. The disk_io_thread
// has an internal counter and won't release the network
// thread until they're all dead (via m_work).
m_disk_thread->abort(false);
// now it's OK for the network thread to exit
| ||
| relevance 3 | ../src/session_impl.cpp:1654 | the logic in this if-block should be factored out into a separate function. At least most of it |
the logic in this if-block should be factored out into a
separate function. At least most of it../src/session_impl.cpp:1654 , (lep.flags & listen_socket_t::local_network) ? "local-network " : ""
, (lep.flags & listen_socket_t::accept_incoming) ? "accept-incoming " : "no-incoming "
, (lep.flags & listen_socket_t::was_expanded) ? "expanded-ip " : ""
, (lep.flags & listen_socket_t::proxy) ? "proxy " : "");
}
#endif
auto ret = std::make_shared<listen_socket_t>();
ret->ssl = lep.ssl;
ret->original_port = bind_ep.port();
ret->flags = lep.flags;
ret->netmask = lep.netmask;
operation_t last_op = operation_t::unknown;
socket_type_t const sock_type
= (lep.ssl == transport::ssl)
? socket_type_t::tcp_ssl
: socket_type_t::tcp;
// if we're in force-proxy mode, don't open TCP listen sockets. We cannot
// accept connections on our local machine in this case.
if (ret->flags & listen_socket_t::accept_incoming)
{
ret->sock = std::make_shared<tcp::acceptor>(m_io_context);
ret->sock->open(bind_ep.protocol(), ec);
last_op = operation_t::sock_open;
if (ec)
{
#ifndef TORRENT_DISABLE_LOGGING
if (should_log())
{
session_log("failed to open socket: %s"
, ec.message().c_str());
}
#endif
if (m_alerts.should_post<listen_failed_alert>())
m_alerts.emplace_alert<listen_failed_alert>(lep.device, bind_ep, last_op
, ec, sock_type);
return ret;
}
#ifdef TORRENT_WINDOWS
{
// this is best-effort. ignore errors
error_code err;
ret->sock->set_option(exclusive_address_use(true), err);
#ifndef TORRENT_DISABLE_LOGGING
if (err && should_log())
{
session_log("failed enable exclusive address use on listen socket: %s"
, err.message().c_str());
| ||
| relevance 3 | ../src/session_impl.cpp:2624 | it would be neat if the utp socket manager would handle ICMP errors too |
it would be neat if the utp socket manager would
handle ICMP errors too../src/session_impl.cpp:2624
std::shared_ptr<session_udp_socket> s = socket.lock();
if (!s) return;
struct utp_socket_manager& mgr =
#ifdef TORRENT_SSL_PEERS
ssl == transport::ssl ? m_ssl_utp_socket_manager :
#endif
m_utp_socket_manager;
for (;;)
{
aux::array<udp_socket::packet, 50> p;
error_code err;
int const num_packets = s->sock.read(p, err);
for (udp_socket::packet& packet : span<udp_socket::packet>(p).first(num_packets))
{
if (packet.error)
{
#ifndef TORRENT_DISABLE_DHT
if (m_dht)
m_dht->incoming_error(packet.error, packet.from);
#endif
m_tracker_manager.incoming_error(packet.error, packet.from);
continue;
}
span<char const> const buf = packet.data;
if (!packet.hostname.empty())
{
// only the tracker manager supports receiving UDP packets
// from hostnames. If it won't handle it, no one else will
// either
m_tracker_manager.incoming_packet(packet.hostname, buf);
continue;
}
// give the uTP socket manager first dibs on the packet. Presumably
// the majority of packets are uTP packets.
if (!mgr.incoming_packet(ls, packet.from, buf))
{
// if it wasn't a uTP packet, try the other users of the UDP
// socket
bool handled = false;
#ifndef TORRENT_DISABLE_DHT
auto listen_socket = ls.lock();
if (m_dht && buf.size() > 20
&& buf.front() == 'd'
| ||
| relevance 3 | ../src/session_impl.cpp:4173 | it would probably make sense to have a separate list of peers that are eligible for optimistic unchoke, similar to the torrents perhaps this could even iterate over the pool allocators of torrent_peer objects. It could probably be done in a single pass and collect the n best candidates. maybe just a queue of peers would make even more sense, just pick the next peer in the queue for unchoking. It would be O(1). |
it would probably make sense to have a separate list of peers
that are eligible for optimistic unchoke, similar to the torrents
perhaps this could even iterate over the pool allocators of
torrent_peer objects. It could probably be done in a single pass and
collect the n best candidates. maybe just a queue of peers would make
even more sense, just pick the next peer in the queue for unchoking. It
would be O(1).../src/session_impl.cpp:4173 }
void session_impl::recalculate_optimistic_unchoke_slots()
{
INVARIANT_CHECK;
TORRENT_ASSERT(is_single_thread());
if (m_stats_counters[counters::num_unchoke_slots] == 0) return;
// if we unchoke everyone, skip this logic
if (settings().get_int(settings_pack::choking_algorithm) == settings_pack::fixed_slots_choker
&& settings().get_int(settings_pack::unchoke_slots_limit) < 0)
return;
std::vector<opt_unchoke_candidate> opt_unchoke;
// collect the currently optimistically unchoked peers here, so we can
// choke them when we've found new optimistic unchoke candidates.
std::vector<torrent_peer*> prev_opt_unchoke;
for (auto& i : m_connections)
{
peer_connection* const p = i.get();
TORRENT_ASSERT(p);
torrent_peer* pi = p->peer_info_struct();
if (!pi) continue;
if (pi->web_seed) continue;
if (pi->optimistically_unchoked)
{
prev_opt_unchoke.push_back(pi);
}
torrent const* t = p->associated_torrent().lock().get();
if (!t) continue;
| ||
| relevance 3 | ../src/session_impl.cpp:4196 | peers should know whether their torrent is paused or not, instead of having to ask it over and over again |
peers should know whether their torrent is paused or not,
instead of having to ask it over and over again../src/session_impl.cpp:4196 // collect the currently optimistically unchoked peers here, so we can
// choke them when we've found new optimistic unchoke candidates.
std::vector<torrent_peer*> prev_opt_unchoke;
for (auto& i : m_connections)
{
peer_connection* const p = i.get();
TORRENT_ASSERT(p);
torrent_peer* pi = p->peer_info_struct();
if (!pi) continue;
if (pi->web_seed) continue;
if (pi->optimistically_unchoked)
{
prev_opt_unchoke.push_back(pi);
}
torrent const* t = p->associated_torrent().lock().get();
if (!t) continue;
if (t->is_paused()) continue;
if (!p->is_connecting()
&& !p->is_disconnecting()
&& p->is_peer_interested()
&& t->free_upload_slots()
&& (p->is_choked() || pi->optimistically_unchoked)
&& !p->ignore_unchoke_slots()
&& t->valid_metadata())
{
opt_unchoke.emplace_back(&i);
}
}
// find the peers that has been waiting the longest to be optimistically
// unchoked
int num_opt_unchoke = m_settings.get_int(settings_pack::num_optimistic_unchoke_slots);
int const allowed_unchoke_slots = int(m_stats_counters[counters::num_unchoke_slots]);
if (num_opt_unchoke == 0) num_opt_unchoke = std::max(1, allowed_unchoke_slots / 5);
if (num_opt_unchoke > int(opt_unchoke.size())) num_opt_unchoke =
int(opt_unchoke.size());
// find the n best optimistic unchoke candidates
std::partial_sort(opt_unchoke.begin()
, opt_unchoke.begin() + num_opt_unchoke
, opt_unchoke.end()
#ifndef TORRENT_DISABLE_EXTENSIONS
, last_optimistic_unchoke_cmp(m_ses_extensions[plugins_optimistic_unchoke_idx])
#else
, last_optimistic_unchoke_cmp()
| ||
| relevance 3 | ../src/session_impl.cpp:4442 | there should be a pre-calculated list of all peers eligible for unchoking |
there should be a pre-calculated list of all peers eligible for
unchoking../src/session_impl.cpp:4442 }
void session_impl::recalculate_unchoke_slots()
{
TORRENT_ASSERT(is_single_thread());
time_point const now = aux::time_now();
time_duration const unchoke_interval = now - m_last_choke;
m_last_choke = now;
// if we unchoke everyone, skip this logic
if (settings().get_int(settings_pack::choking_algorithm) == settings_pack::fixed_slots_choker
&& settings().get_int(settings_pack::unchoke_slots_limit) < 0)
{
m_stats_counters.set_value(counters::num_unchoke_slots, std::numeric_limits<int>::max());
return;
}
// build list of all peers that are
// unchokable.
std::vector<peer_connection*> peers;
for (auto i = m_connections.begin(); i != m_connections.end();)
{
std::shared_ptr<peer_connection> p = *i;
TORRENT_ASSERT(p);
++i;
torrent* const t = p->associated_torrent().lock().get();
torrent_peer* const pi = p->peer_info_struct();
if (p->ignore_unchoke_slots() || t == nullptr || pi == nullptr
|| pi->web_seed || t->is_paused())
{
p->reset_choke_counters();
continue;
}
if (!p->is_peer_interested()
|| p->is_disconnecting()
|| p->is_connecting())
{
// this peer is not unchokable. So, if it's unchoked
// already, make sure to choke it.
if (p->is_choked())
{
p->reset_choke_counters();
continue;
}
if (pi && pi->optimistically_unchoked)
{
m_stats_counters.inc_stats_counter(counters::num_peers_up_unchoked_optimistic, -1);
pi->optimistically_unchoked = false;
| ||
| relevance 3 | ../src/session_impl.cpp:6173 | use public_key here instead of std::array |
use public_key here instead of std::array../src/session_impl.cpp:6173
void session_impl::dht_get_immutable_item(sha1_hash const& target)
{
if (!m_dht) return;
m_dht->get_item(target, std::bind(&session_impl::get_immutable_callback
, this, target, _1));
}
// callback for dht_mutable_get
void session_impl::get_mutable_callback(dht::item const& i
, bool const authoritative)
{
TORRENT_ASSERT(i.is_mutable());
m_alerts.emplace_alert<dht_mutable_item_alert>(i.pk().bytes
, i.sig().bytes, i.seq().value
, i.salt(), i.value(), authoritative);
}
// key is a 32-byte binary string, the public key to look up.
// the salt is optional
void session_impl::dht_get_mutable_item(std::array<char, 32> key
, std::string salt)
{
if (!m_dht) return;
m_dht->get_item(dht::public_key(key.data()), std::bind(&session_impl::get_mutable_callback
, this, _1, _2), std::move(salt));
}
namespace {
void on_dht_put_immutable_item(aux::alert_manager& alerts, sha1_hash target, int num)
{
if (alerts.should_post<dht_put_alert>())
alerts.emplace_alert<dht_put_alert>(target, num);
}
void on_dht_put_mutable_item(aux::alert_manager& alerts, dht::item const& i, int num)
{
if (alerts.should_post<dht_put_alert>())
{
dht::signature const sig = i.sig();
dht::public_key const pk = i.pk();
dht::sequence_number const seq = i.seq();
std::string salt = i.salt();
alerts.emplace_alert<dht_put_alert>(pk.bytes, sig.bytes
, std::move(salt), seq.value, num);
}
}
void put_mutable_callback(dht::item& i
, std::function<void(entry&, std::array<char, 64>&
| ||
| relevance 3 | ../src/peer_connection.cpp:3147 | instead of having to ask the torrent whether it's in graceful pause mode or not, the peers should keep that state (and the torrent should update them when it enters graceful pause). When a peer enters graceful pause mode, it should cancel all outstanding requests and clear its request queue. |
instead of having to ask the torrent whether it's in graceful
pause mode or not, the peers should keep that state (and the torrent
should update them when it enters graceful pause). When a peer enters
graceful pause mode, it should cancel all outstanding requests and
clear its request queue.../src/peer_connection.cpp:3147 // to disk or are in the disk write cache
if (picker.is_piece_finished(p.piece) && !was_finished)
{
#if TORRENT_USE_INVARIANT_CHECKS
check_postcondition post_checker2_(t, false);
#endif
t->verify_piece(p.piece);
}
check_graceful_pause();
if (is_disconnecting()) return;
if (request_a_block(*t, *this))
m_counters.inc_stats_counter(counters::incoming_piece_picks);
send_block_requests();
}
void peer_connection::check_graceful_pause()
{
std::shared_ptr<torrent> t = m_torrent.lock();
if (!t || !t->graceful_pause()) return;
if (m_outstanding_bytes > 0) return;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "GRACEFUL_PAUSE", "NO MORE DOWNLOAD");
#endif
disconnect(errors::torrent_paused, operation_t::bittorrent);
}
void peer_connection::on_disk_write_complete(storage_error const& error
, peer_request const& p, std::shared_ptr<torrent> t)
{
TORRENT_ASSERT(is_single_thread());
#ifndef TORRENT_DISABLE_LOGGING
if (should_log(peer_log_alert::info))
{
peer_log(peer_log_alert::info, "FILE_ASYNC_WRITE_COMPLETE", "piece: %d s: %x l: %x e: %s"
, static_cast<int>(p.piece), std::uint32_t(p.start), std::uint32_t(p.length), error.ec.message().c_str());
}
#endif
m_counters.inc_stats_counter(counters::queued_write_bytes, -p.length);
m_outstanding_writing_bytes -= p.length;
TORRENT_ASSERT(m_outstanding_writing_bytes >= 0);
// every peer is entitled to allocate a disk buffer if it has no writes outstanding
// see the comment in incoming_piece
if (m_outstanding_writing_bytes == 0
| ||
| relevance 3 | ../src/peer_connection.cpp:4038 | once peers are properly put in graceful pause mode, they can cancel all outstanding requests and this test can be removed. |
once peers are properly put in graceful pause mode, they can
cancel all outstanding requests and this test can be removed.../src/peer_connection.cpp:4038
if (!p->m_deferred_send_block_requests)
return;
p->m_deferred_send_block_requests = false;
p->send_block_requests_impl();
});
m_deferred_send_block_requests = true;
}
void peer_connection::send_block_requests_impl()
{
TORRENT_ASSERT(is_single_thread());
INVARIANT_CHECK;
std::shared_ptr<torrent> t = m_torrent.lock();
if (!t) return;
if (m_disconnecting) return;
if (t->graceful_pause()) return;
// we can't download pieces in these states
if (t->state() == torrent_status::checking_files
|| t->state() == torrent_status::checking_resume_data
|| t->state() == torrent_status::downloading_metadata)
return;
if (int(m_download_queue.size()) >= m_desired_queue_size
|| t->upload_mode()) return;
bool const empty_download_queue = m_download_queue.empty();
while (!m_request_queue.empty()
&& (int(m_download_queue.size()) < m_desired_queue_size
|| m_queued_time_critical > 0))
{
pending_block block = m_request_queue.front();
m_request_queue.erase(m_request_queue.begin());
if (m_queued_time_critical) --m_queued_time_critical;
// if we're a seed, we don't have a piece picker
// so we don't have to worry about invariants getting
// out of sync with it
if (!t->has_picker()) continue;
// this can happen if a block times out, is re-requested and
// then arrives "unexpectedly"
if (t->picker().is_downloaded(block.block))
{
| ||
| relevance 3 | ../src/peer_connection.cpp:4715 | new_piece should be an optional. piece index -1 should not be allowed |
new_piece should be an optional. piece index -1
should not be allowed../src/peer_connection.cpp:4715
// pieces may be empty if we don't have metadata yet
if (p.pieces.empty())
{
p.progress = 0.f;
p.progress_ppm = 0;
}
else
{
#if TORRENT_NO_FPU
p.progress = 0.f;
#else
p.progress = float(p.pieces.count()) / float(p.pieces.size());
#endif
p.progress_ppm = int(std::int64_t(p.pieces.count()) * 1000000 / p.pieces.size());
}
}
#ifndef TORRENT_DISABLE_SUPERSEEDING
void peer_connection::superseed_piece(piece_index_t const replace_piece
, piece_index_t const new_piece)
{
TORRENT_ASSERT(is_single_thread());
if (is_connecting()) return;
if (in_handshake()) return;
if (new_piece == piece_index_t(-1))
{
if (m_superseed_piece[0] == piece_index_t(-1)) return;
m_superseed_piece[0] = piece_index_t(-1);
m_superseed_piece[1] = piece_index_t(-1);
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "SUPER_SEEDING", "ending");
#endif
std::shared_ptr<torrent> t = m_torrent.lock();
TORRENT_ASSERT(t);
// this will either send a full bitfield or
// a have-all message, effectively terminating
// super-seeding, since the peer may pick any piece
write_bitfield();
return;
}
TORRENT_ASSERT(!has_piece(new_piece));
#ifndef TORRENT_DISABLE_LOGGING
| ||
| relevance 3 | ../src/web_peer_connection.cpp:200 | this should be an optional, piece index -1 should not be allowed |
this should be an optional, piece index -1 should
not be allowed../src/web_peer_connection.cpp:200
auto const range = aux::file_piece_range_inclusive(fs, i);
for (piece_index_t k = std::get<0>(range); k < std::get<1>(range); ++k)
have.clear_bit(k);
}
t->set_seed(peer_info_struct(), false);
if (have.none_set())
{
incoming_have_none();
m_web->interesting = false;
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "WEB-SEED", "have no pieces, not interesting. %s", m_url.c_str());
#endif
}
else
{
incoming_bitfield(have);
}
}
if (m_web->restart_request.piece != piece_index_t(-1))
{
// increase the chances of requesting the block
// we have partial data for already, to finish it
incoming_suggest(m_web->restart_request.piece);
}
web_connection_base::on_connected();
}
void web_peer_connection::disconnect(error_code const& ec
, operation_t op, disconnect_severity_t const error)
{
if (is_disconnecting()) return;
if (op == operation_t::sock_write && ec == boost::system::errc::broken_pipe)
{
#ifndef TORRENT_DISABLE_LOGGING
// a write operation failed with broken-pipe. This typically happens
// with HTTP 1.0 servers that close their incoming channel of the TCP
// stream whenever they're done reading one full request. Instead of
// us bailing out and failing the entire request just because our
// write-end was closed, ignore it and keep reading until the read-end
// also is closed.
peer_log(peer_log_alert::info, "WRITE_DIRECTION", "CLOSED");
#endif
// prevent the peer from trying to send anything more
m_send_buffer.clear();
// when the web server closed our write-end of the socket (i.e. its
// read-end), if it's an HTTP 1.0 server. we will stop sending more
| ||
| relevance 3 | ../src/web_peer_connection.cpp:420 | do we really need a special case here? wouldn't the multi-file case handle single file torrents correctly too? |
do we really need a special case here? wouldn't the multi-file
case handle single file torrents correctly too?../src/web_peer_connection.cpp:420 }
}
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::outgoing_message, "REQUESTING", "(piece: %d start: %d) - (piece: %d end: %d)"
, static_cast<int>(r.piece), r.start
, static_cast<int>(pr.piece), pr.start + pr.length);
#endif
bool const single_file_request = t->torrent_file().num_files() == 1;
int const proxy_type = m_settings.get_int(settings_pack::proxy_type);
bool const using_proxy = (proxy_type == settings_pack::http
|| proxy_type == settings_pack::http_pw) && !m_ssl;
// the number of pad files that have been "requested". In case we _only_
// request padfiles, we can't rely on handling them in the on_receive()
// callback (because we won't receive anything), instead we have to post a
// pretend read callback where we can deliver the zeroes for the partfile
int num_pad_files = 0;
if (single_file_request)
{
file_request_t file_req;
file_req.file_index = file_index_t(0);
file_req.start = std::int64_t(static_cast<int>(req.piece)) * info.piece_length()
+ req.start;
file_req.length = req.length;
request += "GET ";
// do not encode single file paths, they are
// assumed to be encoded in the torrent file
request += using_proxy ? m_url : m_path;
request += " HTTP/1.1\r\n";
add_headers(request, m_settings, using_proxy);
request += "\r\nRange: bytes=";
request += to_string(file_req.start).data();
request += "-";
request += to_string(file_req.start + file_req.length - 1).data();
request += "\r\n\r\n";
m_first_request = false;
m_file_requests.push_back(file_req);
}
else
{
// for v2 torrents, req.length is the sum of real-data block sizes which
// excludes pad bytes. map_block requires a torrent-contiguous byte span,
// so compute the actual span from req start to end of last block (pr).
int const map_length = info.v2()
? int(static_cast<int>(pr.piece) * std::int64_t(piece_size)
+ pr.start + pr.length
| ||
| relevance 3 | ../src/web_peer_connection.cpp:514 | file_index_t should not allow negative values |
file_index_t should not allow negative values../src/web_peer_connection.cpp:514 // with the correct slashes. Don't encode it again
request += m_path;
}
request += escape_file_path(info.orig_files(), f.file_index);
}
request += " HTTP/1.1\r\n";
add_headers(request, m_settings, using_proxy);
request += "\r\nRange: bytes=";
request += to_string(f.offset).data();
request += "-";
request += to_string(f.offset + f.size - 1).data();
request += "\r\n\r\n";
m_first_request = false;
#if 0
std::cerr << this << " SEND-REQUEST: f: " << f.file_index
<< " s: " << f.offset
<< " e: " << (f.offset + f.size - 1) << std::endl;
#endif
TORRENT_ASSERT(f.file_index >= file_index_t(0));
m_file_requests.push_back(file_req);
}
}
if (num_pad_files == int(m_file_requests.size()))
{
post(get_context(), std::bind(
&web_peer_connection::on_receive_padfile,
std::static_pointer_cast<web_peer_connection>(self())));
return;
}
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::outgoing_message, "REQUEST", "%s", request.c_str());
#endif
send_buffer(request);
}
namespace {
std::string get_peer_name(http_parser const& p, std::string const& host)
{
std::string const& server_version = p.header("server");
if (!server_version.empty())
return server_version;
return host;
}
| ||
| relevance 3 | ../src/web_peer_connection.cpp:710 | this could be made more efficient for the case when we use an HTTP proxy. Then we wouldn't need to add new web seeds to the torrent, we could just make the redirect table contain full URLs. |
this could be made more efficient for the case when we use an
HTTP proxy. Then we wouldn't need to add new web seeds to the torrent,
we could just make the redirect table contain full URLs.../src/web_peer_connection.cpp:710 // the no_local_ips flag
auto const web_seed_flags = torrent::ephemeral
| ((m_settings.get_bool(settings_pack::ssrf_mitigation) && aux::is_global(remote().address()))
? torrent::no_local_ips : web_seed_flag_t{});
// add the redirected url and remove the current one
if (!single_file_request)
{
if (m_file_requests.empty())
{
// the server sent a redirect response without a matching request
disconnect(errors::http_parse_error, operation_t::bittorrent, peer_error);
return;
}
file_index_t const file_index = m_file_requests.front().file_index;
location = resolve_redirect_location(m_url, location);
#ifndef TORRENT_DISABLE_LOGGING
peer_log(peer_log_alert::info, "LOCATION", "%s", location.c_str());
#endif
std::string redirect_base;
std::string redirect_path;
error_code ec;
std::tie(redirect_base, redirect_path) = split_url(location, ec);
if (ec)
{
// we should not try this server again.
disconnect(errors::missing_location, operation_t::bittorrent, failure);
return;
}
// add_web_seed won't add duplicates. If we have already added an entry
// with this URL, we'll get back the existing entry
// Don't forward our credentials (the web_seed_entry::auth value, sent
// as the Authorization header) to a different origin. The redirect
// target may be a third party server that should not see them. Note
// that any user-supplied m_extra_headers are still forwarded -- see the
// warning on web_seed_entry::extra_headers.
std::string const auth = same_origin(m_url, location)
? m_external_auth : std::string();
// "ephemeral" flag should be set to avoid "web_seed_t" saving in resume data.
// E.g. original "web_seed_t" request url points to "http://example1.com/file1" and
// web server responses with redirect location "http://example2.com/subpath/file2".
// "handle_redirect" process this location to create new "web_seed_t"
// with base url=="http://example2.com/" and redirects[0]=="/subpath/file2").
// If we try to load resume with such "web_seed_t" then "web_peer_connection" will send
// request with wrong path "http://example2.com/file1" (cause "redirects" map is not serialized in resume)
web_seed_t* web = t->add_web_seed(redirect_base, web_seed_entry::url_seed
| ||
| relevance 3 | ../src/kademlia/rpc_manager.cpp:71 | move this into it's own .cpp file |
move this into it's own .cpp file../src/kademlia/rpc_manager.cpp:71#include <libtorrent/kademlia/get_item.hpp>
#include <libtorrent/kademlia/sample_infohashes.hpp>
#include <libtorrent/aux_/session_settings.hpp>
#include <libtorrent/socket_io.hpp> // for print_endpoint
#include <libtorrent/aux_/time.hpp> // for aux::time_now
#include < | ||