forked from premiere/premiere-libtorrent
Merge pull request #149 from arvidn/tracker_simulation
add simulation of tracker announces
This commit is contained in:
commit
d3d15297f5
|
@ -30,5 +30,6 @@ alias libtorrent-sims :
|
||||||
[ run test_pe_crypto.cpp ]
|
[ run test_pe_crypto.cpp ]
|
||||||
[ run test_metadata_extension.cpp ]
|
[ run test_metadata_extension.cpp ]
|
||||||
[ run test_trackers_extension.cpp ]
|
[ run test_trackers_extension.cpp ]
|
||||||
|
[ run test_tracker.cpp ]
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a1985197c925d7668922b9066c378c232ebc06c4
|
Subproject commit 0a91d85ac0aa2203e71564d1cedd41a4748e60d7
|
|
@ -51,8 +51,9 @@ namespace {
|
||||||
|
|
||||||
struct swarm
|
struct swarm
|
||||||
{
|
{
|
||||||
swarm(int num_nodes, swarm_setup_provider& config)
|
swarm(int num_nodes, sim::simulation& sim, swarm_setup_provider& config)
|
||||||
: m_config(config)
|
: m_sim(sim)
|
||||||
|
, m_config(config)
|
||||||
, m_ios(m_sim, asio::ip::address_v4::from_string("0.0.0.0"))
|
, m_ios(m_sim, asio::ip::address_v4::from_string("0.0.0.0"))
|
||||||
, m_start_time(lt::clock_type::now())
|
, m_start_time(lt::clock_type::now())
|
||||||
, m_timer(m_ios)
|
, m_timer(m_ios)
|
||||||
|
@ -94,13 +95,9 @@ struct swarm
|
||||||
{
|
{
|
||||||
if (ec || m_shutting_down) return;
|
if (ec || m_shutting_down) return;
|
||||||
|
|
||||||
lt::time_duration d = lt::clock_type::now() - m_start_time;
|
|
||||||
boost::uint32_t millis = lt::duration_cast<lt::milliseconds>(d).count();
|
|
||||||
printf("%4d.%03d: TICK %d\n", millis / 1000, millis % 1000, m_tick);
|
|
||||||
|
|
||||||
++m_tick;
|
++m_tick;
|
||||||
|
|
||||||
if (m_tick > 120)
|
if (m_config.tick(m_tick))
|
||||||
{
|
{
|
||||||
terminate();
|
terminate();
|
||||||
return;
|
return;
|
||||||
|
@ -190,10 +187,9 @@ struct swarm
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
sim::simulation& m_sim;
|
||||||
swarm_setup_provider& m_config;
|
swarm_setup_provider& m_config;
|
||||||
|
|
||||||
sim::default_config cfg;
|
|
||||||
sim::simulation m_sim{cfg};
|
|
||||||
asio::io_service m_ios;
|
asio::io_service m_ios;
|
||||||
lt::time_point m_start_time;
|
lt::time_point m_start_time;
|
||||||
|
|
||||||
|
@ -210,7 +206,14 @@ private:
|
||||||
|
|
||||||
void setup_swarm(int num_nodes, swarm_setup_provider& cfg)
|
void setup_swarm(int num_nodes, swarm_setup_provider& cfg)
|
||||||
{
|
{
|
||||||
swarm s(num_nodes, cfg);
|
sim::default_config network_cfg;
|
||||||
|
sim::simulation sim{network_cfg};
|
||||||
|
setup_swarm(num_nodes, sim, cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setup_swarm(int num_nodes, sim::simulation& sim, swarm_setup_provider& cfg)
|
||||||
|
{
|
||||||
|
swarm s(num_nodes, sim, cfg);
|
||||||
s.run();
|
s.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,9 +70,14 @@ struct swarm_setup_provider
|
||||||
|
|
||||||
// called for every session that's added
|
// called for every session that's added
|
||||||
virtual libtorrent::settings_pack add_session(int idx) = 0;
|
virtual libtorrent::settings_pack add_session(int idx) = 0;
|
||||||
|
|
||||||
|
// called once a second. if it returns true, the simulation is terminated
|
||||||
|
// by default, simulations end after 200 seconds
|
||||||
|
virtual bool tick(int t) { return t > 200; }
|
||||||
};
|
};
|
||||||
|
|
||||||
void setup_swarm(int num_nodes, swarm_setup_provider& config);
|
void setup_swarm(int num_nodes, swarm_setup_provider& config);
|
||||||
|
void setup_swarm(int num_nodes, sim::simulation& sim, swarm_setup_provider& config);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
/*
|
||||||
|
|
||||||
|
Copyright (c) 2010, Arvid Norberg
|
||||||
|
All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions
|
||||||
|
are met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer in
|
||||||
|
the documentation and/or other materials provided with the distribution.
|
||||||
|
* Neither the name of the author nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived
|
||||||
|
from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "test.hpp"
|
||||||
|
#include "settings.hpp"
|
||||||
|
#include "setup_swarm.hpp"
|
||||||
|
#include "swarm_config.hpp"
|
||||||
|
#include "simulator/simulator.hpp"
|
||||||
|
#include "simulator/http_server.hpp"
|
||||||
|
#include "libtorrent/alert_types.hpp"
|
||||||
|
|
||||||
|
using namespace libtorrent;
|
||||||
|
using namespace sim;
|
||||||
|
namespace lt = libtorrent;
|
||||||
|
|
||||||
|
// seconds
|
||||||
|
const int duration = 10000;
|
||||||
|
|
||||||
|
struct test_swarm_config : swarm_config
|
||||||
|
{
|
||||||
|
test_swarm_config(int num_torrents, std::vector<std::string>* announces=NULL)
|
||||||
|
: swarm_config()
|
||||||
|
, m_announces(announces)
|
||||||
|
, m_num_torrents(num_torrents)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void on_session_added(int idx, session& ses) override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual libtorrent::add_torrent_params add_torrent(int idx) override
|
||||||
|
{
|
||||||
|
add_torrent_params p = swarm_config::add_torrent(idx);
|
||||||
|
|
||||||
|
// add the tracker to the last torrent (if there's only one, it will be a
|
||||||
|
// seed from the start, if there are more than one, it will be a torrent
|
||||||
|
// that downloads the torrent and turns into a seed)
|
||||||
|
if (m_num_torrents - 1 == idx)
|
||||||
|
{
|
||||||
|
p.trackers.push_back("http://2.2.2.2:8080/announce");
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool on_alert(libtorrent::alert const* alert
|
||||||
|
, int session_idx
|
||||||
|
, std::vector<libtorrent::torrent_handle> const& handles
|
||||||
|
, libtorrent::session& ses) override
|
||||||
|
{
|
||||||
|
if (m_announces == NULL) return false;
|
||||||
|
|
||||||
|
char type = 0;
|
||||||
|
if (lt::alert_cast<lt::tracker_announce_alert>(alert))
|
||||||
|
{
|
||||||
|
type = 'A';
|
||||||
|
}
|
||||||
|
else if (lt::alert_cast<lt::tracker_error_alert>(alert))
|
||||||
|
{
|
||||||
|
type = 'E';
|
||||||
|
}
|
||||||
|
else if (lt::alert_cast<lt::tracker_warning_alert>(alert))
|
||||||
|
{
|
||||||
|
type = 'W';
|
||||||
|
}
|
||||||
|
else if (lt::alert_cast<lt::tracker_reply_alert>(alert))
|
||||||
|
{
|
||||||
|
type = 'R';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char msg[500];
|
||||||
|
snprintf(msg, sizeof(msg), "%c: %d %s", type
|
||||||
|
, int(duration_cast<chrono::seconds>(alert->timestamp().time_since_epoch()).count())
|
||||||
|
, alert->message().c_str());
|
||||||
|
m_announces->push_back(msg);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void on_exit(std::vector<torrent_handle> const& torrents) override
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool tick(int t) override { return t > duration; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<std::string>* m_announces;
|
||||||
|
int m_num_torrents;
|
||||||
|
};
|
||||||
|
|
||||||
|
void test_interval(int interval)
|
||||||
|
{
|
||||||
|
using sim::asio::ip::address_v4;
|
||||||
|
sim::default_config network_cfg;
|
||||||
|
sim::simulation sim{network_cfg};
|
||||||
|
|
||||||
|
lt::time_point start = lt::clock_type::now();
|
||||||
|
|
||||||
|
sim::asio::io_service web_server(sim, address_v4::from_string("2.2.2.2"));
|
||||||
|
// listen on port 8080
|
||||||
|
sim::http_server http(web_server, 8080);
|
||||||
|
|
||||||
|
// the timestamps (in seconds) of all announces
|
||||||
|
std::vector<int> announces;
|
||||||
|
|
||||||
|
http.register_handler("/announce"
|
||||||
|
, [&announces,interval,start](std::string method, std::string req)
|
||||||
|
{
|
||||||
|
boost::uint32_t seconds = chrono::duration_cast<lt::seconds>(
|
||||||
|
lt::clock_type::now() - start).count();
|
||||||
|
announces.push_back(seconds);
|
||||||
|
|
||||||
|
char response[500];
|
||||||
|
int size = snprintf(response, sizeof(response), "d8:intervali%de5:peers0:e", interval);
|
||||||
|
return sim::send_response(200, "OK", size) + response;
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<std::string> announce_alerts;
|
||||||
|
test_swarm_config cfg(1, &announce_alerts);
|
||||||
|
setup_swarm(1, sim, cfg);
|
||||||
|
|
||||||
|
int counter = 0;
|
||||||
|
for (int i = 0; i < int(announces.size()); ++i)
|
||||||
|
{
|
||||||
|
TEST_EQUAL(announces[i], counter);
|
||||||
|
counter += interval;
|
||||||
|
if (counter > duration + 1) counter = duration + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: verify that announce_alerts seem right as well
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_completed()
|
||||||
|
{
|
||||||
|
using sim::asio::ip::address_v4;
|
||||||
|
sim::default_config network_cfg;
|
||||||
|
sim::simulation sim{network_cfg};
|
||||||
|
|
||||||
|
lt::time_point start = lt::clock_type::now();
|
||||||
|
|
||||||
|
sim::asio::io_service web_server(sim, address_v4::from_string("2.2.2.2"));
|
||||||
|
// listen on port 8080
|
||||||
|
sim::http_server http(web_server, 8080);
|
||||||
|
|
||||||
|
// the timestamps (in seconds) of all announces
|
||||||
|
std::vector<std::string> announces;
|
||||||
|
|
||||||
|
const int interval = 500;
|
||||||
|
|
||||||
|
http.register_handler("/announce"
|
||||||
|
, [&announces,interval,start](std::string method, std::string req)
|
||||||
|
{
|
||||||
|
TEST_EQUAL(method, "GET");
|
||||||
|
announces.push_back(req);
|
||||||
|
|
||||||
|
char response[500];
|
||||||
|
int size = snprintf(response, sizeof(response), "d8:intervali%de5:peers0:e", interval);
|
||||||
|
return sim::send_response(200, "OK", size) + response;
|
||||||
|
});
|
||||||
|
|
||||||
|
test_swarm_config cfg(2);
|
||||||
|
setup_swarm(2, sim, cfg);
|
||||||
|
|
||||||
|
// the first announce should be event=started, the second should be
|
||||||
|
// event=completed, then all but the last should have no event and the last
|
||||||
|
// be event=stopped.
|
||||||
|
for (int i = 0; i < int(announces.size()); ++i)
|
||||||
|
{
|
||||||
|
std::string const& str = announces[i];
|
||||||
|
const bool has_start = str.find("&event=started")
|
||||||
|
!= std::string::npos;
|
||||||
|
const bool has_completed = str.find("&event=completed")
|
||||||
|
!= std::string::npos;
|
||||||
|
const bool has_stopped = str.find("&event=stopped")
|
||||||
|
!= std::string::npos;
|
||||||
|
|
||||||
|
// we there can only be one event
|
||||||
|
const bool has_event = str.find("&event=") != std::string::npos;
|
||||||
|
|
||||||
|
fprintf(stderr, "- %s\n", str.c_str());
|
||||||
|
|
||||||
|
TEST_EQUAL(int(has_start) + int(has_completed) + int(has_stopped)
|
||||||
|
, int(has_event));
|
||||||
|
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
TEST_CHECK(has_start);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
TEST_CHECK(has_completed);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
if (i == int(announces.size()) - 1)
|
||||||
|
{
|
||||||
|
TEST_CHECK(has_stopped);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TEST_CHECK(!has_event);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TORRENT_TEST(event_completed)
|
||||||
|
{
|
||||||
|
test_completed();
|
||||||
|
}
|
||||||
|
|
||||||
|
TORRENT_TEST(announce_interval_440)
|
||||||
|
{
|
||||||
|
test_interval(440);
|
||||||
|
}
|
||||||
|
|
||||||
|
TORRENT_TEST(announce_interval_1800)
|
||||||
|
{
|
||||||
|
test_interval(1800);
|
||||||
|
}
|
||||||
|
|
||||||
|
TORRENT_TEST(announce_interval_1200)
|
||||||
|
{
|
||||||
|
test_interval(3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: test with different queuing settings
|
||||||
|
// TODO: test when a torrent transitions from downloading to finished and
|
||||||
|
// finished to seeding
|
||||||
|
// TODO: test that left, downloaded and uploaded are reported correctly
|
||||||
|
|
||||||
|
// TODO: test scrape
|
||||||
|
|
|
@ -432,8 +432,8 @@ void http_connection::on_timeout(boost::weak_ptr<http_connection> p
|
||||||
|
|
||||||
time_point now = clock_type::now();
|
time_point now = clock_type::now();
|
||||||
|
|
||||||
if (c->m_start_time + c->m_completion_timeout < now
|
if (c->m_start_time + c->m_completion_timeout <= now
|
||||||
|| c->m_last_receive + c->m_read_timeout < now)
|
|| c->m_last_receive + c->m_read_timeout <= now)
|
||||||
{
|
{
|
||||||
// the connection timed out. If we have more endpoints to try, just
|
// the connection timed out. If we have more endpoints to try, just
|
||||||
// close this connection. The on_connect handler will try the next
|
// close this connection. The on_connect handler will try the next
|
||||||
|
|
Loading…
Reference in New Issue