File Coverage

blib/lib/CGI/Compress/Gzip/FileHandle.pm
Criterion Covered Total %
statement 18 78 23.0
branch 0 28 0.0
condition 0 21 0.0
subroutine 6 10 60.0
pod n/a
total 24 137 17.5


line stmt bran cond sub pod time code
1             package CGI::Compress::Gzip::FileHandle;
2              
3 1     1   16 use 5.006;
  1         3  
  1         39  
4 1     1   6 use warnings;
  1         1  
  1         32  
5 1     1   5 use strict;
  1         2  
  1         31  
6 1     1   4 use English qw(-no_match_vars);
  1         2  
  1         5  
7 1     1   383 use Compress::Zlib;
  1         1  
  1         333  
8              
9 1     1   7 use base qw(IO::Zlib);
  1         2  
  1         1003  
10             our $VERSION = '1.03';
11              
12             #=encoding utf8
13              
14             =for stopwords zlib
15              
16             =head1 NAME
17              
18             CGI::Compress::Gzip::FileHandle - CGI::Compress::Gzip helper package
19              
20             =head1 LICENSE
21              
22             Copyright 2006-2007 Clotho Advanced Media, Inc.,
23              
24             Copyright 2007-2008 Chris Dolan
25              
26             This library is free software; you can redistribute it and/or modify it
27             under the same terms as Perl itself.
28              
29             =head1 SYNOPSIS
30              
31             use CGI::Compress::Gzip;
32            
33             my $cgi = new CGI::Compress::Gzip;
34             print $cgi->header();
35             print " ...";
36              
37             =head1 DESCRIPTION
38              
39             This is intended for internal use only! Use CGI::Compress::Gzip
40             instead.
41              
42             This CGI::Compress::Gzip helper class subclasses IO::Zlib. It is
43             is needed to make sure that output is not compressed until the CGI
44             header is emitted. This filehandle delays the ignition of the zlib
45             filter until it sees the exact same header generated by
46             CGI::Compress::Gzip::header() pass through it's WRITE() method. If
47             you change the header before printing it, this class will throw an
48             exception.
49              
50             This class holds one global variable representing the previous default
51             filehandle used before the gzip filter is put in place. This
52             filehandle, usually STDOUT, is replaced after the gzip stream finishes
53             (which is usually when the CGI object goes out of scope and is
54             destroyed).
55              
56             =head1 FUNCTIONS
57              
58             =over
59              
60             =item OPEN
61              
62             Overrides IO::Zlib::OPEN. This method doesn't actually do anything --
63             it just stores it's arguments for a later call to SUPER::OPEN in
64             WRITE(). The reason is that we may not have seen the header yet, so
65             we don't yet know whether to compress output.
66              
67             =cut
68              
69             sub OPEN
70             {
71 0     0     my ($self, $fh, @args) = @_;
72              
73             # Delay opening until after the header is printed.
74 0           $self->{out_fh} = $fh;
75 0           $self->{openargs} = \@args;
76 0           $self->{outtype} = undef;
77 0           $self->{buffer} = q{};
78 0           $self->{pending_header} = q{};
79 0           return $self;
80             }
81              
82             =item WRITE buffer, length, offset
83              
84             Emit the uncompressed header followed by the compressed body.
85              
86             =cut
87              
88             sub WRITE
89             {
90 0     0     my ($self, $buf, $length, $offset) = @_;
91              
92             # Appropriated from IO::Zlib:
93 0 0         if ($length > length $buf)
94             {
95 0           die 'bad LENGTH';
96             }
97 0 0 0       if (defined $offset && $offset != 0)
98             {
99 0           die 'OFFSET not supported';
100             }
101              
102 0           my $bytes = 0;
103 0 0         if ($self->{pending_header})
104             {
105             # Side effects: $buf and $self->{pending_header} are trimmed
106 0           $bytes = $self->_print_header(\$buf, $length);
107 0           $length -= $bytes;
108             }
109 0 0         return $bytes if (!$length); # if length is zero, there's no body content to print
110              
111 0 0         if (!defined $self->{outtype})
112             {
113             # Determine whether we can stream data to the output filehandle
114              
115             # default case: no, cannot stream
116 0           $self->{outtype} = 'block';
117              
118             # Mod perl already does funky filehandle stuff, so don't stream
119 0   0       my $is_mod_perl = ($ENV{MOD_PERL} ||
120             ($ENV{GATEWAY_INTERFACE} &&
121             $ENV{GATEWAY_INTERFACE} =~ m/ \A CGI-Perl\/ /xms));
122              
123 0           my $type = ref $self->{out_fh};
124              
125 0 0 0       if (!$is_mod_perl && $type)
126             {
127 0   0       my $is_glob = $type eq 'GLOB' && defined $self->{out_fh}->fileno();
128 0   0       my $is_filehandle = ($type !~ m/ \A GLOB|SCALAR|HASH|ARRAY|CODE \z /xms &&
129             $self->{out_fh}->can('fileno') &&
130             defined $self->{out_fh}->fileno());
131              
132 0 0 0       if ($is_glob || $is_filehandle)
133             {
134             # Complete delayed open
135 0 0         if (!$self->SUPER::OPEN($self->{out_fh}, @{$self->{openargs}}))
  0            
136             {
137 0           die 'Failed to open the compressed output stream';
138             }
139            
140 0           $self->{outtype} = 'stream';
141             }
142             }
143             }
144              
145 0 0         if ($self->{outtype} eq 'stream')
146             {
147 0           $bytes += $self->SUPER::WRITE($buf, $length, $offset);
148             }
149             else
150             {
151 0           $self->{buffer} .= $buf;
152 0           $bytes += length $buf;
153             }
154              
155 0           return $bytes;
156             }
157              
158             sub _print_header
159             {
160 0     0     my ($self, $buf, $length) = @_;
161              
162 0           my $header = $self->{pending_header};
163 0 0         if ($length < length $header)
164             {
165 0           $self->{pending_header} = substr $header, $length;
166 0           $header = substr $header, 0, $length;
167             }
168             else
169             {
170 0           $self->{pending_header} = q{};
171             }
172              
173 0 0         if (${$buf} !~ s/ \A \Q$header\E //xms)
  0            
174             {
175 0           die 'Expected to print the CGI header';
176             }
177              
178 0           my $out_fh = $self->{out_fh};
179 0 0         if (!print {$out_fh} $header)
  0            
180             {
181 0           die 'Failed to print the uncompressed CGI header';
182             }
183            
184 0           return length $header;
185             }
186              
187             =item CLOSE
188              
189             Flush the compressed output.
190              
191             =cut
192              
193             sub CLOSE
194             {
195 0     0     my ($self) = @_;
196              
197 0           my $out_fh = $self->{out_fh};
198 0           $self->{out_fh} = undef; # clear it, so we can't write to it after this method ends
199              
200 0           my $result;
201 0 0 0       if ($self->{outtype} && $self->{outtype} eq 'stream')
202             {
203 0           $result = $self->SUPER::CLOSE();
204 0 0         if (!$result)
205             {
206 0           die "Failed to close gzip $OS_ERROR";
207             }
208             }
209             else
210             {
211 0           print {$out_fh} Compress::Zlib::memGzip($self->{buffer});
  0            
212 0           $result = 1;
213             }
214              
215 0           return $result;
216             }
217              
218             1;
219             __END__