Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various cleanups and speedups for the Vhosts plugin #13

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
16 changes: 16 additions & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,3 +1,19 @@
-- Allow vhost mappings to be deleted via the management port.
(Jason Stubbs <[email protected]>)

-- Return an error rather than silently continuing when a vhost maps to a
service that doesn't exist. (Jason Stubbs <[email protected]>)

-- Don't check for a vhost twiddle request_uri if twiddling hasn't been
configured, so that /__using/... uris aren't needlessly appropriated.
(Jason Stubbs <[email protected]>)

-- Fix handling of a www.exammple.com.:80 type host header in the vhost
plugin (Jason Stubbs <[email protected]>)

-- Do vhost twiddling after all host header cleanups in order to standardize
with regular vhost configuration (Jason Stubbs <[email protected]>)

-- Fix Test::More 0.94 (Which is our required version) testing problems.
(Jonathan Steinert <[email protected]>)

Expand Down
109 changes: 53 additions & 56 deletions lib/Perlbal/Plugin/Vhosts.pm
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ sub load {
my $class = shift;

Perlbal::register_global_hook('manage_command.vhost', sub {
my $mc = shift->parse(qr/^vhost\s+(?:(\w+)\s+)?(\S+)\s*=\s*(\w+)$/,
"usage: VHOST [<service>] <host_or_pattern> = <dest_service>");
my $mc = shift->parse(qr/^vhost\s+(?:(\w+)\s+)?(\S+)\s*=\s*(\w+)?$/,
"usage: VHOST [<service>] <host_or_pattern> = [<dest_service>]");
my ($selname, $host, $target) = $mc->args;
unless ($selname ||= $mc->{ctx}{last_created}) {
return $mc->err("omitted service name not implied from context");
Expand All @@ -36,8 +36,13 @@ sub load {
return $mc->err("invalid host pattern: '$host'")
unless $host =~ /^[\w\-\_\.\*\;\:]+$/;

$ss->{extra_config}->{_vhosts} ||= {};
$ss->{extra_config}->{_vhosts}{$host} = $target;
if ($target) {
$ss->{extra_config}->{_vhosts} ||= {};
$ss->{extra_config}->{_vhosts}{$host} = $target;
$ss->{extra_config}->{_vhost_twiddling} ||= ($host =~ /;/);
} else {
delete $ss->{extra_config}->{_vhosts}{$host};
}

return $mc->ok;
});
Expand Down Expand Up @@ -98,82 +103,74 @@ sub vhost_selector {
my $req = $cb->{req_headers};
return $cb->_simple_response(404, "Not Found (no reqheaders)") unless $req;

my $maps = $cb->{service}{extra_config}{_vhosts} ||= {};
my $svc_name;

my $vhost = $req->header("Host");

# Browsers and the Apache API considers 'www.example.com.' == 'www.example.com'
$vhost and $vhost =~ s/\.$//;
# foo.site.com should match:
# foo.site.com
# *.foo.site.com -- this one's questionable, but might as well?
# *.site.com
# *.com
# *

my $uri = $req->request_uri;
my $maps = $cb->{service}{extra_config}{_vhosts} ||= {};
# if no vhost, just try the * mapping
goto USE_FALLBACK unless $vhost;

# Strip off the :portnumber, if any
$vhost =~ s/:\d+$//;

# Browsers and the Apache API considers 'www.example.com.' == 'www.example.com'
$vhost =~ s/\.$//;

# ability to ask for one host, but actually use another. (for
# circumventing javascript/java/browser host restrictions when you
# actually control two domains).
if ($vhost && $uri =~ m!^/__using/([\w\.]+)(?:/\w+)(?:\?.*)?$!) {
if ($cb->{service}{extra_config}{_vhost_twiddling} &&
$req->request_uri =~ m!^/__using/([\w\.]+)(?:/\w+)(?:\?.*)?$!) {
my $alt_host = $1;

# update our request object's Host header, if we ended up switching them
# around with /__using/...
my $svc_name = $maps->{"$vhost;using:$alt_host"};
my $svc = $svc_name ? Perlbal->service($svc_name) : undef;
unless ($svc) {
$svc_name = $maps->{"$vhost;using:$alt_host"};
unless ($svc_name) {
$cb->_simple_response(404, "Vhost twiddling not configured for requested pair.");
return 1;
}

# update our request object's Host header, if we ended up switching them
# around with /__using/...
$req->header("Host", $alt_host);
$svc->adopt_base_client($cb);
return 1;
}

# returns 1 if done with client, 0 if no action taken
my $map_using = sub {
my ($match_on, $force) = @_;

my $map_name = $maps->{$match_on};
my $svc = $map_name ? Perlbal->service($map_name) : undef;

return 0 unless $svc || $force;

unless ($svc) {
$cb->_simple_response(404, "Not Found (no configured vhost)");
return 1;
}

$svc->adopt_base_client($cb);
return 1;
};

# foo.site.com should match:
# foo.site.com
# *.foo.site.com -- this one's questionable, but might as well?
# *.site.com
# *.com
# *

# if no vhost, just try the * mapping
return $map_using->("*", 1) unless $vhost;

# Strip off the :portnumber, if any
$vhost =~ s/:\d+$//;
goto USE_SERVICE;
}

# try the literal mapping
return if $map_using->($vhost);
goto USE_SERVICE if ($svc_name = $maps->{$vhost});

# and now try wildcard mappings, removing one part of the domain
# at a time until we find something, or end up at "*"

# first wildcard, prepending the "*."
my $wild = "*.$vhost";
return if $map_using->($wild);
do {
goto USE_SERVICE if ($svc_name = $maps->{$wild});
} while ($wild =~ s/^\*\.[\w\-\_]+/*/);

USE_FALLBACK:
# last option: use the "*" wildcard
$svc_name = $maps->{"*"};
unless ($svc_name) {
$cb->_simple_response(404, "Not Found (no configured vhost)");
return 1;
}

# now peel away subdomains
while ($wild =~ s/^\*\.[\w\-\_]+/*/) {
return if $map_using->($wild);
USE_SERVICE:
my $svc = Perlbal->service($svc_name);
unless ($svc) {
$cb->_simple_response(500, "Failed to map vhost to service (misconfigured)");
return 1;
}

# last option: use the "*" wildcard
return $map_using->("*", 1);
$svc->adopt_base_client($cb);
return 1;
}

1;