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   219844 use strict;
  34         137  
  34         822  
3 34     34   158 use warnings;
  34         52  
  34         823  
4 34     34   1527 use parent qw(Plack::Component);
  34         1128  
  34         135  
5 34 50   34   1720 use constant DEBUG => $ENV{PLACK_URLMAP_DEBUG} ? 1 : 0;
  34         187  
  34         1855  
6              
7 34     34   172 use Carp ();
  34         53  
  34         12473  
8              
9 0     0 1 0 sub mount { shift->map(@_) }
10              
11             sub map {
12 22     22 1 61 my $self = shift;
13 22         48 my($location, $app) = @_;
14              
15 22         26 my $host;
16 22 100       66 if ($location =~ m!^https?://(.*?)(/.*)!) {
17 3         8 $host = $1;
18 3         9 $location = $2;
19             }
20              
21 22 50       90 if ($location !~ m!^/!) {
22 0         0 Carp::croak("Paths need to start with /");
23             }
24 22         58 $location =~ s!/$!!;
25              
26 22         32 push @{$self->{_mapping}}, [ $host, $location, qr/^\Q$location\E/, $app ];
  22         523  
27             }
28              
29             sub prepare_app {
30 20     20 1 34 my $self = shift;
31             # sort by path length
32             $self->{_sorted_mapping} = [
33 58         76 map { [ @{$_}[2..5] ] }
  58         161  
34 50 50       107 sort { $b->[0] <=> $a->[0] || $b->[1] <=> $a->[1] }
35 20 100       32 map { [ ($_->[0] ? length $_->[0] : 0), length($_->[1]), @$_ ] } @{$self->{_mapping}},
  58         220  
  20         47  
36             ];
37             }
38              
39             sub call {
40 30     30 1 69 my ($self, $env) = @_;
41              
42 30         55 my $path_info = $env->{PATH_INFO};
43 30         46 my $script_name = $env->{SCRIPT_NAME};
44              
45 30         49 my($http_host, $server_name) = @{$env}{qw( HTTP_HOST SERVER_NAME )};
  30         71  
46              
47 30 50 33     135 if ($http_host and my $port = $env->{SERVER_PORT}) {
48 30         195 $http_host =~ s/:$port$//;
49             }
50              
51 30         49 for my $map (@{ $self->{_sorted_mapping} }) {
  30         77  
52 66         121 my($host, $location, $location_re, $app) = @$map;
53 66         89 my $path = $path_info; # copy
54 34     34   222 no warnings 'uninitialized';
  34         83  
  34         8283  
55 66         64 DEBUG && warn "Matching request (Host=$http_host Path=$path) and the map (Host=$host Path=$location)\n";
56 66 100 100     181 next unless not defined $host or
      66        
57             $http_host eq $host or
58             $server_name eq $host;
59 51 100 100     248 next unless $location eq '' or $path =~ s!$location_re!!;
60 33 100 100     140 next unless $path eq '' or $path =~ m!^/!;
61 30         39 DEBUG && warn "-> Matched!\n";
62              
63 30         52 my $orig_path_info = $env->{PATH_INFO};
64 30         42 my $orig_script_name = $env->{SCRIPT_NAME};
65              
66 30         47 $env->{PATH_INFO} = $path;
67 30         53 $env->{SCRIPT_NAME} = $script_name . $location;
68             return $self->response_cb($app->($env), sub {
69 30     30   52 $env->{PATH_INFO} = $orig_path_info;
70 30         66 $env->{SCRIPT_NAME} = $orig_script_name;
71 30         92 });
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__