Enhance XSendFileMapping to handle apache

Update xsendfile
This commit is contained in:
Nicolas Le Goff
2013-07-01 12:29:41 +02:00
parent 6c62fe7d16
commit 8e31a692cb
31 changed files with 803 additions and 394 deletions

View File

@@ -3,16 +3,23 @@
namespace Alchemy\Tests\Phrasea\Http;
use Alchemy\Phrasea\Http\ServeFileResponseFactory;
use Alchemy\Phrasea\Http\XsendfileMapping;
use Alchemy\Phrasea\Http\XSendFile\NginxMode;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\Request;
class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract
{
protected $factory;
public function testDeliverFileFactoryCreation()
{
$factory = ServeFileResponseFactory::create(self::$DI['app']);
$this->assertInstanceOf('Alchemy\Phrasea\Http\ServeFileResponseFactory', $factory);
}
public function testDeliverFile()
{
$this->factory = new ServeFileResponseFactory(false, new \unicode());
$this->factory = new ServeFileResponseFactory(new \unicode());
$response = $this->factory->deliverFile(__DIR__ . '/../../../../files/cestlafete.jpg');
@@ -25,7 +32,7 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract
public function testDeliverFileWithDuration()
{
$this->factory = new ServeFileResponseFactory(false, new \unicode());
$this->factory = new ServeFileResponseFactory(new \unicode());
$response = $this->factory->deliverFile(__DIR__ . '/../../../../files/cestlafete.jpg', 'hello', 'attachment', 'application/json', 23456);
@@ -35,7 +42,7 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract
public function testDeliverFileWithFilename()
{
$this->factory = new ServeFileResponseFactory(false, new \unicode());
$this->factory = new ServeFileResponseFactory(new \unicode());
$response = $this->factory->deliverFile(__DIR__ . '/../../../../files/cestlafete.jpg', 'toto.jpg');
@@ -45,7 +52,7 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract
public function testDeliverFileWithFilenameAndDisposition()
{
$this->factory = new ServeFileResponseFactory(false, new \unicode());
$this->factory = new ServeFileResponseFactory(new \unicode());
$response = $this->factory->deliverFile(__DIR__ . '/../../../../files/cestlafete.jpg', 'toto.jpg', 'attachment');
@@ -55,15 +62,18 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract
public function testDeliverFileWithFilenameAndDispositionAndXSendFile()
{
$this->factory = new ServeFileResponseFactory(true, new \unicode());
$request = Request::create('/');
$request->headers->set('X-SendFile-Type', 'X-Accel-Redirect');
$request->headers->set('X-Accel-Mapping', (string) new XsendfileMapping(array(
BinaryFileResponse::trustXSendfileTypeHeader();
$this->factory = new ServeFileResponseFactory(new \unicode());
$mode = new NginxMode(
array(
'directory' => __DIR__ . '/../../../../files/',
'mount-point' => '/protected/'
array(
'directory' => __DIR__ . '/../../../../files/',
'mount-point' => '/protected/'
)
)
)));
);
$request = Request::create('/');
$mode->setHeaders($request);
$response = $this->factory->deliverFile(__DIR__ . '/../../../../files/cestlafete.jpg', 'toto.jpg', 'attachment');
$response->prepare($request);
@@ -75,15 +85,18 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract
public function testDeliverFileWithFilenameAndDispositionAndXSendFileAndNoTrailingSlashes()
{
$this->factory = new ServeFileResponseFactory(true, new \unicode());
$request = Request::create('/');
$request->headers->set('X-SendFile-Type', 'X-Accel-Redirect');
$request->headers->set('X-Accel-Mapping', (string) new XsendfileMapping(array(
BinaryFileResponse::trustXSendfileTypeHeader();
$this->factory = new ServeFileResponseFactory(new \unicode());
$mode = new NginxMode(
array(
'directory' => __DIR__ . '/../../../../files/',
'mount-point' => '/protected/'
array(
'directory' => __DIR__ . '/../../../../files',
'mount-point' => '/protected'
)
)
)));
);
$request = Request::create('/');
$mode->setHeaders($request);
$response = $this->factory->deliverFile(__DIR__ . '/../../../../files/cestlafete.jpg', 'toto.jpg', 'attachment');
$response->prepare($request);
@@ -98,22 +111,26 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract
*/
public function testDeliverUnexistingFile()
{
$this->factory = new ServeFileResponseFactory(true, new \unicode());
BinaryFileResponse::trustXSendfileTypeHeader();
$this->factory = new ServeFileResponseFactory(new \unicode());
$this->factory->deliverFile(__DIR__ . '/../../../../files/does_not_exists.jpg', 'toto.jpg', 'attachment');
}
public function testDeliverFileWithFilenameAndDispositionAndXSendFileButFileNotInXAccelMapping()
{
$this->factory = new ServeFileResponseFactory(true, new \unicode());
$request = Request::create('/');
$request->headers->set('X-SendFile-Type', 'X-Accel-Redirect');
$request->headers->set('X-Accel-Mapping', (string) new XsendfileMapping(array(
BinaryFileResponse::trustXSendfileTypeHeader();
$this->factory = new ServeFileResponseFactory(new \unicode());
$mode = new NginxMode(
array(
'directory' => __DIR__ . '/../../../../files/',
'mount-point' => '/protected/'
array(
'directory' => __DIR__ . '/../../../../files/',
'mount-point' => '/protected/'
)
)
)));
);
$request = Request::create('/');
$mode->setHeaders($request);
$file = __DIR__ . '/../../../../classes/PhraseanetPHPUnitAbstract.php';
@@ -127,7 +144,7 @@ class ServeFileResponseFactoryTest extends \PhraseanetWebTestCaseAbstract
public function testDeliverDatas()
{
$this->factory = new ServeFileResponseFactory(false, new \unicode());
$this->factory = new ServeFileResponseFactory(new \unicode());
$data = 'Sex,Name,Birthday
M,Alphonse,1932

View File

@@ -0,0 +1,47 @@
<?php
namespace Alchemy\Tests\Phrasea\Http\XSendFile;
use Alchemy\Phrasea\Http\XSendFile\ApacheMode;
use Symfony\Component\HttpFoundation\Request;
class ApacheModeTest extends \PhraseanetPHPUnitAbstract
{
public function testGetVirtualHost()
{
$mode = new ApacheMode(array(array('directory' => __DIR__ )));
$conf = $mode->getVirtualHostConfiguration();
$this->assertRegExp('#'.__DIR__ . '#', $conf);
}
public function testSetValidHeaders()
{
$request = Request::create('/');
$mode = new ApacheMode(array(array('directory' => __DIR__ )));
$mode->setHeaders($request);
$this->assertArrayHasKey('x-sendfile-type', $request->headers->all());
}
public function testUnextingDirectoryMapping()
{
$mode = new ApacheMode(array(array('directory' => __DIR__ . '/Unknown/Dir')));
$this->assertEquals(array(), $mode->getMapping());
}
/**
* @dataProvider provideMappings
* @expectedException Alchemy\Phrasea\Exception\InvalidArgumentException
*/
public function testInvalidMapping($mapping)
{
new ApacheMode($mapping);
}
public function provideMappings()
{
return array(
array(array(array('wrong-key' => __DIR__))),
array(array('not-an-array')),
);
}
}

View File

@@ -0,0 +1,82 @@
<?php
namespace Alchemy\Tests\Phrasea\Http\XSendFile;
use Alchemy\Phrasea\Http\XSendFile\NginxMode;
use Symfony\Component\HttpFoundation\Request;
class XSendFileModeNginxTest extends \PhraseanetPHPUnitAbstract
{
public function testGetVirtualHost()
{
$mode = new NginxMode(array(array(
'directory' => __DIR__, 'mount-point' => '/download')));
$conf = $mode->getVirtualHostConfiguration();
$this->assertRegExp('#'.__DIR__ . '#', $conf);
}
public function testSetValidHeaders()
{
$request = Request::create('/');
$mode = new NginxMode(array(array('directory' => __DIR__, 'mount-point' => '/download')));
$mode->setHeaders($request);
$this->assertArrayHasKey('x-sendfile-type', $request->headers->all());
$this->assertArrayHasKey('x-accel-mapping', $request->headers->all());
}
public function testSetValidMultiHeaders()
{
$protected = __DIR__ . '/../../../../../files/';
$upload = __DIR__ . '/../../../../../';
$request = Request::create('/');
$mode = new NginxMode(array(
array(
'directory' => $protected,
'mount-point' => '/protected/'
),
array(
'directory' => $upload,
'mount-point' => '/uploads/'
),
));
$mode->setHeaders($request);
$this->assertArrayHasKey('x-sendfile-type', $request->headers->all());
$this->assertArrayHasKey('x-accel-mapping', $request->headers->all());
$this->assertEquals('/protected='.realpath($protected).',/uploads='.realpath($upload), $request->headers->get('X-Accel-Mapping'));
}
public function testSetInvalidHeaders()
{
$request = Request::create('/');
$mode = new NginxMode(array(array('directory' => __DIR__ . '/Unknown/Dir', 'mount-point' => '/download')));
$mode->setHeaders($request);
$this->assertArrayNotHasKey('x-sendfile-type', $request->headers->all());
$this->assertArrayNotHasKey('x-accel-mapping', $request->headers->all());
}
public function testUnextingDirectoryMapping()
{
$mode = new NginxMode(array(array('directory' => __DIR__ . '/Unknown/Dir', 'mount-point' => '/download')));
$this->assertEquals(array(), $mode->getMapping());
}
/**
* @dataProvider provideMappings
* @expectedException Alchemy\Phrasea\Exception\InvalidArgumentException
*/
public function testInvalidMapping($mapping)
{
new NginxMode($mapping);
}
public function provideMappings()
{
return array(
array(array(array('Directory' => __DIR__))),
array(array(array('wrong-key' => __DIR__, 'mount-point' => '/'))),
array(array(array('directory' => __DIR__, 'wrong-key' => '/'))),
array(array('not-an-array')),
);
}
}

View File

@@ -0,0 +1,25 @@
<?php
namespace Alchemy\Tests\Phrasea\Http\XSendFile;
use Alchemy\Phrasea\Http\XSendFile\NullMode;
use Symfony\Component\HttpFoundation\Request;
class NullModeTest extends \PhraseanetPHPUnitAbstract
{
public function testGetVirtualHost()
{
$mode = new NullMode();
$conf = $mode->getVirtualHostConfiguration();
$this->assertSame("\n", $conf);
}
public function testSetHeaders()
{
$mode = new NullMode();
$request = Request::create('/');
$before = (string) $request->headers;
$mode->setHeaders($request);
$this->assertSame($before, (string) $request->headers);
}
}

View File

@@ -0,0 +1,132 @@
<?php
namespace Alchemy\Tests\Phrasea\Http\XSendFile;
use Alchemy\Phrasea\Http\XSendFile\XSendFileFactory;
class XSendFileFactoryTest extends \PhraseanetPHPUnitAbstract
{
public function testFactoryCreation()
{
$factory = XSendFileFactory::create(self::$DI['app']);
$this->assertInstanceOf('Alchemy\Phrasea\Http\XSendFile\XSendFileFactory', $factory);
}
public function testFactoryWithXsendFileEnable()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$factory = new XSendFileFactory($logger, true, 'nginx', $this->getNginxMapping());
$this->assertInstanceOf('Alchemy\Phrasea\Http\XSendFile\ModeInterface', $factory->getMode());
}
public function testFactoryWithXsendFileDisabled()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$factory = new XSendFileFactory($logger, false, 'nginx',$this->getNginxMapping());
$this->assertInstanceOf('Alchemy\Phrasea\Http\XSendFile\NullMode', $factory->getMode());
$this->assertFalse($factory->isXSendFileModeEnabled());
}
/**
* @expectedException Alchemy\Phrasea\Exception\InvalidArgumentException
*/
public function testFactoryWithWrongTypeThrowsAnExceptionIfRequired()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$factory = new XSendFileFactory($logger, true, 'wrong-type', $this->getNginxMapping());
$factory->getMode(true);
}
public function testFactoryWithWrongTypeDoesNotThrowsAnExceptio()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger->expects($this->once())
->method('error')
->with($this->isType('string'));
$factory = new XSendFileFactory($logger, true, 'wrong-type', $this->getNginxMapping());
$this->assertInstanceOf('Alchemy\Phrasea\Http\XSendFile\NullMode', $factory->getMode(false));
}
/**
* @dataProvider provideTypes
*/
public function testFactoryType($type, $mapping, $classmode)
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$factory = new XSendFileFactory($logger, true, $type, $mapping);
$this->assertInstanceOf($classmode, $factory->getMode());
}
public function provideTypes()
{
return array(
array('apache', $this->getApacheMapping(), 'Alchemy\Phrasea\Http\XSendFile\ApacheMode'),
array('apache2', $this->getApacheMapping(), 'Alchemy\Phrasea\Http\XSendFile\ApacheMode'),
array('xsendfile', $this->getApacheMapping(), 'Alchemy\Phrasea\Http\XSendFile\ApacheMode'),
array('nginx',$this->getNginxMapping(), 'Alchemy\Phrasea\Http\XSendFile\NginxMode'),
array('sendfile',$this->getNginxMapping(), 'Alchemy\Phrasea\Http\XSendFile\NginxMode'),
array('xaccel',$this->getNginxMapping(), 'Alchemy\Phrasea\Http\XSendFile\NginxMode'),
array('xaccelredirect',$this->getNginxMapping(), 'Alchemy\Phrasea\Http\XSendFile\NginxMode'),
array('x-accel',$this->getNginxMapping(), 'Alchemy\Phrasea\Http\XSendFile\NginxMode'),
array('x-accel-redirect',$this->getNginxMapping(), 'Alchemy\Phrasea\Http\XSendFile\NginxMode'),
);
}
public function testInvalidMappingThrowsAnExceptionIfRequired()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger->expects($this->once())
->method('error')
->with($this->isType('string'));
$factory = new XSendFileFactory($logger, true, 'nginx', array());
$this->setExpectedException('Alchemy\Phrasea\Exception\RuntimeException');
$factory->getMode(true);
}
public function testInvalidMappingDoesNotThrowsAnException()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger->expects($this->once())
->method('error')
->with($this->isType('string'));
$factory = new XSendFileFactory($logger, true, 'nginx', array());
$this->assertInstanceOf('Alchemy\Phrasea\Http\XSendFile\NginxMode', $factory->getMode(false));
}
public function testInvalidMappingDoesNotThrowsAnExceptionByDefault()
{
$logger = $this->getMock('Psr\Log\LoggerInterface');
$logger->expects($this->once())
->method('error')
->with($this->isType('string'));
$factory = new XSendFileFactory($logger, true, 'nginx', array());
$this->assertInstanceOf('Alchemy\Phrasea\Http\XSendFile\NginxMode', $factory->getMode());
}
private function getNginxMapping()
{
return array(array(
'directory' => __DIR__ . '/../../../../files/',
'mount-point' => '/protected/'
));
}
private function getApacheMapping()
{
return array(array(
'directory' => __DIR__ . '/../../../../files/',
));
}
}

View File

@@ -1,103 +0,0 @@
<?php
namespace Alchemy\Tests\Phrasea\Http;
use Alchemy\Phrasea\Http\XsendfileMapping;
class XsendfileMappingTest extends \PhraseanetWebTestCaseAbstract
{
public function testOneMapping()
{
$dir = __DIR__ . '/../../../../files/';
$mapping = new XsendfileMapping(array(
array(
'directory' => $dir,
'mount-point' => '/protected/'
)
));
$this->assertEquals('/protected='.realpath($dir), (string) $mapping);
}
public function testMultiMapping()
{
$protected = __DIR__ . '/../../../../files/';
$upload = __DIR__ . '/../../../../';
$mapping = new XsendfileMapping(array(
array(
'directory' => $protected,
'mount-point' => '/protected/'
),
array(
'directory' => $upload,
'mount-point' => '/uploads/'
),
));
$this->assertEquals('/protected='.realpath($protected).',/uploads='.realpath($upload), (string) $mapping);
}
public function testMultiMappingWithANotExsistingDir()
{
$protected = __DIR__ . '/../../../../files/';
$mapping = new XsendfileMapping(array(
array(
'directory' => $protected,
'mount-point' => '/protected/'
),
array(
'directory' => '/path/to/nonexistent/directory',
'mount-point' => '/test/'
),
));
$this->assertEquals('/protected='.realpath($protected), (string) $mapping);
}
public function testEmptyMapping()
{
$mapping = new XsendfileMapping(array());
$this->assertEquals('', (string) $mapping);
}
/**
* @dataProvider provideVariousMappings
*/
public function testGetMapping($map, $expected)
{
$mapping = new XsendfileMapping($map);
$this->assertEquals($expected, $mapping->getMapping());
}
public function provideVariousMappings()
{
return array(
array(array(), array()),
array(array(array('mount-point' => false, 'directory' => false)), array()),
array(array(array('mount-point' => 'mount', 'directory' => false)), array()),
array(array(array('mount-point' => false, 'directory' => __DIR__)), array()),
array(array(array('mount-point' => 'mount', 'directory' => __DIR__)), array(array('mount-point' => '/mount', 'directory' => __DIR__))),
array(array(array('mount-point' => '/mount/', 'directory' => __DIR__ . '/../..')), array(array('mount-point' => '/mount', 'directory' => realpath(__DIR__.'/../..')))),
);
}
/**
* @dataProvider provideInvalidMappings
* @expectedException Alchemy\Phrasea\Exception\InvalidArgumentException
*/
public function testInvalidMapping($map)
{
new XsendfileMapping($map);
}
public function provideInvalidMappings()
{
return array(
array(array('mount-point' => '/mount', 'directory' => __DIR__)),
array(array(array('mount-point' => '/mount'))),
array(array(array('directory' => __DIR__))),
);
}
}