diff --git a/src/Symfony/Component/HttpFoundation/Request.php b/src/Symfony/Component/HttpFoundation/Request.php index b1f2cbd2e3..4208f9a629 100644 --- a/src/Symfony/Component/HttpFoundation/Request.php +++ b/src/Symfony/Component/HttpFoundation/Request.php @@ -2087,6 +2087,11 @@ class Request if (self::$trustedHeaders[self::HEADER_FORWARDED] && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) { $forwardedValues = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]); $forwardedValues = preg_match_all(sprintf('{(?:%s)=(?:"?\[?)([a-zA-Z0-9\.:_\-/]*+)}', self::$forwardedParams[$type]), $forwardedValues, $matches) ? $matches[1] : array(); + if (self::HEADER_CLIENT_PORT === $type) { + foreach ($forwardedValues as $k => $v) { + $forwardedValues[$k] = substr_replace($v, '0.0.0.0', 0, strrpos($v, ':')); + } + } } if (null !== $ip) { diff --git a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php index 9e79eee0ef..712248578d 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/InlineFragmentRenderer.php @@ -16,6 +16,7 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent; +use Symfony\Component\HttpKernel\HttpCache\SubRequestHandler; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; @@ -76,7 +77,7 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer $level = ob_get_level(); try { - return $this->kernel->handle($subRequest, HttpKernelInterface::SUB_REQUEST, false); + return SubRequestHandler::handle($this->kernel, $subRequest, HttpKernelInterface::SUB_REQUEST, false); } catch (\Exception $e) { // we dispatch the exception event to trigger the logging // the response that comes back is simply ignored @@ -109,25 +110,6 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer $cookies = $request->cookies->all(); $server = $request->server->all(); - // Override the arguments to emulate a sub-request. - // Sub-request object will point to localhost as client ip and real client ip - // will be included into trusted header for client ip - try { - if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) { - $currentXForwardedFor = $request->headers->get('X_FORWARDED_FOR', ''); - - $server['HTTP_X_FORWARDED_FOR'] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); - } elseif (method_exists(Request::class, 'getTrustedHeaderName') && $trustedHeaderName = Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP, false)) { - $currentXForwardedFor = $request->headers->get($trustedHeaderName, ''); - - $server['HTTP_'.$trustedHeaderName] = ($currentXForwardedFor ? $currentXForwardedFor.', ' : '').$request->getClientIp(); - } - } catch (\InvalidArgumentException $e) { - // Do nothing - } - - $server['REMOTE_ADDR'] = $this->resolveTrustedProxy(); - unset($server['HTTP_IF_MODIFIED_SINCE']); unset($server['HTTP_IF_NONE_MATCH']); @@ -143,17 +125,6 @@ class InlineFragmentRenderer extends RoutableFragmentRenderer return $subRequest; } - private function resolveTrustedProxy() - { - if (!$trustedProxies = Request::getTrustedProxies()) { - return '127.0.0.1'; - } - - $firstTrustedProxy = reset($trustedProxies); - - return false !== ($i = strpos($firstTrustedProxy, '/')) ? substr($firstTrustedProxy, 0, $i) : $firstTrustedProxy; - } - /** * {@inheritdoc} */ diff --git a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php index d84729330e..44405209d4 100644 --- a/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php +++ b/src/Symfony/Component/HttpKernel/HttpCache/HttpCache.php @@ -444,27 +444,8 @@ class HttpCache implements HttpKernelInterface, TerminableInterface $this->surrogate->addSurrogateCapability($request); } - // modify the X-Forwarded-For header if needed - $forwardedFor = $request->headers->get('X-Forwarded-For'); - if ($forwardedFor) { - $request->headers->set('X-Forwarded-For', $forwardedFor.', '.$request->server->get('REMOTE_ADDR')); - } else { - $request->headers->set('X-Forwarded-For', $request->server->get('REMOTE_ADDR')); - } - - // fix the client IP address by setting it to 127.0.0.1 as HttpCache - // is always called from the same process as the backend. - $request->server->set('REMOTE_ADDR', '127.0.0.1'); - - // make sure HttpCache is a trusted proxy - if (!\in_array('127.0.0.1', $trustedProxies = Request::getTrustedProxies())) { - $trustedProxies[] = '127.0.0.1'; - Request::setTrustedProxies($trustedProxies, Request::HEADER_X_FORWARDED_ALL); - } - // always a "master" request (as the real master request can be in cache) - $response = $this->kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, $catch); - // FIXME: we probably need to also catch exceptions if raw === true + $response = SubRequestHandler::handle($this->kernel, $request, HttpKernelInterface::MASTER_REQUEST, $catch); // we don't implement the stale-if-error on Requests, which is nonetheless part of the RFC if (null !== $entry && \in_array($response->getStatusCode(), array(500, 502, 503, 504))) { diff --git a/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php b/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php new file mode 100644 index 0000000000..789cb6ea6b --- /dev/null +++ b/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php @@ -0,0 +1,104 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\HttpCache; + +use Symfony\Component\HttpFoundation\IpUtils; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +/** + * @author Nicolas Grekas + * + * @internal + */ +class SubRequestHandler +{ + /** + * @return Response + */ + public static function handle(HttpKernelInterface $kernel, Request $request, $type, $catch) + { + // save global state related to trusted headers and proxies + $trustedProxies = Request::getTrustedProxies(); + $trustedHeaderSet = Request::getTrustedHeaderSet(); + if (\method_exists(Request::class, 'getTrustedHeaderName')) { + Request::setTrustedProxies($trustedProxies, -1); + $trustedHeaders = array( + Request::HEADER_FORWARDED => Request::getTrustedHeaderName(Request::HEADER_FORWARDED, false), + Request::HEADER_X_FORWARDED_FOR => Request::getTrustedHeaderName(Request::HEADER_X_FORWARDED_FOR, false), + Request::HEADER_X_FORWARDED_HOST => Request::getTrustedHeaderName(Request::HEADER_X_FORWARDED_HOST, false), + Request::HEADER_X_FORWARDED_PROTO => Request::getTrustedHeaderName(Request::HEADER_X_FORWARDED_PROTO, false), + Request::HEADER_X_FORWARDED_PORT => Request::getTrustedHeaderName(Request::HEADER_X_FORWARDED_PORT, false), + ); + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); + } else { + $trustedHeaders = array( + Request::HEADER_FORWARDED => 'FORWARDED', + Request::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR', + Request::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST', + Request::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO', + Request::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT', + ); + } + + // remove untrusted values + $remoteAddr = $request->server->get('REMOTE_ADDR'); + if (!IpUtils::checkIp($remoteAddr, $trustedProxies)) { + foreach ($trustedHeaders as $key => $name) { + if ($trustedHeaderSet & $key) { + $request->headers->remove($name); + } + } + } + + // compute trusted values, taking any trusted proxies into account + $trustedIps = array(); + $trustedValues = array(); + foreach (array_reverse($request->getClientIps()) as $ip) { + $trustedIps[] = $ip; + $trustedValues[] = sprintf('for="%s"', $ip); + } + if ($ip !== $remoteAddr) { + $trustedIps[] = $remoteAddr; + $trustedValues[] = sprintf('for="%s"', $remoteAddr); + } + + // set trusted values, reusing as much as possible the global trusted settings + if (Request::HEADER_FORWARDED & $trustedHeaderSet) { + $trustedValues[0] .= sprintf(';host="%s";proto=%s', $request->getHttpHost(), $request->getScheme()); + $request->headers->set($trustedHeaders[Request::HEADER_FORWARDED], implode(', ', $trustedValues)); + } + if (Request::HEADER_X_FORWARDED_FOR & $trustedHeaderSet) { + $request->headers->set($trustedHeaders[Request::HEADER_X_FORWARDED_FOR], implode(', ', $trustedIps)); + } elseif (!(Request::HEADER_FORWARDED & $trustedHeaderSet)) { + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet | Request::HEADER_X_FORWARDED_FOR); + $request->headers->set($trustedHeaders[Request::HEADER_X_FORWARDED_FOR], implode(', ', $trustedIps)); + } + + // fix the client IP address by setting it to 127.0.0.1, + // which is the core responsibility of this method + $request->server->set('REMOTE_ADDR', '127.0.0.1'); + + // ensure 127.0.0.1 is set as trusted proxy + if (!IpUtils::checkIp('127.0.0.1', $trustedProxies)) { + Request::setTrustedProxies(array_merge($trustedProxies, array('127.0.0.1')), Request::getTrustedHeaderSet()); + } + + try { + return $kernel->handle($request, $type, $catch); + } finally { + // restore global state + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); + } + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php index db060ef237..c36ea55760 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/InlineFragmentRendererTest.php @@ -46,7 +46,7 @@ class InlineFragmentRendererTest extends TestCase $subRequest = Request::create('/_fragment?_path=_format%3Dhtml%26_locale%3Den%26_controller%3Dmain_controller'); $subRequest->attributes->replace(array('object' => $object, '_format' => 'html', '_controller' => 'main_controller', '_locale' => 'en')); $subRequest->headers->set('x-forwarded-for', array('127.0.0.1')); - $subRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + $subRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($subRequest)); @@ -99,7 +99,10 @@ class InlineFragmentRendererTest extends TestCase { Request::setTrustedProxies(array(), 0); - $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest(Request::create('/'))); + $expectedSubRequest = Request::create('/'); + $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); + + $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); $this->assertSame('foo', $strategy->render('/', Request::create('/'))->getContent()); Request::setTrustedProxies(array(), -1); @@ -190,8 +193,8 @@ class InlineFragmentRendererTest extends TestCase if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) { $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); - $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); } + $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); @@ -202,7 +205,7 @@ class InlineFragmentRendererTest extends TestCase public function testESIHeaderIsKeptInSubrequestWithTrustedHeaderDisabled() { - Request::setTrustedProxies(array(), 0); + Request::setTrustedProxies(array(), Request::HEADER_FORWARDED); $this->testESIHeaderIsKeptInSubrequest(); @@ -213,7 +216,7 @@ class InlineFragmentRendererTest extends TestCase { $expectedSubRequest = Request::create('/'); $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); - $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); $request = Request::create('/', 'GET', array(), array(), array(), array('HTTP_IF_MODIFIED_SINCE' => 'Fri, 01 Jan 2016 00:00:00 GMT', 'HTTP_IF_NONE_MATCH' => '*')); @@ -226,9 +229,9 @@ class InlineFragmentRendererTest extends TestCase $expectedSubRequest = Request::create('/'); $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); - $expectedSubRequest->server->set('REMOTE_ADDR', '1.1.1.1'); + $expectedSubRequest->server->set('REMOTE_ADDR', '127.0.0.1'); $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); - $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest)); @@ -243,9 +246,9 @@ class InlineFragmentRendererTest extends TestCase { $expectedSubRequest = Request::create('/'); $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"'); - $expectedSubRequest->server->set('REMOTE_ADDR', '1.1.1.1'); + $expectedSubRequest->server->set('REMOTE_ADDR', '127.0.0.1'); $expectedSubRequest->headers->set('x-forwarded-for', array('127.0.0.1')); - $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1'); + $expectedSubRequest->headers->set('forwarded', array('for="127.0.0.1";host="localhost";proto=http')); Request::setTrustedProxies(array('1.1.1.1/24'), -1); diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php index 3fb92275a1..a41d866508 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/HttpCacheTest.php @@ -1338,20 +1338,27 @@ class HttpCacheTest extends HttpCacheTestCase $this->setNextResponse(); $this->request('GET', '/', array('REMOTE_ADDR' => '10.0.0.1')); - $this->assertEquals('127.0.0.1', $this->kernel->getBackendRequest()->server->get('REMOTE_ADDR')); + $this->kernel->assert(function ($backendRequest) { + $this->assertSame('127.0.0.1', $backendRequest->server->get('REMOTE_ADDR')); + }); } /** * @dataProvider getTrustedProxyData */ - public function testHttpCacheIsSetAsATrustedProxy(array $existing, array $expected) + public function testHttpCacheIsSetAsATrustedProxy(array $existing) { Request::setTrustedProxies($existing, Request::HEADER_X_FORWARDED_ALL); $this->setNextResponse(); $this->request('GET', '/', array('REMOTE_ADDR' => '10.0.0.1')); + $this->assertSame($existing, Request::getTrustedProxies()); - $this->assertEquals($expected, Request::getTrustedProxies()); + $existing = array_unique(array_merge($existing, array('127.0.0.1'))); + $this->kernel->assert(function ($backendRequest) use ($existing) { + $this->assertSame($existing, Request::getTrustedProxies()); + $this->assertsame('10.0.0.1', $backendRequest->getClientIp()); + }); Request::setTrustedProxies(array(), -1); } @@ -1359,45 +1366,41 @@ class HttpCacheTest extends HttpCacheTestCase public function getTrustedProxyData() { return array( - array(array(), array('127.0.0.1')), - array(array('10.0.0.2'), array('10.0.0.2', '127.0.0.1')), - array(array('10.0.0.2', '127.0.0.1'), array('10.0.0.2', '127.0.0.1')), + array(array()), + array(array('10.0.0.2')), + array(array('10.0.0.2', '127.0.0.1')), ); } /** - * @dataProvider getXForwardedForData + * @dataProvider getForwardedData */ - public function testXForwarderForHeaderForForwardedRequests($xForwardedFor, $expected) + public function testForwarderHeaderForForwardedRequests($forwarded, $expected) { $this->setNextResponse(); $server = array('REMOTE_ADDR' => '10.0.0.1'); - if (false !== $xForwardedFor) { - $server['HTTP_X_FORWARDED_FOR'] = $xForwardedFor; + if (null !== $forwarded) { + Request::setTrustedProxies($server, -1); + $server['HTTP_FORWARDED'] = $forwarded; } $this->request('GET', '/', $server); - $this->assertEquals($expected, $this->kernel->getBackendRequest()->headers->get('X-Forwarded-For')); + $this->kernel->assert(function ($backendRequest) use ($expected) { + $this->assertSame($expected, $backendRequest->headers->get('Forwarded')); + }); + + Request::setTrustedProxies(array(), -1); } - public function getXForwardedForData() + public function getForwardedData() { return array( - array(false, '10.0.0.1'), - array('10.0.0.2', '10.0.0.2, 10.0.0.1'), - array('10.0.0.2, 10.0.0.3', '10.0.0.2, 10.0.0.3, 10.0.0.1'), + array(null, 'for="10.0.0.1";host="localhost";proto=http'), + array('for=10.0.0.2', 'for="10.0.0.2";host="localhost";proto=http, for="10.0.0.1"'), + array('for=10.0.0.2, for=10.0.0.3', 'for="10.0.0.2";host="localhost";proto=http, for="10.0.0.3", for="10.0.0.1"'), ); } - public function testXForwarderForHeaderForPassRequests() - { - $this->setNextResponse(); - $server = array('REMOTE_ADDR' => '10.0.0.1'); - $this->request('POST', '/', $server); - - $this->assertEquals('10.0.0.1', $this->kernel->getBackendRequest()->headers->get('X-Forwarded-For')); - } - public function testEsiCacheRemoveValidationHeadersIfEmbeddedResponses() { $time = \DateTime::createFromFormat('U', time()); diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php new file mode 100644 index 0000000000..dfe0a734bf --- /dev/null +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php @@ -0,0 +1,153 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpKernel\Tests\HttpCache; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpKernel\HttpCache\SubRequestHandler; +use Symfony\Component\HttpKernel\HttpKernelInterface; + +class SubRequestHandlerTest extends TestCase +{ + private static $globalState; + + protected function setUp() + { + self::$globalState = $this->getGlobalState(); + } + + protected function tearDown() + { + Request::setTrustedProxies(self::$globalState[0], self::$globalState[1]); + } + + public function testTrustedHeadersAreKept() + { + Request::setTrustedProxies(array('10.0.0.1'), -1); + $globalState = $this->getGlobalState(); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '10.0.0.1'); + $request->headers->set('X-Forwarded-For', '10.0.0.2'); + $request->headers->set('X-Forwarded-Host', 'Good'); + $request->headers->set('X-Forwarded-Port', '1234'); + $request->headers->set('X-Forwarded-Proto', 'https'); + + $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { + $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); + $this->assertSame('10.0.0.2', $request->getClientIp()); + $this->assertSame('Good', $request->headers->get('X-Forwarded-Host')); + $this->assertSame('1234', $request->headers->get('X-Forwarded-Port')); + $this->assertSame('https', $request->headers->get('X-Forwarded-Proto')); + }); + + SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertSame($globalState, $this->getGlobalState()); + } + + public function testUntrustedHeadersAreRemoved() + { + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '10.0.0.1'); + $request->headers->set('X-Forwarded-For', '10.0.0.2'); + $request->headers->set('X-Forwarded-Host', 'Evil'); + $request->headers->set('X-Forwarded-Port', '1234'); + $request->headers->set('X-Forwarded-Proto', 'http'); + $request->headers->set('Forwarded', 'Evil2'); + + $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { + $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); + $this->assertSame('10.0.0.1', $request->getClientIp()); + $this->assertFalse($request->headers->has('X-Forwarded-Host')); + $this->assertFalse($request->headers->has('X-Forwarded-Port')); + $this->assertFalse($request->headers->has('X-Forwarded-Proto')); + $this->assertSame('for="10.0.0.1";host="localhost";proto=http', $request->headers->get('Forwarded')); + }); + + SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertSame(self::$globalState, $this->getGlobalState()); + } + + public function testTrustedForwardedHeader() + { + Request::setTrustedProxies(array('10.0.0.1'), -1); + $globalState = $this->getGlobalState(); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '10.0.0.1'); + $request->headers->set('Forwarded', 'for="10.0.0.2";host="foo.bar:1234";proto=https'); + + $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { + $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); + $this->assertSame('10.0.0.2', $request->getClientIp()); + $this->assertSame('foo.bar:1234', $request->getHttpHost()); + $this->assertSame('https', $request->getScheme()); + $this->assertSame(1234, $request->getPort()); + }); + + SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertSame($globalState, $this->getGlobalState()); + } + + public function testTrustedXForwardedForHeader() + { + Request::setTrustedProxies(array('10.0.0.1'), -1); + $globalState = $this->getGlobalState(); + + $request = Request::create('/'); + $request->server->set('REMOTE_ADDR', '10.0.0.1'); + $request->headers->set('X-Forwarded-For', '10.0.0.2'); + $request->headers->set('X-Forwarded-Host', 'foo.bar'); + $request->headers->set('X-Forwarded-Proto', 'https'); + + $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { + $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); + $this->assertSame('10.0.0.2', $request->getClientIp()); + $this->assertSame('foo.bar', $request->getHttpHost()); + $this->assertSame('https', $request->getScheme()); + }); + + SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MASTER_REQUEST, true); + + $this->assertSame($globalState, $this->getGlobalState()); + } + + private function getGlobalState() + { + return array( + Request::getTrustedProxies(), + Request::getTrustedHeaderSet(), + ); + } +} + +class TestSubRequestHandlerKernel implements HttpKernelInterface +{ + private $assertCallback; + + public function __construct(\Closure $assertCallback) + { + $this->assertCallback = $assertCallback; + } + + public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = true) + { + $assertCallback = $this->assertCallback; + $assertCallback($request, $type, $catch); + + return new Response(); + } +} diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php index 34e5c30d1b..5dbf02c992 100644 --- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php +++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/TestHttpKernel.php @@ -39,15 +39,25 @@ class TestHttpKernel extends HttpKernel implements ControllerResolverInterface, parent::__construct(new EventDispatcher(), $this, null, $this); } - public function getBackendRequest() + public function assert(\Closure $callback) { - return $this->backendRequest; + $trustedConfig = array(Request::getTrustedProxies(), Request::getTrustedHeaderSet()); + + list($trustedProxies, $trustedHeaderSet, $backendRequest) = $this->backendRequest; + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); + + try { + $callback($backendRequest); + } finally { + list($trustedProxies, $trustedHeaderSet) = $trustedConfig; + Request::setTrustedProxies($trustedProxies, $trustedHeaderSet); + } } public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = false) { $this->catch = $catch; - $this->backendRequest = $request; + $this->backendRequest = array(Request::getTrustedProxies(), Request::getTrustedHeaderSet(), $request); return parent::handle($request, $type, $catch); }