Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@
</parameters>

<services>
<service id="igorw_file_serve.response_factory.php" class="%igorw_file_serve.response_factory.php.class%" public="false" scope="request">
<service id="igorw_file_serve.response_factory.php" class="%igorw_file_serve.response_factory.php.class%" public="false">
<argument>%igorw_file_serve.base_dir%</argument>
<argument type="service" id="request" />
<argument type="service" id="request_stack" />
<argument>%igorw_file_serve.skip_file_exists%</argument>
</service>
<service id="igorw_file_serve.response_factory.sendfile" class="%igorw_file_serve.response_factory.sendfile.class%" public="false" scope="request">
<service id="igorw_file_serve.response_factory.sendfile" class="%igorw_file_serve.response_factory.sendfile.class%" public="false">
<argument>%igorw_file_serve.base_dir%</argument>
<argument type="service" id="request" />
<argument type="service" id="request_stack" />
<argument>%igorw_file_serve.skip_file_exists%</argument>
</service>
<service id="igorw_file_serve.response_factory.xsendfile" class="%igorw_file_serve.response_factory.xsendfile.class%" public="false" scope="request">
<service id="igorw_file_serve.response_factory.xsendfile" class="%igorw_file_serve.response_factory.xsendfile.class%" public="false">
<argument>%igorw_file_serve.base_dir%</argument>
<argument type="service" id="request" />
<argument type="service" id="request_stack" />
<argument>%igorw_file_serve.skip_file_exists%</argument>
</service>
</services>
Expand Down
41 changes: 33 additions & 8 deletions Response/AbstractResponseFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@

namespace Igorw\FileServeBundle\Response;

use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;

abstract class AbstractResponseFactory
{
protected $baseDir;
protected $skipFileExists;
protected $request;
protected $requestStack;
protected $fullFilename;
protected $contentType;
protected $options;

public function __construct($baseDir, Request $request, $skipFileExists = false)
public function __construct($baseDir, RequestStack $requestStack, $skipFileExists = false)
{
$this->baseDir = $baseDir;
$this->request = $request;
$this->requestStack = $requestStack;
$this->skipFileExists = $skipFileExists;
}

Expand Down Expand Up @@ -68,16 +68,41 @@ protected function resolveDispositionHeader(array $options)
}

/**
* Algorithm inspired by phpBB3
* @param string $filename
*
* @return string
*/
protected function resolveDispositionHeaderFilename($filename)
{
$userAgent = $this->request->headers->get('User-Agent');
if ($this->clientSupportsUtf8Filename()) {
$dispositionHeaderFilename = "filename*=UTF-8''".rawurlencode($filename);
} else {
$dispositionHeaderFilename = "filename=".rawurlencode($filename);
}

return $dispositionHeaderFilename;
}

/**
* Algorithm inspired by phpBB3
*
* @throws \LogicException
*
* @return bool
*/
private function clientSupportsUtf8Filename()
{
$request = $this->requestStack->getCurrentRequest();
if (is_null($request)) {
throw new \LogicException('Current request is not available');
}

$userAgent = $request->headers->get('User-Agent');

if (preg_match('#MSIE|Safari|Konqueror#', $userAgent)) {
return "filename=".rawurlencode($filename);
return false;
}

return "filename*=UTF-8''".rawurlencode($filename);
return true;
}
}
49 changes: 39 additions & 10 deletions Tests/Response/PhpResponseFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
namespace Igorw\FileServeBundle\Response;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

class PhpResponseFactoryTest extends \PHPUnit_Framework_TestCase
{
/**
* @test
* @dataProvider provideRequestsAndContentDisposition
*/
public function createShouldReturnResponseWithRequestSpecificContentDisposition($disposition, $request)
public function createShouldReturnResponseWithRequestSpecificContentDisposition($disposition, $requestStack)
{
$factory = new PhpResponseFactory(__DIR__.'/../Fixtures', $request);
$factory = new PhpResponseFactory(__DIR__.'/../Fixtures', $requestStack);

$options = array(
'serve_filename' => 'internet.zip',
Expand All @@ -26,7 +27,8 @@ public function createShouldReturnResponseWithRequestSpecificContentDisposition(
/** @test */
public function createWithRelativePath()
{
$factory = new PhpResponseFactory(__DIR__.'/../Fixtures', new Request());
$requestStack = $this->createRequestStack();
$factory = new PhpResponseFactory(__DIR__.'/../Fixtures', $requestStack);

$response = $factory->create('internet.txt', 'text/plain');

Expand All @@ -40,7 +42,8 @@ public function createWithRelativePath()
/** @test */
public function createWithAbsolutePath()
{
$factory = new PhpResponseFactory(__DIR__.'/../Fixtures', new Request());
$requestStack = $this->createRequestStack();
$factory = new PhpResponseFactory(__DIR__.'/../Fixtures', $requestStack);

$response = $factory->create(__DIR__.'/../Fixtures/internet.txt', 'text/plain', array(
'absolute_path' => true,
Expand All @@ -59,23 +62,49 @@ public function createWithAbsolutePath()
*/
public function createWithNonExistentPathShouldThrowException()
{
$factory = new PhpResponseFactory(__DIR__.'/../Fixtures', new Request());
$requestStack = $this->createRequestStack();
$factory = new PhpResponseFactory(__DIR__.'/../Fixtures', $requestStack);

$response = $factory->create(__DIR__.'/../Fixtures/missing.txt', 'text/plain');
}

public function provideRequestsAndContentDisposition()
{
return array(
array('attachment; filename=internet.zip', $this->createRequestWithUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.11 (KHTML, like Gecko) Ubuntu/12.04 Chromium/20.0.1132.47 Chrome/20.0.1132.47 Safari/536.11')),
array('attachment; filename*=UTF-8\'\'internet.zip', $this->createRequestWithUserAgent('Yeti/1.0 (NHN Corp.; http://help.naver.com/robots/)')),
'user agent without UTF-8 filename support' => array(
'attachment; filename=internet.zip',
$this->createRequestStackWithUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.11 (KHTML, like Gecko) Ubuntu/12.04 Chromium/20.0.1132.47 Chrome/20.0.1132.47 Safari/536.11')
),
'user agent with UTF-8 filename support' => array(
'attachment; filename*=UTF-8\'\'internet.zip',
$this->createRequestStackWithUserAgent('Yeti/1.0 (NHN Corp.; http://help.naver.com/robots/)')
),
);
}

private function createRequestWithUserAgent($userAgent)
/**
* @param string $userAgent
*
* @return RequestStack
*/
private function createRequestStackWithUserAgent($userAgent)
{
$requestStack = $this->createRequestStack();
$requestStack->getCurrentRequest()->headers->set('User-Agent', $userAgent);

return $requestStack;
}

/**
* @return RequestStack
*/
private function createRequestStack()
{
$request = Request::create('/');
$request->headers->set('User-Agent', $userAgent);
return $request;

$requestStack = new RequestStack();
$requestStack->push($request);

return $requestStack;
}
}
17 changes: 16 additions & 1 deletion Tests/Response/SendfileResponseFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,30 @@
namespace Igorw\FileServeBundle\Response;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

class SendfileResponseFactoryTest extends \PHPUnit_Framework_TestCase
{
/** @test */
public function createShouldSetXAccelRedirectHeader()
{
$factory = new SendfileResponseFactory(__DIR__.'/../Fixtures', new Request());
$requestStack = $this->createRequestStack();
$factory = new SendfileResponseFactory(__DIR__.'/../Fixtures', $requestStack);
$response = $factory->create('internet.txt', 'application/zip');

$this->assertSame(__DIR__.'/../Fixtures/internet.txt', $response->headers->get('X-Accel-Redirect'));
}

/**
* @return RequestStack
*/
private function createRequestStack()
{
$request = Request::create('/');

$requestStack = new RequestStack();
$requestStack->push($request);

return $requestStack;
}
}
17 changes: 16 additions & 1 deletion Tests/Response/XsendfileResponseFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,30 @@
namespace Igorw\FileServeBundle\Response;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

class XsendfileResponseFactoryTest extends \PHPUnit_Framework_TestCase
{
/** @test */
public function createShouldSetXSendfileHeader()
{
$factory = new XsendfileResponseFactory(__DIR__.'/../Fixtures', new Request());
$requestStack = $this->createRequestStack();
$factory = new XsendfileResponseFactory(__DIR__.'/../Fixtures', $requestStack);
$response = $factory->create('internet.txt', 'application/zip');

$this->assertSame(__DIR__.'/../Fixtures/internet.txt', $response->headers->get('X-Sendfile'));
}

/**
* @return RequestStack
*/
private function createRequestStack()
{
$request = Request::create('/');

$requestStack = new RequestStack();
$requestStack->push($request);

return $requestStack;
}
}