File Coverage

blib/lib/Web/Machine/FSM/States.pm
Criterion Covered Total %
statement 323 328 98.4
branch 188 202 93.0
condition 28 35 80.0
subroutine 79 80 98.7
pod 0 61 0.0
total 618 706 87.5


line stmt bran cond sub pod time code
1             package Web::Machine::FSM::States;
2             # ABSTRACT: The States for Web Machine
3              
4 13     13   122 use strict;
  13         17  
  13         426  
5 13     13   55 use warnings;
  13         19  
  13         609  
6              
7             our $VERSION = '0.16';
8              
9 13     13   59 use B ();
  13         15  
  13         166  
10 13     13   46 use Hash::MultiValue;
  13         15  
  13         239  
11              
12 13     13   45 use Carp qw[ confess ];
  13         14  
  13         712  
13              
14 13         98 use Web::Machine::Util qw[
15             first
16             pair_key
17             pair_value
18             create_header
19 13     13   1027 ];
  13         21  
20 13         89 use Web::Machine::Util::BodyEncoding qw[
21             encode_body_if_set
22             encode_body
23 13     13   10615 ];
  13         32  
24 13         86 use Web::Machine::Util::ContentNegotiation qw[
25             choose_media_type
26             match_acceptable_media_type
27             choose_language
28             choose_charset
29             choose_encoding
30 13     13   8231 ];
  13         32  
31              
32 13         88 use Sub::Exporter -setup => {
33             exports => [qw[
34             start_state
35             is_status_code
36             is_new_state
37             get_state_name
38             get_state_desc
39             ]]
40 13     13   5571 };
  13         20  
41              
42             my %STATE_DESC;
43              
44             # my exports ...
45              
46 124     124 0 286 sub start_state { \&b13 }
47 2766     2766 0 8089 sub is_status_code { ref $_[0] eq 'SCALAR' }
48 2455     2455 0 5185 sub is_new_state { ref $_[0] eq 'CODE' }
49 1766     1766 0 7451 sub get_state_name { B::svref_2object( shift )->GV->NAME }
50 0 0   0 0 0 sub get_state_desc { $STATE_DESC{ ref $_[0] ? get_state_name( shift ) : shift } }
51              
52             # some utilities ...
53              
54             sub _unquote_header {
55 26     26   351 my $value = shift;
56 26 50       58 if ( $value =~ /^"(.*)"$/ ) {
57 0         0 return $1;
58             }
59 26         75 return $value;
60             }
61              
62             sub _ensure_quoted_header {
63 7     7   9 my $value = shift;
64 7 50       15 return $value if $value =~ /^"(.*)"$/;
65 7         29 return '"' . $value . '"';
66             }
67              
68             sub _get_acceptable_content_type_handler {
69 12     12   18 my ($resource, $request) = @_;
70 12   100     27 my $acceptable = match_acceptable_media_type(
71             ($request->header('Content-Type') || 'application/octet-stream'),
72             $resource->content_types_accepted
73             );
74 12 100       62 return \415 unless $acceptable;
75 10         32 return pair_value( $acceptable );
76             }
77              
78             sub _add_caching_headers {
79 43     43   79 my ($resource, $response) = @_;
80 43 100       176 if ( my $etag = $resource->generate_etag ) {
81 7         27 $response->header( 'Etag' => _ensure_quoted_header( $etag ) );
82             }
83 43 50       312 if ( my $expires = $resource->expires ) {
84 0         0 $response->header( 'Expires' => $expires );
85             }
86 43 100       153 if ( my $modified = $resource->last_modified ) {
87 7         1323 $response->header( 'Last-Modified' => $modified );
88             }
89             }
90              
91             sub _handle_304 {
92 7     7   186 my ($resource, $response) = @_;
93 7         18 $response->headers->remove_header('Content-Type');
94 7         115 $response->headers->remove_header('Content-Encoding');
95 7         82 $response->headers->remove_header('Content-Language');
96 7         75 _add_caching_headers($resource, $response);
97 7         189 return \304;
98             }
99              
100             sub _is_redirect {
101 10     10   12 my ($response) = @_;
102             # NOTE:
103             # this makes a guess that the user has
104             # told the Plack::Response that they
105             # want to redirect. We do this based
106             # on the fact that the ->redirect method
107             # will set the status, while in almost all
108             # other cases the status of the response
109             # will not be set yet.
110             # - SL
111 10 100       23 return 1 if $response->status;
112 7         35 return;
113             }
114              
115             sub _metadata {
116 346     346   349 my ($request) = @_;
117 346         651 return $request->env->{'web.machine.context'};
118             }
119              
120             ## States
121              
122             $STATE_DESC{'b13'} = 'service_available';
123             sub b13 {
124 124     124 0 150 my ($resource, $request, $response) = @_;
125 124 100       522 $resource->service_available ? \&b12 : \503;
126             }
127              
128             $STATE_DESC{'b12'} = 'known_method';
129             sub b12 {
130 122     122 0 137 my ($resource, $request, $response) = @_;
131 122         302 my $method = $request->method;
132 122 100       585 (grep { $method eq $_ } @{ $resource->known_methods }) ? \&b11 : \501;
  956         1091  
  122         466  
133             }
134              
135             $STATE_DESC{'b11'} = 'uri_too_long';
136             sub b11 {
137 121     121 0 147 my ($resource, $request, $response) = @_;
138 121 100       289 $resource->uri_too_long( $request->uri ) ? \414 : \&b10;
139             }
140              
141             $STATE_DESC{'b10'} = 'method_allowed';
142             sub b10 {
143 120     120 0 167 my ($resource, $request, $response) = @_;
144 120         306 my $method = $request->method;
145 120         504 my @allowed_methods = @{ $resource->allowed_methods };
  120         369  
146 120 100       373 return \&b9 if grep { $method eq $_ } @allowed_methods;
  289         662  
147 1         5 $response->header('Allow' => join ", " => @allowed_methods );
148 1         33 return \405;
149             }
150              
151             $STATE_DESC{'b9'} = 'malformed_request';
152             sub b9 {
153 119     119 0 194 my ($resource, $request, $response) = @_;
154 119 100       483 $resource->malformed_request ? \400 : \&b8;
155             }
156              
157             $STATE_DESC{'b8'} = 'is_authorized';
158             sub b8 {
159 118     118 0 143 my ($resource, $request, $response) = @_;
160 118         294 my $result = $resource->is_authorized( $request->header('Authorization') );
161             # if we get back a status, then use it
162 118 100 100     297 if ( is_status_code( $result ) ) {
    100          
163 1         3 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 114         259 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       7 if ( $result ) {
175 1         3 $response->header( 'WWW-Authenticate' => $result );
176             }
177 3         40 return \401;
178             }
179             }
180              
181             $STATE_DESC{'b7'} = 'forbidden';
182             sub b7 {
183 114     114 0 134 my ($resource, $request, $response) = @_;
184 114 100       387 $resource->forbidden ? \403 : \&b6;
185             }
186              
187             $STATE_DESC{'b6'} = 'content_headers_okay';
188             sub b6 {
189 112     112 0 135 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 112         495 my $content_headers = Hash::MultiValue->new;
200             $request->headers->scan(sub {
201 158     158   2131 my ($name, $value) = @_;
202 158 100       614 $content_headers->add( $name, $value ) if (lc $name) =~ /^content-/;
203 112         2764 });
204              
205 112 100       2161 $resource->valid_content_headers( $content_headers ) ? \&b5 : \501;
206             }
207              
208             $STATE_DESC{'b5'} = 'known_content_type';
209             sub b5 {
210 111     111 0 150 my ($resource, $request, $response) = @_;
211 111 100       240 $resource->known_content_type( $request->header('Content-Type') ) ? \&b4 : \415;
212             }
213              
214             $STATE_DESC{'b4'} = 'request_entity_too_large';
215             sub b4 {
216 110     110 0 137 my ($resource, $request, $response) = @_;
217 110 100       276 $resource->valid_entity_length( $request->content_length ) ? \&b3 : \413;
218             }
219              
220             $STATE_DESC{'b3'} = 'method_is_options';
221             sub b3 {
222 109     109 0 126 my ($resource, $request, $response) = @_;
223 109 100       219 if ( $request->method eq 'OPTIONS' ) {
224 1         7 $response->headers( $resource->options );
225 1         50 return \200;
226             }
227 108         568 return \&c3
228             }
229              
230             $STATE_DESC{'c3'} = 'accept_header_exists';
231             sub c3 {
232 108     108 0 113 my ($resource, $request, $response) = @_;
233 108         195 my $metadata = _metadata($request);
234 108 100       378 if ( !$request->header('Accept') ) {
235 96         1942 $metadata->{'Content-Type'} = create_header( MediaType => (
236             pair_key( $resource->content_types_provided->[0] )
237             ));
238 96         117309 return \&d4
239             }
240 12         1063 return \&c4;
241             }
242              
243             $STATE_DESC{'c4'} = 'acceptable_media_type_available';
244             sub c4 {
245 12     12 0 17 my ($resource, $request, $response) = @_;
246 12         18 my $metadata = _metadata($request);
247              
248 12         38 my @types = map { pair_key( $_ ) } @{ $resource->content_types_provided };
  13         85  
  12         28  
249              
250 12 100       52 if ( my $chosen_type = choose_media_type( \@types, $request->header('Accept') ) ) {
251 11         1965 $metadata->{'Content-Type'} = $chosen_type;
252 11         35 return \&d4;
253             }
254              
255 1         203 return \406;
256             }
257              
258             $STATE_DESC{'d4'} = 'accept_language_header_exists';
259             sub d4 {
260 107     107 0 133 my ($resource, $request, $response) = @_;
261 107 100       304 (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 23 my ($resource, $request, $response) = @_;
268 17         24 my $metadata = _metadata($request);
269              
270 17 100       75 if ( my $language = choose_language( $resource->languages_provided, $request->header('Accept-Language') ) ) {
271 15         1317 $metadata->{'Language'} = $language;
272             # handle the short circuit here ...
273 15 100       59 $response->header( 'Content-Language' => $language ) if "$language" ne "1";
274 15         471 return \&e5;
275             }
276              
277 2         227 return \406;
278             }
279              
280             $STATE_DESC{'e5'} = 'accept_charset_exists';
281             sub e5 {
282 105     105 0 125 my ($resource, $request, $response) = @_;
283 105 100       216 (not $request->header('Accept-Charset')) ? \&f6 : \&e6;
284             }
285              
286             $STATE_DESC{'e6'} = 'accept_charset_choice_available';
287             sub e6 {
288 22     22 0 27 my ($resource, $request, $response) = @_;
289 22         35 my $metadata = _metadata($request);
290              
291 22 100       97 if ( my $charset = choose_charset( $resource->charsets_provided, $request->header('Accept-Charset') ) ) {
292             # handle the short circuit here ...
293 20 50       4307 $metadata->{'Charset'} = $charset if "$charset" ne "1";
294 20         53 return \&f6;
295             }
296              
297 2         698 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 103     103 0 129 my ($resource, $request, $response) = @_;
304 103         164 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 103 100 100     704 if ( $resource->default_charset && !$request->header('Accept-Charset') ) {
309 4         92 my $default = $resource->default_charset;
310 4 100       21 $metadata->{'Charset'} = ref $default ? pair_key($default) : $default;
311             }
312              
313 103 100       851 if ( my $charset = $metadata->{'Charset'} ) {
314             # Add the charset to the content type now ...
315 24         86 $metadata->{'Content-Type'}->add_param( 'charset' => $charset );
316             }
317             # put the content type in the header now ...
318 103         639 $response->header( 'Content-Type' => $metadata->{'Content-Type'}->as_string );
319              
320 103 100       5890 if ( $request->header('Accept-Encoding') ) {
321 13         874 return \&f7
322             }
323             else {
324 90 100       1852 if ( my $encoding = choose_encoding( $resource->encodings_provided, "identity;q=1.0,*;q=0.5" ) ) {
325 86 100       40397 $response->header( 'Content-Encoding' => $encoding ) unless $encoding eq 'identity';
326 86         612 $metadata->{'Content-Encoding'} = $encoding;
327 86         241 return \&g7;
328             }
329             else {
330 4         48 return \406;
331             }
332             }
333             }
334              
335             $STATE_DESC{'f7'} = 'accept_encoding_choice_available';
336             sub f7 {
337 13     13 0 18 my ($resource, $request, $response) = @_;
338 13         17 my $metadata = _metadata($request);
339              
340 13 100       57 if ( my $encoding = choose_encoding( $resource->encodings_provided, $request->header('Accept-Encoding') ) ) {
341 9 100       824 $response->header( 'Content-Encoding' => $encoding ) unless $encoding eq 'identity';
342 9         240 $metadata->{'Content-Encoding'} = $encoding;
343 9         36 return \&g7;
344             }
345              
346 4         50 return \406;
347             }
348              
349             $STATE_DESC{'g7'} = 'resource_exists';
350             sub g7 {
351 95     95 0 137 my ($resource, $request, $response) = @_;
352              
353             # NOTE:
354             # set Vary header here since we are
355             # done with content negotiation
356             # - SL
357 95         96 my @variances = @{ $resource->variances };
  95         477  
358              
359 95 100       111 push @variances => 'Accept' if scalar @{ $resource->content_types_provided } > 1;
  95         231  
360 95 100       605 push @variances => 'Accept-Encoding' if scalar keys %{ $resource->encodings_provided } > 1;
  95         179  
361 95 100 66     477 push @variances => 'Accept-Charset' if defined $resource->charsets_provided && scalar @{ $resource->charsets_provided } > 1;
  95         421  
362 95 100       465 push @variances => 'Accept-Language' if scalar @{ $resource->languages_provided } > 1;
  95         239  
363              
364 95 100       361 $response->header( 'Vary' => join ', ' => @variances ) if @variances;
365              
366 95 100       714 $resource->resource_exists ? \&g8 : \&h7;
367             }
368              
369             $STATE_DESC{'g8'} = 'if_match_exists';
370             sub g8 {
371 59     59 0 76 my ($resource, $request, $response) = @_;
372 59 100       165 $request->header('If-Match') ? \&g9 : \&h10;
373             }
374              
375             $STATE_DESC{'g9'} = 'if_match_is_wildcard';
376             sub g9 {
377 3     3 0 4 my ($resource, $request, $response) = @_;
378 3 100       6 _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 3 my ($resource, $request, $response) = @_;
384 2         4 my @etags = map { _unquote_header( $_ ) } split /\s*\,\s*/ => $request->header('If-Match');
  2         40  
385 2         5 my $etag = $resource->generate_etag;
386 2 100       6 (grep { $etag eq $_ } @etags) ? \&h10 : \412;
  2         9  
387             }
388              
389             $STATE_DESC{'h7'} = 'if_match_exists_and_if_match_is_wildcard';
390             sub h7 {
391 36     36 0 102 my ($resource, $request, $response) = @_;
392 36 100 100     82 ($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 58     58 0 79 my ($resource, $request, $response) = @_;
398 58 100       119 $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 8 my ($resource, $request, $response) = @_;
404 6         10 my $metadata = _metadata($request);
405 6 50       25 if ( my $date = $request->header('If-Unmodified-Since') ) {
406 6         251 $metadata->{'If-Unmodified-Since'} = $date;
407 6         13 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 8 my ($resource, $request, $response) = @_;
415 6         8 my $metadata = _metadata($request);
416 6 100 66     26 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 10 my ($resource, $request, $response) = @_;
425 8 100       35 if ( my $uri = $resource->moved_permanently ) {
426 2 100       8 if ( is_status_code( $uri ) ) {
427 1         3 return $uri;
428             }
429 1         3 $response->header('Location' => $uri );
430 1         29 return \301;
431             }
432 6         14 return \&p3;
433             }
434              
435             $STATE_DESC{'i7'} = 'method_is_put';
436             sub i7 {
437 30     30 0 35 my ($resource, $request, $response) = @_;
438 30 100       69 $request->method eq 'PUT' ? \&i4 : \&k7
439             }
440              
441             $STATE_DESC{'i12'} = 'if_none_match_exists';
442             sub i12 {
443 55     55 0 76 my ($resource, $request, $response) = @_;
444 55 100       113 $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 26 my ($resource, $request, $response) = @_;
450 19 100       34 $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 11 my ($resource, $request, $response) = @_;
456 9 100 100     19 $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 22 my ($resource, $request, $response) = @_;
464 20 100       93 if ( my $uri = $resource->moved_permanently ) {
465 2 100       8 if ( is_status_code( $uri ) ) {
466 1         3 return $uri;
467             }
468 1         4 $response->header('Location' => $uri );
469 1         24 return \301;
470             }
471 18         38 return \&l5;
472             }
473              
474             $STATE_DESC{'k7'} = 'previously_existed';
475             sub k7 {
476 22     22 0 26 my ($resource, $request, $response) = @_;
477 22 100       44 $resource->previously_existed ? \&k5 : \&l7;
478             }
479              
480             $STATE_DESC{'k13'} = 'etag_in_if_none_match';
481             sub k13 {
482 13     13 0 15 my ($resource, $request, $response) = @_;
483 13         29 my @etags = map { _unquote_header( $_ ) } split /\s*\,\s*/ => $request->header('If-None-Match');
  13         246  
484 13         35 my $etag = $resource->generate_etag;
485 13 100 100     53 $etag && (grep { $etag eq $_ } @etags) ? \&j18 : \&l13;
486             }
487              
488             $STATE_DESC{'l5'} = 'moved_temporarily';
489             sub l5 {
490 18     18 0 21 my ($resource, $request, $response) = @_;
491 18 100       87 if ( my $uri = $resource->moved_temporarily ) {
492 2 100       8 if ( is_status_code( $uri ) ) {
493 1         3 return $uri;
494             }
495 1         3 $response->header('Location' => $uri );
496 1         24 return \307;
497             }
498 16         33 return \&m5;
499             }
500              
501             $STATE_DESC{'l7'} = 'method_is_post';
502             sub l7 {
503 2     2 0 3 my ($resource, $request, $response) = @_;
504 2 100       5 $request->method eq 'POST' ? \&m7 : \404
505             }
506              
507             $STATE_DESC{'l13'} = 'if_modified_since_exists';
508             sub l13 {
509 46     46 0 60 my ($resource, $request, $response) = @_;
510 46 100       95 $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 13 my ($resource, $request, $response) = @_;
516 9         14 my $metadata = _metadata($request);
517 9 50       38 if ( my $date = $request->header('If-Modified-Since') ) {
518 9         371 $metadata->{'If-Modified-Since'} = $date;
519 9         744 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 12 my ($resource, $request, $response) = @_;
527 9         14 my $metadata = _metadata($request);
528 9 100       37 ($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 2 my ($resource, $request, $response) = @_;
534 1         1 my $metadata = _metadata($request);
535 1 50 33     4 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 17 my ($resource, $request, $response) = @_;
544 16 100       32 $request->method eq 'POST' ? \&n5 : \410
545             }
546              
547             $STATE_DESC{'m7'} = 'allow_post_to_missing_resource';
548             sub m7 {
549 1     1 0 3 my ($resource, $request, $response) = @_;
550 1 50       3 $resource->allow_missing_post ? \&n11 : \404
551             }
552              
553             $STATE_DESC{'m16'} = 'method_is_delete';
554             sub m16 {
555 45     45 0 66 my ($resource, $request, $response) = @_;
556 45 100       115 $request->method eq 'DELETE' ? \&m20 : \&n16
557             }
558              
559             $STATE_DESC{'m20'} = 'delete_enacted_immediately';
560             sub m20 {
561 4     4 0 5 my ($resource, $request, $response) = @_;
562 4 100       10 $resource->delete_resource ? \&m20b : \500
563             }
564              
565             $STATE_DESC{'m20b'} = 'did_delete_complete';
566             sub m20b {
567 3     3 0 4 my ($resource, $request, $response) = @_;
568 3 100       7 $resource->delete_completed ? \&o20 : \202
569             }
570              
571             $STATE_DESC{'n5'} = 'allow_post_to_missing_resource';
572             sub n5 {
573 15     15 0 18 my ($resource, $request, $response) = @_;
574 15 100       32 $resource->allow_missing_post ? \&n11 : \410
575             }
576              
577             sub _n11_create_path {
578 6     6   7 my ($resource, $request, $response) = @_;
579              
580 6         13 my $uri = $resource->create_path;
581 6 100       141 confess "Create Path Nil" unless $uri;
582 5   66     21 my $base_uri = $resource->base_uri || $request->base;
583              
584             # do a little cleanup
585 5 50       522 $base_uri =~ s!/$!! if $uri =~ m!^/!;
586 5 100 33     19 $base_uri .= '/' if $uri !~ m!^/! && $base_uri !~ m!/$!;
587 5         39 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         442 $response->header( 'Location' => $new_uri->path_query );
596             }
597              
598             $STATE_DESC{'n11'} = 'redirect';
599             sub n11 {
600 16     16 0 19 my ($resource, $request, $response) = @_;
601 16 100       43 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       44 _n11_create_path( $resource, $request, $response )
608             if not $resource->create_path_after_handler;
609              
610 5         226 my $handler = _get_acceptable_content_type_handler( $resource, $request );
611 5 100       10 return $handler if is_status_code( $handler );
612              
613 4         12 my $result = $resource->$handler();
614 4 100       12 return $result if is_status_code( $result );
615              
616 3 50       9 _n11_create_path( $resource, $request, $response )
617             if $resource->create_path_after_handler;
618             }
619             else {
620 10         37 my $result = $resource->process_post;
621 10 100       40 if ( $result ) {
622 9 100       17 return $result if is_status_code( $result );
623 7         23 encode_body_if_set( $resource, $response );
624             }
625             else {
626 1         164 confess "Process Post Invalid";
627             }
628             }
629              
630 10 100       74 if ( _is_redirect( $response ) ) {
631 3 100       20 if ( $response->location ) {
632 2         39 return \303;
633             }
634             else {
635 1         165 confess "Bad Redirect"
636             }
637             }
638              
639 7         15 return \&p11;
640             }
641              
642             $STATE_DESC{'n16'} = 'method_is_post';
643             sub n16 {
644 41     41 0 53 my ($resource, $request, $response) = @_;
645 41 100       79 $request->method eq 'POST' ? \&n11 : \&o16
646             }
647              
648             $STATE_DESC{'o14'} = 'in_conflict';
649             sub o14 {
650 3     3 0 5 my ($resource, $request, $response) = @_;
651 3 100       15 return \409 if $resource->is_conflict;
652              
653 2         5 my $handler = _get_acceptable_content_type_handler( $resource, $request );
654 2 50       5 return $handler if is_status_code( $handler );
655              
656 2         6 my $result = $resource->$handler();
657              
658 2 100       5 return $result if is_status_code( $result );
659 1         3 return \&p11;
660             }
661              
662             $STATE_DESC{'o16'} = 'method_is_put';
663             sub o16 {
664 39     39 0 48 my ($resource, $request, $response) = @_;
665 39 100       81 $request->method eq 'PUT' ? \&o14 : \&o18;
666             }
667              
668             $STATE_DESC{'o18'} = 'multiple_representations';
669             sub o18 {
670 40     40 0 57 my ($resource, $request, $response) = @_;
671 40         73 my $metadata = _metadata($request);
672 40 100 100     153 if ( $request->method eq 'GET' || $request->method eq 'HEAD' ) {
673 36         243 _add_caching_headers( $resource, $response );
674              
675 36         53 my $content_type = $metadata->{'Content-Type'};
676             my $match = first {
677 36     36   222 my $ct = create_header( MediaType => pair_key( $_ ) );
678 36         3062 $content_type->match( $ct )
679 36         146 } @{ $resource->content_types_provided };
  36         76  
680              
681 36         1345 my $handler = pair_value( $match );
682 36         157 my $result = $resource->$handler();
683              
684 36 100       2211 return $result if is_status_code( $result );
685              
686 35 100       95 unless($request->method eq 'HEAD') {
687 34 100       208 if (ref($result) eq 'CODE') {
688 5         10 $request->env->{'web.machine.streaming_push'} = $result;
689             }
690             else {
691 29         90 $response->body( $result );
692             }
693 34         292 encode_body( $resource, $response );
694             }
695 35         479 return \&o18b;
696             }
697             else {
698 4         43 return \&o18b;
699             }
700              
701             }
702              
703             $STATE_DESC{'o18b'} = 'multiple_choices';
704             sub o18b {
705 39     39 0 54 my ($resource, $request, $response) = @_;
706 39 100       168 $resource->multiple_choices ? \300 : \200;
707             }
708              
709             $STATE_DESC{'o20'} = 'response_body_includes_entity';
710             sub o20 {
711 7     7 0 10 my ($resource, $request, $response) = @_;
712 7 100       17 $response->body ? \&o18 : \204;
713             }
714              
715             $STATE_DESC{'p3'} = 'in_conflict';
716             sub p3 {
717 6     6 0 8 my ($resource, $request, $response) = @_;
718 6 100       25 return \409 if $resource->is_conflict;
719              
720 5         11 my $handler = _get_acceptable_content_type_handler( $resource, $request );
721 5 100       11 return $handler if is_status_code( $handler );
722              
723 4         13 my $result = $resource->$handler();
724              
725 4 100       11 return $result if is_status_code( $result );
726 3         10 return \&p11;
727             }
728              
729             $STATE_DESC{'p11'} = 'new_resource';
730             sub p11 {
731 11     11 0 12 my ($resource, $request, $response) = @_;
732 11 100       25 (not $response->header('Location')) ? \&o20 : \201
733             }
734              
735             1;
736              
737             __END__