Skip to content
Merged
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
11 changes: 3 additions & 8 deletions core/Controller/AvatarController.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\Files\File;
use OCP\Files\IRootFolder;
Expand Down Expand Up @@ -118,7 +119,7 @@ public function __construct($appName,
*
* @param string $userId
* @param int $size
* @return JSONResponse|DataDisplayResponse
* @return JSONResponse|FileDisplayResponse
*/
public function getAvatar($userId, $size) {
if ($size > 2048) {
Expand All @@ -129,25 +130,19 @@ public function getAvatar($userId, $size) {

try {
$avatar = $this->avatarManager->getAvatar($userId)->getFile($size);
$resp = new DataDisplayResponse($avatar->getContent(),
$resp = new FileDisplayResponse($avatar,
Http::STATUS_OK,
['Content-Type' => $avatar->getMimeType()]);
$resp->setETag($avatar->getEtag());

// Let cache this!
$resp->addHeader('Pragma', 'public');
// Cache for 15 minutes
$resp->cacheFor(900);
// Set last modified
$lastModified = new \DateTime();
$lastModified->setTimestamp($avatar->getMTime());
$resp->setLastModified($lastModified);

$expires = new \DateTime();
$expires->setTimestamp($this->timeFactory->getTime());
$expires->add(new \DateInterval('PT15M'));
$resp->addHeader('Expires', $expires->format(\DateTime::RFC2822));

} catch (NotFoundException $e) {
$user = $this->userManager->get($userId);
$resp = new JSONResponse([
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
'OCP\\AppFramework\\Http\\DataResponse' => $baseDir . '/lib/public/AppFramework/Http/DataResponse.php',
'OCP\\AppFramework\\Http\\DownloadResponse' => $baseDir . '/lib/public/AppFramework/Http/DownloadResponse.php',
'OCP\\AppFramework\\Http\\EmptyContentSecurityPolicy' => $baseDir . '/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php',
'OCP\\AppFramework\\Http\\FileDisplayResponse' => $baseDir . '/lib/public/AppFramework/Http/FileDisplayResponse.php',
'OCP\\AppFramework\\Http\\ICallbackResponse' => $baseDir . '/lib/public/AppFramework/Http/ICallbackResponse.php',
'OCP\\AppFramework\\Http\\IOutput' => $baseDir . '/lib/public/AppFramework/Http/IOutput.php',
'OCP\\AppFramework\\Http\\JSONResponse' => $baseDir . '/lib/public/AppFramework/Http/JSONResponse.php',
Expand Down
1 change: 1 addition & 0 deletions lib/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c
'OCP\\AppFramework\\Http\\DataResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DataResponse.php',
'OCP\\AppFramework\\Http\\DownloadResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/DownloadResponse.php',
'OCP\\AppFramework\\Http\\EmptyContentSecurityPolicy' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php',
'OCP\\AppFramework\\Http\\FileDisplayResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/FileDisplayResponse.php',
'OCP\\AppFramework\\Http\\ICallbackResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/ICallbackResponse.php',
'OCP\\AppFramework\\Http\\IOutput' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/IOutput.php',
'OCP\\AppFramework\\Http\\JSONResponse' => __DIR__ . '/../../..' . '/lib/public/AppFramework/Http/JSONResponse.php',
Expand Down
70 changes: 70 additions & 0 deletions lib/public/AppFramework/Http/FileDisplayResponse.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php
/**
* @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCP\AppFramework\Http;

use OCP\AppFramework\Http;
use OCP\Files\File;

/**
* Class FileDisplayResponse
*
* @package OCP\AppFramework\Http
* @since 9.2.0
*/
class FileDisplayResponse extends Response implements ICallbackResponse {

/** @var File */
private $file;

/**
* FileDisplayResponse constructor.
*
* @param File $file
* @param int $statusCode
* @param array $headers
* @since 9.2.0
*/
public function __construct(File $file, $statusCode=Http::STATUS_OK,
$headers=[]) {
$this->file = $file;
$this->setStatus($statusCode);
$this->setHeaders(array_merge($this->getHeaders(), $headers));
$this->addHeader('Content-Disposition', 'inline; filename="' . rawurldecode($file->getName()) . '"');

$this->setETag($file->getEtag());
$lastModified = new \DateTime();
$lastModified->setTimestamp($file->getMTime());
$this->setLastModified($lastModified);
}

/**
* @param IOutput $output
* @since 9.2.0
*/
public function callback(IOutput $output) {
if ($output->getHttpResponseCode() !== Http::STATUS_NOT_MODIFIED) {
$output->setHeader('Content-Length: ' . $this->file->getSize());
$output->setOutput($this->file->getContent());
Copy link
Member Author

@rullzer rullzer Aug 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@icewind1991: @BernhardPosselt noted here that this might not be very efficient. We might want to use something like http://php.net/manual/en/function.fpassthru.php

But i'd say that is a later optimization

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the filesystem side we can already do $this->file->fopen('r'), so we just need to have the appframework be able to handle streams as output

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah fair enough. But like I say I'll do that later. This is already much better then the current stuff.

}
}
}
109 changes: 109 additions & 0 deletions tests/lib/AppFramework/Http/FileDisplayResponseTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<?php
/**
* @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace Test\AppFramework\Http;

use OCP\AppFramework\Http;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\Files\File;

class FileDisplayResponseTest extends \Test\TestCase {
/** @var File|\PHPUnit_Framework_MockObject_MockObject */
private $file;

/** @var FileDisplayResponse */
private $response;

public function setup() {
$this->file = $this->getMockBuilder('OCP\Files\File')
->getMock();

$this->file->expects($this->once())
->method('getETag')
->willReturn('myETag');
$this->file->expects($this->once())
->method('getName')
->willReturn('myFileName');
$this->file->expects($this->once())
->method('getMTime')
->willReturn(1464825600);

$this->response = new FileDisplayResponse($this->file);
}

public function testHeader() {
$headers = $this->response->getHeaders();
$this->assertArrayHasKey('Content-Disposition', $headers);
$this->assertSame('inline; filename="myFileName"', $headers['Content-Disposition']);
}

public function testETag() {
$this->assertSame('myETag', $this->response->getETag());
}

public function testLastModified() {
$lastModified = $this->response->getLastModified();
$this->assertNotNull($lastModified);
$this->assertSame(1464825600, $lastModified->getTimestamp());
}

public function test304() {
$output = $this->getMockBuilder('OCP\AppFramework\Http\IOutput')
->disableOriginalConstructor()
->getMock();

$output->expects($this->any())
->method('getHttpResponseCode')
->willReturn(Http::STATUS_NOT_MODIFIED);
$output->expects($this->never())
->method('setOutput');
$this->file->expects($this->never())
->method('getContent');

$this->response->callback($output);
}


public function testNon304() {
$output = $this->getMockBuilder('OCP\AppFramework\Http\IOutput')
->disableOriginalConstructor()
->getMock();

$output->expects($this->any())
->method('getHttpResponseCode')
->willReturn(Http::STATUS_OK);
$output->expects($this->once())
->method('setOutput')
->with($this->equalTo('my data'));
$output->expects($this->once())
->method('setHeader')
->with($this->equalTo('Content-Length: 42'));
$this->file->expects($this->once())
->method('getContent')
->willReturn('my data');
$this->file->expects($this->any())
->method('getSize')
->willReturn(42);

$this->response->callback($output);
}
}