File Coverage

blib/lib/Plack/Middleware/AdaptFilehandleRead.pm
Criterion Covered Total %
statement 29 29 100.0
branch 1 2 50.0
condition 3 7 42.8
subroutine 10 10 100.0
pod 1 2 50.0
total 44 50 88.0


line stmt bran cond sub pod time code
1 1     1   210667 use strict;
  1         2  
  1         48  
2 1     1   6 use warnings;
  1         2  
  1         61  
3              
4             package Plack::Middleware::AdaptFilehandleRead;
5              
6 1     1   6 use base 'Plack::Middleware';
  1         8  
  1         898  
7 1     1   9942 use Plack::Middleware::AdaptFilehandleRead::Proxy;
  1         3  
  1         32  
8 1     1   5 use Plack::Util::Accessor 'always_adapt', 'chunksize';
  1         1  
  1         8  
9 1     1   58 use Scalar::Util ();
  1         2  
  1         16  
10 1     1   4 use Plack::Util ();
  1         2  
  1         215  
11              
12             our $VERSION = '0.003';
13              
14             sub looks_like_a_readable_fh {
15 1     1 0 3 my $self = shift;
16 1   50     7 my $body = shift || return; # If there's a body
17 1   33     34 return Scalar::Util::blessed($body) && # and its an object
18             $body->can('read') && # which can 'read'
19             ($self->always_adapt || # and either ->always_adapt
20             !$body->can('getline')); # or doesn't 'getline'
21             }
22              
23             sub call {
24 1     1 1 123337 my($self, $env) = @_;
25 1         17 my $res = $self->app->($env);
26             return Plack::Util::response_cb($res, sub {
27 1 50   1   20 if( $self->looks_like_a_readable_fh((my $r = shift)->[2]) ) {
28              
29             # We have an filehandle like object that doesn't do ->getline
30             # so Plack can't do anything with it. Wrap it in a proxy object
31             # that adds the ->getline method by adapting read. We assume
32             # this ->read works like $fh->read(BUF, LEN, [OFFSET]).
33              
34 1   50     16 $r->[2] = Plack::Middleware::AdaptFilehandleRead::Proxy->new($r->[2], ($self->chunksize || 65536));
35             }
36 1         109 });
37             }
38              
39             1;
40              
41             =head1 NAME
42            
43             Plack::Middleware::AdaptFilehandleRead - Give a filehandle a getline method when its missing
44            
45             =head1 SYNOPSIS
46            
47             use Plack::Builder;
48             use AppReturnsFileHandleWithRead;
49              
50             my $app = AppReturnsFileHandleWithRead->new;
51              
52             builder {
53             enable 'AdaptFilehandleRead';
54             $app;
55             };
56            
57             =head1 DESCRIPTION
58              
59             L allows for the body content to be a glob filehandle or a Filehandle
60             like object. For the later case the object must have a method C
61             which works as described in L. However sometimes you may have
62             a custom filehandle like object that support the common C method. For
63             example many versions of L allowed the body of a response to be a
64             glob or object that does C. People might have written custom streaming
65             applications that had this C method but not C. As a result
66             these custom Filehandle like objects are not compatible with the expectations
67             of L.
68              
69             This middleware exists to help you convert such a custom made filehandle like
70             object. If you have created something like this (or for example using some
71             shared code like L that returns a filehandle that does C
72             but not C) you can use this middleware to wrap said object in a proxy
73             that does the C method by reading from the exising C method.
74              
75             By default, if this middleware is enabled, it will examine any body values and
76             check if they are 1) an object, 2) that does C and 3) doesn't do C
77             If such a case exists it will create an instance of L
78             which had the C method. It also will delegate any other method calls
79             to the wrapped object via AUTOLOAD so if you have some additional custom methods
80             it will still work as expected. It does not currently proxy any Ling.
81              
82             If for some reason your custom filehandle like object does C but its
83             faulty and the C method is correct, you can set C to true
84             and the proxy will be applied even if a C method is detected.
85              
86             builder {
87             enable 'AdaptFilehandleRead', always_adapt=>1;
88             $app;
89             };
90              
91             This middleware will do its best to respect the various allowed values of
92             C<$/> for deciding how to return content from C Currently we support
93             C<$/> values of scalar ref (like \8192 for reading fixed length chunks) or
94             simple scalars (like \n for reading newline delimited records). Currently
95             we don't support C<$/> as undef (for slurping full content) and some of the other
96             more esoteric values of C<$/> as the author percieves that support was not needed
97             withing the context of adapting C for L uses (all exampled L
98             handlers seemed to use the scalar ref fixed length chunk value for C<$/>, but
99             we choose to also support the scalar record deliminator option since its very
100             commonly seen elsewhere).
101              
102             =head1 ATTRIBUTES
103              
104             This middleware has the following attributes:
105              
106             =head2 always_adapt
107              
108             Defaults to false, Optional.
109              
110             Set this to any true value and the proxy will always wrap any filehandle like
111             object, as long as it has a C method (even it if already has a C
112             method.) Use this if you have a custom filehandle like object that you are using
113             as the body of a L reponse that has both C and C but the
114             C is broken in some way (but C isn't).
115              
116             =head2 chunksize
117              
118             Defaults to 65536, Optional.
119              
120             When adapting C, we call for chunks of data 65536 in length. This may not be the
121             most efficient way to read your files based on your specific requirements. If so, you
122             may override the size of the chunks:
123              
124             builder {
125             enable 'AdaptFilehandleRead', chunksize=>65536;
126             $app;
127             };
128              
129             B: Be aware that the chunk is read into memory as each chunk is read. Should a
130             chunk fail to find the deliminator indicated by C<$/>, another chunk would be read.
131             If you entire file contains no match, it is not impossible for the entire file to be
132             thus read into memory should C be used. In these cases you might wish to
133             make sure your underlying L server has other ways to handle these types of
134             files (for example using XSendfile or via some other optimization.) or instead be sure
135             to use the fixed chunk sized option for C<$/>.
136              
137             B: For most L handlers, "$/" is set to a scalar refer, such as:
138              
139             local $/ = \'4096'
140              
141             which is a flag indicating we'd prefer ->getline to return fixed length chunks
142             instead of variable length lines. In this case the 'chunksize' attribute is
143             ignored. Which means if you are using this with L chances are this
144             attribute will not be respected :) You probably should not worry about this!
145              
146             =head1 SEE ALSO
147            
148             L, L, L, L,
149             L.
150              
151             See 'perlvar' docs for more on the possible values of C<$/>.
152            
153             =head1 AUTHOR
154            
155             John Napiorkowski L
156            
157             =head1 COPYRIGHT & LICENSE
158            
159             Copyright 2014, John Napiorkowski L
160            
161             This library is free software; you can redistribute it and/or modify it under
162             the same terms as Perl itself.
163            
164             =cut