comparison vendor/symfony/http-kernel/HttpCache/HttpCache.php @ 17:129ea1e6d783

Update, including to Drupal core 8.6.10
author Chris Cannam
date Thu, 28 Feb 2019 13:21:36 +0000
parents c2387f117808
children
comparison
equal deleted inserted replaced
16:c2387f117808 17:129ea1e6d783
13 * file that was distributed with this source code. 13 * file that was distributed with this source code.
14 */ 14 */
15 15
16 namespace Symfony\Component\HttpKernel\HttpCache; 16 namespace Symfony\Component\HttpKernel\HttpCache;
17 17
18 use Symfony\Component\HttpFoundation\Request;
19 use Symfony\Component\HttpFoundation\Response;
18 use Symfony\Component\HttpKernel\HttpKernelInterface; 20 use Symfony\Component\HttpKernel\HttpKernelInterface;
19 use Symfony\Component\HttpKernel\TerminableInterface; 21 use Symfony\Component\HttpKernel\TerminableInterface;
20 use Symfony\Component\HttpFoundation\Request;
21 use Symfony\Component\HttpFoundation\Response;
22 22
23 /** 23 /**
24 * Cache provides HTTP caching. 24 * Cache provides HTTP caching.
25 * 25 *
26 * @author Fabien Potencier <fabien@symfony.com> 26 * @author Fabien Potencier <fabien@symfony.com>
30 private $kernel; 30 private $kernel;
31 private $store; 31 private $store;
32 private $request; 32 private $request;
33 private $surrogate; 33 private $surrogate;
34 private $surrogateCacheStrategy; 34 private $surrogateCacheStrategy;
35 private $options = array(); 35 private $options = [];
36 private $traces = array(); 36 private $traces = [];
37 37
38 /** 38 /**
39 * Constructor. 39 * Constructor.
40 * 40 *
41 * The available options are: 41 * The available options are:
68 * * stale_if_error Specifies the default number of seconds (the granularity is the second) during which 68 * * stale_if_error Specifies the default number of seconds (the granularity is the second) during which
69 * the cache can serve a stale response when an error is encountered (default: 60). 69 * the cache can serve a stale response when an error is encountered (default: 60).
70 * This setting is overridden by the stale-if-error HTTP Cache-Control extension 70 * This setting is overridden by the stale-if-error HTTP Cache-Control extension
71 * (see RFC 5861). 71 * (see RFC 5861).
72 */ 72 */
73 public function __construct(HttpKernelInterface $kernel, StoreInterface $store, SurrogateInterface $surrogate = null, array $options = array()) 73 public function __construct(HttpKernelInterface $kernel, StoreInterface $store, SurrogateInterface $surrogate = null, array $options = [])
74 { 74 {
75 $this->store = $store; 75 $this->store = $store;
76 $this->kernel = $kernel; 76 $this->kernel = $kernel;
77 $this->surrogate = $surrogate; 77 $this->surrogate = $surrogate;
78 78
79 // needed in case there is a fatal error because the backend is too slow to respond 79 // needed in case there is a fatal error because the backend is too slow to respond
80 register_shutdown_function(array($this->store, 'cleanup')); 80 register_shutdown_function([$this->store, 'cleanup']);
81 81
82 $this->options = array_merge(array( 82 $this->options = array_merge([
83 'debug' => false, 83 'debug' => false,
84 'default_ttl' => 0, 84 'default_ttl' => 0,
85 'private_headers' => array('Authorization', 'Cookie'), 85 'private_headers' => ['Authorization', 'Cookie'],
86 'allow_reload' => false, 86 'allow_reload' => false,
87 'allow_revalidate' => false, 87 'allow_revalidate' => false,
88 'stale_while_revalidate' => 2, 88 'stale_while_revalidate' => 2,
89 'stale_if_error' => 60, 89 'stale_if_error' => 60,
90 ), $options); 90 ], $options);
91 } 91 }
92 92
93 /** 93 /**
94 * Gets the current store. 94 * Gets the current store.
95 * 95 *
96 * @return StoreInterface $store A StoreInterface instance 96 * @return StoreInterface A StoreInterface instance
97 */ 97 */
98 public function getStore() 98 public function getStore()
99 { 99 {
100 return $this->store; 100 return $this->store;
101 } 101 }
115 * 115 *
116 * @return string A log message 116 * @return string A log message
117 */ 117 */
118 public function getLog() 118 public function getLog()
119 { 119 {
120 $log = array(); 120 $log = [];
121 foreach ($this->traces as $request => $traces) { 121 foreach ($this->traces as $request => $traces) {
122 $log[] = sprintf('%s: %s', $request, implode(', ', $traces)); 122 $log[] = sprintf('%s: %s', $request, implode(', ', $traces));
123 } 123 }
124 124
125 return implode('; ', $log); 125 return implode('; ', $log);
162 */ 162 */
163 public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true) 163 public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
164 { 164 {
165 // FIXME: catch exceptions and implement a 500 error page here? -> in Varnish, there is a built-in error page mechanism 165 // FIXME: catch exceptions and implement a 500 error page here? -> in Varnish, there is a built-in error page mechanism
166 if (HttpKernelInterface::MASTER_REQUEST === $type) { 166 if (HttpKernelInterface::MASTER_REQUEST === $type) {
167 $this->traces = array(); 167 $this->traces = [];
168 // Keep a clone of the original request for surrogates so they can access it. 168 // Keep a clone of the original request for surrogates so they can access it.
169 // We must clone here to get a separate instance because the application will modify the request during 169 // We must clone here to get a separate instance because the application will modify the request during
170 // the application flow (we know it always does because we do ourselves by setting REMOTE_ADDR to 127.0.0.1 170 // the application flow (we know it always does because we do ourselves by setting REMOTE_ADDR to 127.0.0.1
171 // and adding the X-Forwarded-For header, see HttpCache::forward()). 171 // and adding the X-Forwarded-For header, see HttpCache::forward()).
172 $this->request = clone $request; 172 $this->request = clone $request;
173 if (null !== $this->surrogate) { 173 if (null !== $this->surrogate) {
174 $this->surrogateCacheStrategy = $this->surrogate->createCacheStrategy(); 174 $this->surrogateCacheStrategy = $this->surrogate->createCacheStrategy();
175 } 175 }
176 } 176 }
177 177
178 $this->traces[$this->getTraceKey($request)] = array(); 178 $this->traces[$this->getTraceKey($request)] = [];
179 179
180 if (!$request->isMethodSafe(false)) { 180 if (!$request->isMethodSafe(false)) {
181 $response = $this->invalidate($request, $catch); 181 $response = $this->invalidate($request, $catch);
182 } elseif ($request->headers->has('expect') || !$request->isMethodCacheable()) { 182 } elseif ($request->headers->has('expect') || !$request->isMethodCacheable()) {
183 $response = $this->pass($request, $catch); 183 $response = $this->pass($request, $catch);
258 if ($response->isSuccessful() || $response->isRedirect()) { 258 if ($response->isSuccessful() || $response->isRedirect()) {
259 try { 259 try {
260 $this->store->invalidate($request); 260 $this->store->invalidate($request);
261 261
262 // As per the RFC, invalidate Location and Content-Location URLs if present 262 // As per the RFC, invalidate Location and Content-Location URLs if present
263 foreach (array('Location', 'Content-Location') as $header) { 263 foreach (['Location', 'Content-Location'] as $header) {
264 if ($uri = $response->headers->get($header)) { 264 if ($uri = $response->headers->get($header)) {
265 $subRequest = Request::create($uri, 'get', array(), array(), array(), $request->server->all()); 265 $subRequest = Request::create($uri, 'get', [], [], [], $request->server->all());
266 266
267 $this->store->invalidate($subRequest); 267 $this->store->invalidate($subRequest);
268 } 268 }
269 } 269 }
270 270
355 $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified')); 355 $subRequest->headers->set('if_modified_since', $entry->headers->get('Last-Modified'));
356 356
357 // Add our cached etag validator to the environment. 357 // Add our cached etag validator to the environment.
358 // We keep the etags from the client to handle the case when the client 358 // We keep the etags from the client to handle the case when the client
359 // has a different private valid entry which is not cached here. 359 // has a different private valid entry which is not cached here.
360 $cachedEtags = $entry->getEtag() ? array($entry->getEtag()) : array(); 360 $cachedEtags = $entry->getEtag() ? [$entry->getEtag()] : [];
361 $requestEtags = $request->getETags(); 361 $requestEtags = $request->getETags();
362 if ($etags = array_unique(array_merge($cachedEtags, $requestEtags))) { 362 if ($etags = array_unique(array_merge($cachedEtags, $requestEtags))) {
363 $subRequest->headers->set('if_none_match', implode(', ', $etags)); 363 $subRequest->headers->set('if_none_match', implode(', ', $etags));
364 } 364 }
365 365
368 if (304 == $response->getStatusCode()) { 368 if (304 == $response->getStatusCode()) {
369 $this->record($request, 'valid'); 369 $this->record($request, 'valid');
370 370
371 // return the response and not the cache entry if the response is valid but not cached 371 // return the response and not the cache entry if the response is valid but not cached
372 $etag = $response->getEtag(); 372 $etag = $response->getEtag();
373 if ($etag && in_array($etag, $requestEtags) && !in_array($etag, $cachedEtags)) { 373 if ($etag && \in_array($etag, $requestEtags) && !\in_array($etag, $cachedEtags)) {
374 return $response; 374 return $response;
375 } 375 }
376 376
377 $entry = clone $entry; 377 $entry = clone $entry;
378 $entry->headers->remove('Date'); 378 $entry->headers->remove('Date');
379 379
380 foreach (array('Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified') as $name) { 380 foreach (['Date', 'Expires', 'Cache-Control', 'ETag', 'Last-Modified'] as $name) {
381 if ($response->headers->has($name)) { 381 if ($response->headers->has($name)) {
382 $entry->headers->set($name, $response->headers->get($name)); 382 $entry->headers->set($name, $response->headers->get($name));
383 } 383 }
384 } 384 }
385 385
442 { 442 {
443 if ($this->surrogate) { 443 if ($this->surrogate) {
444 $this->surrogate->addSurrogateCapability($request); 444 $this->surrogate->addSurrogateCapability($request);
445 } 445 }
446 446
447 // modify the X-Forwarded-For header if needed
448 $forwardedFor = $request->headers->get('X-Forwarded-For');
449 if ($forwardedFor) {
450 $request->headers->set('X-Forwarded-For', $forwardedFor.', '.$request->server->get('REMOTE_ADDR'));
451 } else {
452 $request->headers->set('X-Forwarded-For', $request->server->get('REMOTE_ADDR'));
453 }
454
455 // fix the client IP address by setting it to 127.0.0.1 as HttpCache
456 // is always called from the same process as the backend.
457 $request->server->set('REMOTE_ADDR', '127.0.0.1');
458
459 // make sure HttpCache is a trusted proxy
460 if (!in_array('127.0.0.1', $trustedProxies = Request::getTrustedProxies())) {
461 $trustedProxies[] = '127.0.0.1';
462 Request::setTrustedProxies($trustedProxies, Request::HEADER_X_FORWARDED_ALL);
463 }
464
465 // always a "master" request (as the real master request can be in cache) 447 // always a "master" request (as the real master request can be in cache)
466 $response = $this->kernel->handle($request, HttpKernelInterface::MASTER_REQUEST, $catch); 448 $response = SubRequestHandler::handle($this->kernel, $request, HttpKernelInterface::MASTER_REQUEST, $catch);
467 // FIXME: we probably need to also catch exceptions if raw === true
468 449
469 // we don't implement the stale-if-error on Requests, which is nonetheless part of the RFC 450 // we don't implement the stale-if-error on Requests, which is nonetheless part of the RFC
470 if (null !== $entry && in_array($response->getStatusCode(), array(500, 502, 503, 504))) { 451 if (null !== $entry && \in_array($response->getStatusCode(), [500, 502, 503, 504])) {
471 if (null === $age = $entry->headers->getCacheControlDirective('stale-if-error')) { 452 if (null === $age = $entry->headers->getCacheControlDirective('stale-if-error')) {
472 $age = $this->options['stale_if_error']; 453 $age = $this->options['stale_if_error'];
473 } 454 }
474 455
475 if (abs($entry->getTtl()) < $age) { 456 if (abs($entry->getTtl()) < $age) {
604 } 585 }
605 586
606 $response->setContent(ob_get_clean()); 587 $response->setContent(ob_get_clean());
607 $response->headers->remove('X-Body-Eval'); 588 $response->headers->remove('X-Body-Eval');
608 if (!$response->headers->has('Transfer-Encoding')) { 589 if (!$response->headers->has('Transfer-Encoding')) {
609 $response->headers->set('Content-Length', strlen($response->getContent())); 590 $response->headers->set('Content-Length', \strlen($response->getContent()));
610 } 591 }
611 } elseif ($response->headers->has('X-Body-File')) { 592 } elseif ($response->headers->has('X-Body-File')) {
612 // Response does not include possibly dynamic content (ESI, SSI), so we need 593 // Response does not include possibly dynamic content (ESI, SSI), so we need
613 // not handle the content for HEAD requests 594 // not handle the content for HEAD requests
614 if (!$request->isMethod('HEAD')) { 595 if (!$request->isMethod('HEAD')) {
638 { 619 {
639 foreach ($this->options['private_headers'] as $key) { 620 foreach ($this->options['private_headers'] as $key) {
640 $key = strtolower(str_replace('HTTP_', '', $key)); 621 $key = strtolower(str_replace('HTTP_', '', $key));
641 622
642 if ('cookie' === $key) { 623 if ('cookie' === $key) {
643 if (count($request->cookies->all())) { 624 if (\count($request->cookies->all())) {
644 return true; 625 return true;
645 } 626 }
646 } elseif ($request->headers->has($key)) { 627 } elseif ($request->headers->has($key)) {
647 return true; 628 return true;
648 } 629 }