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\EventListener;
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\ArgumentResolver;
20use Symfony\Component\HttpKernel\Controller\ControllerResolver;
21use Symfony\Component\HttpKernel\Event\GetResponseEvent;
22use Symfony\Component\HttpKernel\EventListener\ExceptionListener;
23use Symfony\Component\HttpKernel\EventListener\RouterListener;
24use Symfony\Component\HttpKernel\EventListener\ValidateRequestListener;
25use Symfony\Component\HttpKernel\HttpKernel;
26use Symfony\Component\HttpKernel\HttpKernelInterface;
27use Symfony\Component\Routing\Exception\NoConfigurationException;
28use Symfony\Component\Routing\RequestContext;
29
30class RouterListenerTest extends TestCase
31{
32    private $requestStack;
33
34    protected function setUp()
35    {
36        $this->requestStack = $this->getMockBuilder('Symfony\Component\HttpFoundation\RequestStack')->disableOriginalConstructor()->getMock();
37    }
38
39    /**
40     * @dataProvider getPortData
41     */
42    public function testPort($defaultHttpPort, $defaultHttpsPort, $uri, $expectedHttpPort, $expectedHttpsPort)
43    {
44        $urlMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\UrlMatcherInterface')
45            ->disableOriginalConstructor()
46            ->getMock();
47        $context = new RequestContext();
48        $context->setHttpPort($defaultHttpPort);
49        $context->setHttpsPort($defaultHttpsPort);
50        $urlMatcher->expects($this->any())
51            ->method('getContext')
52            ->willReturn($context);
53
54        $listener = new RouterListener($urlMatcher, $this->requestStack);
55        $event = $this->createGetResponseEventForUri($uri);
56        $listener->onKernelRequest($event);
57
58        $this->assertEquals($expectedHttpPort, $context->getHttpPort());
59        $this->assertEquals($expectedHttpsPort, $context->getHttpsPort());
60        $this->assertEquals(0 === strpos($uri, 'https') ? 'https' : 'http', $context->getScheme());
61    }
62
63    public function getPortData()
64    {
65        return [
66            [80, 443, 'http://localhost/', 80, 443],
67            [80, 443, 'http://localhost:90/', 90, 443],
68            [80, 443, 'https://localhost/', 80, 443],
69            [80, 443, 'https://localhost:90/', 80, 90],
70        ];
71    }
72
73    /**
74     * @param string $uri
75     *
76     * @return GetResponseEvent
77     */
78    private function createGetResponseEventForUri($uri)
79    {
80        $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
81        $request = Request::create($uri);
82        $request->attributes->set('_controller', null); // Prevents going in to routing process
83
84        return new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST);
85    }
86
87    public function testInvalidMatcher()
88    {
89        $this->expectException('InvalidArgumentException');
90        new RouterListener(new \stdClass(), $this->requestStack);
91    }
92
93    public function testRequestMatcher()
94    {
95        $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
96        $request = Request::create('http://localhost/');
97        $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST);
98
99        $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock();
100        $requestMatcher->expects($this->once())
101                       ->method('matchRequest')
102                       ->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request'))
103                       ->willReturn([]);
104
105        $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext());
106        $listener->onKernelRequest($event);
107    }
108
109    public function testSubRequestWithDifferentMethod()
110    {
111        $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
112        $request = Request::create('http://localhost/', 'post');
113        $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST);
114
115        $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock();
116        $requestMatcher->expects($this->any())
117                       ->method('matchRequest')
118                       ->with($this->isInstanceOf('Symfony\Component\HttpFoundation\Request'))
119                       ->willReturn([]);
120
121        $context = new RequestContext();
122
123        $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext());
124        $listener->onKernelRequest($event);
125
126        // sub-request with another HTTP method
127        $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
128        $request = Request::create('http://localhost/', 'get');
129        $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::SUB_REQUEST);
130
131        $listener->onKernelRequest($event);
132
133        $this->assertEquals('GET', $context->getMethod());
134    }
135
136    /**
137     * @dataProvider getLoggingParameterData
138     */
139    public function testLoggingParameter($parameter, $log, $parameters)
140    {
141        $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock();
142        $requestMatcher->expects($this->once())
143            ->method('matchRequest')
144            ->willReturn($parameter);
145
146        $logger = $this->getMockBuilder('Psr\Log\LoggerInterface')->getMock();
147        $logger->expects($this->once())
148            ->method('info')
149            ->with($this->equalTo($log), $this->equalTo($parameters));
150
151        $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
152        $request = Request::create('http://localhost/');
153
154        $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext(), $logger);
155        $listener->onKernelRequest(new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST));
156    }
157
158    public function getLoggingParameterData()
159    {
160        return [
161            [['_route' => 'foo'], 'Matched route "{route}".', ['route' => 'foo', 'route_parameters' => ['_route' => 'foo'], 'request_uri' => 'http://localhost/', 'method' => 'GET']],
162            [[], 'Matched route "{route}".', ['route' => 'n/a', 'route_parameters' => [], 'request_uri' => 'http://localhost/', 'method' => 'GET']],
163        ];
164    }
165
166    public function testWithBadRequest()
167    {
168        $requestStack = new RequestStack();
169
170        $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock();
171        $requestMatcher->expects($this->never())->method('matchRequest');
172
173        $dispatcher = new EventDispatcher();
174        $dispatcher->addSubscriber(new ValidateRequestListener());
175        $dispatcher->addSubscriber(new RouterListener($requestMatcher, $requestStack, new RequestContext()));
176        $dispatcher->addSubscriber(new ExceptionListener(function () {
177            return new Response('Exception handled', 400);
178        }));
179
180        $kernel = new HttpKernel($dispatcher, new ControllerResolver(), $requestStack, new ArgumentResolver());
181
182        $request = Request::create('http://localhost/');
183        $request->headers->set('host', '###');
184        $response = $kernel->handle($request);
185        $this->assertSame(400, $response->getStatusCode());
186    }
187
188    public function testNoRoutingConfigurationResponse()
189    {
190        $requestStack = new RequestStack();
191
192        $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock();
193        $requestMatcher
194            ->expects($this->once())
195            ->method('matchRequest')
196            ->willThrowException(new NoConfigurationException())
197        ;
198
199        $dispatcher = new EventDispatcher();
200        $dispatcher->addSubscriber(new RouterListener($requestMatcher, $requestStack, new RequestContext()));
201
202        $kernel = new HttpKernel($dispatcher, new ControllerResolver(), $requestStack, new ArgumentResolver());
203
204        $request = Request::create('http://localhost/');
205        $response = $kernel->handle($request);
206        $this->assertSame(404, $response->getStatusCode());
207        $this->assertStringContainsString('Welcome', $response->getContent());
208    }
209
210    public function testRequestWithBadHost()
211    {
212        $this->expectException('Symfony\Component\HttpKernel\Exception\BadRequestHttpException');
213        $kernel = $this->getMockBuilder('Symfony\Component\HttpKernel\HttpKernelInterface')->getMock();
214        $request = Request::create('http://bad host %22/');
215        $event = new GetResponseEvent($kernel, $request, HttpKernelInterface::MASTER_REQUEST);
216
217        $requestMatcher = $this->getMockBuilder('Symfony\Component\Routing\Matcher\RequestMatcherInterface')->getMock();
218
219        $listener = new RouterListener($requestMatcher, $this->requestStack, new RequestContext());
220        $listener->onKernelRequest($event);
221    }
222}
223