File Coverage

lib/Net/BitTorrent/Tracker/WebSeed.pm
Criterion Covered Total %
statement 14 14 100.0
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 19 19 100.0


line stmt bran cond sub pod time code
1 21     21   212841 use v5.40;
  21         79  
2 21     21   214 use feature 'class';
  21         44  
  21         2722  
3 21     21   123 no warnings 'experimental::class';
  21         59  
  21         983  
4 21     21   506 use Net::BitTorrent::Emitter;
  21         48  
  21         2479  
5             class Net::BitTorrent::Tracker::WebSeed v2.0.0 : isa(Net::BitTorrent::Emitter) {
6 21     21   721 use HTTP::Tiny;
  21         41854  
  21         17875  
7             field $url : param : reader; # Base URL
8             field $disabled : reader = 0;
9              
10             method fetch_piece ($segments) {
11             return undef if $disabled;
12             my $http = HTTP::Tiny->new( max_redirect => 5 );
13             my $full_data = '';
14             for my $seg (@$segments) {
15              
16             # ... URL construction ...
17             my $target_url = $self->_build_url($seg);
18             my $response = $http->get( $target_url, { headers => { Range => "bytes=$seg->{offset}-" . ( $seg->{offset} + $seg->{length} - 1 ) } } );
19             if ( $response->{success} ) {
20             $full_data .= $response->{content};
21             }
22             elsif ( $response->{status} == 410 ) {
23             $disabled = 1;
24             $self->_emit( log => " [WebSeed] Resource 410 Gone: $target_url. Disabling webseed.\n", level => 'warn' );
25             return undef;
26             }
27             else {
28             $self->_emit( log => "WebSeed fetch failed: $response->{status} $response->{reason} (URL: $target_url)", level => 'error' );
29             return undef;
30             }
31             }
32             return $full_data;
33             }
34              
35             method _build_url ($seg) {
36             my $target_url = $url;
37             if ( $target_url =~ m{/$} ) {
38             my $rel = $seg->{rel_path} // $seg->{file}->path->basename;
39             $target_url .= $rel;
40             }
41             return $target_url;
42             }
43              
44             # Backward compatibility for single-file v1
45             method fetch_piece_legacy ( $index, $piece_length, $total_size ) {
46             my $start = $index * $piece_length;
47             my $end = $start + $piece_length - 1;
48             $end = $total_size - 1 if $end >= $total_size;
49             return $self->fetch_piece( [ { file => undef, offset => $start, length => ( $end - $start + 1 ), rel_path => undef } ] );
50             }
51             } 1;