root/trunk/lib/Perlbal/Plugin/Redirect.pm

Revision 820, 3.4 kB (checked in by ask, 6 months ago)

More Redirect plugin bugfixing (well, a workaround at best...)

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