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   343360 use strict;
  34         45  
  34         992  
3 34     34   153 use warnings;
  34         41  
  34         1428  
4 34     34   1549 use parent qw(Plack::Component);
  34         933  
  34         143  
5 34 50   34   1613 use constant DEBUG => $ENV{PLACK_URLMAP_DEBUG} ? 1 : 0;
  34         43  
  34         1767  
6              
7 34     34   160 use Carp ();
  34         70  
  34         12702  
8              
9 0     0 1 0 sub mount { shift->map(@_) }
10              
11             sub map {
12 22     22 1 68 my $self = shift;
13 22         42 my($location, $app) = @_;
14              
15 22         23 my $host;
16 22 100       63 if ($location =~ m!^https?://(.*?)(/.*)!) {
17 3         6 $host = $1;
18 3         10 $location = $2;
19             }
20              
21 22 50       66 if ($location !~ m!^/!) {
22 0         0 Carp::croak("Paths need to start with /");
23             }
24 22         56 $location =~ s!/$!!;
25              
26 22         25 push @{$self->{_mapping}}, [ $host, $location, qr/^\Q$location\E/, $app ];
  22         529  
27             }
28              
29             sub prepare_app {
30 20     20 1 25 my $self = shift;
31             # sort by path length
32             $self->{_sorted_mapping} = [
33 58         111 map { [ @{$_}[2..5] ] }
  58         146  
34 50 50       92 sort { $b->[0] <=> $a->[0] || $b->[1] <=> $a->[1] }
35 20 100       25 map { [ ($_->[0] ? length $_->[0] : 0), length($_->[1]), @$_ ] } @{$self->{_mapping}},
  58         185  
  20         44  
36             ];
37             }
38              
39             sub call {
40 30     30 1 51 my ($self, $env) = @_;
41              
42 30         52 my $path_info = $env->{PATH_INFO};
43 30         38 my $script_name = $env->{SCRIPT_NAME};
44              
45 30         38 my($http_host, $server_name) = @{$env}{qw( HTTP_HOST SERVER_NAME )};
  30         89  
46              
47 30 50 33     142 if ($http_host and my $port = $env->{SERVER_PORT}) {
48 30         211 $http_host =~ s/:$port$//;
49             }
50              
51 30         35 for my $map (@{ $self->{_sorted_mapping} }) {
  30         65  
52 66         150 my($host, $location, $location_re, $app) = @$map;
53 66         68 my $path = $path_info; # copy
54 34     34   1665 no warnings 'uninitialized';
  34         57  
  34         8535  
55 66         58 DEBUG && warn "Matching request (Host=$http_host Path=$path) and the map (Host=$host Path=$location)\n";
56 66 100 100     160 next unless not defined $host or
      66        
57             $http_host eq $host or
58             $server_name eq $host;
59 51 100 100     279 next unless $location eq '' or $path =~ s!$location_re!!;
60 33 100 100     122 next unless $path eq '' or $path =~ m!^/!;
61 30         33 DEBUG && warn "-> Matched!\n";
62              
63 30         78 my $orig_path_info = $env->{PATH_INFO};
64 30         40 my $orig_script_name = $env->{SCRIPT_NAME};
65              
66 30         40 $env->{PATH_INFO} = $path;
67 30         48 $env->{SCRIPT_NAME} = $script_name . $location;
68             return $self->response_cb($app->($env), sub {
69 30     30   41 $env->{PATH_INFO} = $orig_path_info;
70 30         65 $env->{SCRIPT_NAME} = $orig_script_name;
71 30         121 });
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__