Completed
Push — 4 ( 5fbfd8...bd8494 )
by Ingo
09:20
created

HTTPTest::testAddCacheHeaders()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 50
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 39
nc 4
nop 0
dl 0
loc 50
rs 9.3333
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\Dev\FunctionalTest;
13
14
/**
15
 * Tests the {@link HTTP} class
16
 *
17
 * @skipUpgrade
18
 */
19
class HTTPTest extends FunctionalTest
20
{
21
    protected function setUp()
22
    {
23
        parent::setUp();
24
        // Set to disabled at null forcing level
25
        HTTPCacheControlMiddleware::config()
26
            ->set('defaultState', 'disabled')
27
            ->set('defaultForcingLevel', 0);
28
        HTTPCacheControlMiddleware::reset();
29
    }
30
31
    public function testAddCacheHeaders()
32
    {
33
        $body = "<html><head></head><body><h1>Mysite</h1></body></html>";
34
        $response = new HTTPResponse($body, 200);
35
        HTTPCacheControlMiddleware::singleton()->publicCache();
36
        HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
37
38
        $this->addCacheHeaders($response);
39
        $this->assertNotEmpty($response->getHeader('Cache-Control'));
40
41
        // Ensure cache headers are set correctly when disabled via config (e.g. when dev)
42
        HTTPCacheControlMiddleware::config()
43
            ->set('defaultState', 'disabled')
44
            ->set('defaultForcingLevel', HTTPCacheControlMiddleware::LEVEL_DISABLED);
45
        HTTPCacheControlMiddleware::reset();
46
        HTTPCacheControlMiddleware::singleton()->publicCache();
47
        HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
48
        $response = new HTTPResponse($body, 200);
49
        $this->addCacheHeaders($response);
50
        $this->assertContains('no-cache', $response->getHeader('Cache-Control'));
51
        $this->assertContains('no-store', $response->getHeader('Cache-Control'));
52
        $this->assertContains('must-revalidate', $response->getHeader('Cache-Control'));
53
54
        // Ensure max-age setting is respected in production.
55
        HTTPCacheControlMiddleware::config()
56
            ->set('defaultState', 'disabled')
57
            ->set('defaultForcingLevel', 0);
58
        HTTPCacheControlMiddleware::reset();
59
        HTTPCacheControlMiddleware::singleton()->publicCache();
60
        HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
61
        $response = new HTTPResponse($body, 200);
62
        $this->addCacheHeaders($response);
63
        $this->assertContains('max-age=30', $response->getHeader('Cache-Control'));
64
        $this->assertNotContains('max-age=0', $response->getHeader('Cache-Control'));
65
66
        // Still "live": Ensure header's aren't overridden if already set (using purposefully different values).
67
        $headers = array(
68
            'Vary' => '*',
69
            'Pragma' => 'no-cache',
70
            'Cache-Control' => 'max-age=0, no-cache, no-store',
71
        );
72
        foreach ($headers as $header => $value) {
73
            $response->addHeader($header, $value);
74
        }
75
        HTTPCacheControlMiddleware::reset();
76
        HTTPCacheControlMiddleware::singleton()->publicCache();
77
        HTTPCacheControlMiddleware::singleton()->setMaxAge(30);
78
        $this->addCacheHeaders($response);
79
        foreach ($headers as $header => $value) {
80
            $this->assertEquals($value, $response->getHeader($header));
81
        }
82
    }
83
84
    public function testConfigVary()
85
    {
86
        $body = "<html><head></head><body><h1>Mysite</h1></body></html>";
87
        $response = new HTTPResponse($body, 200);
88
        HTTPCacheControlMiddleware::singleton()
89
            ->setMaxAge(30)
90
            ->setVary('X-Requested-With, X-Forwarded-Protocol');
91
        $this->addCacheHeaders($response);
92
93
        // Vary set properly
94
        $v = $response->getHeader('Vary');
95
        $this->assertContains("X-Forwarded-Protocol", $v);
96
        $this->assertContains("X-Requested-With", $v);
97
        $this->assertNotContains("Cookie", $v);
98
        $this->assertNotContains("User-Agent", $v);
99
        $this->assertNotContains("Accept", $v);
100
101
        // No vary
102
        HTTPCacheControlMiddleware::singleton()
103
            ->setMaxAge(30)
104
            ->setVary(null);
105
        HTTPCacheControlMiddleware::reset();
106
        HTTPCacheControlMiddleware::config()
107
            ->set('defaultVary', []);
108
109
        $response = new HTTPResponse($body, 200);
110
        $this->addCacheHeaders($response);
111
        $v = $response->getHeader('Vary');
112
        $this->assertEmpty($v);
113
    }
114
115
    /**
116
     * Tests {@link HTTP::getLinksIn()}
117
     */
118
    public function testGetLinksIn()
119
    {
120
        $content = '
121
			<h2><a href="/">My Cool Site</a></h2>
122
123
			<p>
124
				A boy went <a href="home/">home</a> to see his <span><a href="mother/">mother</a></span>. This
125
				involved a short <a href="$Journey">journey</a>, as well as some <a href="space travel">space travel</a>
126
				and <a href=unquoted>unquoted</a> events, as well as a <a href=\'single quote\'>single quote</a> from
127
				his <a href="/father">father</a>.
128
			</p>
129
130
			<p>
131
				There were also some elements with extra <a class=attribute href=\'attributes\'>attributes</a> which
132
				played a part in his <a href=journey"extra id="JourneyLink">journey</a>. HE ALSO DISCOVERED THE
133
				<A HREF="CAPS LOCK">KEY</a>. Later he got his <a href="quotes \'mixed\' up">mixed up</a>.
134
			</p>
135
		';
136
137
        $expected = array (
138
            '/', 'home/', 'mother/', '$Journey', 'space travel', 'unquoted', 'single quote', '/father', 'attributes',
139
            'journey', 'CAPS LOCK', 'quotes \'mixed\' up'
140
        );
141
142
        $result = HTTP::getLinksIn($content);
143
144
        // Results don't neccesarily come out in the order they are in the $content param.
145
        sort($result);
146
        sort($expected);
147
148
        $this->assertTrue(is_array($result));
149
        $this->assertEquals($expected, $result, 'Test that all links within the content are found.');
150
    }
151
152
    /**
153
     * Tests {@link HTTP::setGetVar()}
154
     */
155
    public function testSetGetVar()
156
    {
157
        // Hackery to work around volatile URL formats in test invocation,
158
        // and the inability of Director::absoluteBaseURL() to produce consistent URLs.
159
        Director::mockRequest(function (HTTPRequest $request) {
160
            $controller = new Controller();
161
            $controller->setRequest($request);
162
            $controller->pushCurrent();
163
            try {
164
                $this->assertContains(
165
                    'relative/url?foo=bar',
166
                    HTTP::setGetVar('foo', 'bar'),
167
                    'Omitting a URL falls back to current URL'
168
                );
169
            } finally {
170
                $controller->popCurrent();
171
            }
172
        }, 'relative/url/');
173
174
        $this->assertEquals(
175
            'relative/url?foo=bar',
176
            HTTP::setGetVar('foo', 'bar', 'relative/url'),
177
            'Relative URL without existing query params'
178
        );
179
180
        $this->assertEquals(
181
            'relative/url?baz=buz&foo=bar',
182
            HTTP::setGetVar('foo', 'bar', '/relative/url?baz=buz'),
183
            'Relative URL with existing query params, and new added key'
184
        );
185
186
        $this->assertEquals(
187
            'http://test.com/?foo=new&buz=baz',
188
            HTTP::setGetVar('foo', 'new', 'http://test.com/?foo=old&buz=baz'),
189
            'Absolute URL without path and multipe existing query params, overwriting an existing parameter'
190
        );
191
192
        $this->assertContains(
193
            'http://test.com/?foo=new',
194
            HTTP::setGetVar('foo', 'new', 'http://test.com/?foo=&foo=old'),
195
            'Absolute URL and empty query param'
196
        );
197
        // http_build_query() escapes angular brackets, they should be correctly urldecoded by the browser client
198
        $this->assertEquals(
199
            'http://test.com/?foo%5Btest%5D=one&foo%5Btest%5D=two',
200
            HTTP::setGetVar('foo[test]', 'two', 'http://test.com/?foo[test]=one'),
201
            'Absolute URL and PHP array query string notation'
202
        );
203
204
        $urls = array(
205
            'http://www.test.com:8080',
206
            'http://test.com:3000/',
207
            'http://test.com:3030/baz/',
208
            'http://baz:[email protected]',
209
            'http://[email protected]/',
210
            'http://baz:[email protected]:8080',
211
            'http://[email protected]:8080'
212
        );
213
214
        foreach ($urls as $testURL) {
215
            $this->assertEquals(
216
                $testURL . '?foo=bar',
217
                HTTP::setGetVar('foo', 'bar', $testURL),
218
                'Absolute URL and Port Number'
219
            );
220
        }
221
    }
222
223
    /**
224
     * Test that the the get_mime_type() works correctly
225
     */
226
    public function testGetMimeType()
227
    {
228
        $this->assertEquals('text/plain', HTTP::get_mime_type('file.csv'));
229
        $this->assertEquals('image/gif', HTTP::get_mime_type('file.gif'));
230
        $this->assertEquals('text/html', HTTP::get_mime_type('file.html'));
231
        $this->assertEquals('image/jpeg', HTTP::get_mime_type('file.jpg'));
232
        $this->assertEquals('image/jpeg', HTTP::get_mime_type('upperfile.JPG'));
233
        $this->assertEquals('image/png', HTTP::get_mime_type('file.png'));
234
        $this->assertEquals(
235
            'image/vnd.adobe.photoshop',
236
            HTTP::get_mime_type('file.psd')
237
        );
238
        $this->assertEquals('audio/x-wav', HTTP::get_mime_type('file.wav'));
239
    }
240
241
    /**
242
     * Test that absoluteURLs correctly transforms urls within CSS to absolute
243
     */
244
    public function testAbsoluteURLsCSS()
245
    {
246
        $this->withBaseURL(
247
            'http://www.silverstripe.org/',
248
            function () {
249
250
                // background-image
251
                // Note that using /./ in urls is absolutely acceptable
252
                $this->assertEquals(
253
                    '<div style="background-image: url(\'http://www.silverstripe.org/./images/mybackground.gif\');">' . 'Content</div>',
254
                    HTTP::absoluteURLs('<div style="background-image: url(\'./images/mybackground.gif\');">Content</div>')
255
                );
256
257
                // background
258
                $this->assertEquals(
259
                    '<div style="background: url(\'http://www.silverstripe.org/images/mybackground.gif\');">Content</div>',
260
                    HTTP::absoluteURLs('<div style="background: url(\'images/mybackground.gif\');">Content</div>')
261
                );
262
263
                // list-style-image
264
                $this->assertEquals(
265
                    '<div style=\'background: url(http://www.silverstripe.org/list.png);\'>Content</div>',
266
                    HTTP::absoluteURLs('<div style=\'background: url(list.png);\'>Content</div>')
267
                );
268
269
                // list-style
270
                $this->assertEquals(
271
                    '<div style=\'background: url("http://www.silverstripe.org/./assets/list.png");\'>Content</div>',
272
                    HTTP::absoluteURLs('<div style=\'background: url("./assets/list.png");\'>Content</div>')
273
                );
274
            }
275
        );
276
    }
277
278
    /**
279
     * Test that absoluteURLs correctly transforms urls within html attributes to absolute
280
     */
281
    public function testAbsoluteURLsAttributes()
282
    {
283
        $this->withBaseURL(
284
            'http://www.silverstripe.org/',
285
            function () {
286
                //empty links
287
                $this->assertEquals(
288
                    '<a href="http://www.silverstripe.org/">test</a>',
289
                    HTTP::absoluteURLs('<a href="">test</a>')
290
                );
291
292
                $this->assertEquals(
293
                    '<a href="http://www.silverstripe.org/">test</a>',
294
                    HTTP::absoluteURLs('<a href="/">test</a>')
295
                );
296
297
                //relative
298
                $this->assertEquals(
299
                    '<a href="http://www.silverstripe.org/">test</a>',
300
                    HTTP::absoluteURLs('<a href="./">test</a>')
301
                );
302
                $this->assertEquals(
303
                    '<a href="http://www.silverstripe.org/">test</a>',
304
                    HTTP::absoluteURLs('<a href=".">test</a>')
305
                );
306
307
                // links
308
                $this->assertEquals(
309
                    '<a href=\'http://www.silverstripe.org/blog/\'>SS Blog</a>',
310
                    HTTP::absoluteURLs('<a href=\'/blog/\'>SS Blog</a>')
311
                );
312
313
                // background
314
                // Note that using /./ in urls is absolutely acceptable
315
                $this->assertEquals(
316
                    '<div background="http://www.silverstripe.org/./themes/silverstripe/images/nav-bg-repeat-2.png">' . 'SS Blog</div>',
317
                    HTTP::absoluteURLs('<div background="./themes/silverstripe/images/nav-bg-repeat-2.png">SS Blog</div>')
318
                );
319
320
                //check dot segments
321
                // Assumption: dots are not removed
322
                //if they were, the url should be: http://www.silverstripe.org/abc
323
                $this->assertEquals(
324
                    '<a href="http://www.silverstripe.org/test/page/../../abc">Test</a>',
325
                    HTTP::absoluteURLs('<a href="test/page/../../abc">Test</a>')
326
                );
327
328
                // image
329
                $this->assertEquals(
330
                    '<img src=\'http://www.silverstripe.org/themes/silverstripe/images/logo-org.png\' />',
331
                    HTTP::absoluteURLs('<img src=\'themes/silverstripe/images/logo-org.png\' />')
332
                );
333
334
                // link
335
                $this->assertEquals(
336
                    '<link href=http://www.silverstripe.org/base.css />',
337
                    HTTP::absoluteURLs('<link href=base.css />')
338
                );
339
340
                // Test special characters are retained
341
                $this->assertEquals(
342
                    '<a href="http://www.silverstripe.org/Security/changepassword?m=3&amp;t=7214fdfde">password reset link</a>',
343
                    HTTP::absoluteURLs('<a href="/Security/changepassword?m=3&amp;t=7214fdfde">password reset link</a>')
344
                );
345
            }
346
        );
347
    }
348
349
    /**
350
     *  Make sure URI schemes are not rewritten
351
     */
352
    public function testURISchemes()
353
    {
354
        $this->withBaseURL(
355
            'http://www.silverstripe.org/',
356
            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

356
            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...
357
358
                // mailto
359
                $this->assertEquals(
360
                    '<a href=\'mailto:[email protected]\'>Email Us</a>',
361
                    HTTP::absoluteURLs('<a href=\'mailto:[email protected]\'>Email Us</a>'),
362
                    'Email links are not rewritten'
363
                );
364
365
                // data uri
366
                $this->assertEquals(
367
                    '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38' . 'GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />',
368
                    HTTP::absoluteURLs(
369
                        '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAH' . 'ElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Red dot" />'
370
                    ),
371
                    'Data URI links are not rewritten'
372
                );
373
374
                // call
375
                $this->assertEquals(
376
                    '<a href="callto:12345678" />',
377
                    HTTP::absoluteURLs('<a href="callto:12345678" />'),
378
                    'Call to links are not rewritten'
379
                );
380
            }
381
        );
382
    }
383
384
    public function testFilename2url()
385
    {
386
        $this->withBaseURL(
387
            'http://www.silverstripe.org/',
388
            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

388
            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...
389
                $frameworkTests = ltrim(FRAMEWORK_DIR . '/tests', '/');
390
                $this->assertEquals(
391
                    "http://www.silverstripe.org/$frameworkTests/php/Control/HTTPTest.php",
392
                    HTTP::filename2url(__FILE__)
393
                );
394
            }
395
        );
396
    }
397
398
    /**
399
     * Process cache headers on a response
400
     *
401
     * @param HTTPResponse $response
402
     */
403
    protected function addCacheHeaders(HTTPResponse $response)
404
    {
405
        // Mock request
406
        $session = new Session([]);
407
        $request = new HTTPRequest('GET', '/');
408
        $request->setSession($session);
409
410
        // Run middleware
411
        HTTPCacheControlMiddleware::singleton()
412
            ->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

412
            ->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...
413
                return $response;
414
            });
415
    }
416
}
417