PerlbalPluginProxyPass

Perlbal で Vpaths プラグインをつかって、

LOAD vpaths

CREATE POOL apache
  POOL apache ADD 127.0.0.1

CREATE SERVICE apache_proxy
  SET role = reverse_proxy
  SET pool = apache
ENABLE apache_proxy

CREATE SERVICE selector
  SET   listen  = 0.0.0.0:8080
  SET   role    = selector
  SET   plugins = vpaths
  VPATH /apache = apache_proxy
ENABLE selector

みたいな設定をすると、

http://localhost:8080/apache -> http://localhost:80/apache

といった形でプロキシしてくれるんだけど、これを

http://localhost:8080/apache -> http://localhost:80/

としたくてプラグイン書いてみた。もしかしてプラグイン書かなくてもできるかもしれないけど、やり方がわからなかったのと、プラグインを書く練習ってことで。プラグイン名は Apache mod_proxy の ProxyPass ディレクティブにちなんでます。

#!perl
package Perlbal::Plugin::ProxyPass;

use strict;
use warnings;

sub load {
    my $class = shift;

    Perlbal::register_global_hook('manage_command.proxypass', sub {
        my $mc = shift->parse(qr/proxypass\s+(?:(\w+)\s+)?(\S+)\s*=\s*(\S+)$/,
                              "usage: ProxyPass [<service>] <source path> = <dest path>");
        my ($selname, $source, $target) = $mc->args;
        unless ($selname ||= $mc->{ctx}{last_created}) {
            return $mc->err("omitted service name not implied from context");
        }

        my $ss = Perlbal->service($selname);
        return $mc->err("Service '$selname' is not a reverse_proxy service")
            unless $ss && $ss->{role} eq "reverse_proxy";

        $ss->{extra_config}->{_proxypass} ||= [];
        push @{$ss->{extra_config}->{_proxypass}}, [ $source, $target ];

        return $mc->ok;
    });

    return 1;
}

sub register {
    my ($class, $svc) = @_;
    unless ($svc && $svc->{role} eq "reverse_proxy") {
        die "You can't load the proxypass plugin on a service not of role reverse_proxy.\n";
    }

    $svc->register_hook(
        'ProxyPass' => 'start_proxy_request', sub {
            my Perlbal::ClientProxy $client = shift;
            for my $proxypass ( @{ $svc->{extra_config}->{_proxypass} } ) {
                my $source = $proxypass->[0];
                my $target = $proxypass->[1];
                $client->{req_headers}->{uri} =~ s/$source/$target/;
                $client->{req_headers}->{uri} =~ s!//!/!;
            }
            return 0;
        }
    );

    return 1;
}

1;

設定はこんな感じ。

LOAD vpaths
LOAD ProxyPass

CREATE POOL apache
  POOL apache ADD 127.0.0.1

CREATE SERVICE apache_proxy
  SET       role    = reverse_proxy
  SET       pool    = apache
  SET       plugins = ProxyPass
  ProxyPass /apache = /   
ENABLE apache_proxy

CREATE SERVICE selector
  SET   listen  = 0.0.0.0:8080
  SET   role    = selector
  SET   plugins = vpaths
  VPATH /apache = apache_proxy
ENABLE selector