| 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__ |