[net-bittorrent commit] r49 - in trunk: . lib/Net lib/Net/BitTorrent lib/Net/BitTorrent/Torrent lib/Net/BitTorrent/Torren...

7 views
Skip to first unread message

codesite...@google.com

unread,
Jan 5, 2009, 6:29:58 PM1/5/09
to net-bit...@googlegroups.com
Author: sa...@cpan.org
Date: Mon Jan 5 14:38:02 2009
New Revision: 49

Modified:
trunk/Changes
trunk/MANIFEST.SKIP
trunk/Notes.pod
trunk/TODO.pod
trunk/lib/Net/BitTorrent.pm
trunk/lib/Net/BitTorrent/DHT.pm
trunk/lib/Net/BitTorrent/Peer.pm
trunk/lib/Net/BitTorrent/Torrent.pm
trunk/lib/Net/BitTorrent/Torrent/Tracker.pm
trunk/lib/Net/BitTorrent/Torrent/Tracker/HTTP.pm
trunk/lib/Net/BitTorrent/Torrent/Tracker/UDP.pm
trunk/lib/Net/BitTorrent/Version.pm
trunk/t/000_miniswarm/009_miniswarm_udp.t
trunk/t/700_classes/Net/BitTorrent.t
trunk/t/700_classes/Net/BitTorrent/Peer.t
trunk/t/700_classes/Net/BitTorrent/Torrent.t
trunk/t/700_classes/Net/BitTorrent/Torrent/Tracker.t
trunk/tatoeba/002-debug.pl
trunk/tatoeba/004-resume.pl

Log:
Resume system v2, half-open timeouts, peers remember source
* [fix] N::B::Peer->as_string(1) now gives correct choke/interest statuses
* [etc] Improved description for N::B::Peer disconnect when sent
unrequested piece.
* [new] Several new private methods:
- N::B->_set_half_open()
- N::B->_set_peers_per_torrent()
- N::B->_set_connections_per_host()
* [fix] N::B->_set_max_ul_rate() requires a value greater than 0 (zero)
* [etc] Various tests tie STDERR to make sure as_string() is handled
properly
* [api] Major: Lists of potential peers and complete/incomplete counts (if
applicaable) are kept by their source (Net::BitTorrent::DHT,
Net::BitTorrent::Torrent::Tracker::HTTP,
Net::BitTorrent::Torrent::Tracker::UDP) rather than in the related
Net::BitTorrent::Torrent object. Net::BitTorrent::Torrent keeps
only a few nodes in an internal cache to speed things along.
Eventually, Net::BitTorrent::Peer objects will contain info about
how we found them. This was a Short Term Goal.
* [etc] /tatoeba/002-debug.pl only works until the torrent is completed
* [etc] shorter timeouts for miniswarm tests
* [api] Major: Resume system (yeah, the thing I added a little over a week
ago) is deprecated and has been replaced. I thought about it and
changing the original .torrent's metadata is a bad idea so I
switched from Rakshasa- to Rasterbar-like. The internal structure
is still subject to change and the docs probably don't match.

Modified: trunk/Changes
==============================================================================
--- trunk/Changes (original)
+++ trunk/Changes Mon Jan 5 14:38:02 2009
@@ -1,7 +1,14 @@
-Version 0.0XX |
+Version 0.049 |

API Changes/Compatibility Information:
* Net::BitTorrent::Torrent->peers() is now public
+ * Lists of potential peers are kept by their source (N::B::DHT,
+ N::B::T::Tracker::HTTP, N::B::T::Tracker::UDP) rather than in
+ Net::BitTorrent::Torrent.
+ * Resume system (yeah, the thing I added two versions ago) was
deprecated
+ and has been replaced. I thought about it and changing the original
+ .torrent's metadata is a bad idea so I switched from Rakshasa- to
+ Rasterbar-like.

Resolved Issues/Bug Fixes:
* None
@@ -15,11 +22,21 @@
* New debugging demonstration in /tatoeba/002-debug.pl
* New threaded demonstration in /tatoeba/003-threads.pl
* New resume demonstration in /tatoeba/004-resume.pl
+ * Various tests temporarily tie STDERR to check as_string() output

Notes:
- * No incompatible changes. Safe to upgrade from 0.046.
+ * There are several incompatible changes and I've been coding with the
+ flu this week. Upgrade at your own risk.
+ * THIS PROJECT IS ACTIVELY SEEKING DEVELOPERS. Ahem, I hate to shout
but
+ N::B could really use your help. Yes, you, the person reading this.
If
+ you're interested, see the "Joining the Project" section in
+ Net::BitTorrent::Notes.
* Various N::B::Torrent status and internal schedule fixes were made
none
of which really effect behavior.
+ * Meanwhile...
+ - 2008 ends as it began: uneventfully.
+ - RIAA drops MediaSentry.
+
---
Version 0.046 | 2008-12-30 18:25:17 -0500 (Tue, 30 Dec 2008)


Modified: trunk/MANIFEST.SKIP
==============================================================================
--- trunk/MANIFEST.SKIP (original)
+++ trunk/MANIFEST.SKIP Mon Jan 5 14:38:02 2009
@@ -61,6 +61,8 @@
# Don't package silly stuff
tatoeba/.+/.+
tatoeba/.*.log$
+# Created by tatoeba/004-resume.pl
+tatoeba/.*\.resume$

# Don't package downloaded/perif files
\.avi

Modified: trunk/Notes.pod
==============================================================================
--- trunk/Notes.pod (original)
+++ trunk/Notes.pod Mon Jan 5 14:38:02 2009
@@ -87,48 +87,25 @@

Note: This section describes resume functionality as of C<v0.045>.

-C<Net::BitTorrent> has a complete resume system which is capable of
-resuming single torrents. The resume data is not checked for tampering,
-so there are a few things to remember when handling it:X<badresumedata>
-
-=over
-
-=item 1.
-
-Resume data will contain binary data so if you're storing the data
-to a file, you B<MUST> use C<binmode( )>, (preferably) C<syswrite( )> and
-C<sysread()>, or whatever your favorite way to write raw bytes happens to
-be.
-
-=item 2.
-
-Resume data contains the last-modified times of all related files
-so make sure gathering resume data is the last thing you do and be
-careful with any media players which modify metadata as these will cause
-you nothing but trouble.
-
-=back
+C<Net::BitTorrent> is capable of restoring the state of single torrents
+between sessions. To store resume data, use the
+L<save_resume_data( )|Net::BitTorrent::Torrent/"save_resume_data ( [ PATH
] )">

To resume a single torrent, use a variation of the following to save the
data...

- my $torrent = $bt->add_torrent( { Path=> 'some.torrent' });
+ my $torrent = $bt->add_torrent( { Path=> 'some.torrent', Resume
= '.resume' });

# later...

- rename $torrent->path, $torrent->path . '.bak' # keep a backup just in
case
- open my $D, '>' $torrent->path;
- syswrite $D, $torrent->resume_data(); # save current state to disk
- close $D;
-
-To resume, simply load the new .torrent file as usual:
-
- my $torrent = $bt->add_torrent( { Path=> 'some.torrent' });
-
-...and unless C<Net::BitTorrent> decides the resume data is
-L<bad|/badresumedata>, you'll start right were you left off. I would
suggest
-storing resume data on a regular basis while the client runs and again on
-exit.
+ $torrent->save_resume_data();
+
+...and unless C<Net::BitTorrent::Torrent> decides the resume data is
+bad, you'll start right were you left off. I would suggest storing resume
+data on a regular basis while the client runs and again on exit.
+
+
+To view a fully functioning example, see C</tatoeba/004-resume.pl>.

For more on what exactly you're saving and the structure of the data, see
L<Resume API|/"Resume API"> in the
@@ -486,10 +463,10 @@

=head2 Resume API

-C<Net::BitTorrent::Torrent>'s resume data is stored in the bencoded
-.torrent file under a key named 'net-bittorrent'. To retrieve this data,
-use the L<resume_data( )|Net::BitTorrent::Torrent/"resume_data( [ RAW ] )">
-method of L<Net::BitTorrent::Torrent|Net::BitTorrent::Torrent>.
+C<Net::BitTorrent::Torrent>'s resume data is bencoded and stored in a
+file. To restore this data, use the C<Resume> parameter when calling
+L<Net::BitTorrent::Torrent-E<gt>new( )|Net::BitTorrent::Torrent/"new ( {
[ARGS] } )">
+or L<Net::BitTorrent-E<gt>add_torrent( )|Net::BitTorrent/"add_torrent (
{ ... } )">.

I<Note: The structure and data held in the resume data is subject to
change in future versions.>
@@ -500,6 +477,11 @@

=over

+=item C<.format>
+
+A string describing what sort of file this is. For now, it's value is
+C<Net::BitTorrent resume>.
+
=item C<.t>

Timestamp when data was gathered.
@@ -599,7 +581,7 @@

=item v0.040

-Entire distribution was rewritten. Both the internal and the API have
+Entire distribution was rewritten. Both the internals and the API have
broken compatibility.

=back
@@ -610,6 +592,25 @@
L<Net::BitTorent|Net::BitTorrent>'s development but don't know where to
begin, here are a few ideas.

+=head2 Joining the Project
+
+=for html <span style="color: #F00; font-size: 1.5em;">
+
+THIS PROJECT IS ACTIVELY SEEKING DEVELOPERS. Ahem, I hate to shout but
+over the next few major versions, my priority is Protocol Encryption
+() which requires a (vauge) familiarity with cryptography.
+
+So, if you're interested in helping with that, or anything listed on the
+L<TODO|Net::BitTorrent::Todo> list or, maybe you have a suggestion and
+are willing to see it through to completion, L<contact me|/"Author">; I'm
+handing out SVN commit bits like (expensive) candy!
+
+=for html </span>
+
+In general, N::B could use a second or third pair of eyes. So, if you're
+interested in BitTorrent, p2p, or just Perl in general,
+L<let me know|/"Author">.
+
=head2 Automated Test Reports

Becoming a CPAN Tester is an easy, automatic way to contribute to the
@@ -703,7 +704,7 @@

=back

-=head2 Co-Development and Patch Submission
+=head2 Patch Submission

C<Net::BitTorrent> is too large for just one person to hack on. If
you're Perl adept and would like to help, you can start by fixing
@@ -759,7 +760,7 @@

=item Ohloh

-It's open source social networking. ...I think. Regardless of it's use,
+It's open source social networking. ...I think. Regardless of its use,
Net::BitTorrent has an Ohloh page:
http://www.ohloh.net/projects/net-bittorrent/

@@ -794,7 +795,7 @@

=back

-=head
+=head2 Related Information

=over


Modified: trunk/TODO.pod
==============================================================================
--- trunk/TODO.pod (original)
+++ trunk/TODO.pod Mon Jan 5 14:38:02 2009
@@ -26,9 +26,15 @@

=back

+=item * intermediate .piece file to store incoming blocks

=item * Document DHT stuff in N::B::Protocol

+=item * Remove /scripts/bittorrent.pl from the distribution
+
+It's a crutch and now, with the demonstration scripts, it's no longer
+needed.
+
=back

=head1 Short term goals
@@ -39,9 +45,6 @@

=item * Fast Ext (fast set use)

-=item * Make trackers keep track of their own complete/incomplete tally
-rather than stuffing their value in the parent tier.
-
=item * BEP 9: Extension for Peers to Send Metadata Files

=item * ut_pex ...despite it being undocumented
@@ -52,17 +55,13 @@

=over

-=item * Net::BitTorrent::Notes
-
-=item * Per-torrent transfer limits.
+=item * Per-torrent transfer limits. (?)

=item * improve file handling

=over

=item * large files (> 4G) typically require 64bit math
-
-=item * intermediate .piece file to store incoming blocks

=back


Modified: trunk/lib/Net/BitTorrent.pm
==============================================================================
--- trunk/lib/Net/BitTorrent.pm (original)
+++ trunk/lib/Net/BitTorrent.pm Mon Jan 5 14:38:02 2009
@@ -21,7 +21,7 @@
use Net::BitTorrent::Version;
use version qw[qv];
our $SVN = q[$Id$];
- our $UNSTABLE_RELEASE = 1; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
+ our $UNSTABLE_RELEASE = 2; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
my (@CONTENTS)
= \my (%_tcp, %_udp,
%_schedule, %_tid,
@@ -41,13 +41,13 @@
my ($host, @ports) = (q[0.0.0.0], (0));

# Defaults
- $_peers_per_torrent{refaddr $self} = 100;
- $_connections_per_host{refaddr $self} = 2;
- $_half_open{refaddr $self} = 8;
- $_max_dl_rate{refaddr $self} = 0;
- $_k_dl{refaddr $self} = 0;
$_max_ul_rate{refaddr $self} = 0;
$_k_ul{refaddr $self} = 0;
+ $_max_dl_rate{refaddr $self} = 0;
+ $_k_dl{refaddr $self} = 0;
+ $_peers_per_torrent{refaddr $self} = 100;
+ $_half_open{refaddr $self} = 8;
+ $_connections_per_host{refaddr $self} = 2;
$_torrents{refaddr $self} = {};
$_tid{refaddr $self} = qq[\0] x 5;
$_use_dht{refaddr $self} = 1;
@@ -89,12 +89,18 @@
}

# Accessors | Private
- sub _tcp { return $_tcp{refaddr +shift} }
- sub _udp { return $_udp{refaddr +shift} }
- sub _connections { return $_connections{refaddr +shift} }
- sub _max_ul_rate { return $_max_ul_rate{refaddr +shift} }
- sub _max_dl_rate { return $_max_dl_rate{refaddr +shift} }
- sub _dht { return $_dht{refaddr +shift} }
+ sub _tcp { return $_tcp{refaddr +shift} }
+ sub _udp { return $_udp{refaddr +shift} }
+ sub _connections { return $_connections{refaddr +shift} }
+ sub _max_ul_rate { return $_max_ul_rate{refaddr +shift} }
+ sub _max_dl_rate { return $_max_dl_rate{refaddr +shift} }
+ sub _peers_per_torrent { return $_peers_per_torrent{refaddr +shift} }
+ sub _half_open { return $_half_open{refaddr +shift} }
+
+ sub _connections_per_host {
+ return $_connections_per_host{refaddr +shift};
+ }
+ sub _dht { return $_dht{refaddr +shift} }

sub _use_dht {
my ($s) = @_;
@@ -132,17 +138,11 @@
= unpack_sockaddr_in(getsockname($_udp{refaddr $self}));
return inet_ntoa($packed_ip);
}
- sub _peers_per_torrent { return $_peers_per_torrent{refaddr +shift} }
- sub _half_open { return $_half_open{refaddr +shift} }
-
- sub _connections_per_host {
- return $_connections_per_host{refaddr +shift};
- }

# Setters | Private
sub _set_max_ul_rate { # BYTES per second
my ($self, $value) = @_;
- if (not defined $value or $value !~ m[^\d+$]) {
+ if (not defined $value or $value !~ m[^\d+$] or !$value) {
carp
q[Net::BitTorrent->_set_max_ul_rate( VALUE ) requires an
integer value];
return;
@@ -160,6 +160,36 @@
return $_max_dl_rate{refaddr $self} = $value;
}

+ sub _set_peers_per_torrent {
+ my ($self, $value) = @_;
+ if (not defined $value or $value !~ m[^\d+$] or $value < 1) {
+ carp
+ q[Net::BitTorrent->_set_peers_per_torrent( VALUE )
requires an integer value];
+ return;
+ }
+ return $_peers_per_torrent{refaddr $self} = $value;
+ }
+
+ sub _set_half_open {
+ my ($self, $value) = @_;
+ if (not defined $value or $value !~ m[^\d+$] or $value < 1) {
+ carp
+ q[Net::BitTorrent->_set_half_open( VALUE ) requires an
integer value];
+ return;
+ }
+ return $_half_open{refaddr $self} = $value;
+ }
+
+ sub _set_connections_per_host {
+ my ($self, $value) = @_;
+ if (not defined $value or $value !~ m[^\d+$] or $value < 1) {
+ carp
+ q[Net::BitTorrent->_set_connections_per_host( VALUE )
requires an integer value];
+ return;
+ }
+ return $_connections_per_host{refaddr $self} = $value;
+ }
+
sub _set_use_dht {
my ($self, $value) = @_;
if (not defined $value or $value !~ m[^[10]$]) {
@@ -727,6 +757,19 @@

Net::BitTorrent - BitTorrent peer-to-peer protocol class

+=head1 NOTICE
+
+=for html <span style="color: #F00; font-size: 1.5em;">
+
+THIS PROJECT IS ACTIVELY SEEKING DEVELOPERS. Ahem, I hate to shout but
+N::B could really use a second or third pair of eyes over the next few
+versions. So, if you're interested and have cryptographic experience,
+or you're just familiar with Perl and want to chip in, see the notes
+on L<Joining the Project|Net::BitTorrent::Notes/"Joining the Project">
+and L<let me know|/"Author">.
+
+=for html </span>
+
=head1 Synopsis

use Net::BitTorrent;
@@ -1263,7 +1306,8 @@

=item C<Torrent>

-The L<Net::BitTorrent::Torrent> object related to this event.
+The L<Net::BitTorrent::Torrent|Net::BitTorrent::Torrent> object related to
+this event.

=item C<Index>


Modified: trunk/lib/Net/BitTorrent/DHT.pm
==============================================================================
--- trunk/lib/Net/BitTorrent/DHT.pm (original)
+++ trunk/lib/Net/BitTorrent/DHT.pm Mon Jan 5 14:38:02 2009
@@ -13,7 +13,7 @@
use Net::BitTorrent::Version;
use version qw[qv];
our $SVN = q[$Id$];
- our $UNSTABLE_RELEASE = 0; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
+ our $UNSTABLE_RELEASE = 1; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
my @CONTENTS
= \my
(%_client, %tid, %node_id, %outstanding_p, %nodes, %tracking);
my %REGISTRY;
@@ -72,6 +72,13 @@
return $_client{refaddr + $_[0]};
}

+ sub _peers {
+ my ($self, $info_hash) = @_;
+ return q[] if !$tracking{refaddr $self}{$info_hash};
+ $tracking{refaddr $self}{$info_hash}{q[touch]} = time;
+ return $tracking{refaddr $self}{$info_hash}{q[peers]};
+ }
+
# Accesors | Public
sub node_id {
return if defined $_[1];
@@ -417,17 +424,19 @@
->{q[a]}{q[info_hash]}
);
if ($torrent) {
- for my $value (@{$packet->{q[r]}{q[values]}}) {
-
- #warn q[********** add to torrent: ]
- # . uncompact($value);
- $torrent->_append_nodes($value);
- }
- }
- else {
-
- #
- # xxx - ...what to do? What... to... do...
+ $tracking{refaddr $self}{$torrent->infohash} = {
+ peers =>
+ compact(
+ uncompact(
+ $tracking{refaddr $self}
+
{$torrent->infohash}{q[peers]}
+ ),
+ (map { uncompact($_) }
+ @{$packet->{q[r]}{q[values]}}
+ )
+ ),
+ touch => time
+ };
}
$self->_find_node_out($node, $node_id{refaddr $self});
}

Modified: trunk/lib/Net/BitTorrent/Peer.pm
==============================================================================
--- trunk/lib/Net/BitTorrent/Peer.pm (original)
+++ trunk/lib/Net/BitTorrent/Peer.pm Mon Jan 5 14:38:02 2009
@@ -10,7 +10,7 @@
use Fcntl qw[F_SETFL O_NONBLOCK];
use version qw[qv];
our $SVN = q[$Id$];
- our $UNSTABLE_RELEASE = 2; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
+ our $UNSTABLE_RELEASE = 3; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
use lib q[../../../lib];
use Net::BitTorrent::Protocol qw[:build parse_packet :types];
use Net::BitTorrent::Util qw[:bencode];
@@ -21,7 +21,7 @@
%_bitfield, %_am_choking, %_am_interested,
%_peer_choking, %_peer_interested, %_incoming,
%requests_out, %requests_in, %_last_contact,
- %_incoming_fastset, %_reserved_bytes
+ %_incoming_fastset, %_reserved_bytes, %_source
);
my %REGISTRY;

@@ -84,6 +84,7 @@
$_data_out{refaddr $self} = q[];
$_data_in{refaddr $self} = q[];
$_incoming{refaddr $self} = 1;
+ $_source{refaddr $self} = q[Incoming];
}
else {
if ($args->{q[Address]}
@@ -100,6 +101,11 @@
q[Net::BitTorrent::Peer->new({}) requires a
blessed 'Torrent'];
return;
}
+ if (!$args->{q[Source]}) {
+ carp
+ q[Net::BitTorrent::Peer->new({}) would like to know
where this peer info is from];
+ return;
+ }
my $half_open = grep {
$_->{q[Object]}->isa(q[Net::BitTorrent::Peer])
&& !$_->{q[Object]}->_torrent
@@ -165,6 +171,7 @@
$_data_in{refaddr $self} = q[];
$_client{refaddr $self}->_add_connection($self, q[rw]) or
return;
$_incoming{refaddr $self} = 0;
+ $_source{refaddr $self} = $args->{q[Source]};
}
if ($self) {
${$_am_choking{refaddr $self}} = 1;
@@ -253,6 +260,7 @@
sub _peer_interested { return ${$_peer_interested{refaddr +shift}} }
sub _am_interested { return ${$_am_interested{refaddr +shift}} }
sub _incoming { return $_incoming{refaddr +shift} }
+ sub _source { return $_source{refaddr +shift} }

# Methods | Private
sub _rw {
@@ -648,7 +656,10 @@

if (not defined $request) {
weaken $self;
- $self->_disconnect(q[Handed a piece we never asked for.]);
+ $self->_disconnect(
+ sprintf
+ q[Handed a piece we never asked for (I: %d | O: %d |
L: %d).],
+ $index, $offset, $length);
return;
}
$_torrent{refaddr $self}->_add_downloaded($request->{q[Length]});
@@ -1408,10 +1419,10 @@
: q[Unknown]
),
($_incoming{refaddr $self} ? q[Incoming] : q[Outgoing]),
- (map { $_ ? q[Yes] : q[No] } ($_peer_interested{refaddr $self},
- $_am_interested{refaddr $self},
- $_am_choking{refaddr $self},
- $_peer_choking{refaddr $self}
+ (map { $_ ? q[Yes] : q[No] } (${$_peer_interested{refaddr
$self}},
+ ${$_am_interested{refaddr
$self}},
+ ${$_am_choking{refaddr $self}},
+ ${$_peer_choking{refaddr $self}}
)
),
($_torrent{refaddr $self}

Modified: trunk/lib/Net/BitTorrent/Torrent.pm
==============================================================================
--- trunk/lib/Net/BitTorrent/Torrent.pm (original)
+++ trunk/lib/Net/BitTorrent/Torrent.pm Mon Jan 5 14:38:02 2009
@@ -25,14 +25,14 @@
use Net::BitTorrent::Torrent::Tracker;
use version qw[qv];
our $SVN = q[$Id$];
- our $UNSTABLE_RELEASE = 2; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
+ our $UNSTABLE_RELEASE = 3; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
my %REGISTRY = ();
- my @CONTENTS = \my (%_client, %path, %_basedir,
- %size, %files, %trackers,
- %infohash, %uploaded, %downloaded,
- %_nodes, %bitfield, %_working_pieces,
- %_block_length, %raw_data, %status,
- %error, %_event
+ my @CONTENTS = \my (%_client, %path, %_basedir,
+ %size, %files, %trackers,
+ %infohash, %uploaded, %downloaded,
+ %bitfield, %_working_pieces, %_block_length,
+ %raw_data, %status, %error,
+ %_event, %resume_path, %_nodes
);
sub STARTED {1}
sub CHECKING {2}
@@ -208,80 +208,95 @@
${$status{refaddr $self}} = $args->{q[Status]};
${$status{refaddr $self}} |= LOADED;
${$error{refaddr $self}} = undef;
- my $_start = 0;

- # Resume system
- if ( $raw_data{refaddr $self}{q[net-bittorrent]}
- && $raw_data{refaddr $self}{q[net-bittorrent]}{q[.version]}
- && $raw_data{refaddr $self}{q[net-bittorrent]}{q[.version]}
- <= 1 # apiver
- )
- { $_nodes{refaddr $self}
- = $raw_data{refaddr $self}{q[net-bittorrent]}{q[nodes]};
- my $_okay = 1;
- for my $_index (0 .. $#{$files{refaddr $self}}) {
- if ((!-f $files{refaddr $self}->[$_index]->path
- && $raw_data{refaddr
$self}{q[net-bittorrent]}{q[files]}
- [$_index]{q[mtime]}
- )
- || ((stat($files{refaddr $self}->[$_index]->path))[9]
- || 0 != $raw_data{refaddr $self}{q[net-bittorrent]}
- {q[files]}[$_index]{q[mtime]})
- )
- { ${$status{refaddr $self}} |= START_AFTER_CHECK;
- $_okay = 0;
- }
- $files{refaddr $self}->[$_index]->set_priority(
- $raw_data{refaddr
$self}{q[net-bittorrent]}{q[files]}
- [$_index]{q[priority]});
+ # Resume system v2
+ my $_start = 1;
+ $resume_path{refaddr $self} = undef;
+ if ($args->{q[Resume]}) {
+ $resume_path{refaddr $self} = $args->{q[Resume]};
+ my $_resume_data;
+ if (-f $args->{q[Resume]}) {
+ open(my ($_RD), q[<], $resume_path{refaddr $self});
+ sysread($_RD, $_resume_data, -s $_RD);
+ close $_RD;
}
- if (!$_okay) {
- $self->_set_error(q[Bad resume data. Please hashcheck.]);
- }
- else {
- ${$bitfield{refaddr $self}}
- = $raw_data{refaddr $self}{q[net-bittorrent]}
- {q[bitfield]};
-
- # Accept resume data is the same as hashchecking
- my $start_after_check
- = ${$status{refaddr $self}} & START_AFTER_CHECK;
- ${$status{refaddr $self}} ^= START_AFTER_CHECK
- if ${$status{refaddr $self}} & START_AFTER_CHECK;
- ${$status{refaddr $self}} ^= CHECKED
- if !(${$status{refaddr $self}} & CHECKED);
- if ($start_after_check) { $_start = 1; }
-
- # Reload Blocks
- for my $_piece (
- @{$raw_data{refaddr
$self}{q[net-bittorrent]}{q[working]}}
+ if ($_resume_data) {
+ $_start = 0;
+ $_resume_data = bdecode($_resume_data);
+
+ # Resume system
+ if ( $_resume_data->{q[.format]}
+ && $_resume_data->{q[.format]} eq
+ q[Net::BitTorrent resume]
+ && $_resume_data->{q[.version]}
+ && $_resume_data->{q[.version]} <= 2 # apiver
)
- { $_working_pieces{refaddr $self}{$_piece->{q[Index]}} =
{
- Index => $_piece->{q[Index]},
- Priority => $_piece->{q[Priority]},
- Blocks_Requested =>
- [map { {} } 1 .. $_piece->{q[Block_Count]}],
- Blocks_Received => [
- map {
- vec($_piece->{q[Blocks_Received]}, $_, 1)
- } 1 .. $_piece->{q[Block_Count]}
- ],
- Block_Length => $_piece->{q[Block_Length]},
- Block_Length_Last =>
$_piece->{q[Block_Length_Last]},
- Block_Count => $_piece->{q[Block_Count]},
- Length => $_piece->{q[Length]},
- Endgame => $_piece->{q[Endgame]},
- Slow => 1, # $_piece->{q[Slow]},
- mtime => time
- };
+ { $_nodes{refaddr $self}
+ = $_resume_data->{q[peers]}
+ ? $_resume_data->{q[peers]}
+ : q[];
+ my $_okay = 1;
+ for my $_index (0 .. $#{$files{refaddr $self}}) {
+ if ((!-f $files{refaddr $self}->[$_index]->path
+ &&
$_resume_data->{q[files]}[$_index]{q[mtime]}
+ )
+ || ((stat($files{refaddr
$self}->[$_index]->path))
+ [9]
+ || 0 != $_resume_data->{q[files]}[$_index]
+ {q[mtime]})
+ )
+ { ${$status{refaddr $self}} |= START_AFTER_CHECK;
+ $_okay = 0;
+ }
+ $files{refaddr $self}->[$_index]->set_priority(
+
$_resume_data->{q[files]}[$_index]{q[priority]});
+ }
+ if (!$_okay) {
+ $self->_set_error(
+ q[Bad resume data. Please
hashcheck.]);
+ }
+ else {
+ ${$bitfield{refaddr $self}}
+ = $_resume_data->{q[bitfield]};
+
+ # Accept resume data is the same as hashchecking
+ my $start_after_check
+ = ${$status{refaddr $self}} &
START_AFTER_CHECK;
+ ${$status{refaddr $self}} ^= START_AFTER_CHECK
+ if ${$status{refaddr $self}} &
START_AFTER_CHECK;
+ ${$status{refaddr $self}} ^= CHECKED
+ if !(${$status{refaddr $self}} & CHECKED);
+ if ($start_after_check) { $_start = 1; }
+
+ # Reload Blocks
+ for my $_piece (@{$_resume_data->{q[working]}}) {
+ $_working_pieces{refaddr $self}
+ {$_piece->{q[Index]}} = {
+ Index => $_piece->{q[Index]},
+ Priority => $_piece->{q[Priority]},
+ Blocks_Requested => [
+ map { {} } 1 ..
$_piece->{q[Block_Count]}
+ ],
+ Blocks_Received => [
+ map {
+ vec($_piece->{q[Blocks_Received]},
+ $_, 1)
+ } 1 .. $_piece->{q[Block_Count]}
+ ],
+ Block_Length => $_piece->{q[Block_Length]},
+ Block_Length_Last =>
+ $_piece->{q[Block_Length_Last]},
+ Block_Count => $_piece->{q[Block_Count]},
+ Length => $_piece->{q[Length]},
+ Endgame => $_piece->{q[Endgame]},
+ Slow => 1, # $_piece->{q[Slow]},
+ mtime => time
+ };
+ }
+ }
}
}
}
- else {
-
- # No resume data was found so we'll just assume they want to
start
- $_start = 1;
- }

# Threads stuff
weaken($REGISTRY{refaddr $self} = $self);
@@ -312,18 +327,19 @@
}

# Accessors | Public
- sub infohash { return $infohash{refaddr +shift}; }
- sub trackers { return $trackers{refaddr +shift}; }
- sub bitfield { return ${$bitfield{refaddr +shift}}; }
- sub path { return $path{refaddr +shift}; }
- sub files { return $files{refaddr +shift}; }
- sub size { return $size{refaddr +shift}; }
- sub status { return ${$status{refaddr +shift}}; }
- sub downloaded { return $downloaded{refaddr +shift}; }
- sub uploaded { return $uploaded{refaddr +shift}; }
- sub error { return ${$error{refaddr +shift}}; }
- sub comment { return $raw_data{refaddr +shift}{q[comment]}; }
- sub created_by { return $raw_data{refaddr +shift}{q[created by]}; }
+ sub infohash { return $infohash{refaddr +shift}; }
+ sub trackers { return $trackers{refaddr +shift}; }
+ sub bitfield { return ${$bitfield{refaddr +shift}}; }
+ sub path { return $path{refaddr +shift}; }
+ sub resume_path { return $resume_path{refaddr +shift}; }
+ sub files { return $files{refaddr +shift}; }
+ sub size { return $size{refaddr +shift}; }
+ sub status { return ${$status{refaddr +shift}}; }
+ sub downloaded { return $downloaded{refaddr +shift}; }
+ sub uploaded { return $uploaded{refaddr +shift}; }
+ sub error { return ${$error{refaddr +shift}}; }
+ sub comment { return $raw_data{refaddr +shift}{q[comment]}; }
+ sub created_by { return $raw_data{refaddr +shift}{q[created by]}; }

sub creation_date {
return $raw_data{refaddr +shift}{q[creation date]};
@@ -404,9 +420,10 @@
}

# Accessors | Private
- sub _client { return $_client{refaddr +shift}; }
- sub _block_length { return $_block_length{refaddr +shift} }
- sub _nodes { return $_nodes{refaddr +shift}; }
+ sub _client { return $_client{refaddr +shift}; }
+ sub _block_length { return $_block_length{refaddr +shift} }
+
+ #sub _nodes { return $_nodes{refaddr +shift}; }
sub _working_pieces { return $_working_pieces{refaddr +shift}; }
sub _basedir { return $_basedir{refaddr +shift}; }

@@ -554,14 +571,6 @@
$downloaded{refaddr $self} += (($amount =~ m[^\d+$]) ? $amount :
0);
}

- sub _append_nodes {
- my ($self, $nodes) = @_;
- return if !$nodes;
- $_nodes{refaddr $self} ||= q[];
- return $_nodes{refaddr $self}
- = compact(uncompact($_nodes{refaddr $self} . $nodes));
- }
-
sub _new_peer {
my ($self) = @_;
return if not defined $_client{refaddr $self};
@@ -572,12 +581,15 @@
}
);
return if (${$status{refaddr $self}} & CHECKING);
- return if !${$status{refaddr $self}} & STARTED;
+ return if !(${$status{refaddr $self}} & STARTED);
return if !(${$status{refaddr $self}} & QUEUED);
- return if !$_nodes{refaddr $self};
+
+ # Don't bother if we're at the hard limit
return
if scalar $self->peers
>= $_client{refaddr $self}->_peers_per_torrent;
+
+ #
my $half_open = scalar(
grep {
$_->{q[Object]}->isa(q[Net::BitTorrent::Peer])
@@ -586,21 +598,49 @@
);

#warn sprintf q[%d half open peers], $half_open;
+ # List of peers to make sure we're not already connected to this
peer
+ my @peers = $self->peers;
+
+ # If we haven't any nodes in cache, gather them from various
sources
+ if (!$_nodes{refaddr $self}) {
+ $_nodes{refaddr $self}
+ = $_client{refaddr $self}->_dht->_peers($self->infohash)
+ if !$self->private;
+ for my $tier (@{$trackers{refaddr $self}}) {
+ for my $url (@{$tier->urls}) {
+ $_nodes{refaddr $self} .= $url->_peers;
+ }
+ }
+ }
+
+ # Don't bother if we haven't any nodes to try
+ return if !$_nodes{refaddr $self};
+
+ # Inflate the list and try them one-by-one
my @nodes = uncompact($_nodes{refaddr $self});
for ($half_open .. $_client{refaddr $self}->_half_open - 1) {
last if !@nodes;
my $node = shift @nodes;
-
- #warn $node;
+ next
+ if scalar grep {
+ sprintf(q[%s:%d], ($_->_host || q[]), ($_->_port || 0)) eq
+ $node # already connected to this peer
+ } @peers;
my $ok = $_client{refaddr $self}
->_event(q[ip_filter], {Address => $node});
if (defined $ok and $ok == 0) { next; }
my $peer =
Net::BitTorrent::Peer->new({Address => $node,
- Torrent => $self
+ Torrent => $self,
+ Source => q[TODO]
}
);
}
+
+ # Store only nodes we haven't tried yet
+ $_nodes{refaddr $self} = compact(@nodes);
+
+ # Return
return 1;
}

@@ -1021,15 +1061,34 @@
}

# Methods | Public | Alpha
- sub resume_data {
- my ($self, $raw) = @_;
+ sub save_resume_data {
+ my ($self, $file) = @_;
+ $file ||= $resume_path{refaddr $self};
+ return if !$file; # Don't even bother without a file to write to

# Make sure file handles are closed so we don't mess up 'mtime'
times
for my $_file (@{$files{refaddr $self}}) { $_file->_close }
- my %resume = %{$raw_data{refaddr $self}};
- $resume{q[net-bittorrent]} = {
+
+ # Gather nodes from various sources
+ # Internal
+ my $_nodes = $_nodes{refaddr $self};
+
+ # DHT
+ $_nodes .= (((${$status{refaddr $self}} & QUEUED)
&& !$self->private)
+ ? $_client{refaddr
$self}->_dht->_peers($self->infohash)
+ : q[]
+ );
+
+ # Trackers
+ for my $tier (@{$trackers{refaddr $self}}) {
+ for my $url (@{$tier->urls}) { $_nodes .= $url->_peers; }
+ }
+
+ # The resume data proper
+ my %resume_data = (
+ q[.format] => q[Net::BitTorrent resume],
q[.t] => time,
- q[.version] => 1,
+ q[.version] => 2,
bitfield => ${$bitfield{refaddr $self}},
files => [
map {
@@ -1038,7 +1097,7 @@
}
} @{$files{refaddr $self}}
],
- peers => $_nodes{refaddr $self},
+ peers => ($_nodes ? $_nodes : q[]),
working => [
map {
{Block_Count => $_->{q[Block_Count]},
@@ -1054,8 +1113,12 @@
}
} values %{$_working_pieces{refaddr $self}}
]
- };
- return $raw ? \%resume : bencode \%resume;
+ );
+
+ # Write it to disk
+ open(my ($_RD), q[>], $file) || return;
+ syswrite($_RD, bencode(\%resume_data)) || return;
+ return close $_RD;
}

# Methods | Public | Utility
@@ -1142,7 +1205,8 @@
scalar @{$trackers{refaddr $self}},
@{$trackers{refaddr $self}} != 1 ? q[s] : q[],
join(qq[\n ],
- map { $_->urls->[0]->url } @{$trackers{refaddr $self}}
+ map { $_->url }
+ map { @{$_->urls} } @{$trackers{refaddr $self}}
);
return defined wantarray ? $dump : print STDERR qq[$dump\n];
}
@@ -1230,7 +1294,7 @@

Creates a C<Net::BitTorrent::Torrent> object. This constructor is
called by
-L<Net::BitTorrent::add_torrent( )|Net::BitTorrent/add_torrent ( { ... } )>.
+L<Net::BitTorrent->add_torrent( )|Net::BitTorrent/add_torrent ( { ... } )>.

C<new( )> accepts arguments as a hash, using key-value pairs:

@@ -1263,6 +1327,17 @@

This is the only required parameter.

+=item C<Resume>
+
+The filename used to gather and store resume data.
+
+This is an optional parameter.
+
+No default. Without a defined resume file, resume data will not be
+written on calls to
+L<save_resume_data ( )|/"save_resume_data ( [ PATH ] )"> without a
+C<PATH> parameter.
+
=item C<Status>

Initial status of the torrent. This parameter is ORed with the loaded
@@ -1373,6 +1448,13 @@

Returns the L<filename|/"Path"> of the torrent this object represents.

+=item C<pause ( )>
+
+Pauses an active torrent without closing related sockets.
+
+See also: L<status ( )|/"status ( )">, L<stop ( )|/"stop ( )">,
+L<start ( )|/"start ( )">
+
=item C<peers ( )>

Returns a list of remote L<peers|Net::BitTorrent::Peer> related to this
@@ -1403,13 +1485,17 @@
(if you have other plans for the data) depending on the boolean value of
the optional C<RAW> parameter.

-=item C<resume_data ( [ RAW ] )>
+=item C<resume_path ( )>
+
+Returns the default path used to
+L<store resume data|/"save_resume_data ( [ PATH ] )">. This value is set
+in the C<Resume> parameter to L<new|/"new ( { [ARGS] } )">.
+
+=item C<save_resume_data ( [ PATH ] )>

-One end of Net::BitTorrent's resume system. This method returns the
-data as specified in
-L<Net::BitTorrent::Notes|Net::BitTorrent::Notes/"Resume API"> in either
-bencoded form or as a raw hash (if you have other plans for the data)
-depending on the boolean value of the optional C<RAW> parameter.
+One end of Net::BitTorrent's resume system. This method writes the
+data to the file specified in the call to L<new( )|/"new ( { [ARGS] } )">
+or (if defined) to the C<PATH> parameter.

See also:
L<Resume API|Net::BitTorrent::Notes/"Resume API">
@@ -1602,13 +1688,6 @@

See also: L<status ( )|/"status ( )">, L<start ( )|/"start ( )">,
L<pause ( )|/"pause ( )">
-
-=item C<pause ( )>
-
-Pauses an active torrent without closing related sockets.
-
-See also: L<status ( )|/"status ( )">, L<stop ( )|/"stop ( )">,
-L<start ( )|/"start ( )">

=item C<trackers>


Modified: trunk/lib/Net/BitTorrent/Torrent/Tracker.pm
==============================================================================
--- trunk/lib/Net/BitTorrent/Torrent/Tracker.pm (original)
+++ trunk/lib/Net/BitTorrent/Torrent/Tracker.pm Mon Jan 5 14:38:02 2009
@@ -11,8 +11,8 @@
use Net::BitTorrent::Torrent::Tracker::UDP;
use version qw[qv];
our $SVN = q[$Id$];
- our $UNSTABLE_RELEASE = 1; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
- my (@CONTENTS) = \my (%torrent, %urls, %complete, %incomplete);
+ our $UNSTABLE_RELEASE = 2; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
+ my (@CONTENTS) = \my (%torrent, %urls);
my %REGISTRY;

sub new {
@@ -40,9 +40,7 @@
$self = bless(\$args->{q[URLs]}->[0], $class);
$torrent{refaddr $self} = $args->{q[Torrent]};
weaken $torrent{refaddr $self};
- $complete{refaddr $self} = 0;
- $incomplete{refaddr $self} = 0;
- $urls{refaddr $self} = [];
+ $urls{refaddr $self} = [];
for my $_url (@{$args->{q[URLs]}}) {
push @{$urls{refaddr $self}},
($_url =~ m[^http://]i
@@ -56,25 +54,18 @@
}

# Accessors | Public
- sub incomplete { return $incomplete{refaddr +shift} }
- sub complete { return $complete{refaddr +shift} }
- sub urls { return $urls{refaddr +shift}; }
+ sub urls { return $urls{refaddr +shift}; }

# Accessors | Private
sub _client { return $torrent{refaddr +shift}->_client; }
sub _torrent { return $torrent{refaddr +shift}; }

- # Methods | Private
- sub _set_complete {
- my ($self, $value) = @_;
- return $complete{refaddr $self} = $value;
- }
-
- sub _set_incomplete {
- my ($self, $value) = @_;
- return $incomplete{refaddr $self} = $value;
+ sub _nodes {
+ my ($self) = @_;
+ return compact(map { $_->_nodes } @{$urls{refaddr $self}});
}

+ # Methods | Private
sub _shuffle {
my ($self) = @_;
return (
@@ -99,8 +90,6 @@
Number of URLs: %d
%s
END
- $complete{refaddr $self},
- $incomplete{refaddr $self},
scalar(@{$urls{refaddr $self}}),
join qq[\r\n ], map { $_->url() } @{$urls{refaddr $self}};
return defined wantarray ? $dump : print STDERR qq[$dump\n];

Modified: trunk/lib/Net/BitTorrent/Torrent/Tracker/HTTP.pm
==============================================================================
--- trunk/lib/Net/BitTorrent/Torrent/Tracker/HTTP.pm (original)
+++ trunk/lib/Net/BitTorrent/Torrent/Tracker/HTTP.pm Mon Jan 5 14:38:02
2009
@@ -9,12 +9,15 @@
use Socket qw[PF_INET SOMAXCONN SOCK_STREAM inet_aton
pack_sockaddr_in];
use Fcntl qw[F_SETFL O_NONBLOCK];
use lib q[../../../../../lib];
- use Net::BitTorrent::Util qw[:bencode uncompact];
+ use Net::BitTorrent::Util qw[:bencode :compact];
use version qw[qv];
our $SVN = q[$Id$];
- our $UNSTABLE_RELEASE = 1; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
+ our $UNSTABLE_RELEASE = 2; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
my (@CONTENTS)
- = \my (%_url, %_tier, %resolve, %_event, %_socket, %_data_out);
+ = \my (%_url, %_tier, %resolve,
+ %_event, %_socket, %_data_out,
+ %_peers, %_complete, %_incomplete
+ );
my %REGISTRY;

sub new {
@@ -34,8 +37,11 @@
return;
}
my $self = bless \$args->{q[URL]}, $class;
- $_url{refaddr $self} = $args->{q[URL]};
- $_tier{refaddr $self} = $args->{q[Tier]};
+ $_url{refaddr $self} = $args->{q[URL]};
+ $_tier{refaddr $self} = $args->{q[Tier]};
+ $_peers{refaddr $self} = q[];
+ $_complete{refaddr $self} = 0;
+ $_incomplete{refaddr $self} = 0;
weaken $_tier{refaddr $self};
weaken($REGISTRY{refaddr $self} = $self);
return $self;
@@ -47,6 +53,8 @@
# Accesors | Private
sub _socket { return $_socket{refaddr +shift}; }
sub _tier { return $_tier{refaddr +shift}; }
+ sub _peers { return $_peers{refaddr +shift}; }
+ sub _client { return $_tier{refaddr +shift}->_client }

# Methods | Private
sub _announce {
@@ -267,12 +275,9 @@
);
}
else {
- $_tier{refaddr $self}
- ->_torrent->_append_nodes($data->{q[peers]});
- $_tier{refaddr $self}
- ->_set_complete($data->{q[complete]});
- $_tier{refaddr $self}
- ->_set_incomplete($data->{q[incomplete]});
+ $_peers{refaddr $self} = $data->{q[peers]};
+ $_complete{refaddr $self} = $data->{q[complete]};
+ $_incomplete{refaddr $self} =
$data->{q[incomplete]};
$_tier{refaddr $self}
->_torrent->_event(q[tracker_success],
{Tracker => $self, Payload =>
$data});

Modified: trunk/lib/Net/BitTorrent/Torrent/Tracker/UDP.pm
==============================================================================
--- trunk/lib/Net/BitTorrent/Torrent/Tracker/UDP.pm (original)
+++ trunk/lib/Net/BitTorrent/Torrent/Tracker/UDP.pm Mon Jan 5 14:38:02 2009
@@ -8,15 +8,17 @@
use List::Util qw[sum];
use Socket qw[inet_aton pack_sockaddr_in];
use lib q[../../../../../lib];
- use Net::BitTorrent::Util qw[uncompact];
+ use Net::BitTorrent::Util qw[:compact];
use version qw[qv];
our $SVN = q[$Id$];
- our $UNSTABLE_RELEASE = 0; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
+ our $UNSTABLE_RELEASE = 2; our $VERSION = sprintf(($UNSTABLE_RELEASE ?
q[%.3f_%03d] : q[%.3f]), (version->new((qw$Rev$)[1])->numify / 1000),
$UNSTABLE_RELEASE);
my %REGISTRY = ();
- my @CONTENTS
- = \
- my (%_url, %_tier, %_tid, %_cid, %_outstanding_requests,
- %_packed_host, %_event);
+ my @CONTENTS = \my (%_url, %_tier,
+ %_tid, %_cid,
+ %_outstanding_requests, %_packed_host,
+ %_event, %_peers,
+ %_complete, %_incomplete
+ );

sub new {
my ($class, $args) = @_;
@@ -48,10 +50,13 @@
else { $packed_host = inet_aton($host) }
$_packed_host{refaddr $self}
= pack_sockaddr_in($port, inet_aton($host));
- $_url{refaddr $self} = $args->{q[URL]};
- $_event{refaddr $self} = q[];
- $_tier{refaddr $self} = $args->{q[Tier]};
- $_tid{refaddr $self} = int(rand() * 26**5);
+ $_url{refaddr $self} = $args->{q[URL]};
+ $_event{refaddr $self} = q[];
+ $_tier{refaddr $self} = $args->{q[Tier]};
+ $_peers{refaddr $self} = q[];
+ $_complete{refaddr $self} = 0;
+ $_incomplete{refaddr $self} = 0;
+ $_tid{refaddr $self} = int(rand() * 26**5);
weaken $_tier{refaddr $self};
weaken($REGISTRY{refaddr $self} = $self);
return $self;
@@ -63,6 +68,7 @@
# Accessors | Private
sub _packed_host { return $_packed_host{refaddr +shift} }
sub _tier { return $_tier{refaddr +shift}; }
+ sub _peers { return $_peers{refaddr +shift}; }
sub _client { return $_tier{refaddr +shift}->_client }

# Methods | Private
@@ -208,9 +214,9 @@
if (length($data) >= 20) {
my ($min_interval, $leeches, $seeds, $peers)
= unpack(q[N N N a*], $packet);
- $_tier{refaddr $self}->_torrent->_append_nodes($peers);
- $_tier{refaddr $self}->_set_complete($seeds);
- $_tier{refaddr $self}->_set_incomplete($leeches);
+ $_peers{refaddr $self} = $peers;
+ $_complete{refaddr $self} = $seeds;
+ $_incomplete{refaddr $self} = $leeches;
$_tier{refaddr $self}->_torrent->_event(
q[tracker_success],
{Tracker => $self,

Modified: trunk/lib/Net/BitTorrent/Version.pm
==============================================================================
--- trunk/lib/Net/BitTorrent/Version.pm (original)
+++ trunk/lib/Net/BitTorrent/Version.pm Mon Jan 5 14:38:02 2009
@@ -5,7 +5,7 @@
use warnings;
use version qw[qv];
our $SVN = q[$Id$];
- our $VERSION_BASE = 46; our $UNSTABLE_RELEASE = 2; our $VERSION =
sprintf(($UNSTABLE_RELEASE ? q[%.3f_%03d] : q[%.3f]),
(version->new(($VERSION_BASE))->numify / 1000), $UNSTABLE_RELEASE);
+ our $VERSION_BASE = 49; our $UNSTABLE_RELEASE = 0; our $VERSION =
sprintf(($UNSTABLE_RELEASE ? q[%.3f_%03d] : q[%.3f]),
(version->new(($VERSION_BASE))->numify / 1000), $UNSTABLE_RELEASE);
our $PRODUCT_TOKEN = qq[Net::BitTorrent $VERSION];

sub gen_peerid {

Modified: trunk/t/000_miniswarm/009_miniswarm_udp.t
==============================================================================
--- trunk/t/000_miniswarm/009_miniswarm_udp.t (original)
+++ trunk/t/000_miniswarm/009_miniswarm_udp.t Mon Jan 5 14:38:02 2009
@@ -24,7 +24,7 @@
my $BlockLength = 2**14;
my $Seeds = 1;
my $Peers = 5;
-my $Timeout = 60;
+my $Timeout = 30;
plan tests => int(($Seeds * 2) + ($Peers * 2));
my $sprintf = q[%0] . length($Peers > $Seeds ? $Peers : $Seeds) . q[d];
my $_infohash = q[2b3aaf361bd40540bf7e3bfd140b954b90e4dfbc];

Modified: trunk/t/700_classes/Net/BitTorrent.t
==============================================================================
--- trunk/t/700_classes/Net/BitTorrent.t (original)
+++ trunk/t/700_classes/Net/BitTorrent.t Mon Jan 5 14:38:02 2009
@@ -19,7 +19,7 @@
my $release_testing = $build->notes(q[release_testing]);
my $verbose = $build->notes(q[verbose]);
$SIG{__WARN__} = ($verbose ? sub { diag shift } : sub { });
-plan tests => 84;
+plan tests => 90;
my ($tempdir) = tempdir(q[~NBSF_test_XXXXXXXX], CLEANUP => 1, TMPDIR => 1);
warn(sprintf(q[File::Temp created '%s' for us to play with], $tempdir));
my $torrent;
@@ -242,8 +242,22 @@
is_deeply($client->torrents, {}, q[ Check if torrent was removed]);
like($client->_peers_per_torrent, qr[^\d+$],
q[_peers_per_torrent() is a number]);
-ok($client->as_string(), q[as_string()]);
-ok($client->as_string(1), q[as_string(1)]);
+ok($client->as_string(), q[as_string() | simple]);
+ok($client->as_string(0), q[as_string(0) | simple]);
+is($client->as_string(), $client->as_string(0),
+ q[as_string() == as_string(0)]);
+isn't($client->as_string(), $client->as_string(1),
+ q[as_string() != as_string(1)]);
+sub TIEHANDLE { pass(q[Tied STDERR]); bless \{}, shift; }
+
+sub PRINT {
+ is((caller(0))[0], q[Net::BitTorrent], q[String written to STDERR]);
+}
+sub UNTIE { pass(q[Untied STDERR]); }
+tie(*STDERR, __PACKAGE__);
+$client->as_string();
+$client->as_string(1);
+untie *STDERR;
SKIP: {
skip(q[UDP-based tests have been disabled.],
($test_builder->{q[Expected_Tests]} -
$test_builder->{q[Curr_Test]})

Modified: trunk/t/700_classes/Net/BitTorrent/Peer.t
==============================================================================
--- trunk/t/700_classes/Net/BitTorrent/Peer.t (original)
+++ trunk/t/700_classes/Net/BitTorrent/Peer.t Mon Jan 5 14:38:02 2009
@@ -654,19 +654,54 @@
undef,
q[Torrent => bless(\{}, 'junk')]
);
+ is( Net::BitTorrent::Peer->new({Client => $client,
+ Torrent => $torrent,
+ Address => q[127.0.0.1:0]
+ }
+ ),
+ undef,
+ q[No Source]
+ );
+ is( Net::BitTorrent::Peer->new({Client => $client,
+ Torrent => $torrent,
+ Address => q[127.0.0.1:0],
+ Source => undef
+ }
+ ),
+ undef,
+ q[Source => undef]
+ );
warn sprintf q[%d|%d], 21, $test_builder->{q[Curr_Test]};
warn(q[Test incoming peers]);
{
$peers{q[A]} =
Net::BitTorrent::Peer->new({Client => $client,
Torrent => $torrent,
- Address => q[127.0.0.1:0]
+ Address => q[127.0.0.1:0],
+ Source => q[User]
}
);
isa_ok($peers{q[A]}, q[Net::BitTorrent::Peer], q[new()]);
weaken $peers{q[A]};
ok(isweak($peers{q[A]}), q[ ...make $peers{q[A]} a weak ref]);
ok($peers{q[A]}->as_string, q[as_string]);
+ is($peers{q[A]}->as_string,
+ $peers{q[A]}->as_string(0),
+ q[as_string() vs as_string(0)]);
+ isn't($peers{q[A]}->as_string,
+ $peers{q[A]}->as_string(1),
+ q[as_string() vs as_string(1)]);
+ sub TIEHANDLE { pass(q[Tied STDERR]); bless \{}, shift; }
+
+ sub PRINT {
+ is((caller(0))[0],
+ q[Net::BitTorrent::Peer], q[String written to STDERR]);
+ }
+ sub UNTIE { pass(q[Untied STDERR]); }
+ tie(*STDERR, __PACKAGE__);
+ $peers{q[A]}->as_string;
+ $peers{q[A]}->as_string(1);
+ untie *STDERR;
isa_ok($peers{q[A]}->_socket, q[GLOB], q[_socket]);
isa_ok($peers{q[A]}->_torrent,
q[Net::BitTorrent::Torrent], q[_torrent]);

Modified: trunk/t/700_classes/Net/BitTorrent/Torrent.t
==============================================================================
--- trunk/t/700_classes/Net/BitTorrent/Torrent.t (original)
+++ trunk/t/700_classes/Net/BitTorrent/Torrent.t Mon Jan 5 14:38:02 2009
@@ -4,7 +4,7 @@
use Module::Build;
use Test::More;
use Digest::SHA qw[sha1_hex];
-use File::Temp qw[tempdir];
+use File::Temp qw[tempdir tempfile];
use Scalar::Util qw[/weak/];
use File::Spec::Functions qw[rel2abs];
use lib q[../../../../lib];
@@ -18,7 +18,7 @@
my $build = Module::Build->current;
my %torrents = ();
_locate_torrents();
-plan tests => int(6 + (81 * scalar keys %torrents));
+plan tests => int(6 + (90 * scalar keys %torrents));
my $okay_tcp = $build->notes(q[okay_tcp]);
my $release_testing = $build->notes(q[release_testing]);
my $verbose = $build->notes(q[verbose]);
@@ -105,8 +105,6 @@
);
is($torrent->_client, $client, sprintf q[Client is correct (%s)],
$_key);
- is($torrent->_nodes, q[], sprintf q[Empty list of compact nodes
(%s)],
- $_key);
is($torrent->comment,
_raw_data($_key)->{q[comment]},
sprintf q[comment is correct (%s)], $_key);
@@ -347,11 +345,46 @@
);
is($torrent->bitfield, $_new_bitfield,
sprintf q[Bitfield has not changed. (%s)], $_key);
- ok($torrent->as_string(), sprintf q[as_string( ) | simple (%s)],
- $_key);
- ok($torrent->as_string(1),
- sprintf q[as_string(1) | advanced (%s)], $_key);
- warn q[Test multithreaded stuff...];
+ is($torrent->as_string(), $torrent->infohash,
+ sprintf q[as_string( ) | simple (%s)], $_key);
+ is($torrent->as_string(0),
+ $torrent->infohash, sprintf q[as_string(0) | simple (%s)],
$_key);
+ isn't($torrent->as_string(1),
+ $torrent->infohash, sprintf q[as_string(1) | advanced (%s)],
+ $_key);
+ sub TIEHANDLE { pass(q[Tied STDERR]); bless \{}, shift; }
+
+ sub PRINT {
+ is((caller(0))[0],
+ q[Net::BitTorrent::Torrent], q[String written to STDERR]);
+ }
+ sub UNTIE { pass(q[Untied STDERR]); }
+ tie(*STDERR, __PACKAGE__);
+ $torrent->as_string();
+ $torrent->as_string(1);
+ untie *STDERR;
+ ok(!$torrent->save_resume_data,
+ sprintf q[save_resume_data() (%s)], $_key);
+ my ($_fh, $_filename)
+ = tempfile(q[NB_XXXX],
+ SUFFIX => q[.resume],
+ TMPDIR => 1,
+ UNLINK => 1
+ );
+ is( $torrent->resume_path(),
+ undef,
+ sprintf
+ q[resume_path( ) (empty if not set in call to new()) (%s)],
+ $_key
+ );
+ ok($torrent->save_resume_data($_filename),
+ sprintf q[save_resume_data('%s') (%s)],
+ $_filename, $_key);
+ ok(-f $_filename,
+ sprintf q[Resume data file was created... (%s)], $_key);
+ ok(-s $_filename,
+ sprintf q[ ...and has data. (%s)], $_key);
+ warn q[TODO: Restore data];
SKIP: {
skip q[Multi-threaded tests have been skipped], 5 if !$threads;
skip

Modified: trunk/t/700_classes/Net/BitTorrent/Torrent/Tracker.t
==============================================================================
--- trunk/t/700_classes/Net/BitTorrent/Torrent/Tracker.t (original)
+++ trunk/t/700_classes/Net/BitTorrent/Torrent/Tracker.t Mon Jan 5
14:38:02 2009
@@ -19,7 +19,7 @@
$|++;
my $multi_dot_torrent = q[./t/900_data/950_torrents/952_multi.torrent];
my $single_dot_torrent = q[./t/900_data/950_torrents/951_single.torrent];
-plan tests => 18;
+plan tests => 14;
SKIP: {
my $client = Net::BitTorrent->new();
my $torrent =
@@ -106,10 +106,6 @@
Torrent => $torrent
}
);
- ok($tracker->_set_complete(30), q[Set number of seeds]);
- ok($tracker->_set_incomplete(50), q[Set number of peers]);
- is($tracker->complete(), 30, q[Get number of seeds]);
- is($tracker->incomplete(), 50, q[Get number of seeds]);
is_deeply($tracker->_torrent, $torrent,
q[Get related N::B::Torrent object]);
is($tracker->_client->isa(q[Net::BitTorrent]),

Modified: trunk/tatoeba/002-debug.pl
==============================================================================
--- trunk/tatoeba/002-debug.pl (original)
+++ trunk/tatoeba/002-debug.pl Mon Jan 5 14:38:02 2009
@@ -155,7 +155,8 @@
$torrent->start;
l $torrent->as_string(1);
l q[starting event loop...];
-$client->do_one_loop(0.25) && sleep(0.50) while $torrent;
+$client->do_one_loop(0.25) && sleep(0.50) while !$torrent->is_complete;
+l q[Exiting];

=pod


Modified: trunk/tatoeba/004-resume.pl
==============================================================================
--- trunk/tatoeba/004-resume.pl (original)
+++ trunk/tatoeba/004-resume.pl Mon Jan 5 14:38:02 2009
@@ -3,24 +3,23 @@
use warnings;
use Time::HiRes qw[sleep];
use Net::BitTorrent;
-use Data::Dump qw[pp];
my $client = Net::BitTorrent->new();
-my $torrent = $client->add_torrent({Path => 'a.legal.torrent'}) or exit;
+my $torrent
+ = $client->add_torrent({Path => 'a.legal.torrent', Resume
=> '.resume'})
+ or exit;
$torrent->on_event(q[piece_hash_pass], sub { save(shift) });
-$torrent->hashcheck;
-sleep(0.25) and $client->do_one_loop(0.25) while !$torrent->is_complete;

END {
for my $t (values %{$client->torrents || {}}) { save($t); }
}
+$torrent->hashcheck;
+sleep(0.25) and $client->do_one_loop(0.25) while !$torrent->is_complete;

sub save {
my ($torrent) = @_;
- rename $torrent->path, $torrent->path . q[.bak]
- if !-f $torrent->path . q[.bak];
- open my $TORRENT, q[>], $torrent->path or next;
- syswrite($TORRENT, $torrent->resume_data) or next;
- close $TORRENT;
+ rename $torrent->resume_path, $torrent->resume_path . q[.bak]
+ if !-f $torrent->resume_path . q[.bak];
+ $torrent->save_resume_data;
}

=pod
@@ -42,51 +41,40 @@

=over

-=item Line 8
+=item Line 7

-Loads our .torrent file. If
-L<Net::BitTorrent::Torrent|Net::BitTorrent::Torrent/"new ( { [ARGS] } )">
-finds any resume data, it's automatically restored.
+Loads our .torrent file and sets the filename for the related resume
+data.

-=item Line 9
+=item Line 10

Sets a per-torrent callback which, when triggered, saves our resume data.

-=item Line 13-15
-
-This is probably the most important place to save resume data because on
-restore, the last modified times of each file is compared with the times
-stored in the resume data. If any of them fail to match, all of the
-resume data is considered invalid.
+=item Line 12-14

Here, at the end of the process, we store resume data for every torrent
in the client. Yes, yes, I know... this little script only loads a
single torrent. Consider it a bonus for folks writing your own your own
clients. A small and obvious bonus, sure, but a bonus all the same.

-=item Line 19
+This is probably the most important place to save resume data because on
+restore, the last modified times of each file is compared with the times
+stored in the resume data. If any of them fail to match, all of the
+resume data is considered invalid.
+
+=item Line 20

-Let's keep a backup of the original metadata just in case
-L<Net::BitTorrent::Torrent|Net::BitTorrent::Torrent> (slim chance) or you
-(rather likely) makes a mistake and ruins everything.
-
-=item Line 21
-
-Opens the .torrent file in write mode. Once the file is parsed in
-L<C<new( )>|Net::BitTorrent::Torrent/"new ( { [ARGS] } )">,
-L<Net::BitTorrent::Torrent|Net::BitTorrent::Torrent> is finished with it,
-so if the file is locked and fails to open here, some other process may
-be to blame.
+Let's keep a backup of the
+L<original metadata file|Net::BitTorrent::Torrent/"resume_path ( )"> just
+in case L<Net::BitTorrent::Torrent|Net::BitTorrent::Torrent> makes a
+mistake and ruins everything.

=item Line 22

Writes the new
-L<'resume data'|Net::BitTorrent::Torrent/"resume_data ( [ RAW ] )"> to
-the file. Notice I'm using C<syswrite> here because the resume data will
-certainly contain binary data. For more, see the warnings listed in
-L<Net::BitTorrent::Notes|Net::BitTorrent::Notes/"badresumedata">.
-
-Now, the next time L<Net::BitTorrent::Torrent|Net::BitTorrent::Torrent">
+L<resume data|Net::BitTorrent::Torrent/"save_resume_data ( [ PATH ] )">
+to the file. Now, the next time
+L<Net::BitTorrent::Torrent|Net::BitTorrent::Torrent/"new ( { [ARGS] } )">
loads this file, it will see the resume data and if it looks okay, your
progress will be restored.

@@ -101,7 +89,7 @@
would keep resume data as up to date as possible, but there are certain
internal steps taken while resume data is gathered that would, in the
long run, slow everything down to a crawl. For more, see
-L<C<resume_data ( )>|Net::BitTorrent::Torrent/"resume_data ( [ RAW ] )"> in
+L<C<save_resume_data ( )>|Net::BitTorrent::Torrent/"save_resume_data ( [
PATH ] )"> in
L<Net::BitTorrent::Torrent|Net::BitTorrent::Torrent> and the sections
L<'Resume API'|Net::BitTorrent::Notes/"Resume API"> and
L<'How do I quick Resume a .torrent Session Between Client Sessions?'|
Net::BitTorrent::Notes/"Quick Resume a .torrent Session Between Client
Sessions">

Reply all
Reply to author
Forward
0 new messages