Skip to content
This repository has been archived by the owner on Nov 25, 2020. It is now read-only.

Commit

Permalink
Implement our own accelerators (dl / up) using headers. Todo: recheck…
Browse files Browse the repository at this point in the history
… generic StreamWrapper
  • Loading branch information
cdujeu committed Jul 9, 2016
1 parent e0d7b8e commit 67f291c
Show file tree
Hide file tree
Showing 18 changed files with 641 additions and 191 deletions.
73 changes: 73 additions & 0 deletions core/src/core/src/pydio/Core/Http/Message/ExternalUploadedFile.php
@@ -0,0 +1,73 @@
<?php
/*
* Copyright 2007-2016 Abstrium <contact (at) pydio.com>
* This file is part of Pydio.
*
* Pydio 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.
*
* Pydio 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 Pydio. If not, see <http://www.gnu.org/licenses/>.
*
* The latest code can be found at <https://pydio.com/>.
*/
namespace Pydio\Core\Http\Message;

use Zend\Diactoros\UploadedFile;

defined('AJXP_EXEC') or die('Access not allowed');

/**
* Class ExternalUploadedFile
* @package Pydio\Core\Http\Message
*/
class ExternalUploadedFile extends UploadedFile
{
const STATUS_REQUEST_OPTIONS = "request-options";
const STATUS_UPLOAD_FINISHED = "upload-finished";
const STATUS_UPLOAD_ERROR = "upload-error";

private $status;

/**
* ExternalUploadedFile constructor.
* @param string $status self::STATUS_REQUEST_OPTIONS, UPLOAD_FINISHED, UPLOAD_ERROR
* @param int $size
* @param int $clientFilename
*/
public function __construct($status, $size, $clientFilename)
{
parent::__construct(
"fake-tmp-file",
$size,
$status === self::STATUS_UPLOAD_ERROR ? UPLOAD_ERR_NO_FILE : UPLOAD_ERR_OK,
$clientFilename,
null
);
$this->status = $status;
}

/**
* @return string One of the ExternalUploadedFile::STATUS_XXX constant value
*/
public function getStatus(){
return $this->status;
}

/**
* Check if status has a valid value
* @param $status
* @return bool
*/
public static function isValidStatus($status){
return in_array($status, [self::STATUS_REQUEST_OPTIONS, self::STATUS_UPLOAD_ERROR, self::STATUS_UPLOAD_FINISHED]);
}

}
143 changes: 105 additions & 38 deletions core/src/core/src/pydio/Core/Http/Response/FileReaderResponse.php
Expand Up @@ -26,6 +26,7 @@
use Pydio\Core\Controller\HTMLWriter;
use Pydio\Core\Services\ConfService;
use Pydio\Core\Utils\ApplicationState;
use Pydio\Core\Utils\FileHelper;
use Pydio\Core\Utils\Vars\StatHelper;
use Pydio\Core\Utils\TextEncoder;
use Pydio\Log\Core\Logger;
Expand Down Expand Up @@ -69,6 +70,9 @@ class FileReaderResponse extends AsyncResponseStream
/** @var callable */
private $postRead;

/** @var boolean */
private $unlinkAfterRead;

/**
* FileReaderResponse constructor.
* @param AJXP_Node|string $nodeOrFile
Expand Down Expand Up @@ -139,6 +143,13 @@ public function setPostReadCallback(callable $post){
$this->postRead = $post;
}

/**
* Set a flag to trigger an unlink after reading the file.
*/
public function setUnlinkAfterRead(){
$this->unlinkAfterRead = true;
}

/**
* Actually read the data to the output
* @throws \Exception
Expand Down Expand Up @@ -178,8 +189,13 @@ public function readFile($node = null, $filePath = null, $data = null, $headerTy
}
$confGzip = ConfService::getGlobalConf("GZIP_COMPRESSION");
$confGzipLimit = ConfService::getGlobalConf("GZIP_LIMIT");
$confUseXSendFile = ConfService::getGlobalConf("USE_XSENDFILE");
$confUseXAccelRedirect = ConfService::getGlobalConf("USE_XACCELREDIRECT");
$confUseAccelerator = ConfService::getGlobalConf("USE_DOWNLOAD_ACCELERATOR");
if($this->unlinkAfterRead && $filePathOrData !== null && empty($confUseAccelerator)){
register_shutdown_function(function () use ($filePathOrData){
FileHelper::silentUnlink($filePathOrData);
});
}

$fakeReq = ServerRequestFactory::fromGlobals();
$serverParams = $fakeReq->getServerParams();

Expand Down Expand Up @@ -320,42 +336,9 @@ public function readFile($node = null, $filePath = null, $data = null, $headerTy

} else {

if(($node !== null && !$node->wrapperIsRemote()) || $filePath !== null){

if ($confUseXSendFile) {
if($node != null) {
$filePathOrData = $node->getRealFile();
}
$filePathOrData = str_replace("\\", "/", $filePathOrData);
$server_name = $serverParams["SERVER_SOFTWARE"];
$regex = '/^(lighttpd\/1.4).([0-9]{2}$|[0-9]{3}$|[0-9]{4}$)+/';
if(preg_match($regex, $server_name))
$header_sendfile = "X-LIGHTTPD-send-file";
else
$header_sendfile = "X-Sendfile";


header($header_sendfile.": ".TextEncoder::toUTF8($filePathOrData));
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($filePathOrData) . '"');
return;
}
if ($confUseXAccelRedirect && array_key_exists("X-Accel-Mapping", $serverParams)) {
if($node !== null) {
$filePathOrData = $node->getRealFile();
}
$filePathOrData = str_replace("\\", "/", $filePathOrData);
$filePathOrData = TextEncoder::toUTF8($filePathOrData);
$mapping = explode('=',$serverParams['X-Accel-Mapping']);
$replacecount = 0;
$accelfile = str_replace($mapping[0],$mapping[1],$filePathOrData,$replacecount);
if ($replacecount == 1) {
header("X-Accel-Redirect: $accelfile");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($accelfile) . '"');
} else {
$this->logDebug("X-Accel-Redirect: Problem with X-Accel-Mapping for file $filePathOrData");
}
if ( !empty($confUseAccelerator)){
$requestSent = $this->sendToAccelerator($confUseAccelerator, ($node !== null ? $node : $filePath), $serverParams);
if($requestSent){
return;
}
}
Expand Down Expand Up @@ -400,5 +383,89 @@ public function readFile($node = null, $filePath = null, $data = null, $headerTy
}
}

/**
* @param string $accelConfiguration
* @param string|AJXP_Node $localPathOrNode
* @param array $serverParams
* @return bool Wether headers were sent and we should interrupt DL now or not.
*/
protected function sendToAccelerator($accelConfiguration, $localPathOrNode, $serverParams){

$remoteNode = false;
if($localPathOrNode instanceof AJXP_Node) {
$filePathOrData = $localPathOrNode->getRealFile();
$remoteNode = $localPathOrNode->wrapperIsRemote();
}else{
$filePathOrData = $localPathOrNode;
}

// TRY XSendFile for local FS nodes or local file
if (!$remoteNode && $accelConfiguration === "xsendfile") {

$filePathOrData = str_replace("\\", "/", $filePathOrData);
$server_name = $serverParams["SERVER_SOFTWARE"];
$regex = '/^(lighttpd\/1.4).([0-9]{2}$|[0-9]{3}$|[0-9]{4}$)+/';
if(preg_match($regex, $server_name))
$header_sendfile = "X-LIGHTTPD-send-file";
else
$header_sendfile = "X-Sendfile";


header($header_sendfile.": ".TextEncoder::toUTF8($filePathOrData));
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($filePathOrData) . '"');
return true;

}

// TRY XAccelRedirect for local FS nodes or local file
if (!$remoteNode && $accelConfiguration === "xaccelredirect" && array_key_exists("HTTP_X_ACCEL_MAPPING", $serverParams)) {

$filePathOrData = str_replace("\\", "/", $filePathOrData);
$filePathOrData = TextEncoder::toUTF8($filePathOrData);
$mapping = explode('=',$serverParams['X-Accel-Mapping']);
$replacecount = 0;
$accelfile = str_replace($mapping[0],$mapping[1],$filePathOrData,$replacecount);
if ($replacecount == 1) {
header("X-Accel-Redirect: $accelfile");
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($accelfile) . '"');
return true;
} else {
$this->logDebug("X-Accel-Redirect: Problem with X-Accel-Mapping for file $filePathOrData");
return false;
}

}

// Pydio Agent acceleration - We make sure that request was really proxied by Agent, by checking a specific header.
if($accelConfiguration === "pydio" && array_key_exists("HTTP_X_PYDIO_DOWNLOAD_SUPPORTED", $serverParams)) {

if ($localPathOrNode instanceof AJXP_Node) {
$options = MetaStreamWrapper::getResolvedOptionsForNode($localPathOrNode);
if($options["TYPE"] === "php"){
// Not implemented
return false;
}
$path = $localPathOrNode->getPath();
}else{
$options = ["TYPE" => "local"];
$path = $localPathOrNode;
}
$data = [
"OPTIONS" => $options,
"PATH" => $path
];
if($this->unlinkAfterRead){
$data["UNLINK_AFTER_READ"] = true;
}
header("X-Pydio-Download-Redirect: ".json_encode($data));
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . basename($path) . '"');
return true;
}

return false;
}

}

0 comments on commit 67f291c

Please sign in to comment.