diff --git a/CHANGES b/CHANGES index 6c7bcba..c9eb9ac 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,19 @@ + -- Allow vhost mappings to be deleted via the management port. + (Jason Stubbs ) + + -- Return an error rather than silently continuing when a vhost maps to a + service that doesn't exist. (Jason Stubbs ) + + -- 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 ) + + -- Fix handling of a www.exammple.com.:80 type host header in the vhost + plugin (Jason Stubbs ) + + -- Do vhost twiddling after all host header cleanups in order to standardize + with regular vhost configuration (Jason Stubbs ) + -- Fix Test::More 0.94 (Which is our required version) testing problems. (Jonathan Steinert ) diff --git a/lib/Perlbal/Plugin/Vhosts.pm b/lib/Perlbal/Plugin/Vhosts.pm index d1a3682..c5f10c2 100644 --- a/lib/Perlbal/Plugin/Vhosts.pm +++ b/lib/Perlbal/Plugin/Vhosts.pm @@ -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 [] = "); + my $mc = shift->parse(qr/^vhost\s+(?:(\w+)\s+)?(\S+)\s*=\s*(\w+)?$/, + "usage: VHOST [] = []"); my ($selname, $host, $target) = $mc->args; unless ($selname ||= $mc->{ctx}{last_created}) { return $mc->err("omitted service name not implied from context"); @@ -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; }); @@ -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;