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   240291 use strict;
  34         92  
  34         832  
3 34     34   135 use warnings;
  34         53  
  34         828  
4 34     34   1640 use parent qw(Plack::Component);
  34         1170  
  34         136  
5 34 50   34   1638 use constant DEBUG => $ENV{PLACK_URLMAP_DEBUG} ? 1 : 0;
  34         172  
  34         1812  
6              
7 34     34   176 use Carp ();
  34         56  
  34         12488  
8              
9 0     0 1 0 sub mount { shift->map(@_) }
10              
11             sub map {
12 22     22 1 58 my $self = shift;
13 22         42 my($location, $app) = @_;
14              
15 22         26 my $host;
16 22 100       70 if ($location =~ m!^https?://(.*?)(/.*)!) {
17 3         7 $host = $1;
18 3         23 $location = $2;
19             }
20              
21 22 50       82 if ($location !~ m!^/!) {
22 0         0 Carp::croak("Paths need to start with /");
23             }
24 22         53 $location =~ s!/$!!;
25              
26 22         29 push @{$self->{_mapping}}, [ $host, $location, qr/^\Q$location\E/, $app ];
  22         493  
27             }
28              
29             sub prepare_app {
30 20     20 1 29 my $self = shift;
31             # sort by path length
32             $self->{_sorted_mapping} = [
33 58         76 map { [ @{$_}[2..5] ] }
  58         177  
34 50 50       125 sort { $b->[0] <=> $a->[0] || $b->[1] <=> $a->[1] }
35 20 100       33 map { [ ($_->[0] ? length $_->[0] : 0), length($_->[1]), @$_ ] } @{$self->{_mapping}},
  58         223  
  20         56  
36             ];
37             }
38              
39             sub call {
40 30     30 1 66 my ($self, $env) = @_;
41              
42 30         56 my $path_info = $env->{PATH_INFO};
43 30         44 my $script_name = $env->{SCRIPT_NAME};
44              
45 30         43 my($http_host, $server_name) = @{$env}{qw( HTTP_HOST SERVER_NAME )};
  30         72  
46              
47 30 50 33     135 if ($http_host and my $port = $env->{SERVER_PORT}) {
48 30         217 $http_host =~ s/:$port$//;
49             }
50              
51 30         55 for my $map (@{ $self->{_sorted_mapping} }) {
  30         75  
52 66         138 my($host, $location, $location_re, $app) = @$map;
53 66         92 my $path = $path_info; # copy
54 34     34   219 no warnings 'uninitialized';
  34         68  
  34         8106  
55 66         71 DEBUG && warn "Matching request (Host=$http_host Path=$path) and the map (Host=$host Path=$location)\n";
56 66 100 100     180 next unless not defined $host or
      66        
57             $http_host eq $host or
58             $server_name eq $host;
59 51 100 100     274 next unless $location eq '' or $path =~ s!$location_re!!;
60 33 100 100     143 next unless $path eq '' or $path =~ m!^/!;
61 30         33 DEBUG && warn "-> Matched!\n";
62              
63 30         49 my $orig_path_info = $env->{PATH_INFO};
64 30         50 my $orig_script_name = $env->{SCRIPT_NAME};
65              
66 30         39 $env->{PATH_INFO} = $path;
67 30         55 $env->{SCRIPT_NAME} = $script_name . $location;
68             return $self->response_cb($app->($env), sub {
69 30     30   58 $env->{PATH_INFO} = $orig_path_info;
70 30         63 $env->{SCRIPT_NAME} = $orig_script_name;
71 30         91 });
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__