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:
commit
889c422d6e
@ -543,9 +543,9 @@ class Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove port number from host
|
// Remove port number from host
|
||||||
$elements = explode(':', $host);
|
$host = preg_replace('/:\d+$/', '', $host);
|
||||||
|
|
||||||
return trim($elements[0]);
|
return trim($host);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setMethod($method)
|
public function setMethod($method)
|
||||||
|
@ -121,6 +121,16 @@ class RequestMatcher implements RequestMatcherInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function checkIp($ip)
|
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, '/')) {
|
if (false !== strpos($this->ip, '/')) {
|
||||||
list($address, $netmask) = explode('/', $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);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,13 @@ class UrlValidator extends ConstraintValidator
|
|||||||
const PATTERN = '~^
|
const PATTERN = '~^
|
||||||
(%s):// # protocol
|
(%s):// # protocol
|
||||||
(
|
(
|
||||||
([a-z0-9-]+\.)+[a-z]{2,6} # a domain name
|
([a-z0-9-]+\.)+[a-z]{2,6} # a domain name
|
||||||
| # or
|
| # or
|
||||||
\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3} # a IP address
|
\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)
|
(:[0-9]+)? # a port (optional)
|
||||||
(/?|/\S+) # a /, nothing or a / with something
|
(/?|/\S+) # a /, nothing or a / with something
|
||||||
|
@ -16,19 +16,28 @@ use Symfony\Component\HttpFoundation\Request;
|
|||||||
|
|
||||||
class RequestMatcherTest extends \PHPUnit_Framework_TestCase
|
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 = new RequestMatcher();
|
||||||
|
$matcher->matchIp($cidr);
|
||||||
|
|
||||||
$matcher->matchIp('192.168.1.1/1');
|
$this->assertEquals($matches, $matcher->matches($request));
|
||||||
$request = Request::create('', 'get', array(), array(), array(), array('REMOTE_ADDR' => '192.168.1.1'));
|
}
|
||||||
$this->assertTrue($matcher->matches($request));
|
|
||||||
|
|
||||||
$matcher->matchIp('192.168.1.0/24');
|
public function testIpProvider()
|
||||||
$this->assertTrue($matcher->matches($request));
|
{
|
||||||
|
return array(
|
||||||
$matcher->matchIp('1.2.3.4/1');
|
array(true, '192.168.1.1', '192.168.1.1/1'),
|
||||||
$this->assertFalse($matcher->matches($request));
|
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()
|
public function testMethod()
|
||||||
|
@ -81,6 +81,22 @@ class RequestTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals(90, $request->getPort());
|
$this->assertEquals(90, $request->getPort());
|
||||||
$this->assertTrue($request->isSecure());
|
$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"]}';
|
$json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}';
|
||||||
$request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json);
|
$request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json);
|
||||||
$this->assertEquals($json, $request->getContent());
|
$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');
|
$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;
|
$request = new Request;
|
||||||
$this->assertEquals('', $request->getClientIp());
|
$this->assertEquals('', $request->getClientIp());
|
||||||
$this->assertEquals('', $request->getClientIp(true));
|
$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());
|
$server = array('REMOTE_ADDR' => $remoteAddr);
|
||||||
$request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_CLIENT_IP' => '88.88.88.88'));
|
if (!is_null($httpClientIp)) {
|
||||||
$this->assertEquals('127.0.0.1', $request->getClientIp());
|
$server['HTTP_CLIENT_IP'] = $httpClientIp;
|
||||||
$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));
|
if (!is_null($httpForwardedFor)) {
|
||||||
$request->initialize(array(), array(), array(), array(), array(), array('REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '88.88.88.88'));
|
$server['HTTP_X_FORWARDED_FOR'] = $httpForwardedFor;
|
||||||
$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));
|
$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()
|
public function testGetContentWorksTwiceInDefaultMode()
|
||||||
|
@ -57,6 +57,8 @@ class UrlValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
array('http://www.symfony.com/'),
|
array('http://www.symfony.com/'),
|
||||||
array('http://127.0.0.1/'),
|
array('http://127.0.0.1/'),
|
||||||
array('http://127.0.0.1:80/'),
|
array('http://127.0.0.1:80/'),
|
||||||
|
array('http://[::1]/'),
|
||||||
|
array('http://[::1]:80/'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user