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