File Coverage

blib/lib/Dancer/Handler.pm
Criterion Covered Total %
statement 105 120 87.5
branch 20 34 58.8
condition 16 23 69.5
subroutine 26 28 92.8
pod 0 8 0.0
total 167 213 78.4


line stmt bran cond sub pod time code
1             package Dancer::Handler;
2             our $AUTHORITY = 'cpan:SUKRIA';
3             # ABSTRACT: Dancer request handler
4             $Dancer::Handler::VERSION = '1.3520';
5 167     167   72292 use strict;
  167         409  
  167         4957  
6 167     167   901 use warnings;
  167         401  
  167         4058  
7 167     167   907 use Carp;
  167         406  
  167         8945  
8              
9 167     167   82886 use File::stat;
  167         1188734  
  167         990  
10 167     167   11634 use HTTP::Headers;
  167         14607  
  167         3754  
11              
12 167     167   1755 use Dancer::Logger;
  167         404  
  167         3162  
13 167     167   2157 use Dancer::GetOpt;
  167         396  
  167         3312  
14 167     167   1826 use Dancer::SharedData;
  167         379  
  167         3194  
15 167     167   2258 use Dancer::Renderer;
  167         464  
  167         5703  
16 167     167   1083 use Dancer::Config 'setting';
  167         422  
  167         7278  
17 167     167   1111 use Dancer::ModuleLoader;
  167         448  
  167         4143  
18 167     167   939 use Dancer::Exception qw(:all);
  167         439  
  167         19717  
19 167     167   1359 use Dancer::Factory::Hook;
  167         454  
  167         4611  
20              
21 167     167   1121 use Encode;
  167         510  
  167         180677  
22              
23             Dancer::Factory::Hook->instance->install_hooks(
24             qw/on_handler_exception/
25             );
26              
27             # This is where we choose which application handler to return
28             sub get_handler {
29 4     4 0 18 my $handler = 'Dancer::Handler::Standalone';
30              
31             # force PSGI is PLACK_ENV is set
32 4 100       14 if ($ENV{'PLACK_ENV'}) {
33 1         7 Dancer::Logger::core("PLACK_ENV is set (".$ENV{'PLACK_ENV'}.") forcing PSGI handler");
34 1         3 setting('apphandler' => 'PSGI');
35             }
36              
37             # if Plack is detected or set by conf, use the PSGI handler
38 4 50       13 if ( defined setting('apphandler') ) {
39 4         11 $handler = 'Dancer::Handler::' . setting('apphandler');
40             }
41              
42             # load the app handler
43 4         25 my ($loaded, $error) = Dancer::ModuleLoader->load($handler);
44 4 100       18 raise core_handler => "Unable to load app handler `$handler': $error" if $error;
45              
46             # OK, everything's fine, load the handler
47 3         22 Dancer::Logger::core('loading ' . $handler . ' handler');
48 3         17 return $handler->new;
49             }
50              
51             # handle an incoming request, process it and return a response
52             sub handle_request {
53 18     18 0 55 my ($self, $request) = @_;
54 18   50     53 my $ip_addr = $request->remote_address || '-';
55              
56 18         67 Dancer::SharedData->reset_all( reset_vars => !$request->is_forward);
57              
58 18         56 Dancer::Logger::core("request: "
59             . $request->method . " "
60             . $request->path_info
61             . " from $ip_addr");
62              
63             # save the request object
64 18         72 Dancer::SharedData->request($request);
65              
66             # deserialize the request body if possible
67 18 50       70 $request = Dancer::Serializer->process_request($request)
68             if Dancer::App->current->setting('serializer');
69              
70             # read cookies from client
71              
72 18         79 Dancer::Cookies->init;
73              
74 18 50       232 Dancer::App->reload_apps if Dancer::Config::setting('auto_reload');
75              
76 18         62 render_request($request);
77 18         68 return $self->render_response();
78             }
79              
80             sub render_request {
81 517     517 0 884 my $request = shift;
82 517         736 my $action;
83             $action = try {
84 517 100 100 517   19213 Dancer::Renderer->render_file
      100        
85             || Dancer::Renderer->render_action
86             || Dancer::Renderer->render_autopage
87             || Dancer::Renderer->render_error(404);
88             } continuation {
89             # workflow exception (continuation)
90 13     13   40 my ($continuation) = @_;
91 13 50 66     68 $continuation->isa('Dancer::Continuation::Halted')
92             || $continuation->isa('Dancer::Continuation::Route')
93             or $continuation->rethrow();
94             # special case for halted workflow continuation: still render the response
95 13         49 Dancer::Serializer->process_response(Dancer::SharedData->response);
96             } catch {
97 32     32   111 my ($exception) = @_;
98 32         130 Dancer::Factory::Hook->execute_hooks('on_handler_exception', $exception);
99 32         239 Dancer::Logger::error(
100             sprintf(
101             'request to %s %s crashed: %s',
102             $request->method, $request->path_info, $exception
103             )
104             );
105              
106             # use stringification, to get exception message in case of a
107             # Dancer::Exception
108 32         273 Dancer::Error->new(
109             code => 500,
110             title => "Runtime Error",
111             message => "$exception",
112             exception => $exception,
113             )->render();
114 517         5330 };
115 517         11098 return $action;
116             }
117              
118             sub psgi_app {
119 2     2 0 19 my $self = shift;
120             sub {
121 1     1   17 my $env = shift;
122 1         13 $self->init_request_headers($env);
123 1         7 my $request = Dancer::Request->new(env => $env);
124 1         4 $self->handle_request($request);
125 2         15 };
126             }
127              
128             sub init_request_headers {
129 0     0 0 0 my ($self, $env) = @_;
130              
131             my $psgi_headers = HTTP::Headers->new(
132             map {
133 0         0 (my $field = $_) =~ s/^HTTPS?_//;
134 0         0 ($field => $env->{$_});
135             }
136 0         0 grep {/^(?:HTTP|CONTENT|COOKIE)/i} keys %$env
  0         0  
137             );
138 0         0 Dancer::SharedData->headers($psgi_headers);
139             }
140              
141             # render a PSGI-formatted response from a response built by
142             # handle_request()
143             sub render_response {
144 25     25 0 74 my $self = shift;
145 25         78 my $response = Dancer::SharedData->response();
146              
147 25         101 my $content = $response->content;
148              
149 25 50       70 unless ( ref($content) eq 'GLOB' ) {
150 25         86 my $charset = setting('charset');
151 25         79 my $ctype = $response->header('Content-Type');
152              
153 25 50 66     930 if ( $charset && $ctype && _is_text($ctype) ) {
      66        
154 3 50       11 $content = Encode::encode( $charset, $content ) unless $response->_already_encoded;
155 3 100       142 $response->header( 'Content-Type' => "$ctype; charset=$charset" )
156             if $ctype !~ /$charset/;
157             }
158 25 100       181 if (!defined $response->header('Content-Length')) {
159 167     167   1711 use bytes; # turn off character semantics
  167         464  
  167         1861  
160 20         716 $response->header( 'Content-Length' => length($content) );
161             }
162 25         977 $content = [$content];
163             }
164             else {
165 0 0       0 if ( !defined $response->header('Content-Length') ) {
166 0         0 my $stat = stat $content;
167 0         0 $response->header( 'Content-Length' => $stat->size );
168             }
169             }
170              
171             # drop content if request is HEAD
172 25 50 66     103 $content = ['']
173             if ( defined Dancer::SharedData->request
174             && Dancer::SharedData->request->is_head() );
175              
176             # drop content AND content_length if response is 1xx or (2|3)04
177 25 50       91 if ($response->status =~ (/^[23]04$/ )) {
178 0         0 $content = [''];
179 0         0 $response->header('Content-Length' => 0);
180             }
181              
182 25         68 Dancer::Logger::core("response: " . $response->status);
183              
184 25         86 my $status = $response->status();
185 25         74 my $headers = $response->headers_to_array();
186              
187             # reverse streaming
188 25 50 33     91 if ( ref $response->streamed and ref $response->streamed eq 'CODE' ) {
189 0         0 return $response->streamed->(
190             $status, $headers
191             );
192             }
193              
194 25         120 return [ $status, $headers, $content ];
195             }
196              
197             sub _is_text {
198 8     8   2265 my ($content_type) = @_;
199 8         66 return $content_type =~ /(\bx(?:ht)?ml\b|text|json|javascript)/;
200             }
201              
202             # Fancy banner to print on startup
203             sub print_banner {
204 0 0   0 0 0 if (setting('startup_info')) {
205 0         0 my $env = setting('environment');
206 0         0 print "== Entering the $env dance floor ...\n";
207             }
208             }
209              
210 1     1 0 6 sub dance { (shift)->start(@_) }
211              
212             1;
213              
214             __END__