line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package CGI::Easy::SendFile; |
2
|
2
|
|
|
2
|
|
116372
|
use 5.010001; |
|
2
|
|
|
|
|
9
|
|
3
|
2
|
|
|
2
|
|
8
|
use warnings; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
37
|
|
4
|
2
|
|
|
2
|
|
7
|
use strict; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
36
|
|
5
|
2
|
|
|
2
|
|
477
|
use utf8; |
|
2
|
|
|
|
|
13
|
|
|
2
|
|
|
|
|
6
|
|
6
|
2
|
|
|
2
|
|
39
|
use Carp; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
111
|
|
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
our $VERSION = 'v2.0.1'; |
9
|
|
|
|
|
|
|
|
10
|
2
|
|
|
2
|
|
449
|
use Export::Attrs; |
|
2
|
|
|
|
|
6432
|
|
|
2
|
|
|
|
|
8
|
|
11
|
2
|
|
|
2
|
|
126
|
use List::Util qw( min ); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
151
|
|
12
|
2
|
|
|
2
|
|
391
|
use CGI::Easy::Util qw( date_http ); |
|
2
|
|
|
|
|
4409
|
|
|
2
|
|
|
|
|
7
|
|
13
|
|
|
|
|
|
|
|
14
|
2
|
|
|
2
|
|
1094
|
use constant STAT_MTIME => 9; |
|
2
|
|
|
|
|
2
|
|
|
2
|
|
|
|
|
178
|
|
15
|
2
|
|
|
2
|
|
14
|
use constant BUF_SIZE => 64*1024; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
561
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
sub send_file :Export { |
19
|
25
|
|
|
25
|
1
|
24825
|
my ($r, $h, $file, $opt) = @_; |
20
|
|
|
|
|
|
|
my %p = ( |
21
|
|
|
|
|
|
|
type => 'application/x-download', |
22
|
|
|
|
|
|
|
range => !ref $file, |
23
|
|
|
|
|
|
|
cache => 0, |
24
|
|
|
|
|
|
|
inline => 0, |
25
|
25
|
100
|
|
|
|
41
|
%{$opt || {}}, |
|
25
|
|
|
|
|
120
|
|
26
|
|
|
|
|
|
|
); |
27
|
|
|
|
|
|
|
|
28
|
25
|
100
|
|
|
|
55
|
if (!$p{cache}) { |
29
|
20
|
|
|
|
|
26
|
$h->{'Expires'} = 'Sat, 01 Jan 2000 00:00:00 GMT'; |
30
|
|
|
|
|
|
|
} |
31
|
|
|
|
|
|
|
else { |
32
|
5
|
|
|
|
|
7
|
delete $h->{'Expires'}; |
33
|
5
|
100
|
|
|
|
9
|
if (!ref $file) { |
34
|
4
|
|
|
|
|
56
|
my $lastmod = date_http((stat $file)[STAT_MTIME]); |
35
|
4
|
|
|
|
|
90
|
my $ifmod = $r->{ENV}{HTTP_IF_MODIFIED_SINCE}; |
36
|
4
|
100
|
100
|
|
|
17
|
if ($ifmod && $ifmod eq $lastmod) { |
37
|
1
|
|
|
|
|
2
|
$h->{'Status'} = '304 Not Modified'; |
38
|
1
|
|
|
|
|
4
|
return \q{}; |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
else { |
41
|
3
|
|
|
|
|
6
|
$h->{'Last-Modified'} = $lastmod; |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
24
|
100
|
|
|
|
88
|
my $len = ref $file ? length ${$file} : -s $file; |
|
19
|
|
|
|
|
25
|
|
47
|
24
|
|
|
|
|
49
|
my ($start, $end) = _get_range($p{range}, $r, $len); |
48
|
24
|
|
|
|
|
38
|
my $size = $end-$start+1; |
49
|
|
|
|
|
|
|
|
50
|
24
|
|
|
|
|
32
|
$h->{'Accept-Ranges'} = 'bytes'; |
51
|
24
|
|
|
|
|
25
|
$h->{'Content-Length'} = $size; |
52
|
24
|
|
|
|
|
31
|
$h->{'Content-Type'} = $p{type}; |
53
|
24
|
100
|
|
|
|
37
|
if (!$p{inline}) { |
54
|
19
|
|
|
|
|
38
|
$h->{'Content-Disposition'} = 'attachment'; |
55
|
|
|
|
|
|
|
} |
56
|
24
|
100
|
100
|
|
|
59
|
if (!($start == 0 && $end == $len-1)) { |
57
|
8
|
|
|
|
|
9
|
$h->{Status} = '206 Partial Content'; |
58
|
8
|
|
|
|
|
16
|
$h->{'Content-Range'} = "bytes $start-$end/$len"; |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
24
|
|
|
|
|
37
|
return _read_block($file, $start, $size); |
62
|
2
|
|
|
2
|
|
12
|
} |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
7
|
|
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
sub _get_range { |
65
|
24
|
|
|
24
|
|
34
|
my ($allow_range, $r, $len) = @_; |
66
|
24
|
|
|
|
|
38
|
my ($start, $end) = (0, $len-1); |
67
|
24
|
100
|
100
|
|
|
76
|
if ($allow_range && defined $r->{ENV}{HTTP_RANGE}) { |
68
|
15
|
50
|
|
|
|
78
|
if ($r->{ENV}{HTTP_RANGE} =~ /\Abytes=(\d*)-(\d*)\z/ixms) { |
69
|
15
|
|
|
|
|
40
|
my ($from, $to) = ($1, $2); |
70
|
15
|
100
|
100
|
|
|
98
|
if ($from ne q{} && $to ne q{} && $from <= $to && $to < $len) { # 0-0, 0-499, 500-999 |
|
|
100
|
66
|
|
|
|
|
|
|
100
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
71
|
5
|
|
|
|
|
7
|
$start = $from; |
72
|
5
|
|
|
|
|
7
|
$end = $to; |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
elsif ($from ne q{} && $to eq q{} && $from < $len) { # 0-, 500-, 999- |
75
|
3
|
|
|
|
|
5
|
$start = $from; |
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
elsif ($from eq q{} && $to ne q{} && 0 < $to && $to <= $len) { # -1, -500, -1000 |
78
|
3
|
|
|
|
|
6
|
$start = $len - $to; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
} |
82
|
24
|
|
|
|
|
45
|
return ($start, $end); |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
sub _read_block { |
86
|
24
|
|
|
24
|
|
32
|
my ($file, $start, $size) = @_; |
87
|
24
|
|
|
|
|
25
|
my $data = q{}; |
88
|
24
|
50
|
|
1
|
|
297
|
open my $fh, '<', $file or croak "open: $!"; |
|
1
|
|
|
|
|
5
|
|
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
4
|
|
89
|
24
|
|
|
|
|
634
|
seek $fh, $start, 0; |
90
|
24
|
|
|
|
|
34
|
my ($n, $buf); |
91
|
24
|
|
|
|
|
157
|
while ($n = read $fh, $buf, min($size, BUF_SIZE)) { |
92
|
24
|
|
|
|
|
30
|
$size -= length $buf; |
93
|
24
|
|
|
|
|
104
|
$data .= $buf; |
94
|
|
|
|
|
|
|
} |
95
|
24
|
50
|
|
|
|
41
|
croak "read: $!" if !defined $n; |
96
|
24
|
50
|
|
|
|
76
|
close $fh or croak "close: $!"; |
97
|
24
|
|
|
|
|
108
|
return \$data; |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
1; # Magic true value required at end of module |
102
|
|
|
|
|
|
|
__END__ |