Mercurial > hg > cmmr2012-drupal-site
comparison core/modules/image/src/Controller/ImageStyleDownloadController.php @ 0:c75dbcec494b
Initial commit from drush-created site
author | Chris Cannam |
---|---|
date | Thu, 05 Jul 2018 14:24:15 +0000 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:c75dbcec494b |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\image\Controller; | |
4 | |
5 use Drupal\Component\Utility\Crypt; | |
6 use Drupal\Core\Image\ImageFactory; | |
7 use Drupal\Core\Lock\LockBackendInterface; | |
8 use Drupal\image\ImageStyleInterface; | |
9 use Drupal\system\FileDownloadController; | |
10 use Symfony\Component\DependencyInjection\ContainerInterface; | |
11 use Symfony\Component\HttpFoundation\BinaryFileResponse; | |
12 use Symfony\Component\HttpFoundation\Request; | |
13 use Symfony\Component\HttpFoundation\Response; | |
14 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | |
15 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; | |
16 use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException; | |
17 | |
18 /** | |
19 * Defines a controller to serve image styles. | |
20 */ | |
21 class ImageStyleDownloadController extends FileDownloadController { | |
22 | |
23 /** | |
24 * The lock backend. | |
25 * | |
26 * @var \Drupal\Core\Lock\LockBackendInterface | |
27 */ | |
28 protected $lock; | |
29 | |
30 /** | |
31 * The image factory. | |
32 * | |
33 * @var \Drupal\Core\Image\ImageFactory | |
34 */ | |
35 protected $imageFactory; | |
36 | |
37 /** | |
38 * A logger instance. | |
39 * | |
40 * @var \Psr\Log\LoggerInterface | |
41 */ | |
42 protected $logger; | |
43 | |
44 /** | |
45 * Constructs a ImageStyleDownloadController object. | |
46 * | |
47 * @param \Drupal\Core\Lock\LockBackendInterface $lock | |
48 * The lock backend. | |
49 * @param \Drupal\Core\Image\ImageFactory $image_factory | |
50 * The image factory. | |
51 */ | |
52 public function __construct(LockBackendInterface $lock, ImageFactory $image_factory) { | |
53 $this->lock = $lock; | |
54 $this->imageFactory = $image_factory; | |
55 $this->logger = $this->getLogger('image'); | |
56 } | |
57 | |
58 /** | |
59 * {@inheritdoc} | |
60 */ | |
61 public static function create(ContainerInterface $container) { | |
62 return new static( | |
63 $container->get('lock'), | |
64 $container->get('image.factory') | |
65 ); | |
66 } | |
67 | |
68 /** | |
69 * Generates a derivative, given a style and image path. | |
70 * | |
71 * After generating an image, transfer it to the requesting agent. | |
72 * | |
73 * @param \Symfony\Component\HttpFoundation\Request $request | |
74 * The request object. | |
75 * @param string $scheme | |
76 * The file scheme, defaults to 'private'. | |
77 * @param \Drupal\image\ImageStyleInterface $image_style | |
78 * The image style to deliver. | |
79 * | |
80 * @return \Symfony\Component\HttpFoundation\BinaryFileResponse|\Symfony\Component\HttpFoundation\Response | |
81 * The transferred file as response or some error response. | |
82 * | |
83 * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException | |
84 * Thrown when the file request is invalid. | |
85 * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException | |
86 * Thrown when the user does not have access to the file. | |
87 * @throws \Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException | |
88 * Thrown when the file is still being generated. | |
89 */ | |
90 public function deliver(Request $request, $scheme, ImageStyleInterface $image_style) { | |
91 $target = $request->query->get('file'); | |
92 $image_uri = $scheme . '://' . $target; | |
93 | |
94 // Check that the style is defined, the scheme is valid, and the image | |
95 // derivative token is valid. Sites which require image derivatives to be | |
96 // generated without a token can set the | |
97 // 'image.settings:allow_insecure_derivatives' configuration to TRUE to | |
98 // bypass the latter check, but this will increase the site's vulnerability | |
99 // to denial-of-service attacks. To prevent this variable from leaving the | |
100 // site vulnerable to the most serious attacks, a token is always required | |
101 // when a derivative of a style is requested. | |
102 // The $target variable for a derivative of a style has | |
103 // styles/<style_name>/... as structure, so we check if the $target variable | |
104 // starts with styles/. | |
105 $valid = !empty($image_style) && file_stream_wrapper_valid_scheme($scheme); | |
106 if (!$this->config('image.settings')->get('allow_insecure_derivatives') || strpos(ltrim($target, '\/'), 'styles/') === 0) { | |
107 $valid &= $request->query->get(IMAGE_DERIVATIVE_TOKEN) === $image_style->getPathToken($image_uri); | |
108 } | |
109 if (!$valid) { | |
110 // Return a 404 (Page Not Found) rather than a 403 (Access Denied) as the | |
111 // image token is for DDoS protection rather than access checking. 404s | |
112 // are more likely to be cached (e.g. at a proxy) which enhances | |
113 // protection from DDoS. | |
114 throw new NotFoundHttpException(); | |
115 } | |
116 | |
117 $derivative_uri = $image_style->buildUri($image_uri); | |
118 $headers = []; | |
119 | |
120 // If using the private scheme, let other modules provide headers and | |
121 // control access to the file. | |
122 if ($scheme == 'private') { | |
123 $headers = $this->moduleHandler()->invokeAll('file_download', [$image_uri]); | |
124 if (in_array(-1, $headers) || empty($headers)) { | |
125 throw new AccessDeniedHttpException(); | |
126 } | |
127 } | |
128 | |
129 // Don't try to generate file if source is missing. | |
130 if (!file_exists($image_uri)) { | |
131 // If the image style converted the extension, it has been added to the | |
132 // original file, resulting in filenames like image.png.jpeg. So to find | |
133 // the actual source image, we remove the extension and check if that | |
134 // image exists. | |
135 $path_info = pathinfo($image_uri); | |
136 $converted_image_uri = $path_info['dirname'] . DIRECTORY_SEPARATOR . $path_info['filename']; | |
137 if (!file_exists($converted_image_uri)) { | |
138 $this->logger->notice('Source image at %source_image_path not found while trying to generate derivative image at %derivative_path.', ['%source_image_path' => $image_uri, '%derivative_path' => $derivative_uri]); | |
139 return new Response($this->t('Error generating image, missing source file.'), 404); | |
140 } | |
141 else { | |
142 // The converted file does exist, use it as the source. | |
143 $image_uri = $converted_image_uri; | |
144 } | |
145 } | |
146 | |
147 // Don't start generating the image if the derivative already exists or if | |
148 // generation is in progress in another thread. | |
149 if (!file_exists($derivative_uri)) { | |
150 $lock_name = 'image_style_deliver:' . $image_style->id() . ':' . Crypt::hashBase64($image_uri); | |
151 $lock_acquired = $this->lock->acquire($lock_name); | |
152 if (!$lock_acquired) { | |
153 // Tell client to retry again in 3 seconds. Currently no browsers are | |
154 // known to support Retry-After. | |
155 throw new ServiceUnavailableHttpException(3, $this->t('Image generation in progress. Try again shortly.')); | |
156 } | |
157 } | |
158 | |
159 // Try to generate the image, unless another thread just did it while we | |
160 // were acquiring the lock. | |
161 $success = file_exists($derivative_uri) || $image_style->createDerivative($image_uri, $derivative_uri); | |
162 | |
163 if (!empty($lock_acquired)) { | |
164 $this->lock->release($lock_name); | |
165 } | |
166 | |
167 if ($success) { | |
168 $image = $this->imageFactory->get($derivative_uri); | |
169 $uri = $image->getSource(); | |
170 $headers += [ | |
171 'Content-Type' => $image->getMimeType(), | |
172 'Content-Length' => $image->getFileSize(), | |
173 ]; | |
174 // \Drupal\Core\EventSubscriber\FinishResponseSubscriber::onRespond() | |
175 // sets response as not cacheable if the Cache-Control header is not | |
176 // already modified. We pass in FALSE for non-private schemes for the | |
177 // $public parameter to make sure we don't change the headers. | |
178 return new BinaryFileResponse($uri, 200, $headers, $scheme !== 'private'); | |
179 } | |
180 else { | |
181 $this->logger->notice('Unable to generate the derived image located at %path.', ['%path' => $derivative_uri]); | |
182 return new Response($this->t('Error generating image.'), 500); | |
183 } | |
184 } | |
185 | |
186 } |