Completed
Pull Request — master (#273)
by
unknown
07:15
created

UserContextSubscriber::onKernelResponse()   C

Complexity

Conditions 8
Paths 5

Size

Total Lines 33
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 8

Importance

Changes 3
Bugs 0 Features 0
Metric Value
c 3
b 0
f 0
dl 0
loc 33
ccs 22
cts 22
cp 1
rs 5.3846
cc 8
eloc 18
nc 5
nop 1
crap 8
1
<?php
2
3
/*
4
 * This file is part of the FOSHttpCacheBundle 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\EventListener;
13
14
use FOS\HttpCache\UserContext\HashGenerator;
15
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
16
use Symfony\Component\HttpFoundation\RequestMatcherInterface;
17
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
18
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
19
use Symfony\Component\HttpFoundation\Response;
20
use Symfony\Component\HttpKernel\HttpKernelInterface;
21
use Symfony\Component\HttpKernel\KernelEvents;
22
23
/**
24
 * Check requests and responses with the matcher.
25
 *
26
 * Abort context hash requests immediately and return the hash.
27
 * Add the vary information on responses to normal requests.
28
 *
29
 * @author Stefan Paschke <[email protected]>
30
 * @author Joel Wurtz <[email protected]>
31
 */
32
class UserContextSubscriber implements EventSubscriberInterface
33
{
34
    /**
35
     * @var RequestMatcherInterface
36
     */
37
    private $requestMatcher;
38
39
    /**
40
     * @var HashGenerator
41
     */
42
    private $hashGenerator;
43
44
    /**
45
     * @var string[]
46
     */
47
    private $userIdentifierHeaders;
48
49
    /**
50
     * @var string
51
     */
52
    private $hash;
53
54
    /**
55
     * @var string
56
     */
57
    private $hashHeader;
58
59
    /**
60
     * @var integer
61
     */
62
    private $ttl;
63
64 25
    public function __construct(
65
        RequestMatcherInterface $requestMatcher,
66
        HashGenerator $hashGenerator,
67
        array $userIdentifierHeaders = array('Cookie', 'Authorization'),
68
        $hashHeader = "X-User-Context-Hash",
69
        $ttl = 0
70
    ) {
71 25
        if (!count($userIdentifierHeaders)) {
72 1
            throw new \InvalidArgumentException('The user context must vary on some request headers');
73
        }
74 24
        $this->requestMatcher        = $requestMatcher;
75 24
        $this->hashGenerator         = $hashGenerator;
76 24
        $this->userIdentifierHeaders = $userIdentifierHeaders;
77 24
        $this->hash                  = null;
78 24
        $this->hashHeader            = $hashHeader;
79 24
        $this->ttl                   = $ttl;
80 24
    }
81
82
    /**
83
     * Return the response to the context hash request with a header containing
84
     * the generated hash.
85
     *
86
     * If the ttl is bigger than 0, cache headers will be set for this response.
87
     *
88
     * @param GetResponseEvent $event
89
     */
90 20
    public function onKernelRequest(GetResponseEvent $event)
91
    {
92 20
        if ($event->getRequestType() != HttpKernelInterface::MASTER_REQUEST) {
93 3
            return;
94
        }
95
96 19
        $hash = $this->hashGenerator->generateHash();
97
98 19
        if (!$this->requestMatcher->matches($event->getRequest())) {
99 17
            $this->hash = $hash;
100
101 17
            return;
102
        }
103
104
105
        // status needs to be 200 as otherwise varnish will not cache the response.
106 2
        $response = new Response('', 200, array(
107 2
            $this->hashHeader => $hash,
108 2
            'Content-Type'    => 'application/vnd.fos.user-context-hash',
109 2
        ));
110
111 2
        if ($this->ttl > 0) {
112 1
            $response->setClientTtl($this->ttl);
113 1
            $response->setVary($this->userIdentifierHeaders);
114 1
            $response->setPublic();
115 1
        } else {
116 1
            $response->setClientTtl(0);
117 1
            $response->headers->addCacheControlDirective('no-cache');
118
        }
119
120 2
        $event->setResponse($response);
121 2
    }
122
123
    /**
124
     * Add the context hash header to the headers to vary on if the header was
125
     * present in the request.
126
     *
127
     * @param FilterResponseEvent $event
128
     */
129 20
    public function onKernelResponse(FilterResponseEvent $event)
130
    {
131 20
        if ($event->getRequestType() != HttpKernelInterface::MASTER_REQUEST) {
132 3
            return;
133
        }
134
135 19
        $response = $event->getResponse();
136 19
        $request = $event->getRequest();
137
138 19
        $vary = $response->getVary();
139
140 19
        if ($request->headers->has($this->hashHeader)) {
141
            // hash has changed, session has most certainly changed, prevent setting incorrect cache
142 3
            if (!is_null($this->hash) && $this->hash !== $request->headers->get($this->hashHeader)) {
143 1
                $response->setClientTtl(0);
144 1
                $response->headers->addCacheControlDirective('no-cache');
145
146 1
                return;
147
            }
148
149 2
            if (!in_array($this->hashHeader, $vary)) {
150 2
                $vary[] = $this->hashHeader;
151 2
            }
152 2
        } else {
153 16
            foreach ($this->userIdentifierHeaders as $header) {
154 16
                if (!in_array($header, $vary)) {
155 16
                    $vary[] = $header;
156 16
                }
157 16
            }
158
        }
159
160 18
        $response->setVary($vary, true);
161 18
    }
162
163
    /**
164
     * {@inheritdoc}
165
     */
166 23
    public static function getSubscribedEvents()
167
    {
168
        return array(
169 23
            KernelEvents::RESPONSE => 'onKernelResponse',
170 23
            KernelEvents::REQUEST  => array('onKernelRequest', 7),
171 23
        );
172
    }
173
}
174