[HttpFoundation] Make host & methods really case insensitive in the RequestMacther

and backport changes from 2.2
This commit is contained in:
Victor Berchet 2012-11-12 13:39:12 +01:00
parent 15a5868ab4
commit e12bd123be
2 changed files with 126 additions and 38 deletions

View File

@ -20,19 +20,49 @@ namespace Symfony\Component\HttpFoundation;
*/
class RequestMatcher implements RequestMatcherInterface
{
/**
* @var string
*/
private $path;
private $host;
private $methods;
private $ip;
private $attributes;
/**
* @var string
*/
private $host;
/**
* @var array
*/
private $methods = array();
/**
* @var string
*/
private $ip;
/**
* Attributes.
*
* @var array
*/
private $attributes = array();
/**
* @param string|null $path
* @param string|null $host
* @param string|string[]|null $methods
* @param string|null $ip
* @param array $attributes
*/
public function __construct($path = null, $host = null, $methods = null, $ip = null, array $attributes = array())
{
$this->path = $path;
$this->host = $host;
$this->methods = $methods;
$this->ip = $ip;
$this->attributes = $attributes;
$this->matchPath($path);
$this->matchHost($host);
$this->matchMethod($methods);
$this->matchIp($ip);
foreach ($attributes as $k => $v) {
$this->matchAttribute($k, $v);
}
}
/**
@ -68,11 +98,11 @@ class RequestMatcher implements RequestMatcherInterface
/**
* Adds a check for the HTTP method.
*
* @param string|array $method An HTTP method or an array of HTTP methods
* @param string|string[]|null $method An HTTP method or an array of HTTP methods
*/
public function matchMethod($method)
{
$this->methods = array_map('strtoupper', is_array($method) ? $method : array($method));
$this->methods = array_map('strtoupper', (array) $method);
}
/**
@ -93,7 +123,7 @@ class RequestMatcher implements RequestMatcherInterface
*/
public function matches(Request $request)
{
if (null !== $this->methods && !in_array($request->getMethod(), $this->methods)) {
if ($this->methods && !in_array($request->getMethod(), $this->methods)) {
return false;
}
@ -111,7 +141,7 @@ class RequestMatcher implements RequestMatcherInterface
}
}
if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#', $request->getHost())) {
if (null !== $this->host && !preg_match('#'.str_replace('#', '\\#', $this->host).'#i', $request->getHost())) {
return false;
}
@ -122,6 +152,14 @@ class RequestMatcher implements RequestMatcherInterface
return true;
}
/**
* Validates an IP address.
*
* @param string $requestIp
* @param string $ip
*
* @return boolean True valid, false if not.
*/
protected function checkIp($requestIp, $ip)
{
// IPv6 address
@ -132,6 +170,14 @@ class RequestMatcher implements RequestMatcherInterface
}
}
/**
* Validates an IPv4 address.
*
* @param string $requestIp
* @param string $ip
*
* @return boolean True valid, false if not.
*/
protected function checkIp4($requestIp, $ip)
{
if (false !== strpos($ip, '/')) {
@ -149,8 +195,15 @@ class RequestMatcher implements RequestMatcherInterface
}
/**
* Validates an IPv6 address.
*
* @author David Soria Parra <dsp at php dot net>
* @see https://github.com/dsp/v6tools
*
* @param string $requestIp
* @param string $ip
*
* @return boolean True valid, false if not.
*/
protected function checkIp6($requestIp, $ip)
{
@ -158,16 +211,25 @@ class RequestMatcher implements RequestMatcherInterface
throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
}
list($address, $netmask) = explode('/', $ip, 2);
if (false !== strpos($ip, '/')) {
list($address, $netmask) = explode('/', $ip, 2);
$bytes_addr = unpack("n*", inet_pton($address));
$bytes_test = unpack("n*", inet_pton($requestIp));
if ($netmask < 1 || $netmask > 128) {
return false;
}
} else {
$address = $ip;
$netmask = 128;
}
$bytesAddr = unpack("n*", inet_pton($address));
$bytesTest = unpack("n*", inet_pton($requestIp));
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) {
$left = $netmask - 16 * ($i-1);
$left = ($left <= 16) ? $left : 16;
$mask = ~(0xffff >> $left) & 0xffff;
if (($bytes_addr[$i] & $mask) != ($bytes_test[$i] & $mask)) {
if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
return false;
}
}
@ -175,3 +237,4 @@ class RequestMatcher implements RequestMatcherInterface
return true;
}
}

View File

@ -66,6 +66,9 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase
return array(
array(true, '2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
array(false, '2a00:198:603:0:396e:4789:8e99:890f', '2a01:198:603:0::/65'),
array(false, '2a01:198:603:0:396e:4789:8e99:890f', '::1'),
array(true, '0:0:0:0:0:0:0:1', '::1'),
array(false, '0:0:603:0:396e:4789:8e99:0001', '::1'),
);
}
@ -89,38 +92,59 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase
}
}
public function testMethod()
/**
* @dataProvider testMethodFixtures
*/
public function testMethod($requestMethod, $matcherMethod, $isMatch)
{
$matcher = new RequestMatcher();
$matcher->matchMethod($matcherMethod);
$request = Request::create('', $requestMethod);
$this->assertSame($isMatch, $matcher->matches($request));
$matcher->matchMethod('get');
$request = Request::create('', 'get');
$this->assertTrue($matcher->matches($request));
$matcher->matchMethod('post');
$this->assertFalse($matcher->matches($request));
$matcher->matchMethod(array('get', 'post'));
$this->assertTrue($matcher->matches($request));
$matcher = new RequestMatcher(null, null, $matcherMethod);
$request = Request::create('', $requestMethod);
$this->assertSame($isMatch, $matcher->matches($request));
}
public function testHost()
public function testMethodFixtures()
{
return array(
array('get', 'get', true),
array('get', array('get', 'post'), true),
array('get', 'post', false),
array('get', 'GET', true),
array('get', array('GET', 'POST'), true),
array('get', 'POST', false),
);
}
/**
* @dataProvider testHostFixture
*/
public function testHost($pattern, $isMatch)
{
$matcher = new RequestMatcher();
$request = Request::create('', 'get', array(), array(), array(), array('HTTP_HOST' => 'foo.example.com'));
$matcher->matchHost('.*\.example\.com');
$this->assertTrue($matcher->matches($request));
$matcher->matchHost($pattern);
$this->assertSame($isMatch, $matcher->matches($request));
$matcher->matchHost('\.example\.com$');
$this->assertTrue($matcher->matches($request));
$matcher= new RequestMatcher(null, $pattern);
$this->assertSame($isMatch, $matcher->matches($request));
}
$matcher->matchHost('^.*\.example\.com$');
$this->assertTrue($matcher->matches($request));
$matcher->matchMethod('.*\.sensio\.com');
$this->assertFalse($matcher->matches($request));
public function testHostFixture()
{
return array(
array('.*\.example\.com', true),
array('\.example\.com$', true),
array('^.*\.example\.com$', true),
array('.*\.sensio\.com', false),
array('.*\.example\.COM', true),
array('\.example\.COM$', true),
array('^.*\.example\.COM$', true),
array('.*\.sensio\.COM', false), );
}
public function testPath()
@ -175,3 +199,4 @@ class RequestMatcherTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($matcher->matches($request));
}
}