File Coverage

blib/lib/CGI/Application/Plugin/Stream.pm
Criterion Covered Total %
statement 59 61 96.7
branch 11 14 78.5
condition 11 16 68.7
subroutine 8 8 100.0
pod 2 2 100.0
total 91 101 90.1


line stmt bran cond sub pod time code
1             package CGI::Application::Plugin::Stream;
2              
3 2     2   41955 use 5.006;
  2         10  
4 2     2   11 use strict;
  2         4  
  2         62  
5 2     2   11 use warnings;
  2         2  
  2         106  
6              
7 2     2   1539 use CGI::Application 3.21;
  2         14023  
  2         68  
8 2     2   16 use File::Basename;
  2         5  
  2         220  
9             require Exporter;
10 2     2   9 use vars (qw/@ISA @EXPORT_OK/);
  2         3  
  2         1030  
11             @ISA = qw(Exporter);
12              
13             @EXPORT_OK = qw(stream stream_file);
14              
15             our $VERSION = '3.00_1';
16              
17             sub stream {
18 20     20 1 7881 my ( $self, $file_or_fh, $bytes ) = @_;
19              
20 20   100     94 $bytes ||= 1024;
21              
22             # Use unbuffered output, but return the state of $| to its previous state when we are done.
23 20         67 local $| = 1;
24              
25 20         19 my ($fh, $basename);
26 20         187 my $size = (stat( $file_or_fh ))[7];
27              
28             # If we have a file path
29 20 100       67 if ( ref( \$file_or_fh ) eq 'SCALAR' ) {
30             # They passed along a scalar, pointing to the path of the file
31             # So we need to open the file
32 4 50       111 open($fh,"<$file_or_fh" ) || die "failed to open file: $file_or_fh: $!";
33             # Now let's go binmode (Thanks, William!)
34 4         13 binmode $fh;
35 4         210 $basename = basename( $file_or_fh );
36             }
37             # We have a file handle.
38             else {
39 16         17 $fh = $file_or_fh;
40 16         22 $basename = 'FILE';
41             }
42              
43             # Use FileHandle to make File::MMagic happy;
44             # bless the filehandle into the FileHandle package to make File::MMagic happy
45 20         1230 require FileHandle;
46 20         15561 bless $fh, "FileHandle";
47              
48             # Check what headers the user has already set and
49             # don't override them.
50 20         71 my %existing_headers = $self->header_props();
51              
52             # Check for a existing type header set with or without a hypheout a hyphen
53 20 100 66     325 unless ( $existing_headers{'-type'} || $existing_headers{'type'} ) {
54 16         16 my $mime_type;
55              
56 16         20 eval {
57 16         1310 require File::MMagic;
58 16         11452 my $magic = File::MMagic->new();
59 16         4812 $mime_type = $magic->checktype_filehandle($fh);
60             };
61 16 50       226727 warn "Failed to load File::MMagic module to determine mime type: $@" if $@;
62            
63             # Set Default
64 16   50     49 $mime_type ||= 'application/octet-stream';
65              
66 16         141 $self->header_add('-type' => $mime_type);
67             }
68              
69              
70 20 100 66     866 unless ( $existing_headers{'Content_Length'}
71             || $existing_headers{'-Content_Length'}
72             ) {
73 16         43 $self->header_add('-Content_Length' => $size);
74             }
75              
76 20 100 66     496 unless ( $existing_headers{'-attachment'}
      66        
77             || $existing_headers{'attachment'}
78             || grep( /-?content-disposition/i, keys %existing_headers )
79             ) {
80 16         39 $self->header_add('-attachment' => $basename);
81             }
82              
83 20         349 $self->header_type( 'none' );
84 20         264 print $self->query->header( $self->header_props() );
85              
86             # This reads in the file in $byte size chunks
87 20         62547 my $first;
88             # File::MMagic may have read some of the file, so seek back to the beginning
89 20         82 seek($fh,0,0);
90 20         241 while ( read( $fh, my $buffer, $bytes ) ) {
91 20         48 print $buffer;
92             }
93              
94 20         148 print ''; # print a null string at the end
95 20         311 close ( $fh );
96 20         147 return 1;
97             }
98              
99             # The old way. Requires manually calling error_mode() if there's a problem,
100             # but error_mode() won't have access to "$@"
101             sub stream_file {
102 10     10 1 7013 my $self = shift;
103 10         17 my $out;
104              
105             # Perhaps bad style to not use a method call here,
106             # But this keeps the legacy case working, where only stream_file() was exported.
107 10         15 eval { stream($self,@_) };
  10         24  
108              
109             # Starting with 3.0, we warn if there's a problem opening the file
110             # instead of ignoring the error.
111 10 50       22 if ($@) {
112 0         0 warn $@;
113 0         0 return 0;
114             }
115             else {
116 10         56 return 1;
117             }
118             }
119              
120             1;
121             __END__