Merge remote branch 'igorw/ipv6'

* igorw/ipv6:
  [HttpFoundation] minor optimization
  minor adjustments suggested by vicb
  [HttpFoundation] IPv6 support for RequestMatcher
  [HttpFoundation] refactor RequestMatcherTest to use dataProvider
  [Validator] use full iPv6 regex
  [Validator] add IPv6 support to UrlValidator
  [HttpFoundation] add IPv6 support to Request
  [HttpFoundation] test Request::create with an IP as host name
  [HttpFoundation] refactor Request::getClientIp test
This commit is contained in:
Fabien Potencier 2011-04-24 22:04:25 +02:00
commit 889c422d6e
6 changed files with 106 additions and 25 deletions

View File

@ -543,9 +543,9 @@ class Request
}
// Remove port number from host
$elements = explode(':', $host);
$host = preg_replace('/:\d+$/', '', $host);
return trim($elements[0]);
return trim($host);
}
public function setMethod($method)

View File

@ -121,6 +121,16 @@ class RequestMatcher implements RequestMatcherInterface
}
protected function checkIp($ip)
{
// IPv6 address
if (false !== strpos($ip, ':')) {
return $this->checkIp6($ip);
} else {
return $this->checkIp4($ip);
}
}
protected function checkIp4($ip)
{
if (false !== strpos($this->ip, '/')) {
list($address, $netmask) = explode('/', $this->ip);
@ -135,4 +145,27 @@ class RequestMatcher implements RequestMatcherInterface
return 0 === substr_compare(sprintf('%032b', ip2long($ip)), sprintf('%032b', ip2long($address)), 0, $netmask);
}
/**
* @author David Soria Parra <dsp at php dot net>
* @see https://github.com/dsp/v6tools
*/
protected function checkIp6($ip)
{
list($address, $netmask) = explode('/', $this->ip);
$bytes_addr = unpack("n*", inet_pton($address));
$bytes_test = unpack("n*", inet_pton($ip));
for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; $i++) {
$left = $netmask - 16 * ($i-1);
$left = ($left <= 16) ?: 16;
$mask = ~(0xffff >> $left) & 0xffff;
if (($bytes_addr[$i] & $mask) != ($bytes_test[$i] & $mask)) {
return false;
}
}
return true;
}
}

View File

@ -20,9 +20,13 @@ class UrlValidator extends ConstraintValidator
const PATTERN = '~^
(%s):// # protocol
(
([a-z0-9-]+\.)+[a-z]{2,6} # a domain name
| # or
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # a IP address
([a-z0-9-]+\.)+[a-z]{2,6} # a domain name
| # or
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # a IP address
| # or
\[
(?:(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-f]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,1}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,2}(?:(?:[0-9a-f]{1,4})))?::(?:(?:(?:[0-9a-f]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,3}(?:(?:[0-9a-f]{1,4})))?::(?:(?:[0-9a-f]{1,4})):)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,4}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-f]{1,4})):(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,5}(?:(?:[0-9a-f]{1,4})))?::)(?:(?:[0-9a-f]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-f]{1,4})):){0,6}(?:(?:[0-9a-f]{1,4})))?::))))
\] # a IPv6 address
)
(:[0-9]+)? # a port (optional)
(/?|/\S+) # a /, nothing or a / with something

View File

@ -16,19 +16,28 @@ use Symfony\Component\HttpFoundation\Request;
class RequestMatcherTest extends \PHPUnit_Framework_TestCase
{
public function testIp()
/**
* @dataProvider testIpProvider
*/
public function testIp($matches, $remoteAddr, $cidr)
{
$request = Request::create('', 'get', array(), array(), array(), array('REMOTE_ADDR' => $remoteAddr));
$matcher = new RequestMatcher();
$matcher->matchIp($cidr);
$matcher->matchIp('192.168.1.1/1');
$request = Request::create('', 'get', array(), array(), array(), array('REMOTE_ADDR' => '192.168.1.1'));
$this->assertTrue($matcher->matches($request));
$this->assertEquals($matches, $matcher->matches($request));
}
$matcher->matchIp('192.168.1.0/24');
$this->assertTrue($matcher->matches($request));
$matcher->matchIp('1.2.3.4/1');
$this->assertFalse($matcher->matches($request));
public function testIpProvider()
{
return array(
array(true, '192.168.1.1', '192.168.1.1/1'),
array(true, '192.168.1.1', '192.168.1.0/24'),
array(false, '192.168.1.1', '1.2.3.4/1'),
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'),
);
}
public function testMethod()

View File

@ -81,6 +81,22 @@ class RequestTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(90, $request->getPort());
$this->assertTrue($request->isSecure());
$request = Request::create('https://127.0.0.1:90/foo');
$this->assertEquals('https://127.0.0.1:90/foo', $request->getUri());
$this->assertEquals('/foo', $request->getPathInfo());
$this->assertEquals('127.0.0.1', $request->getHost());
$this->assertEquals('127.0.0.1:90', $request->getHttpHost());
$this->assertEquals(90, $request->getPort());
$this->assertTrue($request->isSecure());
$request = Request::create('https://[::1]:90/foo');
$this->assertEquals('https://[::1]:90/foo', $request->getUri());
$this->assertEquals('/foo', $request->getPathInfo());
$this->assertEquals('[::1]', $request->getHost());
$this->assertEquals('[::1]:90', $request->getHttpHost());
$this->assertEquals(90, $request->getPort());
$this->assertTrue($request->isSecure());
$json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}';
$request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json);
$this->assertEquals($json, $request->getContent());
@ -434,21 +450,38 @@ class RequestTest extends \PHPUnit_Framework_TestCase
$this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST');
}
public function testGetClientIp()
/**
* @dataProvider testGetClientIpProvider
*/
public function testGetClientIp($expected, $proxy, $remoteAddr, $httpClientIp, $httpForwardedFor)
{
$request = new Request;
$this->assertEquals('', $request->getClientIp());
$this->assertEquals('', $request->getClientIp(true));
$request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '88.88.88.88'));
$this->assertEquals('88.88.88.88', $request->getClientIp());
$request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_CLIENT_IP' => '88.88.88.88'));
$this->assertEquals('127.0.0.1', $request->getClientIp());
$request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_CLIENT_IP' => '88.88.88.88'));
$this->assertEquals('88.88.88.88', $request->getClientIp(true));
$request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '88.88.88.88'));
$this->assertEquals('127.0.0.1', $request->getClientIp());
$request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '88.88.88.88'));
$this->assertEquals('88.88.88.88', $request->getClientIp(true));
$server = array('REMOTE_ADDR' => $remoteAddr);
if (!is_null($httpClientIp)) {
$server['HTTP_CLIENT_IP'] = $httpClientIp;
}
if (!is_null($httpForwardedFor)) {
$server['HTTP_X_FORWARDED_FOR'] = $httpForwardedFor;
}
$request->initialize(array(), array(), array(), array(), array(), $server);
$this->assertEquals($expected, $request->getClientIp($proxy));
}
public function testGetClientIpProvider()
{
return array(
array('88.88.88.88', false, '88.88.88.88', null, null),
array('127.0.0.1', false, '127.0.0.1', '88.88.88.88', null),
array('88.88.88.88', true, '127.0.0.1', '88.88.88.88', null),
array('127.0.0.1', false, '127.0.0.1', null, '88.88.88.88'),
array('88.88.88.88', true, '127.0.0.1', null, '88.88.88.88'),
array('::1', false, '::1', null, null),
array('2620:0:1cfe:face:b00c::3', true, '::1', '2620:0:1cfe:face:b00c::3', null),
);
}
public function testGetContentWorksTwiceInDefaultMode()

View File

@ -57,6 +57,8 @@ class UrlValidatorTest extends \PHPUnit_Framework_TestCase
array('http://www.symfony.com/'),
array('http://127.0.0.1/'),
array('http://127.0.0.1:80/'),
array('http://[::1]/'),
array('http://[::1]:80/'),
);
}