From 08f738dbf1138f25ec41d8092dda27484e3f6080 Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Sat, 4 Feb 2012 20:20:17 +1100 Subject: [PATCH 1/8] Do vhost twiddling *after* all host header cleanups --- CHANGES | 3 +++ lib/Perlbal/Plugin/Vhosts.pm | 40 ++++++++++++++++++------------------ 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/CHANGES b/CHANGES index 761901d..8fb2324 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ + -- Do vhost twiddling after all host header cleanups in order to standardize + with regular vhost configuration (Jason Stubbs ) + -- Force keepalives off when we haven't finished reading a request body, but we are already sending a response. (Jonathan Steinert ) diff --git a/lib/Perlbal/Plugin/Vhosts.pm b/lib/Perlbal/Plugin/Vhosts.pm index d1a3682..43fa84b 100644 --- a/lib/Perlbal/Plugin/Vhosts.pm +++ b/lib/Perlbal/Plugin/Vhosts.pm @@ -106,26 +106,6 @@ sub vhost_selector { my $uri = $req->request_uri; my $maps = $cb->{service}{extra_config}{_vhosts} ||= {}; - # 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+)(?:\?.*)?$!) { - 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) { - $cb->_simple_response(404, "Vhost twiddling not configured for requested pair."); - return 1; - } - - $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) = @_; @@ -157,6 +137,26 @@ sub vhost_selector { # Strip off the :portnumber, if any $vhost =~ s/:\d+$//; + # 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+)(?:\?.*)?$!) { + 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) { + $cb->_simple_response(404, "Vhost twiddling not configured for requested pair."); + return 1; + } + + $req->header("Host", $alt_host); + $svc->adopt_base_client($cb); + return 1; + } + # try the literal mapping return if $map_using->($vhost); From f83dd6b50fc14a59aea16daace54b459757ad046 Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Sat, 4 Feb 2012 20:24:10 +1100 Subject: [PATCH 2/8] Strip port info *before* stripping a trailing period from the domain --- CHANGES | 3 +++ lib/Perlbal/Plugin/Vhosts.pm | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 8fb2324..4337e2d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ + -- 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 ) diff --git a/lib/Perlbal/Plugin/Vhosts.pm b/lib/Perlbal/Plugin/Vhosts.pm index 43fa84b..87008ea 100644 --- a/lib/Perlbal/Plugin/Vhosts.pm +++ b/lib/Perlbal/Plugin/Vhosts.pm @@ -100,9 +100,6 @@ sub vhost_selector { my $vhost = $req->header("Host"); - # Browsers and the Apache API considers 'www.example.com.' == 'www.example.com' - $vhost and $vhost =~ s/\.$//; - my $uri = $req->request_uri; my $maps = $cb->{service}{extra_config}{_vhosts} ||= {}; @@ -137,6 +134,9 @@ sub vhost_selector { # Strip off the :portnumber, if any $vhost =~ s/:\d+$//; + # Browsers and the Apache API considers 'www.example.com.' == 'www.example.com' + $vhost and $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). From 91445075bd8f2883af1306af6d22023d73c7dc4f Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Sat, 4 Feb 2012 20:43:58 +1100 Subject: [PATCH 3/8] Various code cleanups - $uri only used once so replaced usage with $req->request_uri - $map_name was incorrect description for var contents - moved $vhost definition to just before it starts being used - removed needless $vhost empty checks - replaced A; while (X) { A; } with do { A; } while (X); - moved comments next to actual code - add spacing --- lib/Perlbal/Plugin/Vhosts.pm | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/Perlbal/Plugin/Vhosts.pm b/lib/Perlbal/Plugin/Vhosts.pm index 87008ea..1ea995f 100644 --- a/lib/Perlbal/Plugin/Vhosts.pm +++ b/lib/Perlbal/Plugin/Vhosts.pm @@ -98,17 +98,14 @@ sub vhost_selector { my $req = $cb->{req_headers}; return $cb->_simple_response(404, "Not Found (no reqheaders)") unless $req; - my $vhost = $req->header("Host"); - - my $uri = $req->request_uri; my $maps = $cb->{service}{extra_config}{_vhosts} ||= {}; # 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; + my $svc_name = $maps->{$match_on}; + my $svc = $svc_name ? Perlbal->service($svc_name) : undef; return 0 unless $svc || $force; @@ -121,6 +118,8 @@ sub vhost_selector { return 1; }; + my $vhost = $req->header("Host"); + # foo.site.com should match: # foo.site.com # *.foo.site.com -- this one's questionable, but might as well? @@ -135,24 +134,26 @@ sub vhost_selector { $vhost =~ s/:\d+$//; # Browsers and the Apache API considers 'www.example.com.' == 'www.example.com' - $vhost and $vhost =~ s/\.$//; + $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 ($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) { $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; } @@ -162,15 +163,10 @@ sub vhost_selector { # 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); - - # now peel away subdomains - while ($wild =~ s/^\*\.[\w\-\_]+/*/) { + do { return if $map_using->($wild); - } + } while ($wild =~ s/^\*\.[\w\-\_]+/*/); # last option: use the "*" wildcard return $map_using->("*", 1); From 3586c6d79eabfaeb0eb3d600ceb25d341938eb3d Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Sat, 4 Feb 2012 21:04:14 +1100 Subject: [PATCH 4/8] Skip vhost twiddling checks if it hasn't been configured --- CHANGES | 4 ++++ lib/Perlbal/Plugin/Vhosts.pm | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 4337e2d..0dfc561 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ + -- 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 ) diff --git a/lib/Perlbal/Plugin/Vhosts.pm b/lib/Perlbal/Plugin/Vhosts.pm index 1ea995f..1675992 100644 --- a/lib/Perlbal/Plugin/Vhosts.pm +++ b/lib/Perlbal/Plugin/Vhosts.pm @@ -38,6 +38,7 @@ sub load { $ss->{extra_config}->{_vhosts} ||= {}; $ss->{extra_config}->{_vhosts}{$host} = $target; + $ss->{extra_config}->{_vhost_twiddling} ||= ($host =~ /;/); return $mc->ok; }); @@ -139,7 +140,8 @@ sub vhost_selector { # ability to ask for one host, but actually use another. (for # circumventing javascript/java/browser host restrictions when you # actually control two domains). - if ($req->request_uri =~ m!^/__using/([\w\.]+)(?:/\w+)(?:\?.*)?$!) { + if ($cb->{service}{extra_config}{_vhost_twiddling} && + $req->request_uri =~ m!^/__using/([\w\.]+)(?:/\w+)(?:\?.*)?$!) { my $alt_host = $1; my $svc_name = $maps->{"$vhost;using:$alt_host"}; From 2150819c884386586b85787de00fa873b1339784 Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Sat, 4 Feb 2012 21:49:10 +1100 Subject: [PATCH 5/8] Return error when vhost maps to nonexistent service --- CHANGES | 3 +++ lib/Perlbal/Plugin/Vhosts.pm | 18 +++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 0dfc561..112b6fc 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ + -- 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 ) diff --git a/lib/Perlbal/Plugin/Vhosts.pm b/lib/Perlbal/Plugin/Vhosts.pm index 1675992..e1badee 100644 --- a/lib/Perlbal/Plugin/Vhosts.pm +++ b/lib/Perlbal/Plugin/Vhosts.pm @@ -106,12 +106,16 @@ sub vhost_selector { my ($match_on, $force) = @_; my $svc_name = $maps->{$match_on}; - my $svc = $svc_name ? Perlbal->service($svc_name) : undef; + return 0 unless $svc_name || $force; - return 0 unless $svc || $force; + unless ($svc_name) { + $cb->_simple_response(404, "Not Found (no configured vhost)"); + return 1; + } + my $svc = Perlbal->service($svc_name); unless ($svc) { - $cb->_simple_response(404, "Not Found (no configured vhost)"); + $cb->_simple_response(500, "Failed to map vhost to service (misconfigured)"); return 1; } @@ -145,10 +149,14 @@ sub vhost_selector { my $alt_host = $1; my $svc_name = $maps->{"$vhost;using:$alt_host"}; - my $svc = $svc_name ? Perlbal->service($svc_name) : undef; + unless ($svc_name) { + $cb->_simple_response(404, "Vhost twiddling not configured for requested pair."); + return 1; + } + my $svc = Perlbal->service($svc_name); unless ($svc) { - $cb->_simple_response(404, "Vhost twiddling not configured for requested pair."); + $cb->_simple_response(500, "Failed to map vhost to service (misconfigured)"); return 1; } From 193652a56fe91f1a472d0eb1412ed17351b2ded6 Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Sat, 4 Feb 2012 22:08:13 +1100 Subject: [PATCH 6/8] Optimize vhost_selector by refactoring the use of a closure --- CHANGES | 3 ++ lib/Perlbal/Plugin/Vhosts.pm | 57 ++++++++++++++---------------------- 2 files changed, 25 insertions(+), 35 deletions(-) diff --git a/CHANGES b/CHANGES index 112b6fc..97161e8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ + -- Decrease overall CPU usage by 20% when using the vhost plugin. + (Jason Stubbs ) + -- Return an error rather than silently continuing when a vhost maps to a service that doesn't exist. (Jason Stubbs ) diff --git a/lib/Perlbal/Plugin/Vhosts.pm b/lib/Perlbal/Plugin/Vhosts.pm index e1badee..2e4fc81 100644 --- a/lib/Perlbal/Plugin/Vhosts.pm +++ b/lib/Perlbal/Plugin/Vhosts.pm @@ -100,28 +100,7 @@ sub vhost_selector { return $cb->_simple_response(404, "Not Found (no reqheaders)") unless $req; my $maps = $cb->{service}{extra_config}{_vhosts} ||= {}; - - # returns 1 if done with client, 0 if no action taken - my $map_using = sub { - my ($match_on, $force) = @_; - - my $svc_name = $maps->{$match_on}; - return 0 unless $svc_name || $force; - - unless ($svc_name) { - $cb->_simple_response(404, "Not Found (no configured vhost)"); - return 1; - } - - my $svc = Perlbal->service($svc_name); - unless ($svc) { - $cb->_simple_response(500, "Failed to map vhost to service (misconfigured)"); - return 1; - } - - $svc->adopt_base_client($cb); - return 1; - }; + my $svc_name; my $vhost = $req->header("Host"); @@ -133,7 +112,7 @@ sub vhost_selector { # * # if no vhost, just try the * mapping - return $map_using->("*", 1) unless $vhost; + goto USE_FALLBACK unless $vhost; # Strip off the :portnumber, if any $vhost =~ s/:\d+$//; @@ -148,38 +127,46 @@ sub vhost_selector { $req->request_uri =~ m!^/__using/([\w\.]+)(?:/\w+)(?:\?.*)?$!) { my $alt_host = $1; - my $svc_name = $maps->{"$vhost;using:$alt_host"}; + $svc_name = $maps->{"$vhost;using:$alt_host"}; unless ($svc_name) { $cb->_simple_response(404, "Vhost twiddling not configured for requested pair."); return 1; } - my $svc = Perlbal->service($svc_name); - unless ($svc) { - $cb->_simple_response(500, "Failed to map vhost to service (misconfigured)"); - 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; + 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 "*" my $wild = "*.$vhost"; do { - return if $map_using->($wild); + goto USE_SERVICE if ($svc_name = $maps->{$wild}); } while ($wild =~ s/^\*\.[\w\-\_]+/*/); + USE_FALLBACK: # last option: use the "*" wildcard - return $map_using->("*", 1); + $svc_name = $maps->{"*"}; + unless ($svc_name) { + $cb->_simple_response(404, "Not Found (no configured vhost)"); + return 1; + } + + USE_SERVICE: + my $svc = Perlbal->service($svc_name); + unless ($svc) { + $cb->_simple_response(500, "Failed to map vhost to service (misconfigured)"); + return 1; + } + + $svc->adopt_base_client($cb); + return 1; } 1; From 216078ae049868809fadd94fd8c4d504aa629621 Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Sat, 4 Feb 2012 22:13:23 +1100 Subject: [PATCH 7/8] Allow vhost mappings to be deleted via the management port --- CHANGES | 3 +++ lib/Perlbal/Plugin/Vhosts.pm | 14 +++++++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/CHANGES b/CHANGES index 97161e8..862e20c 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ + -- Allow vhost mappings to be deleted via the management port. + (Jason Stubbs ) + -- Decrease overall CPU usage by 20% when using the vhost plugin. (Jason Stubbs ) diff --git a/lib/Perlbal/Plugin/Vhosts.pm b/lib/Perlbal/Plugin/Vhosts.pm index 2e4fc81..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,9 +36,13 @@ sub load { return $mc->err("invalid host pattern: '$host'") unless $host =~ /^[\w\-\_\.\*\;\:]+$/; - $ss->{extra_config}->{_vhosts} ||= {}; - $ss->{extra_config}->{_vhosts}{$host} = $target; - $ss->{extra_config}->{_vhost_twiddling} ||= ($host =~ /;/); + 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; }); From 643e5268ae273cc51530260808ec18a474d036b9 Mon Sep 17 00:00:00 2001 From: Jason Stubbs Date: Mon, 6 Feb 2012 14:38:09 +1100 Subject: [PATCH 8/8] Remove speedup claim as "lab" testing showed it to be more like 3% --- CHANGES | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGES b/CHANGES index 862e20c..27b5f38 100644 --- a/CHANGES +++ b/CHANGES @@ -1,9 +1,6 @@ -- Allow vhost mappings to be deleted via the management port. (Jason Stubbs ) - -- Decrease overall CPU usage by 20% when using the vhost plugin. - (Jason Stubbs ) - -- Return an error rather than silently continuing when a vhost maps to a service that doesn't exist. (Jason Stubbs )