File Coverage

blib/lib/Plack/App/URLMap.pm
Criterion Covered Total %
statement 58 62 93.5
branch 14 18 77.7
condition 12 15 80.0
subroutine 10 11 90.9
pod 4 4 100.0
total 98 110 89.0


line stmt bran cond sub pod time code
1             package Plack::App::URLMap;
2 34     34   508083 use strict;
  34         74  
  34         1317  
3 34     34   182 use warnings;
  34         70  
  34         2161  
4 34     34   1712 use parent qw(Plack::Component);
  34         1268  
  34         185  
5 34 50   34   2311 use constant DEBUG => $ENV{PLACK_URLMAP_DEBUG} ? 1 : 0;
  34         63  
  34         2553  
6              
7 34     34   274 use Carp ();
  34         87  
  34         20319  
8              
9 0     0 1 0 sub mount { shift->map(@_) }
10              
11             sub map {
12 22     22 1 93 my $self = shift;
13 22         56 my($location, $app) = @_;
14              
15 22         34 my $host;
16 22 100       90 if ($location =~ m!^https?://(.*?)(/.*)!) {
17 3         11 $host = $1;
18 3         12 $location = $2;
19             }
20              
21 22 50       90 if ($location !~ m!^/!) {
22 0         0 Carp::croak("Paths need to start with /");
23             }
24 22         72 $location =~ s!/$!!;
25              
26 22         41 push @{$self->{_mapping}}, [ $host, $location, qr/^\Q$location\E/, $app ];
  22         761  
27             }
28              
29             sub prepare_app {
30 20     20 1 31 my $self = shift;
31             # sort by path length
32             $self->{_sorted_mapping} = [
33 58         92 map { [ @{$_}[2..5] ] }
  58         183  
34 50 50       154 sort { $b->[0] <=> $a->[0] || $b->[1] <=> $a->[1] }
35 20 100       36 map { [ ($_->[0] ? length $_->[0] : 0), length($_->[1]), @$_ ] } @{$self->{_mapping}},
  58         221  
  20         54  
36             ];
37             }
38              
39             sub call {
40 30     30 1 70 my ($self, $env) = @_;
41              
42 30         133 my $path_info = $env->{PATH_INFO};
43 30         54 my $script_name = $env->{SCRIPT_NAME};
44              
45 30         139 my($http_host, $server_name) = @{$env}{qw( HTTP_HOST SERVER_NAME )};
  30         133  
46              
47 30 50 33     178 if ($http_host and my $port = $env->{SERVER_PORT}) {
48 30         288 $http_host =~ s/:$port$//;
49             }
50              
51 30         54 for my $map (@{ $self->{_sorted_mapping} }) {
  30         94  
52 66         193 my($host, $location, $location_re, $app) = @$map;
53 66         144 my $path = $path_info; # copy
54 34     34   332 no warnings 'uninitialized';
  34         65  
  34         11315  
55 66         73 DEBUG && warn "Matching request (Host=$http_host Path=$path) and the map (Host=$host Path=$location)\n";
56 66 100 100     224 next unless not defined $host or
      66        
57             $http_host eq $host or
58             $server_name eq $host;
59 51 100 100     301 next unless $location eq '' or $path =~ s!$location_re!!;
60 33 100 100     144 next unless $path eq '' or $path =~ m!^/!;
61 30         43 DEBUG && warn "-> Matched!\n";
62              
63 30         61 my $orig_path_info = $env->{PATH_INFO};
64 30         48 my $orig_script_name = $env->{SCRIPT_NAME};
65              
66 30         54 $env->{PATH_INFO} = $path;
67 30         61 $env->{SCRIPT_NAME} = $script_name . $location;
68             return $self->response_cb($app->($env), sub {
69 30     30   62 $env->{PATH_INFO} = $orig_path_info;
70 30         97 $env->{SCRIPT_NAME} = $orig_script_name;
71 30         108 });
72             }
73              
74 0           DEBUG && warn "All matching failed.\n";
75              
76 0           return [404, [ 'Content-Type' => 'text/plain' ], [ "Not Found" ]];
77             }
78              
79             1;
80              
81             __END__