File Coverage

blib/lib/WebDAO/CV.pm
Criterion Covered Total %
statement 108 153 70.5
branch 28 58 48.2
condition 14 29 48.2
subroutine 17 20 85.0
pod 10 12 83.3
total 177 272 65.0


line stmt bran cond sub pod time code
1             #===============================================================================
2             #
3             # DESCRIPTION: controller
4             #
5             # AUTHOR: Aliaksandr P. Zahatski,
6             #===============================================================================
7             package WebDAO::CV;
8             our $VERSION = '0.02';
9 5     5   5169 use URI;
  5         25129  
  5         148  
10 5     5   1064 use Data::Dumper;
  5         6613  
  5         295  
11 5     5   29 use strict;
  5         9  
  5         112  
12 5     5   25 use warnings;
  5         11  
  5         131  
13 5     5   4200 use HTTP::Body;
  5         291521  
  5         166  
14 5     5   775 use WebDAO::Base;
  5         12  
  5         421  
15 5     5   26 use base qw( WebDAO::Base );
  5         9  
  5         9016  
16              
17             __PACKAGE__->mk_attr(status=>200, _parsed_cookies=>undef);
18              
19             sub new {
20 13     13 1 2752 my $class = shift;
21 13 50 33     130 my $self = bless( ( $#_ == 0 ) ? shift : {@_}, ref($class) || $class );
22 13         66 $self->{headers} = {};
23 13         173 $self
24             }
25              
26             =head2 url (-options1=>1)
27              
28             from url: http://testwd.zag:82/Envs/partsh.sd?23=23
29             where options:
30            
31             -path_info -> /Envs/partsh.sd
32             -base -> http://example.com:82
33              
34             defaul http://testwd.zag:82/Envs/partsh.sd?23=23
35            
36             =cut
37              
38             sub url {
39 20     20 1 1137 my $self = shift;
40 20         55 my %args = @_;
41 20         38 my $env = $self->{env};
42              
43 20 100       64 if ( exists $env->{FCGI_ROLE} ) {
44             ( $env->{PATH_INFO}, $env->{QUERY_STRING} ) =
45 4         27 $env->{REQUEST_URI} =~ /([^?]*)(?:\?(.*)$)?/s;
46             }
47 20   100     99 my $path = $env->{PATH_INFO} || ''; # 'PATH_INFO' => '/Env'
48 20   100     91 my $host = $env->{HTTP_HOST} || 'example.org'; # 'HTTP_HOST' => '127.0.0.1:5000'
49 20   100     76 my $query = $env->{QUERY_STRING}|| ''; # 'QUERY_STRING' => '434=34&erer=2'
50 20   50     84 my $proto = $env->{'psgi.url_scheme'} || 'http';
51 20         52 my $full_path = "$proto://${host}${path}?$query";
52             #clear / at end
53 20 100       79 $full_path =~ s!/$!! if $path =~ m!^/!;
54 20         91 my $uri = URI->new($full_path);
55              
56 20 100       41584 if ( exists $args{-path_info} ) {
    100          
57 5         36 return $uri->path();
58             }
59             elsif ( exists $args{-base} ) {
60 9         125 return "$proto://$host";
61             }
62 6         25 return URI->new($full_path)->canonical;
63             }
64              
65             =head2 method - HTTP method
66              
67             retrun HTTP method
68              
69             =cut
70              
71             sub method {
72 1     1 1 758 my $self = shift;
73 1 50       8 $self->{env}->{REQUEST_METHOD} || "GET";
74             }
75              
76             =head2 accept
77              
78             return hashref
79              
80             {
81             'application/xhtml+xml' => undef,
82             'application/xml' => undef,
83             'text/html' => undef
84             };
85              
86             =cut
87              
88             sub accept {
89 5     5 1 13 my $self = shift;
90 5   100     185 my $accept = $self->{env}->{HTTP_ACCEPT} || return {};
91 1         3 my ($types) = split( ';', $accept );
92 1         3 my %res;
93 1         5 @res{ split( ',', $types ) } = ();
94 1         9 \%res;
95             }
96              
97             =head2 param - return GET and POST params
98              
99             return params
100              
101             =cut
102              
103             sub param {
104 9     9 1 18 my $self = shift;
105 9         19 my $params = $self->{parsed_params};
106 9 100       32 unless ($params) {
107             #init by POST params
108 5         24 $params = $self->_parse_body;
109 5         19 my @get_params = $self->url()->query_form;
110 5         1281 while (my ($k, $v) = splice(@get_params,0,2 )) {
111 1 50       5 unless ( exists $params->{ $k } ) {
112 1         6 $params->{ $k } = $v
113             } else {
114 0         0 my $val = $params->{ $k };
115             #if array ?
116 0 0       0 if ( ref $val ) {
117 0         0 push @$val, $v
118             } else {
119 0 0       0 $params->{ $k } = [$val, ref($v) ? @$v : $v]
120             }
121             }
122             }
123 5         18 $self->{parsed_params} = $params;
124             }
125 9 100       69 return keys %$params unless @_;
126 1 50       5 return undef unless exists $params->{$_[0]};
127 1         3 my $res = $params->{$_[0]};
128 1 50       4 if ( ref($res) ) {
129 0 0       0 return wantarray ? @$res : $res->[0]
130             }
131 1         7 return $res;
132             }
133              
134             #parse body
135             sub _parse_body {
136 5     5   10 my $self = shift;
137              
138 5         17 my $content_type = $self->{env}->{CONTENT_TYPE};
139 5         13 my $content_length = $self->{env}->{CONTENT_LENGTH};
140 5 50 33     36 if (!$content_type && !$content_length) {
141 5         15 return {};
142             }
143              
144 0         0 my $body = HTTP::Body->new($content_type, $content_length);
145 0         0 $body->cleanup(1);
146              
147 0         0 my $input = $self->{env}->{'psgi.input'};
148 0 0       0 if ( $input ) {
149             #reset IO
150 0         0 $input->seek(0,0);
151             }
152             else {
153             # for FCGI, Shell
154 0         0 $input = \*STDIN
155             }
156 0         0 my $spin = 0;
157              
158 0         0 while ($content_length) {
159 0 0       0 $input->read(my $chunk, $content_length < 8192 ? $content_length : 8192);
160 0         0 my $read = length $chunk;
161 0         0 $content_length -= $read;
162 0         0 $body->add($chunk);
163 0 0 0     0 if ($read == 0 && $spin++ > 2000) {
164 0         0 Carp::croak "Bad Content-Length: maybe client disconnect? ($content_length bytes remaining)";
165             }
166             }
167 0         0 $self->{'http.body'} = $body;
168 0         0 return $body->param
169             }
170              
171             =head2 body - HTTP body file descriptor ( see get-body for get content)
172              
173             Return HTTP body file descriptor
174              
175             my $body;
176             {
177             local $/;
178             my $fd = $request->body;
179             $body = <$fd>;
180             }
181              
182             =cut
183              
184             sub body {
185 0     0 1 0 my $self = shift;
186 0 0       0 unless ( exists $self->{'http.body'} ) {
187 0         0 $self->_parse_body();
188             }
189              
190 0   0     0 my $http_body = $self->{'http.body'} || return undef;
191 0         0 return $http_body->body;
192             }
193              
194             =head2 get-body - HTTP body content
195              
196             Return HTTP body text
197              
198             my $body= $r->get_body;
199              
200             =cut
201              
202             sub get_body {
203 0     0 0 0 my $self = shift;
204 0         0 my $body;
205             {
206 0         0 local $/;
  0         0  
207 0 0       0 if ( my $fd = $self->body ) {
208 0         0 $body = <$fd>
209             }
210             }
211 0         0 return $body
212             }
213              
214              
215             =head2 upload - return upload content
216              
217             print Dumper $request->upload;
218              
219             For command:
220              
221             curl -i -X POST -H "Content-Type: multipart/form-data"\
222             -F "data=@UserSettings.txt"\
223             http://example.org/Upload
224              
225             output:
226              
227             {
228             'data' => {
229             'headers' => {
230             'Content-Type' => 'text/plain',
231             'Content-Disposition' => 'form-data; name="data"; filename="UserSettings.txt"'
232             },
233             'tempname' => '/tmp/txBmaz5Bpf.txt',
234             'size' => 6704,
235             'filename' => 'UserSettings.txt',
236             'name' => 'data'
237             }
238             };
239              
240             =cut
241              
242             sub upload {
243 0     0 1 0 my $self = shift;
244 0 0       0 unless ( exists $self->{'http.body'} ) {
245 0         0 $self->_parse_body();
246             }
247              
248 0   0     0 my $http_body = $self->{'http.body'} || return {};
249 0         0 return $http_body->upload;
250             }
251              
252             =head2 set_header
253              
254             $cv->set_header("Content-Type" => 'text/html; charset=utf-8')
255              
256             =cut
257              
258             sub set_header {
259 5     5 1 17 my ( $self, $name, $par ) = @_;
260              
261             #collect -cookies
262 5 50       20 if ( $name eq 'Set-Cookie' ) {
263 0         0 push @{ $self->{headers}->{$name} }, $par;
  0         0  
264             }
265             else {
266 5         29 $self->{headers}->{$name} = $par;
267             }
268             }
269              
270             =head3 print_headers [ header1=>value, ...]
271              
272             Method for output headers
273              
274             =cut
275              
276             sub print_headers {
277 4     4 1 7 my $self = shift;
278             #save cookie
279 4         10 my $cookie = delete $self->{headers}->{"Set-Cookie"};
280             #merge in and exists headers
281 4         6 my %headers = ( %{ $self->{headers} } , @_ );
  4         16  
282             #merge cookies
283 4 50       13 if ( $cookie ) {
284 0         0 push @{ $headers{"Set-Cookie"} }, @$cookie;
  0         0  
285             }
286 4         6 my @cookies_headers = ();
287             #format cookies
288 4 100       14 if ( my $cookies = delete $headers{"Set-Cookie"} ) {
289 1         3 foreach my $c ( @$cookies ) {
290 2         3 my $hvalue;
291 2 50       10 if (ref($c) eq 'HASH') {
292 2   50     10 my $path = $c->{path} || '/';
293             # Set-Cookie: srote=ewe&1&1&2; path=$path
294 2         8 $hvalue = "$c->{name}=$c->{value}; path=$path";
295 2 100       7 if (my $expires = $c->{expires}) {
296 1         4 my @MON = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
297 1         4 my @WDAY = qw( Sun Mon Tue Wed Thu Fri Sat );
298 1         12 my($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime($expires);
299 1         2 $year += 1900;
300 1         8 $expires = sprintf("%s, %02d-%s-%04d %02d:%02d:%02d GMT",
301             $WDAY[$wday], $mday, $MON[$mon], $year, $hour, $min, $sec);
302 1         4 $hvalue .=" ;expires=$expires";
303             }
304 0         0 } else { $hvalue = $c }
305 2         6 push @cookies_headers, "Set-Cookie", $hvalue;
306             }
307             }
308 4         99 my $status = $self->status;
309 4   50     31 my $fd = $self->{writer}->([$status||"200", [%headers, @cookies_headers], undef]);
310 4         72 $self->{fd} = $fd;
311             }
312              
313             sub print {
314 1     1 0 3 my $self = shift;
315 1 50       4 if (exists $self->{fd}) {
316 1         3 foreach my $line (@_) {
317 1 50       5 utf8::encode( $line) if utf8::is_utf8($line);
318 1         5 $self->{fd}->write($line);
319             }
320             } else {
321 0         0 print @_;
322             }
323             }
324              
325             =head2 get_cookie
326              
327             return hashref to {key=>value}
328              
329             =cut
330              
331             sub get_cookie {
332 1     1 1 707 my $self = shift;
333 1   50     6 my $str = $self->{env}->{HTTP_COOKIE} || return {};
334 1 50       29 if ($self->_parsed_cookies) { return $self->_parsed_cookies };
  0         0  
335 1         2 my %res;
336             %res =
337 1         6 map { URI::Escape::uri_unescape($_) } map { split '=',$_,2 } split(/\s*[;]\s*/,
  4         27  
  2         7  
338             $str);
339 1         34 $self->_parsed_cookies(\%res);
340 1         6 \%res;
341             }
342              
343              
344             1;
345              
346