| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- <?php
- /**
- * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
- * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- *
- * Licensed under The MIT License
- * For full copyright and license information, please see the LICENSE.txt
- * Redistributions of files must retain the above copyright notice.
- *
- * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
- * @link https://cakephp.org CakePHP(tm) Project
- * @since 3.3.0
- * @license https://opensource.org/licenses/mit-license.php MIT License
- */
- namespace Cake\Routing\Middleware;
- use Cake\Core\Plugin;
- use Cake\Filesystem\File;
- use Cake\Utility\Inflector;
- use Laminas\Diactoros\Response;
- use Laminas\Diactoros\Stream;
- use Psr\Http\Message\ResponseInterface;
- use Psr\Http\Message\ServerRequestInterface;
- /**
- * Handles serving plugin assets in development mode.
- *
- * This should not be used in production environments as it
- * has sub-optimal performance when compared to serving files
- * with a real webserver.
- */
- class AssetMiddleware
- {
- /**
- * The amount of time to cache the asset.
- *
- * @var string
- */
- protected $cacheTime = '+1 day';
- /**
- * A extension to content type mapping for plain text types.
- *
- * Because finfo doesn't give useful information for plain text types,
- * we have to handle that here.
- *
- * @var array
- */
- protected $typeMap = [
- 'css' => 'text/css',
- 'json' => 'application/json',
- 'js' => 'application/javascript',
- 'ico' => 'image/x-icon',
- 'eot' => 'application/vnd.ms-fontobject',
- 'svg' => 'image/svg+xml',
- 'html' => 'text/html',
- 'rss' => 'application/rss+xml',
- 'xml' => 'application/xml',
- ];
- /**
- * Constructor.
- *
- * @param array $options The options to use
- */
- public function __construct(array $options = [])
- {
- if (!empty($options['cacheTime'])) {
- $this->cacheTime = $options['cacheTime'];
- }
- if (!empty($options['types'])) {
- $this->typeMap = array_merge($this->typeMap, $options['types']);
- }
- }
- /**
- * Serve assets if the path matches one.
- *
- * @param \Psr\Http\Message\ServerRequestInterface $request The request.
- * @param \Psr\Http\Message\ResponseInterface $response The response.
- * @param callable $next Callback to invoke the next middleware.
- * @return \Psr\Http\Message\ResponseInterface A response
- */
- public function __invoke($request, $response, $next)
- {
- $url = $request->getUri()->getPath();
- if (strpos($url, '..') !== false || strpos($url, '.') === false) {
- return $next($request, $response);
- }
- if (strpos($url, '/.') !== false) {
- return $next($request, $response);
- }
- $assetFile = $this->_getAssetFile($url);
- if ($assetFile === null || !file_exists($assetFile)) {
- return $next($request, $response);
- }
- $file = new File($assetFile);
- $modifiedTime = $file->lastChange();
- if ($this->isNotModified($request, $file)) {
- $headers = $response->getHeaders();
- $headers['Last-Modified'] = date(DATE_RFC850, $modifiedTime);
- return new Response('php://memory', 304, $headers);
- }
- return $this->deliverAsset($request, $response, $file);
- }
- /**
- * Check the not modified header.
- *
- * @param \Psr\Http\Message\ServerRequestInterface $request The request to check.
- * @param \Cake\Filesystem\File $file The file object to compare.
- * @return bool
- */
- protected function isNotModified($request, $file)
- {
- $modifiedSince = $request->getHeaderLine('If-Modified-Since');
- if (!$modifiedSince) {
- return false;
- }
- return strtotime($modifiedSince) === $file->lastChange();
- }
- /**
- * Builds asset file path based off url
- *
- * @param string $url Asset URL
- * @return string|null Absolute path for asset file, null on failure
- */
- protected function _getAssetFile($url)
- {
- $parts = explode('/', ltrim($url, '/'));
- $pluginPart = [];
- for ($i = 0; $i < 2; $i++) {
- if (!isset($parts[$i])) {
- break;
- }
- $pluginPart[] = Inflector::camelize($parts[$i]);
- $plugin = implode('/', $pluginPart);
- if ($plugin && Plugin::isLoaded($plugin)) {
- $parts = array_slice($parts, $i + 1);
- $fileFragment = implode(DIRECTORY_SEPARATOR, $parts);
- $pluginWebroot = Plugin::path($plugin) . 'webroot' . DIRECTORY_SEPARATOR;
- return $pluginWebroot . $fileFragment;
- }
- }
- return null;
- }
- /**
- * Sends an asset file to the client
- *
- * @param \Psr\Http\Message\ServerRequestInterface $request The request object to use.
- * @param \Psr\Http\Message\ResponseInterface $response The response object to use.
- * @param \Cake\Filesystem\File $file The file wrapper for the file.
- * @return \Psr\Http\Message\ResponseInterface The response with the file & headers.
- */
- protected function deliverAsset(ServerRequestInterface $request, ResponseInterface $response, $file)
- {
- $contentType = $this->getType($file);
- $modified = $file->lastChange();
- $expire = strtotime($this->cacheTime);
- $maxAge = $expire - time();
- $stream = new Stream(fopen($file->path, 'rb'));
- return $response->withBody($stream)
- ->withHeader('Content-Type', $contentType)
- ->withHeader('Cache-Control', 'public,max-age=' . $maxAge)
- ->withHeader('Date', gmdate('D, j M Y G:i:s \G\M\T', time()))
- ->withHeader('Last-Modified', gmdate('D, j M Y G:i:s \G\M\T', $modified))
- ->withHeader('Expires', gmdate('D, j M Y G:i:s \G\M\T', $expire));
- }
- /**
- * Return the type from a File object
- *
- * @param File $file The file from which you get the type
- * @return string
- */
- protected function getType($file)
- {
- $extension = $file->ext();
- if (isset($this->typeMap[$extension])) {
- return $this->typeMap[$extension];
- }
- return $file->mime() ?: 'application/octet-stream';
- }
- }
|