diff --git a/src/peer_connection.cpp b/src/peer_connection.cpp index 83f1d32f7..327bb2074 100755 --- a/src/peer_connection.cpp +++ b/src/peer_connection.cpp @@ -739,6 +739,14 @@ namespace libtorrent m_allowed_fast.begin(), m_allowed_fast.end(), r.piece); if (i != m_allowed_fast.end()) m_allowed_fast.erase(i); } + else + { + std::vector::iterator i = std::find(m_suggested_pieces.begin() + , m_suggested_pieces.end(), r.piece); + if (i != m_suggested_pieces.end()) + m_suggested_pieces.erase(i); + } + if (m_request_queue.empty()) { if (m_download_queue.size() < 2) diff --git a/src/session_impl.cpp b/src/session_impl.cpp index 2eed1c328..5734225e0 100755 --- a/src/session_impl.cpp +++ b/src/session_impl.cpp @@ -1597,11 +1597,6 @@ namespace detail { assert(!save_path.empty()); - // if you get this assert, you haven't managed to - // open a listen port. call listen_on() first. - if (m_listen_sockets.empty()) - throw std::runtime_error("no listen socket opened"); - if (ti->begin_files() == ti->end_files()) throw std::runtime_error("no files in torrent"); diff --git a/test/Jamfile b/test/Jamfile index 5ae0123f0..d8f84582c 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -25,6 +25,7 @@ test-suite libtorrent : [ run test_storage.cpp ] [ run test_piece_picker.cpp ] # [ run test_entry.cpp ] + [ run test_fast_extension.cpp ] [ run test_pe_crypto.cpp ] [ run test_bencoding.cpp ] [ run test_primitives.cpp ] diff --git a/test/setup_transfer.cpp b/test/setup_transfer.cpp index 5b9a423e3..1d246d604 100644 --- a/test/setup_transfer.cpp +++ b/test/setup_transfer.cpp @@ -38,21 +38,12 @@ boost::intrusive_ptr clone_ptr(boost::intrusive_ptr const& ptr) return boost::intrusive_ptr(new T(*ptr)); } -boost::tuple -setup_transfer(session* ses1, session* ses2, session* ses3 - , bool clear_files, bool use_metadata_transfer, bool connect_peers) +boost::intrusive_ptr create_torrent(std::ostream* file) { - using namespace boost::filesystem; - - assert(ses1); - assert(ses2); - - assert(ses1->id() != ses2->id()); - if (ses3) - assert(ses3->id() != ses2->id()); - char const* tracker_url = "http://non-existent-name.com/announce"; + using namespace boost::filesystem; + boost::intrusive_ptr t(new torrent_info); int total_size = 2 * 1024 * 1024; t->add_file(path("temporary"), total_size); @@ -68,18 +59,40 @@ setup_transfer(session* ses1, session* ses2, session* ses3 sha1_hash ph = hasher(&piece[0], piece.size()).final(); for (int i = 0; i < num; ++i) t->set_hash(i, ph); + t->create_torrent(); + + if (file) + { + while (total_size > 0) + { + file->write(&piece[0], (std::min)(int(piece.size()), total_size)); + total_size -= piece.size(); + } + } + + return t; +} + +boost::tuple +setup_transfer(session* ses1, session* ses2, session* ses3 + , bool clear_files, bool use_metadata_transfer, bool connect_peers) +{ + using namespace boost::filesystem; + + assert(ses1); + assert(ses2); + + assert(ses1->id() != ses2->id()); + if (ses3) + assert(ses3->id() != ses2->id()); + create_directory("./tmp1"); std::ofstream file("./tmp1/temporary"); - while (total_size > 0) - { - file.write(&piece[0], (std::min)(int(piece.size()), total_size)); - total_size -= piece.size(); - } + boost::intrusive_ptr t = create_torrent(&file); file.close(); if (clear_files) remove_all("./tmp2/temporary"); - t->create_torrent(); std::cerr << "generated torrent: " << t->info_hash() << std::endl; ses1->set_severity_level(alert::debug); @@ -95,7 +108,7 @@ setup_transfer(session* ses1, session* ses2, session* ses3 if (ses3) tor3 = ses3->add_torrent(clone_ptr(t), "./tmp3"); if (use_metadata_transfer) - tor2 = ses2->add_torrent(tracker_url + tor2 = ses2->add_torrent("http://non-existent-name.com/announce" , t->info_hash(), 0, "./tmp2"); else tor2 = ses2->add_torrent(clone_ptr(t), "./tmp2"); diff --git a/test/setup_transfer.hpp b/test/setup_transfer.hpp index e218acfd7..6b9454b20 100644 --- a/test/setup_transfer.hpp +++ b/test/setup_transfer.hpp @@ -7,6 +7,8 @@ void test_sleep(int millisec); +boost::intrusive_ptr create_torrent(std::ostream* file = 0); + boost::tuple setup_transfer(libtorrent::session* ses1, libtorrent::session* ses2 diff --git a/test/test_fast_extension.cpp b/test/test_fast_extension.cpp new file mode 100644 index 000000000..0e8e6c8d9 --- /dev/null +++ b/test/test_fast_extension.cpp @@ -0,0 +1,190 @@ +#include "test.hpp" +#include "setup_transfer.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/io.hpp" +#include +#include +#include +#include + +using namespace libtorrent; + +int read_message(stream_socket& s, char* buffer) +{ + using namespace libtorrent::detail; + asio::read(s, asio::buffer(buffer, 4)); + char* ptr = buffer; + int length = read_int32(ptr); + + asio::read(s, asio::buffer(buffer, length)); + return length; +} + +char const* message_name[] = {"choke", "unchoke", "interested", "not_interested" + , "have", "bitfield", "request", "piece", "cancel", "dht_port", "", "", "" + , "suggest_piece", "have_all", "have_none", "reject_request", "allowed_fast"}; + +void send_allow_fast(stream_socket& s, int piece) +{ + using namespace libtorrent::detail; + char msg[] = "\0\0\0\x05\x11\0\0\0\0"; + char* ptr = msg + 5; + write_int32(piece, ptr); + asio::write(s, asio::buffer(msg, 9)); +} + +void send_suggest_piece(stream_socket& s, int piece) +{ + using namespace libtorrent::detail; + char msg[] = "\0\0\0\x05\x0d\0\0\0\0"; + char* ptr = msg + 5; + write_int32(piece, ptr); + asio::write(s, asio::buffer(msg, 9)); +} + +void send_unchoke(stream_socket& s) +{ + char msg[] = "\0\0\0\x01\x01"; + asio::write(s, asio::buffer(msg, 5)); +} + +void do_handshake(stream_socket& s, sha1_hash const& ih, char* buffer) +{ + char handshake[] = "\x13" "BitTorrent protocol\0\0\0\0\0\0\0\x04" + " " // space for info-hash + "aaaaaaaaaaaaaaaaaaaa" // peer-id + "\0\0\0\x01\x0e"; // have_all + std::memcpy(handshake + 28, ih.begin(), 20); + asio::write(s, asio::buffer(handshake, sizeof(handshake) - 1)); + + // read handshake + asio::read(s, asio::buffer(buffer, 68)); + + TEST_CHECK(buffer[0] == 19); + TEST_CHECK(std::memcmp(buffer + 1, "BitTorrent protocol", 19) == 0); + + char* extensions = buffer + 20; + // check for fast extension support + TEST_CHECK(extensions[7] & 0x4); + +#ifndef TORRENT_DISABLE_EXTENSIONS + // check for extension protocol support + TEST_CHECK(extensions[5] & 0x10); +#endif + +#ifndef TORRENT_DISABLE_DHT + // check for DHT support + TEST_CHECK(extensions[7] & 0x1); +#endif + + TEST_CHECK(std::memcmp(buffer + 28, ih.begin(), 20) == 0); +} + +// makes sure that pieces that are allowed and then +// rejected aren't requested again +void test_reject_fast() +{ + boost::intrusive_ptr t = create_torrent(); + sha1_hash ih = t->info_hash(); + session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48000, 49000)); + ses1.add_torrent(t, "./tmp1"); + + test_sleep(2000); + + io_service ios; + stream_socket s(ios); + s.connect(tcp::endpoint(address::from_string("127.0.0.1"), 48000)); + + char recv_buffer[1000]; + do_handshake(s, ih, recv_buffer); + + std::vector allowed_fast; + allowed_fast.push_back(0); + allowed_fast.push_back(1); + allowed_fast.push_back(2); + allowed_fast.push_back(3); + + std::for_each(allowed_fast.begin(), allowed_fast.end() + , bind(&send_allow_fast, boost::ref(s), _1)); + + while (!allowed_fast.empty()) + { + read_message(s, recv_buffer); + std::cerr << "msg: " << message_name[int(recv_buffer[0])] << std::endl; + if (recv_buffer[0] != 0x6) continue; + + using namespace libtorrent::detail; + char* ptr = recv_buffer + 1; + int piece = read_int32(ptr); + + std::vector::iterator i = std::find(allowed_fast.begin() + , allowed_fast.end(), piece); + TEST_CHECK(i != allowed_fast.end()); + if (i != allowed_fast.end()) + allowed_fast.erase(i); + // send reject request + recv_buffer[0] = 0x10; + asio::write(s, asio::buffer("\0\0\0\x0d", 4)); + asio::write(s, asio::buffer(recv_buffer, 13)); + } +} + +void test_respect_suggest() +{ + boost::intrusive_ptr t = create_torrent(); + sha1_hash ih = t->info_hash(); + session ses1(fingerprint("LT", 0, 1, 0, 0), std::make_pair(48000, 49000)); + ses1.add_torrent(t, "./tmp1"); + + test_sleep(2000); + + io_service ios; + stream_socket s(ios); + s.connect(tcp::endpoint(address::from_string("127.0.0.1"), 48000)); + + char recv_buffer[1000]; + do_handshake(s, ih, recv_buffer); + + std::vector suggested; + suggested.push_back(0); + suggested.push_back(1); + suggested.push_back(2); + suggested.push_back(3); + + std::for_each(suggested.begin(), suggested.end() + , bind(&send_suggest_piece, boost::ref(s), _1)); + + send_unchoke(s); + + int fail_counter = 100; + while (!suggested.empty() && fail_counter > 0) + { + read_message(s, recv_buffer); + std::cerr << "msg: " << message_name[int(recv_buffer[0])] << std::endl; + fail_counter--; + if (recv_buffer[0] != 0x6) continue; + + using namespace libtorrent::detail; + char* ptr = recv_buffer + 1; + int piece = read_int32(ptr); + + std::vector::iterator i = std::find(suggested.begin() + , suggested.end(), piece); + TEST_CHECK(i != suggested.end()); + if (i != suggested.end()) + suggested.erase(i); + // send reject request + recv_buffer[0] = 0x10; + asio::write(s, asio::buffer("\0\0\0\x0d", 4)); + asio::write(s, asio::buffer(recv_buffer, 13)); + } + TEST_CHECK(fail_counter > 0); +} + +int test_main() +{ + test_reject_fast(); + test_respect_suggest(); + return 0; +} +