File Coverage

blib/lib/Web/Machine/FSM/States.pm
Criterion Covered Total %
statement 324 329 98.4
branch 188 202 93.0
condition 28 35 80.0
subroutine 80 81 98.7
pod 0 61 0.0
total 620 708 87.5


line stmt bran cond sub pod time code
1             package Web::Machine::FSM::States;
2             BEGIN {
3 13     13   453 $Web::Machine::FSM::States::AUTHORITY = 'cpan:STEVAN';
4             }
5             # ABSTRACT: The States for Web Machine
6             $Web::Machine::FSM::States::VERSION = '0.15';
7 13     13   217 use strict;
  13         29  
  13         565  
8 13     13   69 use warnings;
  13         23  
  13         367  
9              
10 13     13   71 use B ();
  13         25  
  13         202  
11 13     13   66 use Hash::MultiValue;
  13         31  
  13         315  
12              
13 13     13   97 use Carp qw[ confess ];
  13         24  
  13         1056  
14              
15 13         121 use Web::Machine::Util qw[
16             first
17             pair_key
18             pair_value
19             create_header
20 13     13   2820 ];
  13         30  
21 13         106 use Web::Machine::Util::BodyEncoding qw[
22             encode_body_if_set
23             encode_body
24 13     13   22604 ];
  13         48  
25 13         184 use Web::Machine::Util::ContentNegotiation qw[
26             choose_media_type
27             match_acceptable_media_type
28             choose_language
29             choose_charset
30             choose_encoding
31 13     13   15501 ];
  13         50  
32              
33 13         130 use Sub::Exporter -setup => {
34             exports => [qw[
35             start_state
36             is_status_code
37             is_new_state
38             get_state_name
39             get_state_desc
40             ]]
41 13     13   9152 };
  13         27  
42              
43             my %STATE_DESC;
44              
45             # my exports ...
46              
47 123     123 0 496 sub start_state { \&b13 }
48 2741     2741 0 13345 sub is_status_code { ref $_[0] eq 'SCALAR' }
49 2433     2433 0 8849 sub is_new_state { ref $_[0] eq 'CODE' }
50 1766     1766 0 13366 sub get_state_name { B::svref_2object( shift )->GV->NAME }
51 0 0   0 0 0 sub get_state_desc { $STATE_DESC{ ref $_[0] ? get_state_name( shift ) : shift } }
52              
53             # some utilities ...
54              
55             sub _unquote_header {
56 26     26   624 my $value = shift;
57 26 50       97 if ( $value =~ /^"(.*)"$/ ) {
58 0         0 return $1;
59             }
60 26         124 return $value;
61             }
62              
63             sub _ensure_quoted_header {
64 7     7   10 my $value = shift;
65 7 50       29 return $value if $value =~ /^"(.*)"$/;
66 7         33 return '"' . $value . '"';
67             }
68              
69             sub _get_acceptable_content_type_handler {
70 12     12   31 my ($resource, $request) = @_;
71 12   100     44 my $acceptable = match_acceptable_media_type(
72             ($request->header('Content-Type') || 'application/octet-stream'),
73             $resource->content_types_accepted
74             );
75 12 100       101 return \415 unless $acceptable;
76 10         49 return pair_value( $acceptable );
77             }
78              
79             sub _add_caching_headers {
80 43     43   78 my ($resource, $response) = @_;
81 43 100       360 if ( my $etag = $resource->generate_etag ) {
82 7         47 $response->header( 'Etag' => _ensure_quoted_header( $etag ) );
83             }
84 43 50       500 if ( my $expires = $resource->expires ) {
85 0         0 $response->header( 'Expires' => $expires );
86             }
87 43 100       243 if ( my $modified = $resource->last_modified ) {
88 7         2199 $response->header( 'Last-Modified' => $modified );
89             }
90             }
91              
92             sub _handle_304 {
93 7     7   250 my ($resource, $response) = @_;
94 7         29 $response->headers->remove_header('Content-Type');
95 7         177 $response->headers->remove_header('Content-Encoding');
96 7         129 $response->headers->remove_header('Content-Language');
97 7         116 _add_caching_headers($resource, $response);
98 7         340 return \304;
99             }
100              
101             sub _is_redirect {
102 10     10   22 my ($response) = @_;
103             # NOTE:
104             # this makes a guess that the user has
105             # told the Plack::Response that they
106             # want to redirect. We do this based
107             # on the fact that the ->redirect method
108             # will set the status, while in almost all
109             # other cases the status of the response
110             # will not be set yet.
111             # - SL
112 10 100       207 return 1 if $response->status;
113 7         55 return;
114             }
115              
116             sub _metadata {
117 344     344   505 my ($request) = @_;
118 344         1197 return $request->env->{'web.machine.context'};
119             }
120              
121             ## States
122              
123             $STATE_DESC{'b13'} = 'service_available';
124             sub b13 {
125 123     123 0 265 my ($resource, $request, $response) = @_;
126 123 100       949 $resource->service_available ? \&b12 : \503;
127             }
128              
129             $STATE_DESC{'b12'} = 'known_method';
130             sub b12 {
131 121     121 0 258 my ($resource, $request, $response) = @_;
132 121         557 my $method = $request->method;
133 121 100       932 (grep { $method eq $_ } @{ $resource->known_methods }) ? \&b11 : \501;
  948         1670  
  121         872  
134             }
135              
136             $STATE_DESC{'b11'} = 'uri_too_long';
137             sub b11 {
138 120     120 0 409 my ($resource, $request, $response) = @_;
139 120 100       582 $resource->uri_too_long( $request->uri ) ? \414 : \&b10;
140             }
141              
142             $STATE_DESC{'b10'} = 'method_allowed';
143             sub b10 {
144 119     119 0 295 my ($resource, $request, $response) = @_;
145 119         498 my $method = $request->method;
146 119 100       868 return \&b9 if grep { $method eq $_ } @{ $resource->allowed_methods };
  288         1154  
  119         714  
147 1         3 $response->header('Allow' => join ", " => @{ $resource->allowed_methods } );
  1         5  
148 1         74 return \405;
149             }
150              
151             $STATE_DESC{'b9'} = 'malformed_request';
152             sub b9 {
153 118     118 0 253 my ($resource, $request, $response) = @_;
154 118 100       801 $resource->malformed_request ? \400 : \&b8;
155             }
156              
157             $STATE_DESC{'b8'} = 'is_authorized';
158             sub b8 {
159 117     117 0 263 my ($resource, $request, $response) = @_;
160 117         563 my $result = $resource->is_authorized( $request->header('Authorization') );
161             # if we get back a status, then use it
162 117 100 100     617 if ( is_status_code( $result ) ) {
    100          
163 1         6 return $result;
164             }
165             # if we just get back true, then
166             # move onto the next state
167             elsif ( defined $result && "$result" eq "1" ) {
168 113         457 return \&b7
169             }
170             # anything else will either be
171             # a WWW-Authenticate header or
172             # a simple false value
173             else {
174 3 100       11 if ( $result ) {
175 1         7 $response->header( 'WWW-Authenticate' => $result );
176             }
177 3         57 return \401;
178             }
179             }
180              
181             $STATE_DESC{'b7'} = 'forbidden';
182             sub b7 {
183 113     113 0 341 my ($resource, $request, $response) = @_;
184 113 100       776 $resource->forbidden ? \403 : \&b6;
185             }
186              
187             $STATE_DESC{'b6'} = 'content_headers_okay';
188             sub b6 {
189 111     111 0 303 my ($resource, $request, $response) = @_;
190              
191             # FIX-ME
192             # there is a better way to do this,
193             # also, HTTP::Headers will usually
194             # group things into arrays, so we
195             # can either avoid or better take
196             # advantage of Hash::MultiValue.
197             # But we are almost certainly not
198             # handling that case properly maybe.
199 111         1100 my $content_headers = Hash::MultiValue->new;
200             $request->headers->scan(sub {
201 155     155   3398 my ($name, $value) = @_;
202 155 100       969 $content_headers->add( $name, $value ) if (lc $name) =~ /^content-/;
203 111         5452 });
204              
205 111 100       3540 $resource->valid_content_headers( $content_headers ) ? \&b5 : \501;
206             }
207              
208             $STATE_DESC{'b5'} = 'known_content_type';
209             sub b5 {
210 110     110 0 280 my ($resource, $request, $response) = @_;
211 110 100       801 $resource->known_content_type( $request->content_type ) ? \&b4 : \415;
212             }
213              
214             $STATE_DESC{'b4'} = 'request_entity_too_large';
215             sub b4 {
216 109     109 0 243 my ($resource, $request, $response) = @_;
217 109 100       459 $resource->valid_entity_length( $request->content_length ) ? \&b3 : \413;
218             }
219              
220             $STATE_DESC{'b3'} = 'method_is_options';
221             sub b3 {
222 108     108 0 229 my ($resource, $request, $response) = @_;
223 108 100       378 if ( $request->method eq 'OPTIONS' ) {
224 1         10 $response->headers( $resource->options );
225 1         73 return \200;
226             }
227 107         1232 return \&c3
228             }
229              
230             $STATE_DESC{'c3'} = 'accept_header_exists';
231             sub c3 {
232 107     107 0 230 my ($resource, $request, $response) = @_;
233 107         374 my $metadata = _metadata($request);
234 107 100       813 if ( !$request->header('Accept') ) {
235 95         3566 $metadata->{'Content-Type'} = create_header( MediaType => (
236             pair_key( $resource->content_types_provided->[0] )
237             ));
238 95         237183 return \&d4
239             }
240 12         1660 return \&c4;
241             }
242              
243             $STATE_DESC{'c4'} = 'acceptable_media_type_available';
244             sub c4 {
245 12     12 0 31 my ($resource, $request, $response) = @_;
246 12         24 my $metadata = _metadata($request);
247              
248 12         62 my @types = map { pair_key( $_ ) } @{ $resource->content_types_provided };
  13         133  
  12         44  
249              
250 12 100       74 if ( my $chosen_type = choose_media_type( \@types, $request->header('Accept') ) ) {
251 11         2702 $metadata->{'Content-Type'} = $chosen_type;
252 11         51 return \&d4;
253             }
254              
255 1         245 return \406;
256             }
257              
258             $STATE_DESC{'d4'} = 'accept_language_header_exists';
259             sub d4 {
260 106     106 0 369 my ($resource, $request, $response) = @_;
261 106 100       429 (not $request->header('Accept-Language')) ? \&e5 : \&d5;
262             }
263              
264              
265             $STATE_DESC{'d5'} = 'accept_language_choice_available';
266             sub d5 {
267 17     17 0 41 my ($resource, $request, $response) = @_;
268 17         33 my $metadata = _metadata($request);
269              
270 17 100       113 if ( my $language = choose_language( $resource->languages_provided, $request->header('Accept-Language') ) ) {
271 15         1844 $metadata->{'Language'} = $language;
272             # handle the short circuit here ...
273 15 100       92 $response->header( 'Content-Language' => $language ) if "$language" ne "1";
274 15         662 return \&e5;
275             }
276              
277 2         341 return \406;
278             }
279              
280             $STATE_DESC{'e5'} = 'accept_charset_exists';
281             sub e5 {
282 104     104 0 245 my ($resource, $request, $response) = @_;
283 104 100       382 (not $request->header('Accept-Charset')) ? \&f6 : \&e6;
284             }
285              
286             $STATE_DESC{'e6'} = 'accept_charset_choice_available';
287             sub e6 {
288 22     22 0 45 my ($resource, $request, $response) = @_;
289 22         53 my $metadata = _metadata($request);
290              
291 22 100       161 if ( my $charset = choose_charset( $resource->charsets_provided, $request->header('Accept-Charset') ) ) {
292             # handle the short circuit here ...
293 20 50       8376 $metadata->{'Charset'} = $charset if "$charset" ne "1";
294 20         88 return \&f6;
295             }
296              
297 2         864 return \406;
298             }
299              
300             $STATE_DESC{'f6'} = 'accept_encoding_exists';
301             # (also, set content-type header here, now that charset is chosen)
302             sub f6 {
303 102     102 0 276 my ($resource, $request, $response) = @_;
304 102         237 my $metadata = _metadata($request);
305              
306             # If the client doesn't provide an Accept-Charset header we should just
307             # encode with the default.
308 102 100 100     1107 if ( $resource->default_charset && !$request->header('Accept-Charset') ) {
309 4         180 my $default = $resource->default_charset;
310 4 100       37 $metadata->{'Charset'} = ref $default ? pair_key($default) : $default;
311             }
312              
313 102 100       1424 if ( my $charset = $metadata->{'Charset'} ) {
314             # Add the charset to the content type now ...
315 24         145 $metadata->{'Content-Type'}->add_param( 'charset' => $charset );
316             }
317             # put the content type in the header now ...
318 102         1123 $response->header( 'Content-Type' => $metadata->{'Content-Type'}->as_string );
319              
320 102 100       9905 if ( $request->header('Accept-Encoding') ) {
321 13         1464 return \&f7
322             }
323             else {
324 89 100       3295 if ( my $encoding = choose_encoding( $resource->encodings_provided, "identity;q=1.0,*;q=0.5" ) ) {
325 85 100       62004 $response->header( 'Content-Encoding' => $encoding ) unless $encoding eq 'identity';
326 85         1149 $metadata->{'Content-Encoding'} = $encoding;
327 85         436 return \&g7;
328             }
329             else {
330 4         59 return \406;
331             }
332             }
333             }
334              
335             $STATE_DESC{'f7'} = 'accept_encoding_choice_available';
336             sub f7 {
337 13     13 0 32 my ($resource, $request, $response) = @_;
338 13         31 my $metadata = _metadata($request);
339              
340 13 100       99 if ( my $encoding = choose_encoding( $resource->encodings_provided, $request->header('Accept-Encoding') ) ) {
341 9 100       1256 $response->header( 'Content-Encoding' => $encoding ) unless $encoding eq 'identity';
342 9         343 $metadata->{'Content-Encoding'} = $encoding;
343 9         35 return \&g7;
344             }
345              
346 4         72 return \406;
347             }
348              
349             $STATE_DESC{'g7'} = 'resource_exists';
350             sub g7 {
351 94     94 0 231 my ($resource, $request, $response) = @_;
352              
353             # NOTE:
354             # set Vary header here since we are
355             # done with content negotiation
356             # - SL
357 94         174 my @variances = @{ $resource->variances };
  94         710  
358              
359 94 100       186 push @variances => 'Accept' if scalar @{ $resource->content_types_provided } > 1;
  94         329  
360 94 100       968 push @variances => 'Accept-Encoding' if scalar keys %{ $resource->encodings_provided } > 1;
  94         309  
361 94 100 66     800 push @variances => 'Accept-Charset' if defined $resource->charsets_provided && scalar @{ $resource->charsets_provided } > 1;
  94         736  
362 94 100       933 push @variances => 'Accept-Language' if scalar @{ $resource->languages_provided } > 1;
  94         391  
363              
364 94 100       579 $response->header( 'Vary' => join ', ' => @variances ) if @variances;
365              
366 94 100       1509 $resource->resource_exists ? \&g8 : \&h7;
367             }
368              
369             $STATE_DESC{'g8'} = 'if_match_exists';
370             sub g8 {
371 58     58 0 135 my ($resource, $request, $response) = @_;
372 58 100       255 $request->header('If-Match') ? \&g9 : \&h10;
373             }
374              
375             $STATE_DESC{'g9'} = 'if_match_is_wildcard';
376             sub g9 {
377 3     3 0 6 my ($resource, $request, $response) = @_;
378 3 100       12 _unquote_header( $request->header('If-Match') ) eq "*" ? \&h10 : \&g11;
379             }
380              
381             $STATE_DESC{'g11'} = 'etag_in_if_match_list';
382             sub g11 {
383 2     2 0 4 my ($resource, $request, $response) = @_;
384 2         10 my @etags = map { _unquote_header( $_ ) } split /\s*\,\s*/ => $request->header('If-Match');
  2         62  
385 2         8 my $etag = $resource->generate_etag;
386 2 100       10 (grep { $etag eq $_ } @etags) ? \&h10 : \412;
  2         13  
387             }
388              
389             $STATE_DESC{'h7'} = 'if_match_exists_and_if_match_is_wildcard';
390             sub h7 {
391 36     36 0 70 my ($resource, $request, $response) = @_;
392 36 100 100     140 ($request->header('If-Match') && _unquote_header( $request->header('If-Match') ) eq "*") ? \412 : \&i7;
393             }
394              
395             $STATE_DESC{'h10'} = 'if_unmodified_since_exists';
396             sub h10 {
397 57     57 0 136 my ($resource, $request, $response) = @_;
398 57 100       195 $request->header('If-Unmodified-Since') ? \&h11 : \&i12;
399             }
400              
401             $STATE_DESC{'h11'} = 'if_unmodified_since_is_valid_date';
402             sub h11 {
403 6     6 0 16 my ($resource, $request, $response) = @_;
404 6         13 my $metadata = _metadata($request);
405 6 50       36 if ( my $date = $request->header('If-Unmodified-Since') ) {
406 6         414 $metadata->{'If-Unmodified-Since'} = $date;
407 6         21 return \&h12;
408             }
409 0         0 return \&i12;
410             }
411              
412             $STATE_DESC{'h12'} = 'last_modified_is_greater_than_if_unmodified_since';
413             sub h12 {
414 6     6 0 13 my ($resource, $request, $response) = @_;
415 6         11 my $metadata = _metadata($request);
416 6 100 66     39 defined $resource->last_modified
417             &&
418             ($resource->last_modified->epoch > $metadata->{'If-Unmodified-Since'}->epoch)
419             ? \412 : \&i12;
420             }
421              
422             $STATE_DESC{'i4'} = 'moved_permanently';
423             sub i4 {
424 8     8 0 16 my ($resource, $request, $response) = @_;
425 8 100       51 if ( my $uri = $resource->moved_permanently ) {
426 2 100       21 if ( is_status_code( $uri ) ) {
427 1         4 return $uri;
428             }
429 1         7 $response->header('Location' => $uri );
430 1         48 return \301;
431             }
432 6         21 return \&p3;
433             }
434              
435             $STATE_DESC{'i7'} = 'method_is_put';
436             sub i7 {
437 30     30 0 67 my ($resource, $request, $response) = @_;
438 30 100       103 $request->method eq 'PUT' ? \&i4 : \&k7
439             }
440              
441             $STATE_DESC{'i12'} = 'if_none_match_exists';
442             sub i12 {
443 54     54 0 134 my ($resource, $request, $response) = @_;
444 54 100       206 $request->header('If-None-Match') ? \&i13 : \&l13
445             }
446              
447             $STATE_DESC{'i13'} = 'if_none_match_is_wildcard';
448             sub i13 {
449 19     19 0 43 my ($resource, $request, $response) = @_;
450 19 100       61 $request->header('If-None-Match') eq "*" ? \&j18 : \&k13
451             }
452              
453             $STATE_DESC{'j18'} = 'method_is_get_or_head';
454             sub j18 {
455 9     9 0 21 my ($resource, $request, $response) = @_;
456 9 100 100     29 $request->method eq 'GET' || $request->method eq 'HEAD'
457             ? _handle_304( $resource, $response )
458             : \412
459             }
460              
461             $STATE_DESC{'k5'} = 'moved_permanently';
462             sub k5 {
463 20     20 0 95 my ($resource, $request, $response) = @_;
464 20 100       170 if ( my $uri = $resource->moved_permanently ) {
465 2 100       12 if ( is_status_code( $uri ) ) {
466 1         5 return $uri;
467             }
468 1         4 $response->header('Location' => $uri );
469 1         32 return \301;
470             }
471 18         253 return \&l5;
472             }
473              
474             $STATE_DESC{'k7'} = 'previously_existed';
475             sub k7 {
476 22     22 0 55 my ($resource, $request, $response) = @_;
477 22 100       102 $resource->previously_existed ? \&k5 : \&l7;
478             }
479              
480             $STATE_DESC{'k13'} = 'etag_in_if_none_match';
481             sub k13 {
482 13     13 0 40 my ($resource, $request, $response) = @_;
483 13         55 my @etags = map { _unquote_header( $_ ) } split /\s*\,\s*/ => $request->header('If-None-Match');
  13         523  
484 13         60 my $etag = $resource->generate_etag;
485 13 100 100     174 $etag && (grep { $etag eq $_ } @etags) ? \&j18 : \&l13;
486             }
487              
488             $STATE_DESC{'l5'} = 'moved_temporarily';
489             sub l5 {
490 18     18 0 48 my ($resource, $request, $response) = @_;
491 18 100       143 if ( my $uri = $resource->moved_temporarily ) {
492 2 100       14 if ( is_status_code( $uri ) ) {
493 1         3 return $uri;
494             }
495 1         5 $response->header('Location' => $uri );
496 1         40 return \307;
497             }
498 16         63 return \&m5;
499             }
500              
501             $STATE_DESC{'l7'} = 'method_is_post';
502             sub l7 {
503 2     2 0 5 my ($resource, $request, $response) = @_;
504 2 100       7 $request->method eq 'POST' ? \&m7 : \404
505             }
506              
507             $STATE_DESC{'l13'} = 'if_modified_since_exists';
508             sub l13 {
509 45     45 0 107 my ($resource, $request, $response) = @_;
510 45 100       169 $request->header('If-Modified-Since') ? \&l14 : \&m16
511             }
512              
513             $STATE_DESC{'l14'} = 'if_modified_since_is_valid_date';
514             sub l14 {
515 9     9 0 21 my ($resource, $request, $response) = @_;
516 9         25 my $metadata = _metadata($request);
517 9 50       70 if ( my $date = $request->header('If-Modified-Since') ) {
518 9         657 $metadata->{'If-Modified-Since'} = $date;
519 9         35 return \&l15;
520             }
521 0         0 return \&m16;
522             }
523              
524             $STATE_DESC{'l15'} = 'if_modified_since_greater_than_now';
525             sub l15 {
526 9     9 0 31 my ($resource, $request, $response) = @_;
527 9         21 my $metadata = _metadata($request);
528 9 100       76 ($metadata->{'If-Modified-Since'}->epoch > (scalar time)) ? \&m16 : \&l17;
529             }
530              
531             $STATE_DESC{'l17'} = 'last_modified_is_greater_than_if_modified_since';
532             sub l17 {
533 1     1 0 3 my ($resource, $request, $response) = @_;
534 1         2 my $metadata = _metadata($request);
535 1 50 33     8 defined $resource->last_modified
536             &&
537             ($resource->last_modified->epoch > $metadata->{'If-Modified-Since'}->epoch)
538             ? \&m16 : _handle_304( $resource, $response );
539             }
540              
541             $STATE_DESC{'m5'} = 'method_is_post';
542             sub m5 {
543 16     16 0 37 my ($resource, $request, $response) = @_;
544 16 100       56 $request->method eq 'POST' ? \&n5 : \410
545             }
546              
547             $STATE_DESC{'m7'} = 'allow_post_to_missing_resource';
548             sub m7 {
549 1     1 0 4 my ($resource, $request, $response) = @_;
550 1 50       5 $resource->allow_missing_post ? \&n11 : \404
551             }
552              
553             $STATE_DESC{'m16'} = 'method_is_delete';
554             sub m16 {
555 44     44 0 105 my ($resource, $request, $response) = @_;
556 44 100       176 $request->method eq 'DELETE' ? \&m20 : \&n16
557             }
558              
559             $STATE_DESC{'m20'} = 'delete_enacted_immediately';
560             sub m20 {
561 4     4 0 10 my ($resource, $request, $response) = @_;
562 4 100       17 $resource->delete_resource ? \&m20b : \500
563             }
564              
565             $STATE_DESC{'m20b'} = 'did_delete_complete';
566             sub m20b {
567 3     3 0 8 my ($resource, $request, $response) = @_;
568 3 100       14 $resource->delete_completed ? \&o20 : \202
569             }
570              
571             $STATE_DESC{'n5'} = 'allow_post_to_missing_resource';
572             sub n5 {
573 15     15 0 32 my ($resource, $request, $response) = @_;
574 15 100       57 $resource->allow_missing_post ? \&n11 : \410
575             }
576              
577             sub _n11_create_path {
578 6     6   15 my ($resource, $request, $response) = @_;
579              
580 6         20 my $uri = $resource->create_path;
581 6 100       207 confess "Create Path Nil" unless $uri;
582 5   66     27 my $base_uri = $resource->base_uri || $request->base;
583              
584             # do a little cleanup
585 5 50       882 $base_uri =~ s!/$!! if $uri =~ m!^/!;
586 5 100 33     34 $base_uri .= '/' if $uri !~ m!^/! && $base_uri !~ m!/$!;
587 5         51 my $new_uri = URI->new( $base_uri . $uri )->canonical;
588             # NOTE:
589             # the ruby and JS versions will set the path_info
590             # for the request object here, but since our requests
591             # are immutable, we don't allow that. I don't see
592             # where this ends up being useful so I am going to
593             # skip it and not bother.
594             # - SL
595 5         657 $response->header( 'Location' => $new_uri->path_query );
596             }
597              
598             $STATE_DESC{'n11'} = 'redirect';
599             sub n11 {
600 15     15 0 43 my ($resource, $request, $response) = @_;
601 15 100       71 if ( $resource->post_is_create ) {
602              
603             # the default behavior as specified by
604             # the Erlang/Ruby versions, however this
605             # is a very unpopular "feature" so we are
606             # allowing it to be bypassed here.
607 6 50       76 _n11_create_path( $resource, $request, $response )
608             if not $resource->create_path_after_handler;
609              
610 5         308 my $handler = _get_acceptable_content_type_handler( $resource, $request );
611 5 100       20 return $handler if is_status_code( $handler );
612              
613 4         17 my $result = $resource->$handler();
614 4 100       17 return $result if is_status_code( $result );
615              
616 3 50       14 _n11_create_path( $resource, $request, $response )
617             if $resource->create_path_after_handler;
618             }
619             else {
620 9         223 my $result = $resource->process_post;
621 9 100       227 if ( $result ) {
622 8 100       20 return $result if is_status_code( $result );
623 7         52 encode_body_if_set( $resource, $response );
624             }
625             else {
626 1         370 confess "Process Post Invalid";
627             }
628             }
629              
630 10 100       383 if ( _is_redirect( $response ) ) {
631 3 100       193 if ( $response->location ) {
632 2         816 return \303;
633             }
634             else {
635 1         251 confess "Bad Redirect"
636             }
637             }
638              
639 7         27 return \&p11;
640             }
641              
642             $STATE_DESC{'n16'} = 'method_is_post';
643             sub n16 {
644 40     40 0 84 my ($resource, $request, $response) = @_;
645 40 100       125 $request->method eq 'POST' ? \&n11 : \&o16
646             }
647              
648             $STATE_DESC{'o14'} = 'in_conflict';
649             sub o14 {
650 3     3 0 9 my ($resource, $request, $response) = @_;
651 3 100       24 return \409 if $resource->is_conflict;
652              
653 2         8 my $handler = _get_acceptable_content_type_handler( $resource, $request );
654 2 50       9 return $handler if is_status_code( $handler );
655              
656 2         11 my $result = $resource->$handler();
657              
658 2 100       7 return $result if is_status_code( $result );
659 1         5 return \&p11;
660             }
661              
662             $STATE_DESC{'o16'} = 'method_is_put';
663             sub o16 {
664 39     39 0 84 my ($resource, $request, $response) = @_;
665 39 100       1275 $request->method eq 'PUT' ? \&o14 : \&o18;
666             }
667              
668             $STATE_DESC{'o18'} = 'multiple_representations';
669             sub o18 {
670 40     40 0 82 my ($resource, $request, $response) = @_;
671 40         117 my $metadata = _metadata($request);
672 40 100 100     255 if ( $request->method eq 'GET' || $request->method eq 'HEAD' ) {
673 36         368 _add_caching_headers( $resource, $response );
674              
675 36         102 my $content_type = $metadata->{'Content-Type'};
676             my $match = first {
677 36     36   347 my $ct = create_header( MediaType => pair_key( $_ ) );
678 36         3994 $content_type->match( $ct )
679 36         207 } @{ $resource->content_types_provided };
  36         134  
680              
681 36         2063 my $handler = pair_value( $match );
682 36         307 my $result = $resource->$handler();
683              
684 36 100       3913 return $result if is_status_code( $result );
685              
686 35 100       141 unless($request->method eq 'HEAD') {
687 34 100       311 if (ref($result) eq 'CODE') {
688 5         14 $request->env->{'web.machine.streaming_push'} = $result;
689             }
690             else {
691 29         179 $response->body( $result );
692             }
693 34         377 encode_body( $resource, $response );
694             }
695 35         611 return \&o18b;
696             }
697             else {
698 4         73 return \&o18b;
699             }
700              
701             }
702              
703             $STATE_DESC{'o18b'} = 'multiple_choices';
704             sub o18b {
705 39     39 0 88 my ($resource, $request, $response) = @_;
706 39 100       273 $resource->multiple_choices ? \300 : \200;
707             }
708              
709             $STATE_DESC{'o20'} = 'response_body_includes_entity';
710             sub o20 {
711 7     7 0 18 my ($resource, $request, $response) = @_;
712 7 100       28 $response->body ? \&o18 : \204;
713             }
714              
715             $STATE_DESC{'p3'} = 'in_conflict';
716             sub p3 {
717 6     6 0 16 my ($resource, $request, $response) = @_;
718 6 100       39 return \409 if $resource->is_conflict;
719              
720 5         20 my $handler = _get_acceptable_content_type_handler( $resource, $request );
721 5 100       16 return $handler if is_status_code( $handler );
722              
723 4         18 my $result = $resource->$handler();
724              
725 4 100       15 return $result if is_status_code( $result );
726 3         16 return \&p11;
727             }
728              
729             $STATE_DESC{'p11'} = 'new_resource';
730             sub p11 {
731 11     11 0 31 my ($resource, $request, $response) = @_;
732 11 100       41 (not $response->header('Location')) ? \&o20 : \201
733             }
734              
735             1;
736              
737             __END__