Completed
Push — master ( 5591c4...1f9686 )
by Daniel
37:10 queued 28:40
created

HTTPTest::testDeprecatedVaryHandling()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 8
nc 1
nop 0
dl 0
loc 12
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Control\Tests;
4
5
use SilverStripe\Control\Controller;
6
use SilverStripe\Control\Director;
7
use SilverStripe\Control\HTTP;
8
use SilverStripe\Control\HTTPRequest;
9
use SilverStripe\Control\HTTPResponse;
10
use SilverStripe\Control\Middleware\HTTPCacheControlMiddleware;
11
use SilverStripe\Control\Session;
12
use SilverStripe\Core\Config\Config;
13
use SilverStripe\Dev\FunctionalTest;
14
15
/**
16
 * Tests the {@link HTTP} class
17
 *
18
 * @skipUpgrade
19
 */
20
class HTTPTest extends FunctionalTest
21
{
22
    protected function setUp()
23
    {
24
        parent::setUp();
25
        // Set to disabled at null forcing level
26
        HTTPCacheControlMiddleware::config()
27
            ->set('defaultState', 'disabled')
28
            ->set('defaultForcingLevel', 0);
29
        HTTPCacheControlMiddleware::reset();
30
    }
31
32
    public function testAddCacheHeaders()
33
    {
34
        $body = "<html><head></head><body><h1>Mysite</h1></body></html>";
35
        $response = new HTTPResponse($body, 200);
36
        HTTPCacheControlMiddleware::singleton()->publicCache();
37
        HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
38
39
        $this->addCacheHeaders($response);
40
        $this->assertNotEmpty($response->getHeader('Cache-Control'));
41
42
        // Ensure cache headers are set correctly when disabled via config (e.g. when dev)
43
        HTTPCacheControlMiddleware::config()
44
            ->set('defaultState', 'disabled')
45
            ->set('defaultForcingLevel', HTTPCacheControlMiddleware::LEVEL_DISABLED);
46
        HTTPCacheControlMiddleware::reset();
47
        HTTPCacheControlMiddleware::singleton()->publicCache();
48
        HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
49
        $response = new HTTPResponse($body, 200);
50
        $this->addCacheHeaders($response);
51
        $this->assertContains('no-cache', $response->getHeader('Cache-Control'));
52
        $this->assertContains('no-store', $response->getHeader('Cache-Control'));
53
        $this->assertContains('must-revalidate', $response->getHeader('Cache-Control'));
54
55
        // Ensure max-age setting is respected in production.
56
        HTTPCacheControlMiddleware::config()
57
            ->set('defaultState', 'disabled')
58
            ->set('defaultForcingLevel', 0);
59
        HTTPCacheControlMiddleware::reset();
60
        HTTPCacheControlMiddleware::singleton()->publicCache();
61
        HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
62
        $response = new HTTPResponse($body, 200);
63
        $this->addCacheHeaders($response);
64
        $this->assertContains('max-age=30', $response->getHeader('Cache-Control'));
65
        $this->assertNotContains('max-age=0', $response->getHeader('Cache-Control'));
66
67
        // Still "live": Ensure header's aren't overridden if already set (using purposefully different values).
68
        $headers = array(
69
            'Vary' => '*',
70
            'Pragma' => 'no-cache',
71
            'Cache-Control' => 'max-age=0, no-cache, no-store',
72
        );
73
        foreach ($headers as $header => $value) {
74
            $response->addHeader($header, $value);
75
        }
76
        HTTPCacheControlMiddleware::reset();
77
        HTTPCacheControlMiddleware::singleton()->publicCache();
78
        HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
79
        $this->addCacheHeaders($response);
80
        foreach ($headers as $header => $value) {
81
            $this->assertEquals($value, $response->getHeader($header));
82
        }
83
    }
84
85
    public function testConfigVary()
86
    {
87
        $body = "<html><head></head><body><h1>Mysite</h1></body></html>";
88
        $response = new HTTPResponse($body, 200);
89
        HTTPCacheControlMiddleware::singleton()
90
            ->setMaxAge(30)
91
            ->setVary('X-Requested-With, X-Forwarded-Protocol');
92
        $this->addCacheHeaders($response);
93
94
        // Vary set properly
95
        $v = $response->getHeader('Vary');
96
        $this->assertContains("X-Forwarded-Protocol", $v);
97
        $this->assertContains("X-Requested-With", $v);
98
        $this->assertNotContains("Cookie", $v);
99
        $this->assertNotContains("User-Agent", $v);
100
        $this->assertNotContains("Accept", $v);
101
102
        // No vary
103
        HTTPCacheControlMiddleware::singleton()
104
            ->setMaxAge(30)
105
            ->setVary(null);
106
        HTTPCacheControlMiddleware::reset();
107
        HTTPCacheControlMiddleware::config()
108
            ->set('defaultVary', []);
109
110
        $response = new HTTPResponse($body, 200);
111
        $this->addCacheHeaders($response);
112
        $v = $response->getHeader('Vary');
113
        $this->assertEmpty($v);
114
    }
115
116
    public function testDeprecatedVaryHandling()
117
    {
118
        /** @var Config */
119
        Config::modify()->set(
0 ignored issues
show
Bug introduced by
The method set() does not exist on SilverStripe\Core\Config\Config. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
        Config::modify()->/** @scrutinizer ignore-call */ set(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
120
            HTTP::class,
121
            'vary',
122
            'X-Foo'
123
        );
124
        $response = new HTTPResponse('', 200);
125
        $this->addCacheHeaders($response);
126
        $header = $response->getHeader('Vary');
127
        $this->assertContains('X-Foo', $header);
128
    }
129
130
    public function testDeprecatedCacheControlHandling()
131
    {
132
        HTTPCacheControlMiddleware::singleton()->publicCache();
133
134
        /** @var Config */
135
        Config::modify()->set(
136
            HTTP::class,
137
            'cache_control',
138
            [
139
                'no-store' => true,
140
                'no-cache' => true,
141
            ]
142
        );
143
        $response = new HTTPResponse('', 200);
144
        $this->addCacheHeaders($response);
145
        $header = $response->getHeader('Cache-Control');
146
        $this->assertContains('no-store', $header);
147
        $this->assertContains('no-cache', $header);
148
    }
149
150
    public function testDeprecatedCacheControlHandlingOnMaxAge()
151
    {
152
        HTTPCacheControlMiddleware::singleton()->publicCache();
153
154
        /** @var Config */
155
        Config::modify()->set(
156
            HTTP::class,
157
            'cache_control',
158
            [
159
                // Needs to be separate from no-cache and no-store,
160
                // since that would unset max-age
161
                'max-age' => 99,
162
            ]
163
        );
164
        $response = new HTTPResponse('', 200);
165
        $this->addCacheHeaders($response);
166
        $header = $response->getHeader('Cache-Control');
167
        $this->assertContains('max-age=99', $header);
168
    }
169
170
    /**
171
     * @expectedException \LogicException
172
     * @expectedExceptionMessageRegExp /Found unsupported legacy directives in HTTP\.cache_control: unknown/
173
     */
174
    public function testDeprecatedCacheControlHandlingThrowsWithUnknownDirectives()
175
    {
176
        /** @var Config */
177
        Config::modify()->set(
178
            HTTP::class,
179
            'cache_control',
180
            [
181
                'no-store' => true,
182
                'unknown' => true,
183
            ]
184
        );
185
        $response = new HTTPResponse('', 200);
186
        $this->addCacheHeaders($response);
187
    }
188
189
    /**
190
     * Tests {@link HTTP::getLinksIn()}
191
     */
192
    public function testGetLinksIn()
193
    {
194
        $content = '
195
			<h2><a href="/">My Cool Site</a></h2>
196
197
			<p>
198
				A boy went <a href="home/">home</a> to see his <span><a href="mother/">mother</a></span>. This
199
				involved a short <a href="$Journey">journey</a>, as well as some <a href="space travel">space travel</a>
200
				and <a href=unquoted>unquoted</a> events, as well as a <a href=\'single quote\'>single quote</a> from
201
				his <a href="/father">father</a>.
202
			</p>
203
204
			<p>
205
				There were also some elements with extra <a class=attribute href=\'attributes\'>attributes</a> which
206
				played a part in his <a href=journey"extra id="JourneyLink">journey</a>. HE ALSO DISCOVERED THE
207
				<A HREF="CAPS LOCK">KEY</a>. Later he got his <a href="quotes \'mixed\' up">mixed up</a>.
208
			</p>
209
		';
210
211
        $expected = array (
212
            '/', 'home/', 'mother/', '$Journey', 'space travel', 'unquoted', 'single quote', '/father', 'attributes',
213
            'journey', 'CAPS LOCK', 'quotes \'mixed\' up'
214
        );
215
216
        $result = HTTP::getLinksIn($content);
217
218
        // Results don't neccesarily come out in the order they are in the $content param.
219
        sort($result);
220
        sort($expected);
221
222
        $this->assertTrue(is_array($result));
223
        $this->assertEquals($expected, $result, 'Test that all links within the content are found.');
224
    }
225
226
    /**
227
     * Tests {@link HTTP::setGetVar()}
228
     */
229
    public function testSetGetVar()
230
    {
231
        // Hackery to work around volatile URL formats in test invocation,
232
        // and the inability of Director::absoluteBaseURL() to produce consistent URLs.
233
        Director::mockRequest(function (HTTPRequest $request) {
234
            $controller = new Controller();
235
            $controller->setRequest($request);
236
            $controller->pushCurrent();
237
            try {
238
                $this->assertContains(
239
                    'relative/url?foo=bar',
240
                    HTTP::setGetVar('foo', 'bar'),
241
                    'Omitting a URL falls back to current URL'
242
                );
243
            } finally {
244
                $controller->popCurrent();
245
            }
246
        }, 'relative/url/');
247
248
        $this->assertEquals(
249
            'relative/url?foo=bar',
250
            HTTP::setGetVar('foo', 'bar', 'relative/url'),
251
            'Relative URL without existing query params'
252
        );
253
254
        $this->assertEquals(
255
            'relative/url?baz=buz&foo=bar',
256
            HTTP::setGetVar('foo', 'bar', '/relative/url?baz=buz'),
257
            'Relative URL with existing query params, and new added key'
258
        );
259
260
        $this->assertEquals(
261
            'http://test.com/?foo=new&buz=baz',
262
            HTTP::setGetVar('foo', 'new', 'http://test.com/?foo=old&buz=baz'),
263
            'Absolute URL without path and multipe existing query params, overwriting an existing parameter'
264
        );
265
266
        $this->assertContains(
267
            'http://test.com/?foo=new',
268
            HTTP::setGetVar('foo', 'new', 'http://test.com/?foo=&foo=old'),
269
            'Absolute URL and empty query param'
270
        );
271
        // http_build_query() escapes angular brackets, they should be correctly urldecoded by the browser client
272
        $this->assertEquals(
273
            'http://test.com/?foo%5Btest%5D=one&foo%5Btest%5D=two',
274
            HTTP::setGetVar('foo[test]', 'two', 'http://test.com/?foo[test]=one'),
275
            'Absolute URL and PHP array query string notation'
276
        );
277
278
        $urls = array(
279
            'http://www.test.com:8080',
280
            'http://test.com:3000/',
281
            'http://test.com:3030/baz/',
282
            'http://baz:[email protected]',
283
            'http://[email protected]/',
284
            'http://baz:[email protected]:8080',
285
            'http://[email protected]:8080'
286
        );
287
288
        foreach ($urls as $testURL) {
289
            $this->assertEquals(
290
                $testURL . '?foo=bar',
291
                HTTP::setGetVar('foo', 'bar', $testURL),
292
                'Absolute URL and Port Number'
293
            );
294
        }
295
    }
296
297
    /**
298
     * Test that the the get_mime_type() works correctly
299
     */
300
    public function testGetMimeType()
301
    {
302
        $this->assertEquals('text/plain', HTTP::get_mime_type('file.csv'));
303
        $this->assertEquals('image/gif', HTTP::get_mime_type('file.gif'));
304
        $this->assertEquals('text/html', HTTP::get_mime_type('file.html'));
305
        $this->assertEquals('image/jpeg', HTTP::get_mime_type('file.jpg'));
306
        $this->assertEquals('image/jpeg', HTTP::get_mime_type('upperfile.JPG'));
307
        $this->assertEquals('image/png', HTTP::get_mime_type('file.png'));
308
        $this->assertEquals(
309
            'image/vnd.adobe.photoshop',
310
            HTTP::get_mime_type('file.psd')
311
        );
312
        $this->assertEquals('audio/x-wav', HTTP::get_mime_type('file.wav'));
313
    }
314
315
    /**
316
     * Test that absoluteURLs correctly transforms urls within CSS to absolute
317
     */
318
    public function testAbsoluteURLsCSS()
319
    {
320
        $this->withBaseURL(
321
            'http://www.silverstripe.org/',
322
            function () {
323
324
                // background-image
325
                // Note that using /./ in urls is absolutely acceptable
326
                $this->assertEquals(
327
                    '<div style="background-image: url(\'http://www.silverstripe.org/./images/mybackground.gif\');">'
328
                    . 'Content</div>',
329
                    HTTP::absoluteURLs(
330
                        '<div style="background-image: url(\'./images/mybackground.gif\');">Content</div>'
331
                    )
332
                );
333
334
                // background
335
                $this->assertEquals(
336
                    '<div style="background: url(\'http://www.silverstripe.org/images/mybackground.gif\');">'
337
                    . 'Content</div>',
338
                    HTTP::absoluteURLs(
339
                        '<div style="background: url(\'images/mybackground.gif\');">Content</div>'
340
                    )
341
                );
342
343
                // list-style-image
344
                $this->assertEquals(
345
                    '<div style=\'background: url(http://www.silverstripe.org/list.png);\'>Content</div>',
346
                    HTTP::absoluteURLs(
347
                        '<div style=\'background: url(list.png);\'>Content</div>'
348
                    )
349
                );
350
351
                // list-style
352
                $this->assertEquals(
353
                    '<div style=\'background: url("http://www.silverstripe.org/./assets/list.png");\'>Content</div>',
354
                    HTTP::absoluteURLs(
355
                        '<div style=\'background: url("./assets/list.png");\'>Content</div>'
356
                    )
357
                );
358
            }
359
        );
360
    }
361
362
    /**
363
     * Test that absoluteURLs correctly transforms urls within html attributes to absolute
364
     */
365
    public function testAbsoluteURLsAttributes()
366
    {
367
        $this->withBaseURL(
368
            'http://www.silverstripe.org/',
369
            function () {
370
                //empty links
371
                $this->assertEquals(
372
                    '<a href="http://www.silverstripe.org/">test</a>',
373
                    HTTP::absoluteURLs('<a href="">test</a>')
374
                );
375
376
                $this->assertEquals(
377
                    '<a href="http://www.silverstripe.org/">test</a>',
378
                    HTTP::absoluteURLs('<a href="/">test</a>')
379
                );
380
381
                //relative
382
                $this->assertEquals(
383
                    '<a href="http://www.silverstripe.org/">test</a>',
384
                    HTTP::absoluteURLs('<a href="./">test</a>')
385
                );
386
                $this->assertEquals(
387
                    '<a href="http://www.silverstripe.org/">test</a>',
388
                    HTTP::absoluteURLs('<a href=".">test</a>')
389
                );
390
391
                // links
392
                $this->assertEquals(
393
                    '<a href=\'http://www.silverstripe.org/blog/\'>SS Blog</a>',
394
                    HTTP::absoluteURLs('<a href=\'/blog/\'>SS Blog</a>')
395
                );
396
397
                // background
398
                // Note that using /./ in urls is absolutely acceptable
399
                $this->assertEquals(
400
                    '<div background="http://www.silverstripe.org/./themes/silverstripe/images/nav-bg-repeat-2.png">'
401
                    . 'SS Blog</div>',
402
                    HTTP::absoluteURLs(
403
                        '<div background="./themes/silverstripe/images/nav-bg-repeat-2.png">SS Blog</div>'
404
                    )
405
                );
406
407
                //check dot segments
408
                // Assumption: dots are not removed
409
                //if they were, the url should be: http://www.silverstripe.org/abc
410
                $this->assertEquals(
411
                    '<a href="http://www.silverstripe.org/test/page/../../abc">Test</a>',
412
                    HTTP::absoluteURLs('<a href="test/page/../../abc">Test</a>')
413
                );
414
415
                // image
416
                $this->assertEquals(
417
                    '<img src=\'http://www.silverstripe.org/themes/silverstripe/images/logo-org.png\' />',
418
                    HTTP::absoluteURLs('<img src=\'themes/silverstripe/images/logo-org.png\' />')
419
                );
420
421
                // link
422
                $this->assertEquals(
423
                    '<link href=http://www.silverstripe.org/base.css />',
424
                    HTTP::absoluteURLs('<link href=base.css />')
425
                );
426
427
                // Test special characters are retained
428
                $this->assertEquals(
429
                    '<a href="http://www.silverstripe.org/Security/changepassword?m=3&amp;t=7214fdfde">password'
430
                    . ' reset link</a>',
431
                    HTTP::absoluteURLs(
432
                        '<a href="/Security/changepassword?m=3&amp;t=7214fdfde">password reset link</a>'
433
                    )
434
                );
435
            }
436
        );
437
    }
438
439
    /**
440
     *  Make sure URI schemes are not rewritten
441
     */
442
    public function testURISchemes()
443
    {
444
        $this->withBaseURL(
445
            'http://www.silverstripe.org/',
446
            function ($test) {
0 ignored issues
show
Unused Code introduced by
The parameter $test is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

446
            function (/** @scrutinizer ignore-unused */ $test) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
447
448
                // mailto
449
                $this->assertEquals(
450
                    '<a href=\'mailto:[email protected]\'>Email Us</a>',
451
                    HTTP::absoluteURLs('<a href=\'mailto:[email protected]\'>Email Us</a>'),
452
                    'Email links are not rewritten'
453
                );
454
455
                // data uri
456
                $this->assertEquals(
457
                    '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI'
458
                    . '12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />',
459
                    HTTP::absoluteURLs(
460
                        '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH'
461
                        . 'ElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />'
462
                    ),
463
                    'Data URI links are not rewritten'
464
                );
465
466
                // call
467
                $this->assertEquals(
468
                    '<a href="callto:12345678" />',
469
                    HTTP::absoluteURLs('<a href="callto:12345678" />'),
470
                    'Call to links are not rewritten'
471
                );
472
            }
473
        );
474
    }
475
476
    public function testFilename2url()
477
    {
478
        $this->withBaseURL(
479
            'http://www.silverstripe.org/',
480
            function ($test) {
0 ignored issues
show
Unused Code introduced by
The parameter $test is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

480
            function (/** @scrutinizer ignore-unused */ $test) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
481
                $frameworkTests = ltrim(FRAMEWORK_DIR . '/tests', '/');
482
                $this->assertEquals(
483
                    "http://www.silverstripe.org/$frameworkTests/php/Control/HTTPTest.php",
484
                    HTTP::filename2url(__FILE__)
485
                );
486
            }
487
        );
488
    }
489
490
    /**
491
     * Process cache headers on a response
492
     *
493
     * @param HTTPResponse $response
494
     */
495
    protected function addCacheHeaders(HTTPResponse $response)
496
    {
497
        // Mock request
498
        $session = new Session([]);
499
        $request = new HTTPRequest('GET', '/');
500
        $request->setSession($session);
501
502
        // Run middleware
503
        HTTPCacheControlMiddleware::singleton()
504
            ->process($request, function (HTTPRequest $request) use ($response) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

504
            ->process($request, function (/** @scrutinizer ignore-unused */ HTTPRequest $request) use ($response) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
505
                return $response;
506
            });
507
    }
508
}
509