File Coverage

blib/lib/Plack/App/CGIBin/Streaming/Request.pm
Criterion Covered Total %
statement 109 114 95.6
branch 24 28 85.7
condition 1 3 33.3
subroutine 22 26 84.6
pod 5 6 83.3
total 161 177 90.9


line stmt bran cond sub pod time code
1             package Plack::App::CGIBin::Streaming::Request;
2              
3 15     15   348 use 5.014;
  15         42  
  15         468  
4 15     15   75 use strict;
  15         135  
  15         432  
5 15     15   72 use warnings;
  15         21  
  15         411  
6 15     15   66 no warnings 'uninitialized';
  15         15  
  15         414  
7 15     15   63 use Carp;
  15         18  
  15         936  
8              
9             # my %trace=
10             # (
11             # new=>sub {warn "NEW: @_"},
12             # header=>sub {warn "HEADER: @_"},
13             # flush=>sub {warn "FLUSH: @_"},
14             # status_out=>sub {warn "STATUS OUT: @_"},
15             # content=>sub {warn "CONTENT: @_"},
16             # finalize_start=>sub {warn "FINALIZE START: @_"},
17             # finalize_end=>sub {warn "FINALIZE END: @_"},
18             # );
19             # use constant TRACE=>sub {
20             # my $what=shift;
21             # local $SIG{__WARN__};
22             # $trace{$what} and $trace{$what}->(@_);
23             # } ;
24              
25 15     15   81 use constant TRACE=>0;
  15         45  
  15         1968  
26              
27             our @attr;
28              
29             our $DEFAULT_CONTENT_TYPE='text/plain';
30             our $DEFAULT_MAX_BUFFER=8000;
31              
32             BEGIN {
33 15     15   69 @attr=(qw/env responder writer _buffer _buflen _headers max_buffer
34             content_type filter_before filter_after on_status_output
35             parse_headers _header_buffer status notes on_flush on_finalize
36             suppress_flush/);
37 15         36 for (@attr) {
38 270         321 my $attr=$_;
39 15     15   99 no strict 'refs';
  15         24  
  15         1392  
40 270         18558 *{__PACKAGE__.'::'.$attr}=sub : lvalue {
41 83509     83509   86801 my $I=$_[0];
42 83509 100       142934 $I->{$attr}=$_[1] if @_>1;
43 83509         184175 $I->{$attr};
44 270         687 };
45             }
46             }
47              
48             sub new {
49 17     17 0 575 my $class=shift;
50 17   33     126 $class=ref($class) || $class;
51             my $self=bless {
52             content_type=>$DEFAULT_CONTENT_TYPE,
53             max_buffer=>$DEFAULT_MAX_BUFFER,
54 83318     83318   81463 filter_before=>sub{},
55 83326     83326   185101 filter_after=>sub{},
56 13     13   24 on_status_output=>sub{},
57 990     990   1126 on_flush=>sub{},
58 13     13   22 on_finalize=>sub{},
59 17         753 notes=>+{},
60             _headers=>[],
61             _buffer=>[],
62             _buflen=>0,
63             status=>200,
64             }, $class;
65              
66 17         111 for( my $i=0; $i<@_; $i+=2 ) {
67 70         475 my $method=$_[$i];
68 70         360 $self->$method($_[$i+1]);
69             }
70              
71 17         36 if (TRACE) {
72             (ref(TRACE) eq 'CODE'
73             ? TRACE->(new=>$self)
74             : warn "NEW $self");
75             }
76              
77 17         101 return $self;
78             }
79              
80             sub print_header {
81 39     39 1 113 my $self = shift;
82              
83 39 50       120 croak "KEY => VALUE pairs expected" if @_%2;
84 39 50       110 croak "It's too late to set a HTTP header" if $self->{writer};
85              
86 39         48 if (TRACE) {
87             (ref(TRACE)
88             ? TRACE->(header=>$self, @_)
89             : warn "print_header $self: @_");
90             }
91              
92 39         56 push @{$self->{_headers}}, @_;
  39         244  
93             }
94              
95             sub print_content {
96 83349     83349 1 95115 my $self = shift;
97              
98 83349 100       171922 if ($self->{parse_headers}) {
99 13         130 $self->{_header_buffer}.=join('', @_);
100 13         331 while( $self->{_header_buffer}=~s/\A(\S+)[ \t]*:[ \t]*(.+?)\r?\n// ) {
101 28         102 my ($hdr, $val)=($1, $2);
102 28 100       190 if ($hdr=~/\Astatus\z/i) {
    100          
103 9         93 $self->{status}=$val;
104             } elsif ($hdr=~/\Acontent-type\z/i) {
105 13         107 $self->{content_type}=$val;
106             } else {
107 6         25 $self->print_header($hdr, $val);
108             }
109             }
110 13 50       185 if ($self->{_header_buffer}=~s/\A\r?\n//) {
111 13         36 delete $self->{parse_headers}; # done
112 13 100       114 $self->print_content(delete $self->{_header_buffer})
113             if length $self->{_header_buffer};
114             }
115 13         62 return;
116             }
117              
118 83336         131052 my @data=@_;
119 83336         173962 $self->{filter_before}->($self, \@data);
120              
121 83336         90076 my $len = 0;
122 83336         190468 $len += length $_ for @data;
123              
124 83336         87982 if (TRACE) {
125             (ref(TRACE)
126             ? TRACE->(content=>$self, @data)
127             : warn "print_content $self: $len bytes");
128             }
129              
130 83336         81753 push @{$self->{_buffer}}, @data;
  83336         138212  
131 83336         113151 $len += $self->{_buflen};
132 83336         98610 $self->{_buflen}=$len;
133              
134 83336 100       149388 if ($len > $self->{max_buffer}) {
135 990         2346 local $self->{suppress_flush};
136 990         2128 $self->flush;
137             }
138              
139 83336         158407 $self->filter_after->($self, \@data);
140             }
141              
142             sub _status_out {
143 17     17   37 my $self = shift;
144 17         28 my $is_done = shift;
145              
146 17         31 if (TRACE) {
147             (ref(TRACE)
148             ? TRACE->(status_out=>$self, $is_done)
149             : warn "status_out $self: $self->{status}");
150             }
151              
152 17         72 $self->print_header('Content-Type', $self->{content_type});
153 17 100       87 $self->print_header('Content-Length', $self->{_buflen})
154             if $is_done;
155 17         120 $self->on_status_output->($self);
156              
157 17 100       150 $self->{writer}=$self->{responder}->([$self->{status},
158             $self->{_headers},
159             $is_done ? $self->{_buffer}: ()]);
160             }
161              
162             sub status_written {
163 6     6 1 79 my $self = shift;
164 6         30 return !!$self->{writer};
165             }
166              
167             sub flush {
168 992     992 1 1106 my $self = shift;
169 992 50       954 return 0 unless @{$self->{_buffer}};
  992         2292  
170              
171 992         910 if (TRACE) {
172             (ref(TRACE)
173             ? TRACE->(flush=>$self)
174             : warn "flush $self");
175             }
176              
177 992 100       2339 $self->_status_out unless $self->{writer};
178              
179 992         3150 $self->{writer}->write(join '', @{$self->{_buffer}});
  992         14359  
180 992         214160 @{$self->{_buffer}}=();
  992         8171  
181 992         1740 $self->{_buflen}=0;
182              
183 992         2219 $self->{on_flush}->($self);
184              
185 992         2326 return 0;
186             }
187              
188             sub finalize {
189 17     17 1 38 my $self = shift;
190              
191 17         26 if (TRACE) {
192             (ref(TRACE)
193             ? TRACE->(finalize_start=>$self)
194             : warn "finalize start $self");
195             }
196              
197 17         69 $self->{on_finalize}->($self);
198 17 100       82 if ($self->{writer}) {
199 6         20 $self->{writer}->write(join '', @{$self->{_buffer}});
  6         111  
200 6         653 $self->{writer}->close;
201             } else {
202 11         47 $self->_status_out(1);
203             }
204              
205 17         51696 if (TRACE) {
206             (ref(TRACE)
207             ? TRACE->(finalize_end=>$self)
208             : warn "finalize end $self");
209             }
210              
211 17         553 %$self=();
212 17         399 bless $self, 'Plack::App::CGIBin::Streaming::Request::Demolished';
213             }
214              
215             package # prevent CPAN indexing
216             Plack::App::CGIBin::Streaming::Request::Demolished;
217 15     15   87 use strict;
  15         33  
  15         1554  
218              
219             sub AUTOLOAD {
220 0     0     our $AUTOLOAD;
221 0           die "Calling $AUTOLOAD on a demolished request.";
222             }
223              
224 0     0     sub flush {}
225 0     0     sub finalize {}
226 0     0     sub DESTROY {}
227              
228             1;
229              
230             __END__