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\HttpFoundation\Tests;
13
14use PHPUnit\Framework\TestCase;
15use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
16use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
17use Symfony\Component\HttpFoundation\Session\Session;
18use Symfony\Component\HttpFoundation\Request;
19
20class RequestTest extends TestCase
21{
22    protected function tearDown()
23    {
24        // reset
25        Request::setTrustedProxies(array(), -1);
26    }
27
28    public function testInitialize()
29    {
30        $request = new Request();
31
32        $request->initialize(array('foo' => 'bar'));
33        $this->assertEquals('bar', $request->query->get('foo'), '->initialize() takes an array of query parameters as its first argument');
34
35        $request->initialize(array(), array('foo' => 'bar'));
36        $this->assertEquals('bar', $request->request->get('foo'), '->initialize() takes an array of request parameters as its second argument');
37
38        $request->initialize(array(), array(), array('foo' => 'bar'));
39        $this->assertEquals('bar', $request->attributes->get('foo'), '->initialize() takes an array of attributes as its third argument');
40
41        $request->initialize(array(), array(), array(), array(), array(), array('HTTP_FOO' => 'bar'));
42        $this->assertEquals('bar', $request->headers->get('FOO'), '->initialize() takes an array of HTTP headers as its sixth argument');
43    }
44
45    public function testGetLocale()
46    {
47        $request = new Request();
48        $request->setLocale('pl');
49        $locale = $request->getLocale();
50        $this->assertEquals('pl', $locale);
51    }
52
53    public function testGetUser()
54    {
55        $request = Request::create('http://user:password@test.com');
56        $user = $request->getUser();
57
58        $this->assertEquals('user', $user);
59    }
60
61    public function testGetPassword()
62    {
63        $request = Request::create('http://user:password@test.com');
64        $password = $request->getPassword();
65
66        $this->assertEquals('password', $password);
67    }
68
69    public function testIsNoCache()
70    {
71        $request = new Request();
72        $isNoCache = $request->isNoCache();
73
74        $this->assertFalse($isNoCache);
75    }
76
77    public function testGetContentType()
78    {
79        $request = new Request();
80        $contentType = $request->getContentType();
81
82        $this->assertNull($contentType);
83    }
84
85    public function testSetDefaultLocale()
86    {
87        $request = new Request();
88        $request->setDefaultLocale('pl');
89        $locale = $request->getLocale();
90
91        $this->assertEquals('pl', $locale);
92    }
93
94    public function testCreate()
95    {
96        $request = Request::create('http://test.com/foo?bar=baz');
97        $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
98        $this->assertEquals('/foo', $request->getPathInfo());
99        $this->assertEquals('bar=baz', $request->getQueryString());
100        $this->assertEquals(80, $request->getPort());
101        $this->assertEquals('test.com', $request->getHttpHost());
102        $this->assertFalse($request->isSecure());
103
104        $request = Request::create('http://test.com/foo', 'GET', array('bar' => 'baz'));
105        $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
106        $this->assertEquals('/foo', $request->getPathInfo());
107        $this->assertEquals('bar=baz', $request->getQueryString());
108        $this->assertEquals(80, $request->getPort());
109        $this->assertEquals('test.com', $request->getHttpHost());
110        $this->assertFalse($request->isSecure());
111
112        $request = Request::create('http://test.com/foo?bar=foo', 'GET', array('bar' => 'baz'));
113        $this->assertEquals('http://test.com/foo?bar=baz', $request->getUri());
114        $this->assertEquals('/foo', $request->getPathInfo());
115        $this->assertEquals('bar=baz', $request->getQueryString());
116        $this->assertEquals(80, $request->getPort());
117        $this->assertEquals('test.com', $request->getHttpHost());
118        $this->assertFalse($request->isSecure());
119
120        $request = Request::create('https://test.com/foo?bar=baz');
121        $this->assertEquals('https://test.com/foo?bar=baz', $request->getUri());
122        $this->assertEquals('/foo', $request->getPathInfo());
123        $this->assertEquals('bar=baz', $request->getQueryString());
124        $this->assertEquals(443, $request->getPort());
125        $this->assertEquals('test.com', $request->getHttpHost());
126        $this->assertTrue($request->isSecure());
127
128        $request = Request::create('test.com:90/foo');
129        $this->assertEquals('http://test.com:90/foo', $request->getUri());
130        $this->assertEquals('/foo', $request->getPathInfo());
131        $this->assertEquals('test.com', $request->getHost());
132        $this->assertEquals('test.com:90', $request->getHttpHost());
133        $this->assertEquals(90, $request->getPort());
134        $this->assertFalse($request->isSecure());
135
136        $request = Request::create('https://test.com:90/foo');
137        $this->assertEquals('https://test.com:90/foo', $request->getUri());
138        $this->assertEquals('/foo', $request->getPathInfo());
139        $this->assertEquals('test.com', $request->getHost());
140        $this->assertEquals('test.com:90', $request->getHttpHost());
141        $this->assertEquals(90, $request->getPort());
142        $this->assertTrue($request->isSecure());
143
144        $request = Request::create('https://127.0.0.1:90/foo');
145        $this->assertEquals('https://127.0.0.1:90/foo', $request->getUri());
146        $this->assertEquals('/foo', $request->getPathInfo());
147        $this->assertEquals('127.0.0.1', $request->getHost());
148        $this->assertEquals('127.0.0.1:90', $request->getHttpHost());
149        $this->assertEquals(90, $request->getPort());
150        $this->assertTrue($request->isSecure());
151
152        $request = Request::create('https://[::1]:90/foo');
153        $this->assertEquals('https://[::1]:90/foo', $request->getUri());
154        $this->assertEquals('/foo', $request->getPathInfo());
155        $this->assertEquals('[::1]', $request->getHost());
156        $this->assertEquals('[::1]:90', $request->getHttpHost());
157        $this->assertEquals(90, $request->getPort());
158        $this->assertTrue($request->isSecure());
159
160        $request = Request::create('https://[::1]/foo');
161        $this->assertEquals('https://[::1]/foo', $request->getUri());
162        $this->assertEquals('/foo', $request->getPathInfo());
163        $this->assertEquals('[::1]', $request->getHost());
164        $this->assertEquals('[::1]', $request->getHttpHost());
165        $this->assertEquals(443, $request->getPort());
166        $this->assertTrue($request->isSecure());
167
168        $json = '{"jsonrpc":"2.0","method":"echo","id":7,"params":["Hello World"]}';
169        $request = Request::create('http://example.com/jsonrpc', 'POST', array(), array(), array(), array(), $json);
170        $this->assertEquals($json, $request->getContent());
171        $this->assertFalse($request->isSecure());
172
173        $request = Request::create('http://test.com');
174        $this->assertEquals('http://test.com/', $request->getUri());
175        $this->assertEquals('/', $request->getPathInfo());
176        $this->assertEquals('', $request->getQueryString());
177        $this->assertEquals(80, $request->getPort());
178        $this->assertEquals('test.com', $request->getHttpHost());
179        $this->assertFalse($request->isSecure());
180
181        $request = Request::create('http://test.com?test=1');
182        $this->assertEquals('http://test.com/?test=1', $request->getUri());
183        $this->assertEquals('/', $request->getPathInfo());
184        $this->assertEquals('test=1', $request->getQueryString());
185        $this->assertEquals(80, $request->getPort());
186        $this->assertEquals('test.com', $request->getHttpHost());
187        $this->assertFalse($request->isSecure());
188
189        $request = Request::create('http://test.com:90/?test=1');
190        $this->assertEquals('http://test.com:90/?test=1', $request->getUri());
191        $this->assertEquals('/', $request->getPathInfo());
192        $this->assertEquals('test=1', $request->getQueryString());
193        $this->assertEquals(90, $request->getPort());
194        $this->assertEquals('test.com:90', $request->getHttpHost());
195        $this->assertFalse($request->isSecure());
196
197        $request = Request::create('http://username:password@test.com');
198        $this->assertEquals('http://test.com/', $request->getUri());
199        $this->assertEquals('/', $request->getPathInfo());
200        $this->assertEquals('', $request->getQueryString());
201        $this->assertEquals(80, $request->getPort());
202        $this->assertEquals('test.com', $request->getHttpHost());
203        $this->assertEquals('username', $request->getUser());
204        $this->assertEquals('password', $request->getPassword());
205        $this->assertFalse($request->isSecure());
206
207        $request = Request::create('http://username@test.com');
208        $this->assertEquals('http://test.com/', $request->getUri());
209        $this->assertEquals('/', $request->getPathInfo());
210        $this->assertEquals('', $request->getQueryString());
211        $this->assertEquals(80, $request->getPort());
212        $this->assertEquals('test.com', $request->getHttpHost());
213        $this->assertEquals('username', $request->getUser());
214        $this->assertSame('', $request->getPassword());
215        $this->assertFalse($request->isSecure());
216
217        $request = Request::create('http://test.com/?foo');
218        $this->assertEquals('/?foo', $request->getRequestUri());
219        $this->assertEquals(array('foo' => ''), $request->query->all());
220
221        // assume rewrite rule: (.*) --> app/app.php; app/ is a symlink to a symfony web/ directory
222        $request = Request::create('http://test.com/apparthotel-1234', 'GET', array(), array(), array(),
223            array(
224                'DOCUMENT_ROOT' => '/var/www/www.test.com',
225                'SCRIPT_FILENAME' => '/var/www/www.test.com/app/app.php',
226                'SCRIPT_NAME' => '/app/app.php',
227                'PHP_SELF' => '/app/app.php/apparthotel-1234',
228            ));
229        $this->assertEquals('http://test.com/apparthotel-1234', $request->getUri());
230        $this->assertEquals('/apparthotel-1234', $request->getPathInfo());
231        $this->assertEquals('', $request->getQueryString());
232        $this->assertEquals(80, $request->getPort());
233        $this->assertEquals('test.com', $request->getHttpHost());
234        $this->assertFalse($request->isSecure());
235    }
236
237    public function testCreateCheckPrecedence()
238    {
239        // server is used by default
240        $request = Request::create('/', 'DELETE', array(), array(), array(), array(
241            'HTTP_HOST' => 'example.com',
242            'HTTPS' => 'on',
243            'SERVER_PORT' => 443,
244            'PHP_AUTH_USER' => 'fabien',
245            'PHP_AUTH_PW' => 'pa$$',
246            'QUERY_STRING' => 'foo=bar',
247            'CONTENT_TYPE' => 'application/json',
248        ));
249        $this->assertEquals('example.com', $request->getHost());
250        $this->assertEquals(443, $request->getPort());
251        $this->assertTrue($request->isSecure());
252        $this->assertEquals('fabien', $request->getUser());
253        $this->assertEquals('pa$$', $request->getPassword());
254        $this->assertEquals('', $request->getQueryString());
255        $this->assertEquals('application/json', $request->headers->get('CONTENT_TYPE'));
256
257        // URI has precedence over server
258        $request = Request::create('http://thomas:pokemon@example.net:8080/?foo=bar', 'GET', array(), array(), array(), array(
259            'HTTP_HOST' => 'example.com',
260            'HTTPS' => 'on',
261            'SERVER_PORT' => 443,
262        ));
263        $this->assertEquals('example.net', $request->getHost());
264        $this->assertEquals(8080, $request->getPort());
265        $this->assertFalse($request->isSecure());
266        $this->assertEquals('thomas', $request->getUser());
267        $this->assertEquals('pokemon', $request->getPassword());
268        $this->assertEquals('foo=bar', $request->getQueryString());
269    }
270
271    public function testDuplicate()
272    {
273        $request = new Request(array('foo' => 'bar'), array('foo' => 'bar'), array('foo' => 'bar'), array(), array(), array('HTTP_FOO' => 'bar'));
274        $dup = $request->duplicate();
275
276        $this->assertEquals($request->query->all(), $dup->query->all(), '->duplicate() duplicates a request an copy the current query parameters');
277        $this->assertEquals($request->request->all(), $dup->request->all(), '->duplicate() duplicates a request an copy the current request parameters');
278        $this->assertEquals($request->attributes->all(), $dup->attributes->all(), '->duplicate() duplicates a request an copy the current attributes');
279        $this->assertEquals($request->headers->all(), $dup->headers->all(), '->duplicate() duplicates a request an copy the current HTTP headers');
280
281        $dup = $request->duplicate(array('foo' => 'foobar'), array('foo' => 'foobar'), array('foo' => 'foobar'), array(), array(), array('HTTP_FOO' => 'foobar'));
282
283        $this->assertEquals(array('foo' => 'foobar'), $dup->query->all(), '->duplicate() overrides the query parameters if provided');
284        $this->assertEquals(array('foo' => 'foobar'), $dup->request->all(), '->duplicate() overrides the request parameters if provided');
285        $this->assertEquals(array('foo' => 'foobar'), $dup->attributes->all(), '->duplicate() overrides the attributes if provided');
286        $this->assertEquals(array('foo' => array('foobar')), $dup->headers->all(), '->duplicate() overrides the HTTP header if provided');
287    }
288
289    public function testDuplicateWithFormat()
290    {
291        $request = new Request(array(), array(), array('_format' => 'json'));
292        $dup = $request->duplicate();
293
294        $this->assertEquals('json', $dup->getRequestFormat());
295        $this->assertEquals('json', $dup->attributes->get('_format'));
296
297        $request = new Request();
298        $request->setRequestFormat('xml');
299        $dup = $request->duplicate();
300
301        $this->assertEquals('xml', $dup->getRequestFormat());
302    }
303
304    /**
305     * @dataProvider getFormatToMimeTypeMapProviderWithAdditionalNullFormat
306     */
307    public function testGetFormatFromMimeType($format, $mimeTypes)
308    {
309        $request = new Request();
310        foreach ($mimeTypes as $mime) {
311            $this->assertEquals($format, $request->getFormat($mime));
312        }
313        $request->setFormat($format, $mimeTypes);
314        foreach ($mimeTypes as $mime) {
315            $this->assertEquals($format, $request->getFormat($mime));
316
317            if (null !== $format) {
318                $this->assertEquals($mimeTypes[0], $request->getMimeType($format));
319            }
320        }
321    }
322
323    public function getFormatToMimeTypeMapProviderWithAdditionalNullFormat()
324    {
325        return array_merge(
326            array(array(null, array(null, 'unexistent-mime-type'))),
327            $this->getFormatToMimeTypeMapProvider()
328        );
329    }
330
331    public function testGetFormatFromMimeTypeWithParameters()
332    {
333        $request = new Request();
334        $this->assertEquals('json', $request->getFormat('application/json; charset=utf-8'));
335    }
336
337    /**
338     * @dataProvider getFormatToMimeTypeMapProvider
339     */
340    public function testGetMimeTypeFromFormat($format, $mimeTypes)
341    {
342        $request = new Request();
343        $this->assertEquals($mimeTypes[0], $request->getMimeType($format));
344    }
345
346    /**
347     * @dataProvider getFormatToMimeTypeMapProvider
348     */
349    public function testGetMimeTypesFromFormat($format, $mimeTypes)
350    {
351        $this->assertEquals($mimeTypes, Request::getMimeTypes($format));
352    }
353
354    public function testGetMimeTypesFromInexistentFormat()
355    {
356        $request = new Request();
357        $this->assertNull($request->getMimeType('foo'));
358        $this->assertEquals(array(), Request::getMimeTypes('foo'));
359    }
360
361    public function testGetFormatWithCustomMimeType()
362    {
363        $request = new Request();
364        $request->setFormat('custom', 'application/vnd.foo.api;myversion=2.3');
365        $this->assertEquals('custom', $request->getFormat('application/vnd.foo.api;myversion=2.3'));
366    }
367
368    public function getFormatToMimeTypeMapProvider()
369    {
370        return array(
371            array('txt', array('text/plain')),
372            array('js', array('application/javascript', 'application/x-javascript', 'text/javascript')),
373            array('css', array('text/css')),
374            array('json', array('application/json', 'application/x-json')),
375            array('jsonld', array('application/ld+json')),
376            array('xml', array('text/xml', 'application/xml', 'application/x-xml')),
377            array('rdf', array('application/rdf+xml')),
378            array('atom', array('application/atom+xml')),
379        );
380    }
381
382    public function testGetUri()
383    {
384        $server = array();
385
386        // Standard Request on non default PORT
387        // http://host:8080/index.php/path/info?query=string
388
389        $server['HTTP_HOST'] = 'host:8080';
390        $server['SERVER_NAME'] = 'servername';
391        $server['SERVER_PORT'] = '8080';
392
393        $server['QUERY_STRING'] = 'query=string';
394        $server['REQUEST_URI'] = '/index.php/path/info?query=string';
395        $server['SCRIPT_NAME'] = '/index.php';
396        $server['PATH_INFO'] = '/path/info';
397        $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info';
398        $server['PHP_SELF'] = '/index_dev.php/path/info';
399        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
400
401        $request = new Request();
402
403        $request->initialize(array(), array(), array(), array(), array(), $server);
404
405        $this->assertEquals('http://host:8080/index.php/path/info?query=string', $request->getUri(), '->getUri() with non default port');
406
407        // Use std port number
408        $server['HTTP_HOST'] = 'host';
409        $server['SERVER_NAME'] = 'servername';
410        $server['SERVER_PORT'] = '80';
411
412        $request->initialize(array(), array(), array(), array(), array(), $server);
413
414        $this->assertEquals('http://host/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port');
415
416        // Without HOST HEADER
417        unset($server['HTTP_HOST']);
418        $server['SERVER_NAME'] = 'servername';
419        $server['SERVER_PORT'] = '80';
420
421        $request->initialize(array(), array(), array(), array(), array(), $server);
422
423        $this->assertEquals('http://servername/index.php/path/info?query=string', $request->getUri(), '->getUri() with default port without HOST_HEADER');
424
425        // Request with URL REWRITING (hide index.php)
426        //   RewriteCond %{REQUEST_FILENAME} !-f
427        //   RewriteRule ^(.*)$ index.php [QSA,L]
428        // http://host:8080/path/info?query=string
429        $server = array();
430        $server['HTTP_HOST'] = 'host:8080';
431        $server['SERVER_NAME'] = 'servername';
432        $server['SERVER_PORT'] = '8080';
433
434        $server['REDIRECT_QUERY_STRING'] = 'query=string';
435        $server['REDIRECT_URL'] = '/path/info';
436        $server['SCRIPT_NAME'] = '/index.php';
437        $server['QUERY_STRING'] = 'query=string';
438        $server['REQUEST_URI'] = '/path/info?toto=test&1=1';
439        $server['SCRIPT_NAME'] = '/index.php';
440        $server['PHP_SELF'] = '/index.php';
441        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
442
443        $request->initialize(array(), array(), array(), array(), array(), $server);
444        $this->assertEquals('http://host:8080/path/info?query=string', $request->getUri(), '->getUri() with rewrite');
445
446        // Use std port number
447        //  http://host/path/info?query=string
448        $server['HTTP_HOST'] = 'host';
449        $server['SERVER_NAME'] = 'servername';
450        $server['SERVER_PORT'] = '80';
451
452        $request->initialize(array(), array(), array(), array(), array(), $server);
453
454        $this->assertEquals('http://host/path/info?query=string', $request->getUri(), '->getUri() with rewrite and default port');
455
456        // Without HOST HEADER
457        unset($server['HTTP_HOST']);
458        $server['SERVER_NAME'] = 'servername';
459        $server['SERVER_PORT'] = '80';
460
461        $request->initialize(array(), array(), array(), array(), array(), $server);
462
463        $this->assertEquals('http://servername/path/info?query=string', $request->getUri(), '->getUri() with rewrite, default port without HOST_HEADER');
464
465        // With encoded characters
466
467        $server = array(
468            'HTTP_HOST' => 'host:8080',
469            'SERVER_NAME' => 'servername',
470            'SERVER_PORT' => '8080',
471            'QUERY_STRING' => 'query=string',
472            'REQUEST_URI' => '/ba%20se/index_dev.php/foo%20bar/in+fo?query=string',
473            'SCRIPT_NAME' => '/ba se/index_dev.php',
474            'PATH_TRANSLATED' => 'redirect:/index.php/foo bar/in+fo',
475            'PHP_SELF' => '/ba se/index_dev.php/path/info',
476            'SCRIPT_FILENAME' => '/some/where/ba se/index_dev.php',
477        );
478
479        $request->initialize(array(), array(), array(), array(), array(), $server);
480
481        $this->assertEquals(
482            'http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string',
483            $request->getUri()
484        );
485
486        // with user info
487
488        $server['PHP_AUTH_USER'] = 'fabien';
489        $request->initialize(array(), array(), array(), array(), array(), $server);
490        $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri());
491
492        $server['PHP_AUTH_PW'] = 'symfony';
493        $request->initialize(array(), array(), array(), array(), array(), $server);
494        $this->assertEquals('http://host:8080/ba%20se/index_dev.php/foo%20bar/in+fo?query=string', $request->getUri());
495    }
496
497    public function testGetUriForPath()
498    {
499        $request = Request::create('http://test.com/foo?bar=baz');
500        $this->assertEquals('http://test.com/some/path', $request->getUriForPath('/some/path'));
501
502        $request = Request::create('http://test.com:90/foo?bar=baz');
503        $this->assertEquals('http://test.com:90/some/path', $request->getUriForPath('/some/path'));
504
505        $request = Request::create('https://test.com/foo?bar=baz');
506        $this->assertEquals('https://test.com/some/path', $request->getUriForPath('/some/path'));
507
508        $request = Request::create('https://test.com:90/foo?bar=baz');
509        $this->assertEquals('https://test.com:90/some/path', $request->getUriForPath('/some/path'));
510
511        $server = array();
512
513        // Standard Request on non default PORT
514        // http://host:8080/index.php/path/info?query=string
515
516        $server['HTTP_HOST'] = 'host:8080';
517        $server['SERVER_NAME'] = 'servername';
518        $server['SERVER_PORT'] = '8080';
519
520        $server['QUERY_STRING'] = 'query=string';
521        $server['REQUEST_URI'] = '/index.php/path/info?query=string';
522        $server['SCRIPT_NAME'] = '/index.php';
523        $server['PATH_INFO'] = '/path/info';
524        $server['PATH_TRANSLATED'] = 'redirect:/index.php/path/info';
525        $server['PHP_SELF'] = '/index_dev.php/path/info';
526        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
527
528        $request = new Request();
529
530        $request->initialize(array(), array(), array(), array(), array(), $server);
531
532        $this->assertEquals('http://host:8080/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with non default port');
533
534        // Use std port number
535        $server['HTTP_HOST'] = 'host';
536        $server['SERVER_NAME'] = 'servername';
537        $server['SERVER_PORT'] = '80';
538
539        $request->initialize(array(), array(), array(), array(), array(), $server);
540
541        $this->assertEquals('http://host/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port');
542
543        // Without HOST HEADER
544        unset($server['HTTP_HOST']);
545        $server['SERVER_NAME'] = 'servername';
546        $server['SERVER_PORT'] = '80';
547
548        $request->initialize(array(), array(), array(), array(), array(), $server);
549
550        $this->assertEquals('http://servername/index.php/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with default port without HOST_HEADER');
551
552        // Request with URL REWRITING (hide index.php)
553        //   RewriteCond %{REQUEST_FILENAME} !-f
554        //   RewriteRule ^(.*)$ index.php [QSA,L]
555        // http://host:8080/path/info?query=string
556        $server = array();
557        $server['HTTP_HOST'] = 'host:8080';
558        $server['SERVER_NAME'] = 'servername';
559        $server['SERVER_PORT'] = '8080';
560
561        $server['REDIRECT_QUERY_STRING'] = 'query=string';
562        $server['REDIRECT_URL'] = '/path/info';
563        $server['SCRIPT_NAME'] = '/index.php';
564        $server['QUERY_STRING'] = 'query=string';
565        $server['REQUEST_URI'] = '/path/info?toto=test&1=1';
566        $server['SCRIPT_NAME'] = '/index.php';
567        $server['PHP_SELF'] = '/index.php';
568        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
569
570        $request->initialize(array(), array(), array(), array(), array(), $server);
571        $this->assertEquals('http://host:8080/some/path', $request->getUriForPath('/some/path'), '->getUri() with rewrite');
572
573        // Use std port number
574        //  http://host/path/info?query=string
575        $server['HTTP_HOST'] = 'host';
576        $server['SERVER_NAME'] = 'servername';
577        $server['SERVER_PORT'] = '80';
578
579        $request->initialize(array(), array(), array(), array(), array(), $server);
580
581        $this->assertEquals('http://host/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite and default port');
582
583        // Without HOST HEADER
584        unset($server['HTTP_HOST']);
585        $server['SERVER_NAME'] = 'servername';
586        $server['SERVER_PORT'] = '80';
587
588        $request->initialize(array(), array(), array(), array(), array(), $server);
589
590        $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'), '->getUriForPath() with rewrite, default port without HOST_HEADER');
591        $this->assertEquals('servername', $request->getHttpHost());
592
593        // with user info
594
595        $server['PHP_AUTH_USER'] = 'fabien';
596        $request->initialize(array(), array(), array(), array(), array(), $server);
597        $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'));
598
599        $server['PHP_AUTH_PW'] = 'symfony';
600        $request->initialize(array(), array(), array(), array(), array(), $server);
601        $this->assertEquals('http://servername/some/path', $request->getUriForPath('/some/path'));
602    }
603
604    /**
605     * @dataProvider getRelativeUriForPathData()
606     */
607    public function testGetRelativeUriForPath($expected, $pathinfo, $path)
608    {
609        $this->assertEquals($expected, Request::create($pathinfo)->getRelativeUriForPath($path));
610    }
611
612    public function getRelativeUriForPathData()
613    {
614        return array(
615            array('me.png', '/foo', '/me.png'),
616            array('../me.png', '/foo/bar', '/me.png'),
617            array('me.png', '/foo/bar', '/foo/me.png'),
618            array('../baz/me.png', '/foo/bar/b', '/foo/baz/me.png'),
619            array('../../fooz/baz/me.png', '/foo/bar/b', '/fooz/baz/me.png'),
620            array('baz/me.png', '/foo/bar/b', 'baz/me.png'),
621        );
622    }
623
624    public function testGetUserInfo()
625    {
626        $request = new Request();
627
628        $server = array('PHP_AUTH_USER' => 'fabien');
629        $request->initialize(array(), array(), array(), array(), array(), $server);
630        $this->assertEquals('fabien', $request->getUserInfo());
631
632        $server['PHP_AUTH_USER'] = '0';
633        $request->initialize(array(), array(), array(), array(), array(), $server);
634        $this->assertEquals('0', $request->getUserInfo());
635
636        $server['PHP_AUTH_PW'] = '0';
637        $request->initialize(array(), array(), array(), array(), array(), $server);
638        $this->assertEquals('0:0', $request->getUserInfo());
639    }
640
641    public function testGetSchemeAndHttpHost()
642    {
643        $request = new Request();
644
645        $server = array();
646        $server['SERVER_NAME'] = 'servername';
647        $server['SERVER_PORT'] = '90';
648        $request->initialize(array(), array(), array(), array(), array(), $server);
649        $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
650
651        $server['PHP_AUTH_USER'] = 'fabien';
652        $request->initialize(array(), array(), array(), array(), array(), $server);
653        $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
654
655        $server['PHP_AUTH_USER'] = '0';
656        $request->initialize(array(), array(), array(), array(), array(), $server);
657        $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
658
659        $server['PHP_AUTH_PW'] = '0';
660        $request->initialize(array(), array(), array(), array(), array(), $server);
661        $this->assertEquals('http://servername:90', $request->getSchemeAndHttpHost());
662    }
663
664    /**
665     * @dataProvider getQueryStringNormalizationData
666     */
667    public function testGetQueryString($query, $expectedQuery, $msg)
668    {
669        $request = new Request();
670
671        $request->server->set('QUERY_STRING', $query);
672        $this->assertSame($expectedQuery, $request->getQueryString(), $msg);
673    }
674
675    public function getQueryStringNormalizationData()
676    {
677        return array(
678            array('foo', 'foo', 'works with valueless parameters'),
679            array('foo=', 'foo=', 'includes a dangling equal sign'),
680            array('bar=&foo=bar', 'bar=&foo=bar', '->works with empty parameters'),
681            array('foo=bar&bar=', 'bar=&foo=bar', 'sorts keys alphabetically'),
682
683            // GET parameters, that are submitted from a HTML form, encode spaces as "+" by default (as defined in enctype application/x-www-form-urlencoded).
684            // PHP also converts "+" to spaces when filling the global _GET or when using the function parse_str.
685            array('him=John%20Doe&her=Jane+Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes spaces in both encodings "%20" and "+"'),
686
687            array('foo[]=1&foo[]=2', 'foo%5B%5D=1&foo%5B%5D=2', 'allows array notation'),
688            array('foo=1&foo=2', 'foo=1&foo=2', 'allows repeated parameters'),
689            array('pa%3Dram=foo%26bar%3Dbaz&test=test', 'pa%3Dram=foo%26bar%3Dbaz&test=test', 'works with encoded delimiters'),
690            array('0', '0', 'allows "0"'),
691            array('Jane Doe&John%20Doe', 'Jane%20Doe&John%20Doe', 'normalizes encoding in keys'),
692            array('her=Jane Doe&him=John%20Doe', 'her=Jane%20Doe&him=John%20Doe', 'normalizes encoding in values'),
693            array('foo=bar&&&test&&', 'foo=bar&test', 'removes unneeded delimiters'),
694            array('formula=e=m*c^2', 'formula=e%3Dm%2Ac%5E2', 'correctly treats only the first "=" as delimiter and the next as value'),
695
696            // Ignore pairs with empty key, even if there was a value, e.g. "=value", as such nameless values cannot be retrieved anyway.
697            // PHP also does not include them when building _GET.
698            array('foo=bar&=a=b&=x=y', 'foo=bar', 'removes params with empty key'),
699        );
700    }
701
702    public function testGetQueryStringReturnsNull()
703    {
704        $request = new Request();
705
706        $this->assertNull($request->getQueryString(), '->getQueryString() returns null for non-existent query string');
707
708        $request->server->set('QUERY_STRING', '');
709        $this->assertNull($request->getQueryString(), '->getQueryString() returns null for empty query string');
710    }
711
712    public function testGetHost()
713    {
714        $request = new Request();
715
716        $request->initialize(array('foo' => 'bar'));
717        $this->assertEquals('', $request->getHost(), '->getHost() return empty string if not initialized');
718
719        $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com'));
720        $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header');
721
722        // Host header with port number
723        $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.example.com:8080'));
724        $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from Host Header with port number');
725
726        // Server values
727        $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com'));
728        $this->assertEquals('www.example.com', $request->getHost(), '->getHost() from server name');
729
730        $request->initialize(array(), array(), array(), array(), array(), array('SERVER_NAME' => 'www.example.com', 'HTTP_HOST' => 'www.host.com'));
731        $this->assertEquals('www.host.com', $request->getHost(), '->getHost() value from Host header has priority over SERVER_NAME ');
732    }
733
734    public function testGetPort()
735    {
736        $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
737            'HTTP_X_FORWARDED_PROTO' => 'https',
738            'HTTP_X_FORWARDED_PORT' => '443',
739        ));
740        $port = $request->getPort();
741
742        $this->assertEquals(80, $port, 'Without trusted proxies FORWARDED_PROTO and FORWARDED_PORT are ignored.');
743
744        Request::setTrustedProxies(array('1.1.1.1'), Request::HEADER_X_FORWARDED_ALL);
745        $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
746            'HTTP_X_FORWARDED_PROTO' => 'https',
747            'HTTP_X_FORWARDED_PORT' => '8443',
748        ));
749        $this->assertEquals(80, $request->getPort(), 'With PROTO and PORT on untrusted connection server value takes precedence.');
750        $request->server->set('REMOTE_ADDR', '1.1.1.1');
751        $this->assertEquals(8443, $request->getPort(), 'With PROTO and PORT set PORT takes precedence.');
752
753        $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
754            'HTTP_X_FORWARDED_PROTO' => 'https',
755        ));
756        $this->assertEquals(80, $request->getPort(), 'With only PROTO set getPort() ignores trusted headers on untrusted connection.');
757        $request->server->set('REMOTE_ADDR', '1.1.1.1');
758        $this->assertEquals(443, $request->getPort(), 'With only PROTO set getPort() defaults to 443.');
759
760        $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
761            'HTTP_X_FORWARDED_PROTO' => 'http',
762        ));
763        $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() ignores trusted headers on untrusted connection.');
764        $request->server->set('REMOTE_ADDR', '1.1.1.1');
765        $this->assertEquals(80, $request->getPort(), 'If X_FORWARDED_PROTO is set to HTTP getPort() returns port of the original request.');
766
767        $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
768            'HTTP_X_FORWARDED_PROTO' => 'On',
769        ));
770        $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is On, getPort() ignores trusted headers on untrusted connection.');
771        $request->server->set('REMOTE_ADDR', '1.1.1.1');
772        $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is On, getPort() defaults to 443.');
773
774        $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
775            'HTTP_X_FORWARDED_PROTO' => '1',
776        ));
777        $this->assertEquals(80, $request->getPort(), 'With only PROTO set and value is 1, getPort() ignores trusted headers on untrusted connection.');
778        $request->server->set('REMOTE_ADDR', '1.1.1.1');
779        $this->assertEquals(443, $request->getPort(), 'With only PROTO set and value is 1, getPort() defaults to 443.');
780
781        $request = Request::create('http://example.com', 'GET', array(), array(), array(), array(
782            'HTTP_X_FORWARDED_PROTO' => 'something-else',
783        ));
784        $port = $request->getPort();
785        $this->assertEquals(80, $port, 'With only PROTO set and value is not recognized, getPort() defaults to 80.');
786    }
787
788    /**
789     * @expectedException \RuntimeException
790     */
791    public function testGetHostWithFakeHttpHostValue()
792    {
793        $request = new Request();
794        $request->initialize(array(), array(), array(), array(), array(), array('HTTP_HOST' => 'www.host.com?query=string'));
795        $request->getHost();
796    }
797
798    public function testGetSetMethod()
799    {
800        $request = new Request();
801
802        $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns GET if no method is defined');
803
804        $request->setMethod('get');
805        $this->assertEquals('GET', $request->getMethod(), '->getMethod() returns an uppercased string');
806
807        $request->setMethod('PURGE');
808        $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method even if it is not a standard one');
809
810        $request->setMethod('POST');
811        $this->assertEquals('POST', $request->getMethod(), '->getMethod() returns the method POST if no _method is defined');
812
813        $request->setMethod('POST');
814        $request->request->set('_method', 'purge');
815        $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled');
816
817        $request = new Request();
818        $request->setMethod('POST');
819        $request->request->set('_method', 'purge');
820
821        $this->assertFalse(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be disabled by default');
822
823        Request::enableHttpMethodParameterOverride();
824
825        $this->assertTrue(Request::getHttpMethodParameterOverride(), 'httpMethodParameterOverride should be enabled now but it is not');
826
827        $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST');
828        $this->disableHttpMethodParameterOverride();
829
830        $request = new Request();
831        $request->setMethod('POST');
832        $request->query->set('_method', 'purge');
833        $this->assertEquals('POST', $request->getMethod(), '->getMethod() does not return the method from _method if defined and POST but support not enabled');
834
835        $request = new Request();
836        $request->setMethod('POST');
837        $request->query->set('_method', 'purge');
838        Request::enableHttpMethodParameterOverride();
839        $this->assertEquals('PURGE', $request->getMethod(), '->getMethod() returns the method from _method if defined and POST');
840        $this->disableHttpMethodParameterOverride();
841
842        $request = new Request();
843        $request->setMethod('POST');
844        $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete');
845        $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override even though _method is set if defined and POST');
846
847        $request = new Request();
848        $request->setMethod('POST');
849        $request->headers->set('X-HTTP-METHOD-OVERRIDE', 'delete');
850        $this->assertEquals('DELETE', $request->getMethod(), '->getMethod() returns the method from X-HTTP-Method-Override if defined and POST');
851    }
852
853    /**
854     * @dataProvider getClientIpsProvider
855     */
856    public function testGetClientIp($expected, $remoteAddr, $httpForwardedFor, $trustedProxies)
857    {
858        $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies);
859
860        $this->assertEquals($expected[0], $request->getClientIp());
861    }
862
863    /**
864     * @dataProvider getClientIpsProvider
865     */
866    public function testGetClientIps($expected, $remoteAddr, $httpForwardedFor, $trustedProxies)
867    {
868        $request = $this->getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies);
869
870        $this->assertEquals($expected, $request->getClientIps());
871    }
872
873    /**
874     * @dataProvider getClientIpsForwardedProvider
875     */
876    public function testGetClientIpsForwarded($expected, $remoteAddr, $httpForwarded, $trustedProxies)
877    {
878        $request = $this->getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies);
879
880        $this->assertEquals($expected, $request->getClientIps());
881    }
882
883    public function getClientIpsForwardedProvider()
884    {
885        //              $expected                                  $remoteAddr  $httpForwarded                                       $trustedProxies
886        return array(
887            array(array('127.0.0.1'),                              '127.0.0.1', 'for="_gazonk"',                                      null),
888            array(array('127.0.0.1'),                              '127.0.0.1', 'for="_gazonk"',                                      array('127.0.0.1')),
889            array(array('88.88.88.88'),                            '127.0.0.1', 'for="88.88.88.88:80"',                               array('127.0.0.1')),
890            array(array('192.0.2.60'),                             '::1',       'for=192.0.2.60;proto=http;by=203.0.113.43',          array('::1')),
891            array(array('2620:0:1cfe:face:b00c::3', '192.0.2.43'), '::1',       'for=192.0.2.43, for=2620:0:1cfe:face:b00c::3',       array('::1')),
892            array(array('2001:db8:cafe::17'),                      '::1',       'for="[2001:db8:cafe::17]:4711',                      array('::1')),
893        );
894    }
895
896    public function getClientIpsProvider()
897    {
898        //        $expected                   $remoteAddr                $httpForwardedFor            $trustedProxies
899        return array(
900            // simple IPv4
901            array(array('88.88.88.88'),              '88.88.88.88',              null,                        null),
902            // trust the IPv4 remote addr
903            array(array('88.88.88.88'),              '88.88.88.88',              null,                        array('88.88.88.88')),
904
905            // simple IPv6
906            array(array('::1'),                      '::1',                      null,                        null),
907            // trust the IPv6 remote addr
908            array(array('::1'),                      '::1',                      null,                        array('::1')),
909
910            // forwarded for with remote IPv4 addr not trusted
911            array(array('127.0.0.1'),                '127.0.0.1',                '88.88.88.88',               null),
912            // forwarded for with remote IPv4 addr trusted
913            array(array('88.88.88.88'),              '127.0.0.1',                '88.88.88.88',               array('127.0.0.1')),
914            // forwarded for with remote IPv4 and all FF addrs trusted
915            array(array('88.88.88.88'),              '127.0.0.1',                '88.88.88.88',               array('127.0.0.1', '88.88.88.88')),
916            // forwarded for with remote IPv4 range trusted
917            array(array('88.88.88.88'),              '123.45.67.89',             '88.88.88.88',               array('123.45.67.0/24')),
918
919            // forwarded for with remote IPv6 addr not trusted
920            array(array('1620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3',  null),
921            // forwarded for with remote IPv6 addr trusted
922            array(array('2620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3',  array('1620:0:1cfe:face:b00c::3')),
923            // forwarded for with remote IPv6 range trusted
924            array(array('88.88.88.88'),              '2a01:198:603:0:396e:4789:8e99:890f', '88.88.88.88',     array('2a01:198:603:0::/65')),
925
926            // multiple forwarded for with remote IPv4 addr trusted
927            array(array('88.88.88.88', '87.65.43.21', '127.0.0.1'), '123.45.67.89', '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89')),
928            // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted
929            array(array('87.65.43.21', '127.0.0.1'), '123.45.67.89',             '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '88.88.88.88')),
930            // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle
931            array(array('88.88.88.88', '127.0.0.1'), '123.45.67.89',             '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21')),
932            // multiple forwarded for with remote IPv4 addr and all reverse proxies trusted
933            array(array('127.0.0.1'),                '123.45.67.89',             '127.0.0.1, 87.65.43.21, 88.88.88.88', array('123.45.67.89', '87.65.43.21', '88.88.88.88', '127.0.0.1')),
934
935            // multiple forwarded for with remote IPv6 addr trusted
936            array(array('2620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3')),
937            // multiple forwarded for with remote IPv6 addr and some reverse proxies trusted
938            array(array('3620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '2620:0:1cfe:face:b00c::3')),
939            // multiple forwarded for with remote IPv4 addr and some reverse proxies trusted but in the middle
940            array(array('2620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3'), '1620:0:1cfe:face:b00c::3', '4620:0:1cfe:face:b00c::3,3620:0:1cfe:face:b00c::3,2620:0:1cfe:face:b00c::3', array('1620:0:1cfe:face:b00c::3', '3620:0:1cfe:face:b00c::3')),
941
942            // client IP with port
943            array(array('88.88.88.88'), '127.0.0.1', '88.88.88.88:12345, 127.0.0.1', array('127.0.0.1')),
944
945            // invalid forwarded IP is ignored
946            array(array('88.88.88.88'), '127.0.0.1', 'unknown,88.88.88.88', array('127.0.0.1')),
947            array(array('88.88.88.88'), '127.0.0.1', '}__test|O:21:&quot;JDatabaseDriverMysqli&quot;:3:{s:2,88.88.88.88', array('127.0.0.1')),
948        );
949    }
950
951    /**
952     * @expectedException \Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException
953     * @dataProvider getClientIpsWithConflictingHeadersProvider
954     */
955    public function testGetClientIpsWithConflictingHeaders($httpForwarded, $httpXForwardedFor)
956    {
957        $request = new Request();
958
959        $server = array(
960            'REMOTE_ADDR' => '88.88.88.88',
961            'HTTP_FORWARDED' => $httpForwarded,
962            'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
963        );
964
965        Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_ALL | Request::HEADER_FORWARDED);
966
967        $request->initialize(array(), array(), array(), array(), array(), $server);
968
969        $request->getClientIps();
970    }
971
972    /**
973     * @dataProvider getClientIpsWithConflictingHeadersProvider
974     */
975    public function testGetClientIpsOnlyXHttpForwardedForTrusted($httpForwarded, $httpXForwardedFor)
976    {
977        $request = new Request();
978
979        $server = array(
980            'REMOTE_ADDR' => '88.88.88.88',
981            'HTTP_FORWARDED' => $httpForwarded,
982            'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
983        );
984
985        Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_FOR);
986
987        $request->initialize(array(), array(), array(), array(), array(), $server);
988
989        $this->assertSame(array_reverse(explode(',', $httpXForwardedFor)), $request->getClientIps());
990    }
991
992    public function getClientIpsWithConflictingHeadersProvider()
993    {
994        //        $httpForwarded                   $httpXForwardedFor
995        return array(
996            array('for=87.65.43.21',                 '192.0.2.60'),
997            array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60'),
998            array('for=192.0.2.60',                  '192.0.2.60,87.65.43.21'),
999            array('for="::face", for=192.0.2.60',    '192.0.2.60,192.0.2.43'),
1000            array('for=87.65.43.21, for=192.0.2.60', '192.0.2.60,87.65.43.21'),
1001        );
1002    }
1003
1004    /**
1005     * @dataProvider getClientIpsWithAgreeingHeadersProvider
1006     */
1007    public function testGetClientIpsWithAgreeingHeaders($httpForwarded, $httpXForwardedFor, $expectedIps)
1008    {
1009        $request = new Request();
1010
1011        $server = array(
1012            'REMOTE_ADDR' => '88.88.88.88',
1013            'HTTP_FORWARDED' => $httpForwarded,
1014            'HTTP_X_FORWARDED_FOR' => $httpXForwardedFor,
1015        );
1016
1017        Request::setTrustedProxies(array('88.88.88.88'), Request::HEADER_X_FORWARDED_ALL);
1018
1019        $request->initialize(array(), array(), array(), array(), array(), $server);
1020
1021        $clientIps = $request->getClientIps();
1022
1023        $this->assertSame($expectedIps, $clientIps);
1024    }
1025
1026    public function getClientIpsWithAgreeingHeadersProvider()
1027    {
1028        //        $httpForwarded                               $httpXForwardedFor
1029        return array(
1030            array('for="192.0.2.60"',                          '192.0.2.60',             array('192.0.2.60')),
1031            array('for=192.0.2.60, for=87.65.43.21',           '192.0.2.60,87.65.43.21', array('87.65.43.21', '192.0.2.60')),
1032            array('for="[::face]", for=192.0.2.60',            '::face,192.0.2.60',      array('192.0.2.60', '::face')),
1033            array('for="192.0.2.60:80"',                       '192.0.2.60',             array('192.0.2.60')),
1034            array('for=192.0.2.60;proto=http;by=203.0.113.43', '192.0.2.60',             array('192.0.2.60')),
1035            array('for="[2001:db8:cafe::17]:4711"',            '2001:db8:cafe::17',      array('2001:db8:cafe::17')),
1036        );
1037    }
1038
1039    public function testGetContentWorksTwiceInDefaultMode()
1040    {
1041        $req = new Request();
1042        $this->assertEquals('', $req->getContent());
1043        $this->assertEquals('', $req->getContent());
1044    }
1045
1046    public function testGetContentReturnsResource()
1047    {
1048        $req = new Request();
1049        $retval = $req->getContent(true);
1050        $this->assertInternalType('resource', $retval);
1051        $this->assertEquals('', fread($retval, 1));
1052        $this->assertTrue(feof($retval));
1053    }
1054
1055    public function testGetContentReturnsResourceWhenContentSetInConstructor()
1056    {
1057        $req = new Request(array(), array(), array(), array(), array(), array(), 'MyContent');
1058        $resource = $req->getContent(true);
1059
1060        $this->assertInternalType('resource', $resource);
1061        $this->assertEquals('MyContent', stream_get_contents($resource));
1062    }
1063
1064    public function testContentAsResource()
1065    {
1066        $resource = fopen('php://memory', 'r+');
1067        fwrite($resource, 'My other content');
1068        rewind($resource);
1069
1070        $req = new Request(array(), array(), array(), array(), array(), array(), $resource);
1071        $this->assertEquals('My other content', stream_get_contents($req->getContent(true)));
1072        $this->assertEquals('My other content', $req->getContent());
1073    }
1074
1075    /**
1076     * @expectedException \LogicException
1077     * @dataProvider getContentCantBeCalledTwiceWithResourcesProvider
1078     */
1079    public function testGetContentCantBeCalledTwiceWithResources($first, $second)
1080    {
1081        if (\PHP_VERSION_ID >= 50600) {
1082            $this->markTestSkipped('PHP >= 5.6 allows to open php://input several times.');
1083        }
1084
1085        $req = new Request();
1086        $req->getContent($first);
1087        $req->getContent($second);
1088    }
1089
1090    public function getContentCantBeCalledTwiceWithResourcesProvider()
1091    {
1092        return array(
1093            'Resource then fetch' => array(true, false),
1094            'Resource then resource' => array(true, true),
1095        );
1096    }
1097
1098    /**
1099     * @dataProvider getContentCanBeCalledTwiceWithResourcesProvider
1100     * @requires PHP 5.6
1101     */
1102    public function testGetContentCanBeCalledTwiceWithResources($first, $second)
1103    {
1104        $req = new Request();
1105        $a = $req->getContent($first);
1106        $b = $req->getContent($second);
1107
1108        if ($first) {
1109            $a = stream_get_contents($a);
1110        }
1111
1112        if ($second) {
1113            $b = stream_get_contents($b);
1114        }
1115
1116        $this->assertSame($a, $b);
1117    }
1118
1119    public function getContentCanBeCalledTwiceWithResourcesProvider()
1120    {
1121        return array(
1122            'Fetch then fetch' => array(false, false),
1123            'Fetch then resource' => array(false, true),
1124            'Resource then fetch' => array(true, false),
1125            'Resource then resource' => array(true, true),
1126        );
1127    }
1128
1129    public function provideOverloadedMethods()
1130    {
1131        return array(
1132            array('PUT'),
1133            array('DELETE'),
1134            array('PATCH'),
1135            array('put'),
1136            array('delete'),
1137            array('patch'),
1138        );
1139    }
1140
1141    /**
1142     * @dataProvider provideOverloadedMethods
1143     */
1144    public function testCreateFromGlobals($method)
1145    {
1146        $normalizedMethod = strtoupper($method);
1147
1148        $_GET['foo1'] = 'bar1';
1149        $_POST['foo2'] = 'bar2';
1150        $_COOKIE['foo3'] = 'bar3';
1151        $_FILES['foo4'] = array('bar4');
1152        $_SERVER['foo5'] = 'bar5';
1153
1154        $request = Request::createFromGlobals();
1155        $this->assertEquals('bar1', $request->query->get('foo1'), '::fromGlobals() uses values from $_GET');
1156        $this->assertEquals('bar2', $request->request->get('foo2'), '::fromGlobals() uses values from $_POST');
1157        $this->assertEquals('bar3', $request->cookies->get('foo3'), '::fromGlobals() uses values from $_COOKIE');
1158        $this->assertEquals(array('bar4'), $request->files->get('foo4'), '::fromGlobals() uses values from $_FILES');
1159        $this->assertEquals('bar5', $request->server->get('foo5'), '::fromGlobals() uses values from $_SERVER');
1160
1161        unset($_GET['foo1'], $_POST['foo2'], $_COOKIE['foo3'], $_FILES['foo4'], $_SERVER['foo5']);
1162
1163        $_SERVER['REQUEST_METHOD'] = $method;
1164        $_SERVER['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
1165        $request = RequestContentProxy::createFromGlobals();
1166        $this->assertEquals($normalizedMethod, $request->getMethod());
1167        $this->assertEquals('mycontent', $request->request->get('content'));
1168
1169        unset($_SERVER['REQUEST_METHOD'], $_SERVER['CONTENT_TYPE']);
1170
1171        Request::createFromGlobals();
1172        Request::enableHttpMethodParameterOverride();
1173        $_POST['_method'] = $method;
1174        $_POST['foo6'] = 'bar6';
1175        $_SERVER['REQUEST_METHOD'] = 'PoSt';
1176        $request = Request::createFromGlobals();
1177        $this->assertEquals($normalizedMethod, $request->getMethod());
1178        $this->assertEquals('POST', $request->getRealMethod());
1179        $this->assertEquals('bar6', $request->request->get('foo6'));
1180
1181        unset($_POST['_method'], $_POST['foo6'], $_SERVER['REQUEST_METHOD']);
1182        $this->disableHttpMethodParameterOverride();
1183    }
1184
1185    public function testOverrideGlobals()
1186    {
1187        $request = new Request();
1188        $request->initialize(array('foo' => 'bar'));
1189
1190        // as the Request::overrideGlobals really work, it erase $_SERVER, so we must backup it
1191        $server = $_SERVER;
1192
1193        $request->overrideGlobals();
1194
1195        $this->assertEquals(array('foo' => 'bar'), $_GET);
1196
1197        $request->initialize(array(), array('foo' => 'bar'));
1198        $request->overrideGlobals();
1199
1200        $this->assertEquals(array('foo' => 'bar'), $_POST);
1201
1202        $this->assertArrayNotHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER);
1203
1204        $request->headers->set('X_FORWARDED_PROTO', 'https');
1205
1206        Request::setTrustedProxies(array('1.1.1.1'), Request::HEADER_X_FORWARDED_ALL);
1207        $this->assertFalse($request->isSecure());
1208        $request->server->set('REMOTE_ADDR', '1.1.1.1');
1209        $this->assertTrue($request->isSecure());
1210
1211        $request->overrideGlobals();
1212
1213        $this->assertArrayHasKey('HTTP_X_FORWARDED_PROTO', $_SERVER);
1214
1215        $request->headers->set('CONTENT_TYPE', 'multipart/form-data');
1216        $request->headers->set('CONTENT_LENGTH', 12345);
1217
1218        $request->overrideGlobals();
1219
1220        $this->assertArrayHasKey('CONTENT_TYPE', $_SERVER);
1221        $this->assertArrayHasKey('CONTENT_LENGTH', $_SERVER);
1222
1223        $request->initialize(array('foo' => 'bar', 'baz' => 'foo'));
1224        $request->query->remove('baz');
1225
1226        $request->overrideGlobals();
1227
1228        $this->assertEquals(array('foo' => 'bar'), $_GET);
1229        $this->assertEquals('foo=bar', $_SERVER['QUERY_STRING']);
1230        $this->assertEquals('foo=bar', $request->server->get('QUERY_STRING'));
1231
1232        // restore initial $_SERVER array
1233        $_SERVER = $server;
1234    }
1235
1236    public function testGetScriptName()
1237    {
1238        $request = new Request();
1239        $this->assertEquals('', $request->getScriptName());
1240
1241        $server = array();
1242        $server['SCRIPT_NAME'] = '/index.php';
1243
1244        $request->initialize(array(), array(), array(), array(), array(), $server);
1245
1246        $this->assertEquals('/index.php', $request->getScriptName());
1247
1248        $server = array();
1249        $server['ORIG_SCRIPT_NAME'] = '/frontend.php';
1250        $request->initialize(array(), array(), array(), array(), array(), $server);
1251
1252        $this->assertEquals('/frontend.php', $request->getScriptName());
1253
1254        $server = array();
1255        $server['SCRIPT_NAME'] = '/index.php';
1256        $server['ORIG_SCRIPT_NAME'] = '/frontend.php';
1257        $request->initialize(array(), array(), array(), array(), array(), $server);
1258
1259        $this->assertEquals('/index.php', $request->getScriptName());
1260    }
1261
1262    public function testGetBasePath()
1263    {
1264        $request = new Request();
1265        $this->assertEquals('', $request->getBasePath());
1266
1267        $server = array();
1268        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
1269        $request->initialize(array(), array(), array(), array(), array(), $server);
1270        $this->assertEquals('', $request->getBasePath());
1271
1272        $server = array();
1273        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
1274        $server['SCRIPT_NAME'] = '/index.php';
1275        $request->initialize(array(), array(), array(), array(), array(), $server);
1276
1277        $this->assertEquals('', $request->getBasePath());
1278
1279        $server = array();
1280        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
1281        $server['PHP_SELF'] = '/index.php';
1282        $request->initialize(array(), array(), array(), array(), array(), $server);
1283
1284        $this->assertEquals('', $request->getBasePath());
1285
1286        $server = array();
1287        $server['SCRIPT_FILENAME'] = '/some/where/index.php';
1288        $server['ORIG_SCRIPT_NAME'] = '/index.php';
1289        $request->initialize(array(), array(), array(), array(), array(), $server);
1290
1291        $this->assertEquals('', $request->getBasePath());
1292    }
1293
1294    public function testGetPathInfo()
1295    {
1296        $request = new Request();
1297        $this->assertEquals('/', $request->getPathInfo());
1298
1299        $server = array();
1300        $server['REQUEST_URI'] = '/path/info';
1301        $request->initialize(array(), array(), array(), array(), array(), $server);
1302
1303        $this->assertEquals('/path/info', $request->getPathInfo());
1304
1305        $server = array();
1306        $server['REQUEST_URI'] = '/path%20test/info';
1307        $request->initialize(array(), array(), array(), array(), array(), $server);
1308
1309        $this->assertEquals('/path%20test/info', $request->getPathInfo());
1310
1311        $server = array();
1312        $server['REQUEST_URI'] = '?a=b';
1313        $request->initialize(array(), array(), array(), array(), array(), $server);
1314
1315        $this->assertEquals('/', $request->getPathInfo());
1316    }
1317
1318    public function testGetParameterPrecedence()
1319    {
1320        $request = new Request();
1321        $request->attributes->set('foo', 'attr');
1322        $request->query->set('foo', 'query');
1323        $request->request->set('foo', 'body');
1324
1325        $this->assertSame('attr', $request->get('foo'));
1326
1327        $request->attributes->remove('foo');
1328        $this->assertSame('query', $request->get('foo'));
1329
1330        $request->query->remove('foo');
1331        $this->assertSame('body', $request->get('foo'));
1332
1333        $request->request->remove('foo');
1334        $this->assertNull($request->get('foo'));
1335    }
1336
1337    public function testGetPreferredLanguage()
1338    {
1339        $request = new Request();
1340        $this->assertNull($request->getPreferredLanguage());
1341        $this->assertNull($request->getPreferredLanguage(array()));
1342        $this->assertEquals('fr', $request->getPreferredLanguage(array('fr')));
1343        $this->assertEquals('fr', $request->getPreferredLanguage(array('fr', 'en')));
1344        $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'fr')));
1345        $this->assertEquals('fr-ch', $request->getPreferredLanguage(array('fr-ch', 'fr-fr')));
1346
1347        $request = new Request();
1348        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
1349        $this->assertEquals('en', $request->getPreferredLanguage(array('en', 'en-us')));
1350
1351        $request = new Request();
1352        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
1353        $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en')));
1354
1355        $request = new Request();
1356        $request->headers->set('Accept-language', 'zh, en-us; q=0.8');
1357        $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en')));
1358
1359        $request = new Request();
1360        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, fr-fr; q=0.6, fr; q=0.5');
1361        $this->assertEquals('en', $request->getPreferredLanguage(array('fr', 'en')));
1362    }
1363
1364    public function testIsXmlHttpRequest()
1365    {
1366        $request = new Request();
1367        $this->assertFalse($request->isXmlHttpRequest());
1368
1369        $request->headers->set('X-Requested-With', 'XMLHttpRequest');
1370        $this->assertTrue($request->isXmlHttpRequest());
1371
1372        $request->headers->remove('X-Requested-With');
1373        $this->assertFalse($request->isXmlHttpRequest());
1374    }
1375
1376    /**
1377     * @requires extension intl
1378     */
1379    public function testIntlLocale()
1380    {
1381        $request = new Request();
1382
1383        $request->setDefaultLocale('fr');
1384        $this->assertEquals('fr', $request->getLocale());
1385        $this->assertEquals('fr', \Locale::getDefault());
1386
1387        $request->setLocale('en');
1388        $this->assertEquals('en', $request->getLocale());
1389        $this->assertEquals('en', \Locale::getDefault());
1390
1391        $request->setDefaultLocale('de');
1392        $this->assertEquals('en', $request->getLocale());
1393        $this->assertEquals('en', \Locale::getDefault());
1394    }
1395
1396    public function testGetCharsets()
1397    {
1398        $request = new Request();
1399        $this->assertEquals(array(), $request->getCharsets());
1400        $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6');
1401        $this->assertEquals(array(), $request->getCharsets()); // testing caching
1402
1403        $request = new Request();
1404        $request->headers->set('Accept-Charset', 'ISO-8859-1, US-ASCII, UTF-8; q=0.8, ISO-10646-UCS-2; q=0.6');
1405        $this->assertEquals(array('ISO-8859-1', 'US-ASCII', 'UTF-8', 'ISO-10646-UCS-2'), $request->getCharsets());
1406
1407        $request = new Request();
1408        $request->headers->set('Accept-Charset', 'ISO-8859-1,utf-8;q=0.7,*;q=0.7');
1409        $this->assertEquals(array('ISO-8859-1', 'utf-8', '*'), $request->getCharsets());
1410    }
1411
1412    public function testGetEncodings()
1413    {
1414        $request = new Request();
1415        $this->assertEquals(array(), $request->getEncodings());
1416        $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch');
1417        $this->assertEquals(array(), $request->getEncodings()); // testing caching
1418
1419        $request = new Request();
1420        $request->headers->set('Accept-Encoding', 'gzip,deflate,sdch');
1421        $this->assertEquals(array('gzip', 'deflate', 'sdch'), $request->getEncodings());
1422
1423        $request = new Request();
1424        $request->headers->set('Accept-Encoding', 'gzip;q=0.4,deflate;q=0.9,compress;q=0.7');
1425        $this->assertEquals(array('deflate', 'compress', 'gzip'), $request->getEncodings());
1426    }
1427
1428    public function testGetAcceptableContentTypes()
1429    {
1430        $request = new Request();
1431        $this->assertEquals(array(), $request->getAcceptableContentTypes());
1432        $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*');
1433        $this->assertEquals(array(), $request->getAcceptableContentTypes()); // testing caching
1434
1435        $request = new Request();
1436        $request->headers->set('Accept', 'application/vnd.wap.wmlscriptc, text/vnd.wap.wml, application/vnd.wap.xhtml+xml, application/xhtml+xml, text/html, multipart/mixed, */*');
1437        $this->assertEquals(array('application/vnd.wap.wmlscriptc', 'text/vnd.wap.wml', 'application/vnd.wap.xhtml+xml', 'application/xhtml+xml', 'text/html', 'multipart/mixed', '*/*'), $request->getAcceptableContentTypes());
1438    }
1439
1440    public function testGetLanguages()
1441    {
1442        $request = new Request();
1443        $this->assertEquals(array(), $request->getLanguages());
1444
1445        $request = new Request();
1446        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
1447        $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages());
1448        $this->assertEquals(array('zh', 'en_US', 'en'), $request->getLanguages());
1449
1450        $request = new Request();
1451        $request->headers->set('Accept-language', 'zh, en-us; q=0.6, en; q=0.8');
1452        $this->assertEquals(array('zh', 'en', 'en_US'), $request->getLanguages()); // Test out of order qvalues
1453
1454        $request = new Request();
1455        $request->headers->set('Accept-language', 'zh, en, en-us');
1456        $this->assertEquals(array('zh', 'en', 'en_US'), $request->getLanguages()); // Test equal weighting without qvalues
1457
1458        $request = new Request();
1459        $request->headers->set('Accept-language', 'zh; q=0.6, en, en-us; q=0.6');
1460        $this->assertEquals(array('en', 'zh', 'en_US'), $request->getLanguages()); // Test equal weighting with qvalues
1461
1462        $request = new Request();
1463        $request->headers->set('Accept-language', 'zh, i-cherokee; q=0.6');
1464        $this->assertEquals(array('zh', 'cherokee'), $request->getLanguages());
1465    }
1466
1467    public function testGetRequestFormat()
1468    {
1469        $request = new Request();
1470        $this->assertEquals('html', $request->getRequestFormat());
1471
1472        // Ensure that setting different default values over time is possible,
1473        // aka. setRequestFormat determines the state.
1474        $this->assertEquals('json', $request->getRequestFormat('json'));
1475        $this->assertEquals('html', $request->getRequestFormat('html'));
1476
1477        $request = new Request();
1478        $this->assertNull($request->getRequestFormat(null));
1479
1480        $request = new Request();
1481        $this->assertNull($request->setRequestFormat('foo'));
1482        $this->assertEquals('foo', $request->getRequestFormat(null));
1483
1484        $request = new Request(array('_format' => 'foo'));
1485        $this->assertEquals('html', $request->getRequestFormat());
1486    }
1487
1488    public function testHasSession()
1489    {
1490        $request = new Request();
1491
1492        $this->assertFalse($request->hasSession());
1493        $request->setSession(new Session(new MockArraySessionStorage()));
1494        $this->assertTrue($request->hasSession());
1495    }
1496
1497    public function testGetSession()
1498    {
1499        $request = new Request();
1500
1501        $request->setSession(new Session(new MockArraySessionStorage()));
1502        $this->assertTrue($request->hasSession());
1503
1504        $session = $request->getSession();
1505        $this->assertObjectHasAttribute('storage', $session);
1506        $this->assertObjectHasAttribute('flashName', $session);
1507        $this->assertObjectHasAttribute('attributeName', $session);
1508    }
1509
1510    public function testHasPreviousSession()
1511    {
1512        $request = new Request();
1513
1514        $this->assertFalse($request->hasPreviousSession());
1515        $request->cookies->set('MOCKSESSID', 'foo');
1516        $this->assertFalse($request->hasPreviousSession());
1517        $request->setSession(new Session(new MockArraySessionStorage()));
1518        $this->assertTrue($request->hasPreviousSession());
1519    }
1520
1521    public function testToString()
1522    {
1523        $request = new Request();
1524
1525        $request->headers->set('Accept-language', 'zh, en-us; q=0.8, en; q=0.6');
1526        $request->cookies->set('Foo', 'Bar');
1527
1528        $asString = (string) $request;
1529
1530        $this->assertContains('Accept-Language: zh, en-us; q=0.8, en; q=0.6', $asString);
1531        $this->assertContains('Cookie: Foo=Bar', $asString);
1532
1533        $request->cookies->set('Another', 'Cookie');
1534
1535        $asString = (string) $request;
1536
1537        $this->assertContains('Cookie: Foo=Bar; Another=Cookie', $asString);
1538    }
1539
1540    public function testIsMethod()
1541    {
1542        $request = new Request();
1543        $request->setMethod('POST');
1544        $this->assertTrue($request->isMethod('POST'));
1545        $this->assertTrue($request->isMethod('post'));
1546        $this->assertFalse($request->isMethod('GET'));
1547        $this->assertFalse($request->isMethod('get'));
1548
1549        $request->setMethod('GET');
1550        $this->assertTrue($request->isMethod('GET'));
1551        $this->assertTrue($request->isMethod('get'));
1552        $this->assertFalse($request->isMethod('POST'));
1553        $this->assertFalse($request->isMethod('post'));
1554    }
1555
1556    /**
1557     * @dataProvider getBaseUrlData
1558     */
1559    public function testGetBaseUrl($uri, $server, $expectedBaseUrl, $expectedPathInfo)
1560    {
1561        $request = Request::create($uri, 'GET', array(), array(), array(), $server);
1562
1563        $this->assertSame($expectedBaseUrl, $request->getBaseUrl(), 'baseUrl');
1564        $this->assertSame($expectedPathInfo, $request->getPathInfo(), 'pathInfo');
1565    }
1566
1567    public function getBaseUrlData()
1568    {
1569        return array(
1570            array(
1571                '/fruit/strawberry/1234index.php/blah',
1572                array(
1573                    'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/fruit/index.php',
1574                    'SCRIPT_NAME' => '/fruit/index.php',
1575                    'PHP_SELF' => '/fruit/index.php',
1576                ),
1577                '/fruit',
1578                '/strawberry/1234index.php/blah',
1579            ),
1580            array(
1581                '/fruit/strawberry/1234index.php/blah',
1582                array(
1583                    'SCRIPT_FILENAME' => 'E:/Sites/cc-new/public_html/index.php',
1584                    'SCRIPT_NAME' => '/index.php',
1585                    'PHP_SELF' => '/index.php',
1586                ),
1587                '',
1588                '/fruit/strawberry/1234index.php/blah',
1589            ),
1590            array(
1591                '/foo%20bar/',
1592                array(
1593                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
1594                    'SCRIPT_NAME' => '/foo bar/app.php',
1595                    'PHP_SELF' => '/foo bar/app.php',
1596                ),
1597                '/foo%20bar',
1598                '/',
1599            ),
1600            array(
1601                '/foo%20bar/home',
1602                array(
1603                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
1604                    'SCRIPT_NAME' => '/foo bar/app.php',
1605                    'PHP_SELF' => '/foo bar/app.php',
1606                ),
1607                '/foo%20bar',
1608                '/home',
1609            ),
1610            array(
1611                '/foo%20bar/app.php/home',
1612                array(
1613                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
1614                    'SCRIPT_NAME' => '/foo bar/app.php',
1615                    'PHP_SELF' => '/foo bar/app.php',
1616                ),
1617                '/foo%20bar/app.php',
1618                '/home',
1619            ),
1620            array(
1621                '/foo%20bar/app.php/home%3Dbaz',
1622                array(
1623                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo bar/app.php',
1624                    'SCRIPT_NAME' => '/foo bar/app.php',
1625                    'PHP_SELF' => '/foo bar/app.php',
1626                ),
1627                '/foo%20bar/app.php',
1628                '/home%3Dbaz',
1629            ),
1630            array(
1631                '/foo/bar+baz',
1632                array(
1633                    'SCRIPT_FILENAME' => '/home/John Doe/public_html/foo/app.php',
1634                    'SCRIPT_NAME' => '/foo/app.php',
1635                    'PHP_SELF' => '/foo/app.php',
1636                ),
1637                '/foo',
1638                '/bar+baz',
1639            ),
1640        );
1641    }
1642
1643    /**
1644     * @dataProvider urlencodedStringPrefixData
1645     */
1646    public function testUrlencodedStringPrefix($string, $prefix, $expect)
1647    {
1648        $request = new Request();
1649
1650        $me = new \ReflectionMethod($request, 'getUrlencodedPrefix');
1651        $me->setAccessible(true);
1652
1653        $this->assertSame($expect, $me->invoke($request, $string, $prefix));
1654    }
1655
1656    public function urlencodedStringPrefixData()
1657    {
1658        return array(
1659            array('foo', 'foo', 'foo'),
1660            array('fo%6f', 'foo', 'fo%6f'),
1661            array('foo/bar', 'foo', 'foo'),
1662            array('fo%6f/bar', 'foo', 'fo%6f'),
1663            array('f%6f%6f/bar', 'foo', 'f%6f%6f'),
1664            array('%66%6F%6F/bar', 'foo', '%66%6F%6F'),
1665            array('fo+o/bar', 'fo+o', 'fo+o'),
1666            array('fo%2Bo/bar', 'fo+o', 'fo%2Bo'),
1667        );
1668    }
1669
1670    private function disableHttpMethodParameterOverride()
1671    {
1672        $class = new \ReflectionClass('Symfony\\Component\\HttpFoundation\\Request');
1673        $property = $class->getProperty('httpMethodParameterOverride');
1674        $property->setAccessible(true);
1675        $property->setValue(false);
1676    }
1677
1678    private function getRequestInstanceForClientIpTests($remoteAddr, $httpForwardedFor, $trustedProxies)
1679    {
1680        $request = new Request();
1681
1682        $server = array('REMOTE_ADDR' => $remoteAddr);
1683        if (null !== $httpForwardedFor) {
1684            $server['HTTP_X_FORWARDED_FOR'] = $httpForwardedFor;
1685        }
1686
1687        if ($trustedProxies) {
1688            Request::setTrustedProxies($trustedProxies, Request::HEADER_X_FORWARDED_ALL);
1689        }
1690
1691        $request->initialize(array(), array(), array(), array(), array(), $server);
1692
1693        return $request;
1694    }
1695
1696    private function getRequestInstanceForClientIpsForwardedTests($remoteAddr, $httpForwarded, $trustedProxies)
1697    {
1698        $request = new Request();
1699
1700        $server = array('REMOTE_ADDR' => $remoteAddr);
1701
1702        if (null !== $httpForwarded) {
1703            $server['HTTP_FORWARDED'] = $httpForwarded;
1704        }
1705
1706        if ($trustedProxies) {
1707            Request::setTrustedProxies($trustedProxies, Request::HEADER_FORWARDED);
1708        }
1709
1710        $request->initialize(array(), array(), array(), array(), array(), $server);
1711
1712        return $request;
1713    }
1714
1715    public function testTrustedProxiesXForwardedFor()
1716    {
1717        $request = Request::create('http://example.com/');
1718        $request->server->set('REMOTE_ADDR', '3.3.3.3');
1719        $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2');
1720        $request->headers->set('X_FORWARDED_HOST', 'foo.example.com:1234, real.example.com:8080');
1721        $request->headers->set('X_FORWARDED_PROTO', 'https');
1722        $request->headers->set('X_FORWARDED_PORT', 443);
1723
1724        // no trusted proxies
1725        $this->assertEquals('3.3.3.3', $request->getClientIp());
1726        $this->assertEquals('example.com', $request->getHost());
1727        $this->assertEquals(80, $request->getPort());
1728        $this->assertFalse($request->isSecure());
1729
1730        // disabling proxy trusting
1731        Request::setTrustedProxies(array(), Request::HEADER_X_FORWARDED_ALL);
1732        $this->assertEquals('3.3.3.3', $request->getClientIp());
1733        $this->assertEquals('example.com', $request->getHost());
1734        $this->assertEquals(80, $request->getPort());
1735        $this->assertFalse($request->isSecure());
1736
1737        // request is forwarded by a non-trusted proxy
1738        Request::setTrustedProxies(array('2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
1739        $this->assertEquals('3.3.3.3', $request->getClientIp());
1740        $this->assertEquals('example.com', $request->getHost());
1741        $this->assertEquals(80, $request->getPort());
1742        $this->assertFalse($request->isSecure());
1743
1744        // trusted proxy via setTrustedProxies()
1745        Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
1746        $this->assertEquals('1.1.1.1', $request->getClientIp());
1747        $this->assertEquals('foo.example.com', $request->getHost());
1748        $this->assertEquals(443, $request->getPort());
1749        $this->assertTrue($request->isSecure());
1750
1751        // trusted proxy via setTrustedProxies()
1752        Request::setTrustedProxies(array('3.3.3.4', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
1753        $this->assertEquals('3.3.3.3', $request->getClientIp());
1754        $this->assertEquals('example.com', $request->getHost());
1755        $this->assertEquals(80, $request->getPort());
1756        $this->assertFalse($request->isSecure());
1757
1758        // check various X_FORWARDED_PROTO header values
1759        Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
1760        $request->headers->set('X_FORWARDED_PROTO', 'ssl');
1761        $this->assertTrue($request->isSecure());
1762
1763        $request->headers->set('X_FORWARDED_PROTO', 'https, http');
1764        $this->assertTrue($request->isSecure());
1765    }
1766
1767    /**
1768     * @group legacy
1769     * @expectedDeprecation The "Symfony\Component\HttpFoundation\Request::setTrustedHeaderName()" method is deprecated since Symfony 3.3 and will be removed in 4.0. Use the $trustedHeaderSet argument of the Request::setTrustedProxies() method instead.
1770     */
1771    public function testLegacyTrustedProxies()
1772    {
1773        $request = Request::create('http://example.com/');
1774        $request->server->set('REMOTE_ADDR', '3.3.3.3');
1775        $request->headers->set('X_FORWARDED_FOR', '1.1.1.1, 2.2.2.2');
1776        $request->headers->set('X_FORWARDED_HOST', 'foo.example.com, real.example.com:8080');
1777        $request->headers->set('X_FORWARDED_PROTO', 'https');
1778        $request->headers->set('X_FORWARDED_PORT', 443);
1779        $request->headers->set('X_MY_FOR', '3.3.3.3, 4.4.4.4');
1780        $request->headers->set('X_MY_HOST', 'my.example.com');
1781        $request->headers->set('X_MY_PROTO', 'http');
1782        $request->headers->set('X_MY_PORT', 81);
1783
1784        Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_X_FORWARDED_ALL);
1785
1786        // custom header names
1787        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_MY_FOR');
1788        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_MY_HOST');
1789        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_MY_PORT');
1790        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_MY_PROTO');
1791        $this->assertEquals('4.4.4.4', $request->getClientIp());
1792        $this->assertEquals('my.example.com', $request->getHost());
1793        $this->assertEquals(81, $request->getPort());
1794        $this->assertFalse($request->isSecure());
1795
1796        // disabling via empty header names
1797        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, null);
1798        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, null);
1799        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, null);
1800        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, null);
1801        $this->assertEquals('3.3.3.3', $request->getClientIp());
1802        $this->assertEquals('example.com', $request->getHost());
1803        $this->assertEquals(80, $request->getPort());
1804        $this->assertFalse($request->isSecure());
1805
1806        //reset
1807        Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED');
1808        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR');
1809        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST');
1810        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT');
1811        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO');
1812    }
1813
1814    public function testTrustedProxiesForwarded()
1815    {
1816        $request = Request::create('http://example.com/');
1817        $request->server->set('REMOTE_ADDR', '3.3.3.3');
1818        $request->headers->set('FORWARDED', 'for=1.1.1.1, host=foo.example.com:8080, proto=https, for=2.2.2.2, host=real.example.com:8080');
1819
1820        // no trusted proxies
1821        $this->assertEquals('3.3.3.3', $request->getClientIp());
1822        $this->assertEquals('example.com', $request->getHost());
1823        $this->assertEquals(80, $request->getPort());
1824        $this->assertFalse($request->isSecure());
1825
1826        // disabling proxy trusting
1827        Request::setTrustedProxies(array(), Request::HEADER_FORWARDED);
1828        $this->assertEquals('3.3.3.3', $request->getClientIp());
1829        $this->assertEquals('example.com', $request->getHost());
1830        $this->assertEquals(80, $request->getPort());
1831        $this->assertFalse($request->isSecure());
1832
1833        // request is forwarded by a non-trusted proxy
1834        Request::setTrustedProxies(array('2.2.2.2'), Request::HEADER_FORWARDED);
1835        $this->assertEquals('3.3.3.3', $request->getClientIp());
1836        $this->assertEquals('example.com', $request->getHost());
1837        $this->assertEquals(80, $request->getPort());
1838        $this->assertFalse($request->isSecure());
1839
1840        // trusted proxy via setTrustedProxies()
1841        Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_FORWARDED);
1842        $this->assertEquals('1.1.1.1', $request->getClientIp());
1843        $this->assertEquals('foo.example.com', $request->getHost());
1844        $this->assertEquals(8080, $request->getPort());
1845        $this->assertTrue($request->isSecure());
1846
1847        // trusted proxy via setTrustedProxies()
1848        Request::setTrustedProxies(array('3.3.3.4', '2.2.2.2'), Request::HEADER_FORWARDED);
1849        $this->assertEquals('3.3.3.3', $request->getClientIp());
1850        $this->assertEquals('example.com', $request->getHost());
1851        $this->assertEquals(80, $request->getPort());
1852        $this->assertFalse($request->isSecure());
1853
1854        // check various X_FORWARDED_PROTO header values
1855        Request::setTrustedProxies(array('3.3.3.3', '2.2.2.2'), Request::HEADER_FORWARDED);
1856        $request->headers->set('FORWARDED', 'proto=ssl');
1857        $this->assertTrue($request->isSecure());
1858
1859        $request->headers->set('FORWARDED', 'proto=https, proto=http');
1860        $this->assertTrue($request->isSecure());
1861    }
1862
1863    /**
1864     * @group legacy
1865     * @expectedException \InvalidArgumentException
1866     */
1867    public function testSetTrustedProxiesInvalidHeaderName()
1868    {
1869        Request::create('http://example.com/');
1870        Request::setTrustedHeaderName('bogus name', 'X_MY_FOR');
1871    }
1872
1873    /**
1874     * @group legacy
1875     * @expectedException \InvalidArgumentException
1876     */
1877    public function testGetTrustedProxiesInvalidHeaderName()
1878    {
1879        Request::create('http://example.com/');
1880        Request::getTrustedHeaderName('bogus name');
1881    }
1882
1883    /**
1884     * @dataProvider iisRequestUriProvider
1885     */
1886    public function testIISRequestUri($headers, $server, $expectedRequestUri)
1887    {
1888        $request = new Request();
1889        $request->headers->replace($headers);
1890        $request->server->replace($server);
1891
1892        $this->assertEquals($expectedRequestUri, $request->getRequestUri(), '->getRequestUri() is correct');
1893
1894        $subRequestUri = '/bar/foo';
1895        $subRequest = Request::create($subRequestUri, 'get', array(), array(), array(), $request->server->all());
1896        $this->assertEquals($subRequestUri, $subRequest->getRequestUri(), '->getRequestUri() is correct in sub request');
1897    }
1898
1899    public function iisRequestUriProvider()
1900    {
1901        return array(
1902            array(
1903                array(
1904                    'X_ORIGINAL_URL' => '/foo/bar',
1905                ),
1906                array(),
1907                '/foo/bar',
1908            ),
1909            array(
1910                array(
1911                    'X_REWRITE_URL' => '/foo/bar',
1912                ),
1913                array(),
1914                '/foo/bar',
1915            ),
1916            array(
1917                array(),
1918                array(
1919                    'IIS_WasUrlRewritten' => '1',
1920                    'UNENCODED_URL' => '/foo/bar',
1921                ),
1922                '/foo/bar',
1923            ),
1924            array(
1925                array(
1926                    'X_ORIGINAL_URL' => '/foo/bar',
1927                ),
1928                array(
1929                    'HTTP_X_ORIGINAL_URL' => '/foo/bar',
1930                ),
1931                '/foo/bar',
1932            ),
1933            array(
1934                array(
1935                    'X_ORIGINAL_URL' => '/foo/bar',
1936                ),
1937                array(
1938                    'IIS_WasUrlRewritten' => '1',
1939                    'UNENCODED_URL' => '/foo/bar',
1940                ),
1941                '/foo/bar',
1942            ),
1943            array(
1944                array(
1945                    'X_ORIGINAL_URL' => '/foo/bar',
1946                ),
1947                array(
1948                    'HTTP_X_ORIGINAL_URL' => '/foo/bar',
1949                    'IIS_WasUrlRewritten' => '1',
1950                    'UNENCODED_URL' => '/foo/bar',
1951                ),
1952                '/foo/bar',
1953            ),
1954            array(
1955                array(),
1956                array(
1957                    'ORIG_PATH_INFO' => '/foo/bar',
1958                ),
1959                '/foo/bar',
1960            ),
1961            array(
1962                array(),
1963                array(
1964                    'ORIG_PATH_INFO' => '/foo/bar',
1965                    'QUERY_STRING' => 'foo=bar',
1966                ),
1967                '/foo/bar?foo=bar',
1968            ),
1969        );
1970    }
1971
1972    public function testTrustedHosts()
1973    {
1974        // create a request
1975        $request = Request::create('/');
1976
1977        // no trusted host set -> no host check
1978        $request->headers->set('host', 'evil.com');
1979        $this->assertEquals('evil.com', $request->getHost());
1980
1981        // add a trusted domain and all its subdomains
1982        Request::setTrustedHosts(array('^([a-z]{9}\.)?trusted\.com$'));
1983
1984        // untrusted host
1985        $request->headers->set('host', 'evil.com');
1986        try {
1987            $request->getHost();
1988            $this->fail('Request::getHost() should throw an exception when host is not trusted.');
1989        } catch (SuspiciousOperationException $e) {
1990            $this->assertEquals('Untrusted Host "evil.com".', $e->getMessage());
1991        }
1992
1993        // trusted hosts
1994        $request->headers->set('host', 'trusted.com');
1995        $this->assertEquals('trusted.com', $request->getHost());
1996        $this->assertEquals(80, $request->getPort());
1997
1998        $request->server->set('HTTPS', true);
1999        $request->headers->set('host', 'trusted.com');
2000        $this->assertEquals('trusted.com', $request->getHost());
2001        $this->assertEquals(443, $request->getPort());
2002        $request->server->set('HTTPS', false);
2003
2004        $request->headers->set('host', 'trusted.com:8000');
2005        $this->assertEquals('trusted.com', $request->getHost());
2006        $this->assertEquals(8000, $request->getPort());
2007
2008        $request->headers->set('host', 'subdomain.trusted.com');
2009        $this->assertEquals('subdomain.trusted.com', $request->getHost());
2010
2011        // reset request for following tests
2012        Request::setTrustedHosts(array());
2013    }
2014
2015    public function testFactory()
2016    {
2017        Request::setFactory(function (array $query = array(), array $request = array(), array $attributes = array(), array $cookies = array(), array $files = array(), array $server = array(), $content = null) {
2018            return new NewRequest();
2019        });
2020
2021        $this->assertEquals('foo', Request::create('/')->getFoo());
2022
2023        Request::setFactory(null);
2024    }
2025
2026    /**
2027     * @dataProvider getLongHostNames
2028     */
2029    public function testVeryLongHosts($host)
2030    {
2031        $start = microtime(true);
2032
2033        $request = Request::create('/');
2034        $request->headers->set('host', $host);
2035        $this->assertEquals($host, $request->getHost());
2036        $this->assertLessThan(5, microtime(true) - $start);
2037    }
2038
2039    /**
2040     * @dataProvider getHostValidities
2041     */
2042    public function testHostValidity($host, $isValid, $expectedHost = null, $expectedPort = null)
2043    {
2044        $request = Request::create('/');
2045        $request->headers->set('host', $host);
2046
2047        if ($isValid) {
2048            $this->assertSame($expectedHost ?: $host, $request->getHost());
2049            if ($expectedPort) {
2050                $this->assertSame($expectedPort, $request->getPort());
2051            }
2052        } else {
2053            if (method_exists($this, 'expectException')) {
2054                $this->expectException(SuspiciousOperationException::class);
2055                $this->expectExceptionMessage('Invalid Host');
2056            } else {
2057                $this->setExpectedException(SuspiciousOperationException::class, 'Invalid Host');
2058            }
2059
2060            $request->getHost();
2061        }
2062    }
2063
2064    public function getHostValidities()
2065    {
2066        return array(
2067            array('.a', false),
2068            array('a..', false),
2069            array('a.', true),
2070            array("\xE9", false),
2071            array('[::1]', true),
2072            array('[::1]:80', true, '[::1]', 80),
2073            array(str_repeat('.', 101), false),
2074        );
2075    }
2076
2077    public function getLongHostNames()
2078    {
2079        return array(
2080            array('a'.str_repeat('.a', 40000)),
2081            array(str_repeat(':', 101)),
2082        );
2083    }
2084
2085    /**
2086     * @dataProvider methodIdempotentProvider
2087     */
2088    public function testMethodIdempotent($method, $idempotent)
2089    {
2090        $request = new Request();
2091        $request->setMethod($method);
2092        $this->assertEquals($idempotent, $request->isMethodIdempotent());
2093    }
2094
2095    public function methodIdempotentProvider()
2096    {
2097        return array(
2098            array('HEAD', true),
2099            array('GET', true),
2100            array('POST', false),
2101            array('PUT', true),
2102            array('PATCH', false),
2103            array('DELETE', true),
2104            array('PURGE', true),
2105            array('OPTIONS', true),
2106            array('TRACE', true),
2107            array('CONNECT', false),
2108        );
2109    }
2110
2111    /**
2112     * @dataProvider methodSafeProvider
2113     */
2114    public function testMethodSafe($method, $safe)
2115    {
2116        $request = new Request();
2117        $request->setMethod($method);
2118        $this->assertEquals($safe, $request->isMethodSafe(false));
2119    }
2120
2121    public function methodSafeProvider()
2122    {
2123        return array(
2124            array('HEAD', true),
2125            array('GET', true),
2126            array('POST', false),
2127            array('PUT', false),
2128            array('PATCH', false),
2129            array('DELETE', false),
2130            array('PURGE', false),
2131            array('OPTIONS', true),
2132            array('TRACE', true),
2133            array('CONNECT', false),
2134        );
2135    }
2136
2137    /**
2138     * @group legacy
2139     * @expectedDeprecation Checking only for cacheable HTTP methods with Symfony\Component\HttpFoundation\Request::isMethodSafe() is deprecated since Symfony 3.2 and will throw an exception in 4.0. Disable checking only for cacheable methods by calling the method with `false` as first argument or use the Request::isMethodCacheable() instead.
2140     */
2141    public function testMethodSafeChecksCacheable()
2142    {
2143        $request = new Request();
2144        $request->setMethod('OPTIONS');
2145        $this->assertFalse($request->isMethodSafe());
2146    }
2147
2148    /**
2149     * @dataProvider methodCacheableProvider
2150     */
2151    public function testMethodCacheable($method, $chacheable)
2152    {
2153        $request = new Request();
2154        $request->setMethod($method);
2155        $this->assertEquals($chacheable, $request->isMethodCacheable());
2156    }
2157
2158    public function methodCacheableProvider()
2159    {
2160        return array(
2161            array('HEAD', true),
2162            array('GET', true),
2163            array('POST', false),
2164            array('PUT', false),
2165            array('PATCH', false),
2166            array('DELETE', false),
2167            array('PURGE', false),
2168            array('OPTIONS', false),
2169            array('TRACE', false),
2170            array('CONNECT', false),
2171        );
2172    }
2173
2174    /**
2175     * @group legacy
2176     */
2177    public function testGetTrustedHeaderName()
2178    {
2179        Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_X_FORWARDED_ALL);
2180
2181        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
2182        $this->assertSame('X_FORWARDED_FOR', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
2183        $this->assertSame('X_FORWARDED_HOST', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
2184        $this->assertSame('X_FORWARDED_PORT', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
2185        $this->assertSame('X_FORWARDED_PROTO', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
2186
2187        Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED);
2188
2189        $this->assertSame('FORWARDED', Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
2190        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
2191        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
2192        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
2193        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
2194
2195        Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'A');
2196        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, '');
2197        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'C');
2198        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'D');
2199        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'E');
2200
2201        Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED);
2202
2203        $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
2204        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
2205        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
2206        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
2207        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
2208
2209        Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_X_FORWARDED_ALL);
2210
2211        $this->assertNull(Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
2212        $this->assertSame('', Request::getTrustedHeaderName(Request::HEADER_CLIENT_IP));
2213        $this->assertSame('C', Request::getTrustedHeaderName(Request::HEADER_CLIENT_HOST));
2214        $this->assertSame('D', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PORT));
2215        $this->assertSame('E', Request::getTrustedHeaderName(Request::HEADER_CLIENT_PROTO));
2216
2217        Request::setTrustedProxies(array('8.8.8.8'), Request::HEADER_FORWARDED);
2218
2219        $this->assertSame('A', Request::getTrustedHeaderName(Request::HEADER_FORWARDED));
2220
2221        //reset
2222        Request::setTrustedHeaderName(Request::HEADER_FORWARDED, 'FORWARDED');
2223        Request::setTrustedHeaderName(Request::HEADER_CLIENT_IP, 'X_FORWARDED_FOR');
2224        Request::setTrustedHeaderName(Request::HEADER_CLIENT_HOST, 'X_FORWARDED_HOST');
2225        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PORT, 'X_FORWARDED_PORT');
2226        Request::setTrustedHeaderName(Request::HEADER_CLIENT_PROTO, 'X_FORWARDED_PROTO');
2227    }
2228
2229    /**
2230     * @dataProvider protocolVersionProvider
2231     */
2232    public function testProtocolVersion($serverProtocol, $trustedProxy, $via, $expected)
2233    {
2234        if ($trustedProxy) {
2235            Request::setTrustedProxies(array('1.1.1.1'), -1);
2236        }
2237
2238        $request = new Request();
2239        $request->server->set('SERVER_PROTOCOL', $serverProtocol);
2240        $request->server->set('REMOTE_ADDR', '1.1.1.1');
2241        $request->headers->set('Via', $via);
2242
2243        $this->assertSame($expected, $request->getProtocolVersion());
2244    }
2245
2246    public function protocolVersionProvider()
2247    {
2248        return array(
2249            'untrusted without via' => array('HTTP/2.0', false, '', 'HTTP/2.0'),
2250            'untrusted with via' => array('HTTP/2.0', false, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/2.0'),
2251            'trusted without via' => array('HTTP/2.0', true, '', 'HTTP/2.0'),
2252            'trusted with via' => array('HTTP/2.0', true, '1.0 fred, 1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'),
2253            'trusted with via and protocol name' => array('HTTP/2.0', true, 'HTTP/1.0 fred, HTTP/1.1 nowhere.com (Apache/1.1)', 'HTTP/1.0'),
2254            'trusted with broken via' => array('HTTP/2.0', true, 'HTTP/1^0 foo', 'HTTP/2.0'),
2255            'trusted with partially-broken via' => array('HTTP/2.0', true, '1.0 fred, foo', 'HTTP/1.0'),
2256        );
2257    }
2258
2259    public function nonstandardRequestsData()
2260    {
2261        return array(
2262            array('',  '', '/', 'http://host:8080/', ''),
2263            array('/', '', '/', 'http://host:8080/', ''),
2264
2265            array('hello/app.php/x',  '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'),
2266            array('/hello/app.php/x', '', '/x', 'http://host:8080/hello/app.php/x', '/hello', '/hello/app.php'),
2267
2268            array('',      'a=b', '/', 'http://host:8080/?a=b'),
2269            array('?a=b',  'a=b', '/', 'http://host:8080/?a=b'),
2270            array('/?a=b', 'a=b', '/', 'http://host:8080/?a=b'),
2271
2272            array('x',      'a=b', '/x', 'http://host:8080/x?a=b'),
2273            array('x?a=b',  'a=b', '/x', 'http://host:8080/x?a=b'),
2274            array('/x?a=b', 'a=b', '/x', 'http://host:8080/x?a=b'),
2275
2276            array('hello/x',  '', '/x', 'http://host:8080/hello/x', '/hello'),
2277            array('/hello/x', '', '/x', 'http://host:8080/hello/x', '/hello'),
2278
2279            array('hello/app.php/x',      'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'),
2280            array('hello/app.php/x?a=b',  'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'),
2281            array('/hello/app.php/x?a=b', 'a=b', '/x', 'http://host:8080/hello/app.php/x?a=b', '/hello', '/hello/app.php'),
2282        );
2283    }
2284
2285    /**
2286     * @dataProvider nonstandardRequestsData
2287     */
2288    public function testNonstandardRequests($requestUri, $queryString, $expectedPathInfo, $expectedUri, $expectedBasePath = '', $expectedBaseUrl = null)
2289    {
2290        if (null === $expectedBaseUrl) {
2291            $expectedBaseUrl = $expectedBasePath;
2292        }
2293
2294        $server = array(
2295            'HTTP_HOST' => 'host:8080',
2296            'SERVER_PORT' => '8080',
2297            'QUERY_STRING' => $queryString,
2298            'PHP_SELF' => '/hello/app.php',
2299            'SCRIPT_FILENAME' => '/some/path/app.php',
2300            'REQUEST_URI' => $requestUri,
2301        );
2302
2303        $request = new Request(array(), array(), array(), array(), array(), $server);
2304
2305        $this->assertEquals($expectedPathInfo, $request->getPathInfo());
2306        $this->assertEquals($expectedUri, $request->getUri());
2307        $this->assertEquals($queryString, $request->getQueryString());
2308        $this->assertEquals(8080, $request->getPort());
2309        $this->assertEquals('host:8080', $request->getHttpHost());
2310        $this->assertEquals($expectedBaseUrl, $request->getBaseUrl());
2311        $this->assertEquals($expectedBasePath, $request->getBasePath());
2312    }
2313}
2314
2315class RequestContentProxy extends Request
2316{
2317    public function getContent($asResource = false)
2318    {
2319        return http_build_query(array('_method' => 'PUT', 'content' => 'mycontent'));
2320    }
2321}
2322
2323class NewRequest extends Request
2324{
2325    public function getFoo()
2326    {
2327        return 'foo';
2328    }
2329}
2330