1<?php
2
3/*
4 * This file is part of the Symfony package.
5 *
6 * (c) Fabien Potencier <fabien@symfony.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Symfony\Component\HttpKernel\Tests\Fragment;
13
14use PHPUnit\Framework\TestCase;
15use Symfony\Component\EventDispatcher\EventDispatcher;
16use Symfony\Component\HttpFoundation\Request;
17use Symfony\Component\HttpFoundation\RequestStack;
18use Symfony\Component\HttpFoundation\Response;
19use Symfony\Component\HttpKernel\Controller\ControllerReference;
20use Symfony\Component\HttpKernel\Event\ExceptionEvent;
21use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer;
22use Symfony\Component\HttpKernel\HttpKernel;
23use Symfony\Component\HttpKernel\KernelEvents;
24use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
25
26class InlineFragmentRendererTest extends TestCase
27{
28    public function testRender()
29    {
30        $strategy = new InlineFragmentRenderer($this->getKernel($this->returnValue(new Response('foo'))));
31
32        $this->assertEquals('foo', $strategy->render('/', Request::create('/'))->getContent());
33    }
34
35    public function testRenderWithControllerReference()
36    {
37        $strategy = new InlineFragmentRenderer($this->getKernel($this->returnValue(new Response('foo'))));
38
39        $this->assertEquals('foo', $strategy->render(new ControllerReference('main_controller', [], []), Request::create('/'))->getContent());
40    }
41
42    public function testRenderWithObjectsAsAttributes()
43    {
44        $object = new \stdClass();
45
46        $subRequest = Request::create('/_fragment?_path=_format%3Dhtml%26_locale%3Den%26_controller%3Dmain_controller');
47        $subRequest->attributes->replace(['object' => $object, '_format' => 'html', '_controller' => 'main_controller', '_locale' => 'en']);
48        $subRequest->headers->set('x-forwarded-for', ['127.0.0.1']);
49        $subRequest->headers->set('forwarded', ['for="127.0.0.1";host="localhost";proto=http']);
50        $subRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
51        $subRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http');
52
53        $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($subRequest));
54
55        $this->assertSame('foo', $strategy->render(new ControllerReference('main_controller', ['object' => $object], []), Request::create('/'))->getContent());
56    }
57
58    public function testRenderWithTrustedHeaderDisabled()
59    {
60        Request::setTrustedProxies([], 0);
61
62        $expectedSubRequest = Request::create('/');
63        $expectedSubRequest->headers->set('x-forwarded-for', ['127.0.0.1']);
64        $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
65
66        $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest));
67        $this->assertSame('foo', $strategy->render('/', Request::create('/'))->getContent());
68
69        Request::setTrustedProxies([], -1);
70    }
71
72    public function testRenderExceptionNoIgnoreErrors()
73    {
74        $this->expectException('RuntimeException');
75        $dispatcher = $this->getMockBuilder(EventDispatcherInterface::class)->getMock();
76        $dispatcher->expects($this->never())->method('dispatch');
77
78        $strategy = new InlineFragmentRenderer($this->getKernel($this->throwException(new \RuntimeException('foo'))), $dispatcher);
79
80        $this->assertEquals('foo', $strategy->render('/', Request::create('/'))->getContent());
81    }
82
83    public function testRenderExceptionIgnoreErrors()
84    {
85        $exception = new \RuntimeException('foo');
86        $kernel = $this->getKernel($this->throwException($exception));
87        $request = Request::create('/');
88        $expectedEvent = new ExceptionEvent($kernel, $request, $kernel::SUB_REQUEST, $exception);
89        $dispatcher = $this->getMockBuilder(EventDispatcherInterface::class)->getMock();
90        $dispatcher->expects($this->once())->method('dispatch')->with($expectedEvent, KernelEvents::EXCEPTION);
91
92        $strategy = new InlineFragmentRenderer($kernel, $dispatcher);
93
94        $this->assertEmpty($strategy->render('/', $request, ['ignore_errors' => true])->getContent());
95    }
96
97    public function testRenderExceptionIgnoreErrorsWithAlt()
98    {
99        $strategy = new InlineFragmentRenderer($this->getKernel($this->onConsecutiveCalls(
100            $this->throwException(new \RuntimeException('foo')),
101            $this->returnValue(new Response('bar'))
102        )));
103
104        $this->assertEquals('bar', $strategy->render('/', Request::create('/'), ['ignore_errors' => true, 'alt' => '/foo'])->getContent());
105    }
106
107    private function getKernel($returnValue)
108    {
109        $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
110        $kernel
111            ->expects($this->any())
112            ->method('handle')
113            ->will($returnValue)
114        ;
115
116        return $kernel;
117    }
118
119    public function testExceptionInSubRequestsDoesNotMangleOutputBuffers()
120    {
121        $controllerResolver = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Controller\\ControllerResolverInterface')->getMock();
122        $controllerResolver
123            ->expects($this->once())
124            ->method('getController')
125            ->willReturn(function () {
126                ob_start();
127                echo 'bar';
128                throw new \RuntimeException();
129            })
130        ;
131
132        $argumentResolver = $this->getMockBuilder('Symfony\\Component\\HttpKernel\\Controller\\ArgumentResolverInterface')->getMock();
133        $argumentResolver
134            ->expects($this->once())
135            ->method('getArguments')
136            ->willReturn([])
137        ;
138
139        $kernel = new HttpKernel(new EventDispatcher(), $controllerResolver, new RequestStack(), $argumentResolver);
140        $renderer = new InlineFragmentRenderer($kernel);
141
142        // simulate a main request with output buffering
143        ob_start();
144        echo 'Foo';
145
146        // simulate a sub-request with output buffering and an exception
147        $renderer->render('/', Request::create('/'), ['ignore_errors' => true]);
148
149        $this->assertEquals('Foo', ob_get_clean());
150    }
151
152    public function testLocaleAndFormatAreIsKeptInSubrequest()
153    {
154        $expectedSubRequest = Request::create('/');
155        $expectedSubRequest->attributes->set('_format', 'foo');
156        $expectedSubRequest->setLocale('fr');
157        if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) {
158            $expectedSubRequest->headers->set('x-forwarded-for', ['127.0.0.1']);
159            $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
160        }
161        $expectedSubRequest->headers->set('forwarded', ['for="127.0.0.1";host="localhost";proto=http']);
162        $expectedSubRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http');
163
164        $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest));
165
166        $request = Request::create('/');
167        $request->attributes->set('_format', 'foo');
168        $request->setLocale('fr');
169        $strategy->render('/', $request);
170    }
171
172    public function testESIHeaderIsKeptInSubrequest()
173    {
174        $expectedSubRequest = Request::create('/');
175        $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
176
177        if (Request::HEADER_X_FORWARDED_FOR & Request::getTrustedHeaderSet()) {
178            $expectedSubRequest->headers->set('x-forwarded-for', ['127.0.0.1']);
179            $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
180        }
181        $expectedSubRequest->headers->set('forwarded', ['for="127.0.0.1";host="localhost";proto=http']);
182        $expectedSubRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http');
183
184        $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest));
185
186        $request = Request::create('/');
187        $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
188        $strategy->render('/', $request);
189    }
190
191    public function testESIHeaderIsKeptInSubrequestWithTrustedHeaderDisabled()
192    {
193        Request::setTrustedProxies([], Request::HEADER_FORWARDED);
194
195        $this->testESIHeaderIsKeptInSubrequest();
196
197        Request::setTrustedProxies([], -1);
198    }
199
200    public function testHeadersPossiblyResultingIn304AreNotAssignedToSubrequest()
201    {
202        $expectedSubRequest = Request::create('/');
203        $expectedSubRequest->headers->set('x-forwarded-for', ['127.0.0.1']);
204        $expectedSubRequest->headers->set('forwarded', ['for="127.0.0.1";host="localhost";proto=http']);
205        $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
206        $expectedSubRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http');
207
208        $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest));
209        $request = Request::create('/', 'GET', [], [], [], ['HTTP_IF_MODIFIED_SINCE' => 'Fri, 01 Jan 2016 00:00:00 GMT', 'HTTP_IF_NONE_MATCH' => '*']);
210        $strategy->render('/', $request);
211    }
212
213    public function testFirstTrustedProxyIsSetAsRemote()
214    {
215        Request::setTrustedProxies(['1.1.1.1'], -1);
216
217        $expectedSubRequest = Request::create('/');
218        $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
219        $expectedSubRequest->server->set('REMOTE_ADDR', '127.0.0.1');
220        $expectedSubRequest->headers->set('x-forwarded-for', ['127.0.0.1']);
221        $expectedSubRequest->headers->set('forwarded', ['for="127.0.0.1";host="localhost";proto=http']);
222        $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
223        $expectedSubRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http');
224
225        $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest));
226
227        $request = Request::create('/');
228        $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
229        $strategy->render('/', $request);
230
231        Request::setTrustedProxies([], -1);
232    }
233
234    public function testIpAddressOfRangedTrustedProxyIsSetAsRemote()
235    {
236        $expectedSubRequest = Request::create('/');
237        $expectedSubRequest->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
238        $expectedSubRequest->server->set('REMOTE_ADDR', '127.0.0.1');
239        $expectedSubRequest->headers->set('x-forwarded-for', ['127.0.0.1']);
240        $expectedSubRequest->headers->set('forwarded', ['for="127.0.0.1";host="localhost";proto=http']);
241        $expectedSubRequest->server->set('HTTP_X_FORWARDED_FOR', '127.0.0.1');
242        $expectedSubRequest->server->set('HTTP_FORWARDED', 'for="127.0.0.1";host="localhost";proto=http');
243
244        Request::setTrustedProxies(['1.1.1.1/24'], -1);
245
246        $strategy = new InlineFragmentRenderer($this->getKernelExpectingRequest($expectedSubRequest));
247
248        $request = Request::create('/');
249        $request->headers->set('Surrogate-Capability', 'abc="ESI/1.0"');
250        $strategy->render('/', $request);
251
252        Request::setTrustedProxies([], -1);
253    }
254
255    /**
256     * Creates a Kernel expecting a request equals to $request
257     * Allows delta in comparison in case REQUEST_TIME changed by 1 second.
258     */
259    private function getKernelExpectingRequest(Request $request, $strict = false)
260    {
261        $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
262        $kernel
263            ->expects($this->once())
264            ->method('handle')
265            ->with($this->equalTo($request, 1))
266            ->willReturn(new Response('foo'));
267
268        return $kernel;
269    }
270}
271
272class Bar
273{
274    public $bar = 'bar';
275
276    public function getBar()
277    {
278        return $this->bar;
279    }
280}
281