Skip to content

Commit d964fdb

Browse files
committed
Moving the user context logic out of the kernel inheritance hierarchy.
1 parent ef23d4c commit d964fdb

File tree

7 files changed

+617
-280
lines changed

7 files changed

+617
-280
lines changed

HttpCache.php

Lines changed: 32 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,13 @@
1111

1212
namespace FOS\HttpCacheBundle;
1313

14+
use FOS\HttpCacheBundle\SymfonyCache\CacheEvent;
15+
use FOS\HttpCacheBundle\SymfonyCache\Events;
1416
use Symfony\Bundle\FrameworkBundle\HttpCache\HttpCache as BaseHttpCache;
17+
use Symfony\Component\EventDispatcher\EventDispatcher;
18+
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
19+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
1520
use Symfony\Component\HttpFoundation\Request;
16-
use Symfony\Component\HttpFoundation\Response;
1721
use Symfony\Component\HttpKernel\HttpKernelInterface;
1822

1923
/**
@@ -26,163 +30,60 @@
2630
abstract class HttpCache extends BaseHttpCache
2731
{
2832
/**
29-
* Hash for anonymous user.
33+
* @var EventDispatcherInterface
3034
*/
31-
const ANONYMOUS_HASH = '38015b703d82206ebc01d17a39c727e5';
35+
private $eventDispatcher;
3236

3337
/**
34-
* Accept header value to be used to request the user hash to the backend application.
35-
* It must match the one defined in FOSHttpCacheBundle's configuration.
36-
*/
37-
const USER_HASH_ACCEPT_HEADER = 'application/vnd.fos.user-context-hash';
38-
39-
/**
40-
* Name of the header the user context hash will be stored into.
41-
* It must match the one defined in FOSHttpCacheBundle's configuration.
42-
*/
43-
const USER_HASH_HEADER = 'X-User-Context-Hash';
44-
45-
/**
46-
* URI used with the forwarded request for user context hash generation.
47-
*/
48-
const USER_HASH_URI = '/_fos_user_context_hash';
49-
50-
/**
51-
* HTTP Method used with the forwarded request for user context hash generation.
52-
*/
53-
const USER_HASH_METHOD = 'GET';
54-
55-
/**
56-
* Prefix for session names.
57-
* Must match your session configuration.
58-
*/
59-
const SESSION_NAME_PREFIX = 'PHPSESSID';
60-
61-
/**
62-
* Generated user hash.
63-
*
64-
* @var string
65-
*/
66-
private $userHash;
67-
68-
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
69-
{
70-
if (!$this->isInternalRequest($request)) {
71-
// Prevent tampering attacks on the hash mechanism
72-
if ($request->headers->get('accept') === static::USER_HASH_ACCEPT_HEADER
73-
|| $request->headers->get(static::USER_HASH_HEADER) !== null) {
74-
return new Response('', 400);
75-
}
76-
77-
if ($request->isMethodSafe()) {
78-
$request->headers->set(static::USER_HASH_HEADER, $this->getUserHash($request));
79-
}
80-
}
81-
82-
return parent::handle($request, $type, $catch);
83-
}
84-
85-
/**
86-
* Checks if passed request object is to be considered internal (e.g. for user hash lookup).
87-
*
88-
* @param Request $request
89-
*
90-
* @return bool
91-
*/
92-
private function isInternalRequest(Request $request)
93-
{
94-
return $request->attributes->get('internalRequest', false) === true;
95-
}
96-
97-
/**
98-
* Returns the user context hash for $request.
38+
* Set event dispatcher
9939
*
100-
* @param Request $request
40+
* @param EventDispatcherInterface $eventDispatcher
10141
*
102-
* @return string
42+
* @return $this
10343
*/
104-
private function getUserHash(Request $request)
44+
public function setEventDispatcher(EventDispatcherInterface $eventDispatcher)
10545
{
106-
if (isset($this->userHash)) {
107-
return $this->userHash;
108-
}
109-
110-
if ($this->isAnonymous($request)) {
111-
return $this->userHash = static::ANONYMOUS_HASH;
112-
}
113-
114-
// Forward the request to generate the user hash
115-
$forwardReq = $this->generateForwardRequest($request);
116-
$resp = $this->handle($forwardReq);
117-
// Store the user hash in memory for sub-requests (processed in the same thread).
118-
$this->userHash = $resp->headers->get(static::USER_HASH_HEADER);
119-
120-
return $this->userHash;
46+
$this->eventDispatcher = $eventDispatcher;
12147
}
12248

12349
/**
124-
* Checks if current request is considered anonymous.
125-
*
126-
* @param Request $request
50+
* Get event dispatcher
12751
*
128-
* @return bool
52+
* @return EventDispatcherInterface
12953
*/
130-
private function isAnonymous(Request $request)
54+
public function getEventDispatcher()
13155
{
132-
foreach ($request->cookies as $name => $value) {
133-
if ($this->isSessionName($name)) {
134-
return false;
135-
}
56+
if (!$this->eventDispatcher) {
57+
$this->eventDispatcher = new EventDispatcher();
13658
}
13759

138-
return true;
139-
}
140-
141-
/**
142-
* Checks if passed string can be considered as a session name, such as would be used in cookies.
143-
*
144-
* @param string $name
145-
*
146-
* @return bool
147-
*/
148-
private function isSessionName($name)
149-
{
150-
return strpos($name, static::SESSION_NAME_PREFIX) === 0;
60+
return $this->eventDispatcher;
15161
}
15262

15363
/**
154-
* Generates the request object that will be forwarded to get the user context hash.
64+
* Add subscriber
15565
*
156-
* @param Request $request
66+
* @param EventSubscriberInterface $subscriber
15767
*
158-
* @return Request
68+
* @return $this
15969
*/
160-
private function generateForwardRequest(Request $request)
70+
public function addSubscriber(EventSubscriberInterface $subscriber)
16171
{
162-
$forwardReq = Request::create(static::USER_HASH_URI, static::USER_HASH_METHOD, array(), array(), array(), $request->server->all());
163-
$forwardReq->attributes->set('internalRequest', true);
164-
$forwardReq->headers->set('Accept', static::USER_HASH_ACCEPT_HEADER);
165-
$this->cleanupForwardRequest($forwardReq, $request);
72+
$this->getEventDispatcher()->addSubscriber($subscriber);
16673

167-
return $forwardReq;
74+
return $this;
16875
}
16976

170-
/**
171-
* Cleans up request to forward for user hash generation.
172-
* Cleans cookie header to only get proper sessionIds in it. This is to make the hash request cacheable.
173-
*
174-
* @param Request $forwardReq
175-
* @param Request $originalRequest
176-
*/
177-
protected function cleanupForwardRequest(Request $forwardReq, Request $originalRequest)
77+
public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
17878
{
179-
$sessionIds = array();
180-
foreach ($originalRequest->cookies as $name => $value) {
181-
if ( $this->isSessionName($name)) {
182-
$sessionIds[$name] = $value;
183-
$forwardReq->cookies->set($name, $value);
79+
if ($this->getEventDispatcher()->hasListeners(Events::PRE_HANDLE)) {
80+
$event = new CacheEvent($this, $request);
81+
$this->getEventDispatcher()->dispatch(Events::PRE_HANDLE, $event);
82+
if ($event->getResponse()) {
83+
return $event->getResponse();
18484
}
18585
}
186-
$forwardReq->headers->set('Cookie', http_build_query($sessionIds, '', '; '));
86+
87+
return parent::handle($request, $type, $catch);
18788
}
18889
}

SymfonyCache/CacheEvent.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
namespace FOS\HttpCacheBundle\SymfonyCache;
4+
5+
use Symfony\Component\EventDispatcher\Event;
6+
use Symfony\Component\HttpFoundation\Request;
7+
use Symfony\Component\HttpFoundation\Response;
8+
use Symfony\Component\HttpKernel\HttpCache\HttpCache;
9+
10+
class CacheEvent extends Event
11+
{
12+
/**
13+
* @var HttpCache
14+
*/
15+
private $kernel;
16+
17+
/**
18+
* @var Request
19+
*/
20+
private $request;
21+
22+
/**
23+
* @var Response
24+
*/
25+
private $response;
26+
27+
/**
28+
* @param HttpCache $kernel The kernel raising with this event.
29+
* @param Request $request The request being processed.
30+
*/
31+
public function __construct(HttpCache $kernel, Request $request)
32+
{
33+
$this->kernel = $kernel;
34+
$this->request = $request;
35+
}
36+
37+
/**
38+
* Get the kernel that raised this event.
39+
*
40+
* @return HttpCache
41+
*/
42+
public function getKernel()
43+
{
44+
return $this->kernel;
45+
}
46+
47+
/**
48+
* Get the request that is being processed.
49+
*
50+
* @return Request
51+
*/
52+
public function getRequest()
53+
{
54+
return $this->request;
55+
}
56+
57+
/**
58+
* @return Response|null The response if one was set.
59+
*/
60+
public function getResponse()
61+
{
62+
return $this->response;
63+
}
64+
65+
/**
66+
* Set this to overwrite the response that would otherwise be given.
67+
*
68+
* @param Response $response
69+
*/
70+
public function setResponse(Response $response)
71+
{
72+
$this->response = $response;
73+
}
74+
}

SymfonyCache/Events.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCache package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace FOS\HttpCacheBundle\SymfonyCache;
13+
14+
/**
15+
* Events used in the customized Symfony built-in reverse proxy HttpCache.
16+
*/
17+
final class Events
18+
{
19+
const PRE_HANDLE = 'fos_http_cache.pre_handle';
20+
}

0 commit comments

Comments
 (0)