File Coverage

blib/lib/Perlbal/Plugin/Redirect.pm
Criterion Covered Total %
statement 34 43 79.0
branch 5 14 35.7
condition 2 5 40.0
subroutine 8 10 80.0
pod 0 6 0.0
total 49 78 62.8


line stmt bran cond sub pod time code
1             package Perlbal::Plugin::Redirect;
2 1     1   8 use strict;
  1         1  
  1         60  
3 1     1   6 use warnings;
  1         86  
  1         716  
4              
5             sub handle_request {
6 1     1 0 3 my ($svc, $pb) = @_;
7              
8 1         4 my $mappings = $svc->{extra_config}{_redirect_host};
9 1         2 my $req_header = $pb->{req_headers};
10              
11             # returns 1 if done with client, 0 if no action taken
12             my $map_using = sub {
13 1     1   2 my ($match_on) = @_;
14              
15 1         3 my $target_host = $mappings->{$match_on};
16              
17 1 50       4 return 0 unless $target_host;
18              
19 1         3 my $path = $req_header->request_uri;
20              
21 1         16 $pb->send_full_response(301, [
22             'Location' => "http://$target_host$path",
23             'Content-Length' => 0
24             ], "");
25              
26 1         10 return 1;
27 1         8 };
28              
29             # The following is lifted wholesale from the vhosts plugin.
30             # FIXME: Factor it out to a utility function, I guess?
31             #
32             # foo.site.com should match:
33             # foo.site.com
34             # *.foo.site.com
35             # *.site.com
36             # *.com
37             # *
38              
39 1         4 my $vhost = lc($req_header->header("Host"));
40              
41             # if no vhost, just try the * mapping
42 1 50       4 return $map_using->("*") unless $vhost;
43              
44             # Strip off the :portnumber, if any
45 1         3 $vhost =~ s/:\d+$//;
46              
47             # try the literal mapping
48 1 50       4 return 1 if $map_using->($vhost);
49              
50             # and now try wildcard mappings, removing one part of the domain
51             # at a time until we find something, or end up at "*"
52              
53             # first wildcard, prepending the "*."
54 0         0 my $wild = "*.$vhost";
55 0 0       0 return 1 if $map_using->($wild);
56              
57             # now peel away subdomains
58 0         0 while ($wild =~ s/^\*\.[\w\-\_]+/*/) {
59 0 0       0 return 1 if $map_using->($wild);
60             }
61              
62             # last option: use the "*" wildcard
63 0         0 return $map_using->("*");
64             }
65              
66             sub register {
67 1     1 0 3 my ($class, $svc) = @_;
68              
69 1     1   9 $svc->register_hook('Redirect', 'start_http_request', sub { handle_request($svc, $_[0]); });
  1         49  
70             }
71              
72             sub unregister {
73 0     0 0 0 my ($class, $svc) = @_;
74 0         0 $svc->unregister_hooks('Redirect');
75             }
76              
77             sub handle_redirect_command {
78 1     1 0 7 my $mc = shift->parse(qr/^redirect\s+host\s+(\S+)\s+(\S+)$/, "usage: REDIRECT HOST ");
79 1         4 my ($match_host, $target_host) = $mc->args;
80              
81 1         4 my $svcname;
82 1 50 33     10 unless ($svcname ||= $mc->{ctx}{last_created}) {
83 0         0 return $mc->err("No service name in context from CREATE SERVICE or USE ");
84             }
85              
86 1         5 my $svc = Perlbal->service($svcname);
87 1 50       5 return $mc->err("Non-existent service '$svcname'") unless $svc;
88              
89 1   50     18 $svc->{extra_config}{_redirect_host} ||= {};
90 1         5 $svc->{extra_config}{_redirect_host}{lc($match_host)} = lc($target_host);
91              
92 1         5 return 1;
93             }
94              
95             # called when we are loaded
96             sub load {
97 1     1 0 6 Perlbal::register_global_hook('manage_command.redirect', \&handle_redirect_command);
98              
99 1         23 return 1;
100             }
101              
102             # called for a global unload
103             sub unload {
104 0     0 0   return 1;
105             }
106              
107             1;
108              
109             =head1 NAME
110              
111             Perlbal::Plugin::Redirect - Plugin to do redirecting in Perlbal land
112              
113             =head1 SYNOPSIS
114              
115             LOAD redirect
116            
117             CREATE SERVICE redirector
118             SET role = web_server
119             SET plugins = redirect
120             REDIRECT HOST example.com www.example.net
121             ENABLE redirector
122              
123             =head1 LIMITATIONS
124              
125             Right now this can only redirect at the hostname level. Also, it just
126             assumes you want an http: URL.