| 3 |
liveuser |
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 |
|
|
|
12 |
namespace Symfony\Component\HttpFoundation\Tests;
|
|
|
13 |
|
|
|
14 |
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
|
|
15 |
use Symfony\Component\HttpFoundation\Request;
|
|
|
16 |
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
|
|
|
17 |
use Symfony\Component\HttpFoundation\Tests\File\FakeFile;
|
|
|
18 |
|
|
|
19 |
class BinaryFileResponseTest extends ResponseTestCase
|
|
|
20 |
{
|
|
|
21 |
public function testConstruction()
|
|
|
22 |
{
|
|
|
23 |
$file = __DIR__.'/../README.md';
|
|
|
24 |
$response = new BinaryFileResponse($file, 404, array('X-Header' => 'Foo'), true, null, true, true);
|
|
|
25 |
$this->assertEquals(404, $response->getStatusCode());
|
|
|
26 |
$this->assertEquals('Foo', $response->headers->get('X-Header'));
|
|
|
27 |
$this->assertTrue($response->headers->has('ETag'));
|
|
|
28 |
$this->assertTrue($response->headers->has('Last-Modified'));
|
|
|
29 |
$this->assertFalse($response->headers->has('Content-Disposition'));
|
|
|
30 |
|
|
|
31 |
$response = BinaryFileResponse::create($file, 404, array(), true, ResponseHeaderBag::DISPOSITION_INLINE);
|
|
|
32 |
$this->assertEquals(404, $response->getStatusCode());
|
|
|
33 |
$this->assertFalse($response->headers->has('ETag'));
|
|
|
34 |
$this->assertEquals('inline; filename="README.md"', $response->headers->get('Content-Disposition'));
|
|
|
35 |
}
|
|
|
36 |
|
|
|
37 |
public function testConstructWithNonAsciiFilename()
|
|
|
38 |
{
|
|
|
39 |
touch(sys_get_temp_dir().'/fööö.html');
|
|
|
40 |
|
|
|
41 |
$response = new BinaryFileResponse(sys_get_temp_dir().'/fööö.html', 200, array(), true, 'attachment');
|
|
|
42 |
|
|
|
43 |
@unlink(sys_get_temp_dir().'/fööö.html');
|
|
|
44 |
|
|
|
45 |
$this->assertSame('fööö.html', $response->getFile()->getFilename());
|
|
|
46 |
}
|
|
|
47 |
|
|
|
48 |
/**
|
|
|
49 |
* @expectedException \LogicException
|
|
|
50 |
*/
|
|
|
51 |
public function testSetContent()
|
|
|
52 |
{
|
|
|
53 |
$response = new BinaryFileResponse(__FILE__);
|
|
|
54 |
$response->setContent('foo');
|
|
|
55 |
}
|
|
|
56 |
|
|
|
57 |
public function testGetContent()
|
|
|
58 |
{
|
|
|
59 |
$response = new BinaryFileResponse(__FILE__);
|
|
|
60 |
$this->assertFalse($response->getContent());
|
|
|
61 |
}
|
|
|
62 |
|
|
|
63 |
public function testSetContentDispositionGeneratesSafeFallbackFilename()
|
|
|
64 |
{
|
|
|
65 |
$response = new BinaryFileResponse(__FILE__);
|
|
|
66 |
$response->setContentDisposition(ResponseHeaderBag::DISPOSITION_ATTACHMENT, 'föö.html');
|
|
|
67 |
|
|
|
68 |
$this->assertSame('attachment; filename="f__.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html', $response->headers->get('Content-Disposition'));
|
|
|
69 |
}
|
|
|
70 |
|
|
|
71 |
/**
|
|
|
72 |
* @dataProvider provideRanges
|
|
|
73 |
*/
|
|
|
74 |
public function testRequests($requestRange, $offset, $length, $responseRange)
|
|
|
75 |
{
|
|
|
76 |
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
|
|
|
77 |
|
|
|
78 |
// do a request to get the ETag
|
|
|
79 |
$request = Request::create('/');
|
|
|
80 |
$response->prepare($request);
|
|
|
81 |
$etag = $response->headers->get('ETag');
|
|
|
82 |
|
|
|
83 |
// prepare a request for a range of the testing file
|
|
|
84 |
$request = Request::create('/');
|
|
|
85 |
$request->headers->set('If-Range', $etag);
|
|
|
86 |
$request->headers->set('Range', $requestRange);
|
|
|
87 |
|
|
|
88 |
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
|
|
89 |
fseek($file, $offset);
|
|
|
90 |
$data = fread($file, $length);
|
|
|
91 |
fclose($file);
|
|
|
92 |
|
|
|
93 |
$this->expectOutputString($data);
|
|
|
94 |
$response = clone $response;
|
|
|
95 |
$response->prepare($request);
|
|
|
96 |
$response->sendContent();
|
|
|
97 |
|
|
|
98 |
$this->assertEquals(206, $response->getStatusCode());
|
|
|
99 |
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
|
|
|
100 |
}
|
|
|
101 |
|
|
|
102 |
/**
|
|
|
103 |
* @dataProvider provideRanges
|
|
|
104 |
*/
|
|
|
105 |
public function testRequestsWithoutEtag($requestRange, $offset, $length, $responseRange)
|
|
|
106 |
{
|
|
|
107 |
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
|
|
108 |
|
|
|
109 |
// do a request to get the LastModified
|
|
|
110 |
$request = Request::create('/');
|
|
|
111 |
$response->prepare($request);
|
|
|
112 |
$lastModified = $response->headers->get('Last-Modified');
|
|
|
113 |
|
|
|
114 |
// prepare a request for a range of the testing file
|
|
|
115 |
$request = Request::create('/');
|
|
|
116 |
$request->headers->set('If-Range', $lastModified);
|
|
|
117 |
$request->headers->set('Range', $requestRange);
|
|
|
118 |
|
|
|
119 |
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
|
|
120 |
fseek($file, $offset);
|
|
|
121 |
$data = fread($file, $length);
|
|
|
122 |
fclose($file);
|
|
|
123 |
|
|
|
124 |
$this->expectOutputString($data);
|
|
|
125 |
$response = clone $response;
|
|
|
126 |
$response->prepare($request);
|
|
|
127 |
$response->sendContent();
|
|
|
128 |
|
|
|
129 |
$this->assertEquals(206, $response->getStatusCode());
|
|
|
130 |
$this->assertEquals($responseRange, $response->headers->get('Content-Range'));
|
|
|
131 |
}
|
|
|
132 |
|
|
|
133 |
public function provideRanges()
|
|
|
134 |
{
|
|
|
135 |
return array(
|
|
|
136 |
array('bytes=1-4', 1, 4, 'bytes 1-4/35'),
|
|
|
137 |
array('bytes=-5', 30, 5, 'bytes 30-34/35'),
|
|
|
138 |
array('bytes=30-', 30, 5, 'bytes 30-34/35'),
|
|
|
139 |
array('bytes=30-30', 30, 1, 'bytes 30-30/35'),
|
|
|
140 |
array('bytes=30-34', 30, 5, 'bytes 30-34/35'),
|
|
|
141 |
);
|
|
|
142 |
}
|
|
|
143 |
|
|
|
144 |
public function testRangeRequestsWithoutLastModifiedDate()
|
|
|
145 |
{
|
|
|
146 |
// prevent auto last modified
|
|
|
147 |
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'), true, null, false, false);
|
|
|
148 |
|
|
|
149 |
// prepare a request for a range of the testing file
|
|
|
150 |
$request = Request::create('/');
|
|
|
151 |
$request->headers->set('If-Range', date('D, d M Y H:i:s').' GMT');
|
|
|
152 |
$request->headers->set('Range', 'bytes=1-4');
|
|
|
153 |
|
|
|
154 |
$this->expectOutputString(file_get_contents(__DIR__.'/File/Fixtures/test.gif'));
|
|
|
155 |
$response = clone $response;
|
|
|
156 |
$response->prepare($request);
|
|
|
157 |
$response->sendContent();
|
|
|
158 |
|
|
|
159 |
$this->assertEquals(200, $response->getStatusCode());
|
|
|
160 |
$this->assertNull($response->headers->get('Content-Range'));
|
|
|
161 |
}
|
|
|
162 |
|
|
|
163 |
/**
|
|
|
164 |
* @dataProvider provideFullFileRanges
|
|
|
165 |
*/
|
|
|
166 |
public function testFullFileRequests($requestRange)
|
|
|
167 |
{
|
|
|
168 |
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
|
|
|
169 |
|
|
|
170 |
// prepare a request for a range of the testing file
|
|
|
171 |
$request = Request::create('/');
|
|
|
172 |
$request->headers->set('Range', $requestRange);
|
|
|
173 |
|
|
|
174 |
$file = fopen(__DIR__.'/File/Fixtures/test.gif', 'r');
|
|
|
175 |
$data = fread($file, 35);
|
|
|
176 |
fclose($file);
|
|
|
177 |
|
|
|
178 |
$this->expectOutputString($data);
|
|
|
179 |
$response = clone $response;
|
|
|
180 |
$response->prepare($request);
|
|
|
181 |
$response->sendContent();
|
|
|
182 |
|
|
|
183 |
$this->assertEquals(200, $response->getStatusCode());
|
|
|
184 |
}
|
|
|
185 |
|
|
|
186 |
public function provideFullFileRanges()
|
|
|
187 |
{
|
|
|
188 |
return array(
|
|
|
189 |
array('bytes=0-'),
|
|
|
190 |
array('bytes=0-34'),
|
|
|
191 |
array('bytes=-35'),
|
|
|
192 |
// Syntactical invalid range-request should also return the full resource
|
|
|
193 |
array('bytes=20-10'),
|
|
|
194 |
array('bytes=50-40'),
|
|
|
195 |
);
|
|
|
196 |
}
|
|
|
197 |
|
|
|
198 |
/**
|
|
|
199 |
* @dataProvider provideInvalidRanges
|
|
|
200 |
*/
|
|
|
201 |
public function testInvalidRequests($requestRange)
|
|
|
202 |
{
|
|
|
203 |
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'))->setAutoEtag();
|
|
|
204 |
|
|
|
205 |
// prepare a request for a range of the testing file
|
|
|
206 |
$request = Request::create('/');
|
|
|
207 |
$request->headers->set('Range', $requestRange);
|
|
|
208 |
|
|
|
209 |
$response = clone $response;
|
|
|
210 |
$response->prepare($request);
|
|
|
211 |
$response->sendContent();
|
|
|
212 |
|
|
|
213 |
$this->assertEquals(416, $response->getStatusCode());
|
|
|
214 |
$this->assertEquals('bytes */35', $response->headers->get('Content-Range'));
|
|
|
215 |
}
|
|
|
216 |
|
|
|
217 |
public function provideInvalidRanges()
|
|
|
218 |
{
|
|
|
219 |
return array(
|
|
|
220 |
array('bytes=-40'),
|
|
|
221 |
array('bytes=30-40'),
|
|
|
222 |
);
|
|
|
223 |
}
|
|
|
224 |
|
|
|
225 |
/**
|
|
|
226 |
* @dataProvider provideXSendfileFiles
|
|
|
227 |
*/
|
|
|
228 |
public function testXSendfile($file)
|
|
|
229 |
{
|
|
|
230 |
$request = Request::create('/');
|
|
|
231 |
$request->headers->set('X-Sendfile-Type', 'X-Sendfile');
|
|
|
232 |
|
|
|
233 |
BinaryFileResponse::trustXSendfileTypeHeader();
|
|
|
234 |
$response = BinaryFileResponse::create($file, 200, array('Content-Type' => 'application/octet-stream'));
|
|
|
235 |
$response->prepare($request);
|
|
|
236 |
|
|
|
237 |
$this->expectOutputString('');
|
|
|
238 |
$response->sendContent();
|
|
|
239 |
|
|
|
240 |
$this->assertContains('README.md', $response->headers->get('X-Sendfile'));
|
|
|
241 |
}
|
|
|
242 |
|
|
|
243 |
public function provideXSendfileFiles()
|
|
|
244 |
{
|
|
|
245 |
return array(
|
|
|
246 |
array(__DIR__.'/../README.md'),
|
|
|
247 |
array('file://'.__DIR__.'/../README.md'),
|
|
|
248 |
);
|
|
|
249 |
}
|
|
|
250 |
|
|
|
251 |
/**
|
|
|
252 |
* @dataProvider getSampleXAccelMappings
|
|
|
253 |
*/
|
|
|
254 |
public function testXAccelMapping($realpath, $mapping, $virtual)
|
|
|
255 |
{
|
|
|
256 |
$request = Request::create('/');
|
|
|
257 |
$request->headers->set('X-Sendfile-Type', 'X-Accel-Redirect');
|
|
|
258 |
$request->headers->set('X-Accel-Mapping', $mapping);
|
|
|
259 |
|
|
|
260 |
$file = new FakeFile($realpath, __DIR__.'/File/Fixtures/test');
|
|
|
261 |
|
|
|
262 |
BinaryFileResponse::trustXSendfileTypeHeader();
|
|
|
263 |
$response = new BinaryFileResponse($file, 200, array('Content-Type' => 'application/octet-stream'));
|
|
|
264 |
$reflection = new \ReflectionObject($response);
|
|
|
265 |
$property = $reflection->getProperty('file');
|
|
|
266 |
$property->setAccessible(true);
|
|
|
267 |
$property->setValue($response, $file);
|
|
|
268 |
|
|
|
269 |
$response->prepare($request);
|
|
|
270 |
$this->assertEquals($virtual, $response->headers->get('X-Accel-Redirect'));
|
|
|
271 |
}
|
|
|
272 |
|
|
|
273 |
public function testDeleteFileAfterSend()
|
|
|
274 |
{
|
|
|
275 |
$request = Request::create('/');
|
|
|
276 |
|
|
|
277 |
$path = __DIR__.'/File/Fixtures/to_delete';
|
|
|
278 |
touch($path);
|
|
|
279 |
$realPath = realpath($path);
|
|
|
280 |
$this->assertFileExists($realPath);
|
|
|
281 |
|
|
|
282 |
$response = new BinaryFileResponse($realPath, 200, array('Content-Type' => 'application/octet-stream'));
|
|
|
283 |
$response->deleteFileAfterSend(true);
|
|
|
284 |
|
|
|
285 |
$response->prepare($request);
|
|
|
286 |
$response->sendContent();
|
|
|
287 |
|
|
|
288 |
$this->assertFileNotExists($path);
|
|
|
289 |
}
|
|
|
290 |
|
|
|
291 |
public function testAcceptRangeOnUnsafeMethods()
|
|
|
292 |
{
|
|
|
293 |
$request = Request::create('/', 'POST');
|
|
|
294 |
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
|
|
295 |
$response->prepare($request);
|
|
|
296 |
|
|
|
297 |
$this->assertEquals('none', $response->headers->get('Accept-Ranges'));
|
|
|
298 |
}
|
|
|
299 |
|
|
|
300 |
public function testAcceptRangeNotOverriden()
|
|
|
301 |
{
|
|
|
302 |
$request = Request::create('/', 'POST');
|
|
|
303 |
$response = BinaryFileResponse::create(__DIR__.'/File/Fixtures/test.gif', 200, array('Content-Type' => 'application/octet-stream'));
|
|
|
304 |
$response->headers->set('Accept-Ranges', 'foo');
|
|
|
305 |
$response->prepare($request);
|
|
|
306 |
|
|
|
307 |
$this->assertEquals('foo', $response->headers->get('Accept-Ranges'));
|
|
|
308 |
}
|
|
|
309 |
|
|
|
310 |
public function getSampleXAccelMappings()
|
|
|
311 |
{
|
|
|
312 |
return array(
|
|
|
313 |
array('/var/www/var/www/files/foo.txt', '/var/www/=/files/', '/files/var/www/files/foo.txt'),
|
|
|
314 |
array('/home/foo/bar.txt', '/var/www/=/files/,/home/foo/=/baz/', '/baz/bar.txt'),
|
|
|
315 |
);
|
|
|
316 |
}
|
|
|
317 |
|
|
|
318 |
protected function provideResponse()
|
|
|
319 |
{
|
|
|
320 |
return new BinaryFileResponse(__DIR__.'/../README.md', 200, array('Content-Type' => 'application/octet-stream'));
|
|
|
321 |
}
|
|
|
322 |
|
|
|
323 |
public static function tearDownAfterClass()
|
|
|
324 |
{
|
|
|
325 |
$path = __DIR__.'/../Fixtures/to_delete';
|
|
|
326 |
if (file_exists($path)) {
|
|
|
327 |
@unlink($path);
|
|
|
328 |
}
|
|
|
329 |
}
|
|
|
330 |
}
|