CachedResource leak in validation code
[WebKit-https.git] / Source / WebCore / loader / cache / CachedRawResource.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "CachedRawResource.h"
28
29 #include "CachedRawResourceClient.h"
30 #include "CachedResourceClientWalker.h"
31 #include "CachedResourceLoader.h"
32 #include "HTTPHeaderNames.h"
33 #include "SharedBuffer.h"
34 #include "SubresourceLoader.h"
35 #include <wtf/PassRefPtr.h>
36 #include <wtf/text/StringView.h>
37
38 namespace WebCore {
39
40 CachedRawResource::CachedRawResource(ResourceRequest& resourceRequest, Type type, SessionID sessionID)
41     : CachedResource(resourceRequest, type, sessionID)
42     , m_identifier(0)
43     , m_allowEncodedDataReplacement(true)
44 {
45     ASSERT(isMainOrRawResource());
46 }
47
48 const char* CachedRawResource::calculateIncrementalDataChunk(SharedBuffer* data, unsigned& incrementalDataLength)
49 {
50     incrementalDataLength = 0;
51     if (!data)
52         return 0;
53
54     unsigned previousDataLength = encodedSize();
55     ASSERT(data->size() >= previousDataLength);
56     incrementalDataLength = data->size() - previousDataLength;
57     return data->data() + previousDataLength;
58 }
59
60 void CachedRawResource::addDataBuffer(SharedBuffer& data)
61 {
62     CachedResourceHandle<CachedRawResource> protect(this);
63     ASSERT(dataBufferingPolicy() == BufferData);
64     m_data = &data;
65
66     unsigned incrementalDataLength;
67     const char* incrementalData = calculateIncrementalDataChunk(&data, incrementalDataLength);
68     setEncodedSize(data.size());
69     notifyClientsDataWasReceived(incrementalData, incrementalDataLength);
70     if (dataBufferingPolicy() == DoNotBufferData) {
71         if (m_loader)
72             m_loader->setDataBufferingPolicy(DoNotBufferData);
73         clear();
74         return;
75     }
76
77     CachedResource::addDataBuffer(data);
78 }
79
80 void CachedRawResource::addData(const char* data, unsigned length)
81 {
82     ASSERT(dataBufferingPolicy() == DoNotBufferData);
83     notifyClientsDataWasReceived(data, length);
84     CachedResource::addData(data, length);
85 }
86
87 void CachedRawResource::finishLoading(SharedBuffer* data)
88 {
89     CachedResourceHandle<CachedRawResource> protect(this);
90     DataBufferingPolicy dataBufferingPolicy = this->dataBufferingPolicy();
91     if (dataBufferingPolicy == BufferData) {
92         m_data = data;
93
94         unsigned incrementalDataLength;
95         const char* incrementalData = calculateIncrementalDataChunk(data, incrementalDataLength);
96         if (data)
97             setEncodedSize(data->size());
98         notifyClientsDataWasReceived(incrementalData, incrementalDataLength);
99     }
100
101     m_allowEncodedDataReplacement = !m_loader->isQuickLookResource();
102
103     CachedResource::finishLoading(data);
104     if (dataBufferingPolicy == BufferData && this->dataBufferingPolicy() == DoNotBufferData) {
105         if (m_loader)
106             m_loader->setDataBufferingPolicy(DoNotBufferData);
107         clear();
108     }
109 }
110
111 void CachedRawResource::notifyClientsDataWasReceived(const char* data, unsigned length)
112 {
113     if (!length)
114         return;
115
116     CachedResourceHandle<CachedRawResource> protect(this);
117     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
118     while (CachedRawResourceClient* c = w.next())
119         c->dataReceived(this, data, length);
120 }
121
122 void CachedRawResource::didAddClient(CachedResourceClient* c)
123 {
124     if (!hasClient(c))
125         return;
126     // The calls to the client can result in events running, potentially causing
127     // this resource to be evicted from the cache and all clients to be removed,
128     // so a protector is necessary.
129     CachedResourceHandle<CachedRawResource> protect(this);
130     CachedRawResourceClient* client = static_cast<CachedRawResourceClient*>(c);
131     size_t redirectCount = m_redirectChain.size();
132     for (size_t i = 0; i < redirectCount; i++) {
133         RedirectPair redirect = m_redirectChain[i];
134         ResourceRequest request(redirect.m_request);
135         bool shouldContinue = true;
136         client->syntheticRedirectReceived(this, request, redirect.m_redirectResponse, shouldContinue);
137         if (!hasClient(c) || !shouldContinue)
138             return;
139     }
140     ASSERT(redirectCount == m_redirectChain.size());
141
142     if (!m_response.isNull()) {
143         ResourceResponse response(m_response);
144         if (validationCompleting())
145             response.setSource(ResourceResponse::Source::MemoryCacheAfterValidation);
146         else {
147             ASSERT(!validationInProgress());
148             response.setSource(ResourceResponse::Source::MemoryCache);
149         }
150         client->responseReceived(this, response);
151     }
152     if (!hasClient(c))
153         return;
154     if (m_data)
155         client->dataReceived(this, m_data->data(), m_data->size());
156     if (!hasClient(c))
157        return;
158     CachedResource::didAddClient(client);
159 }
160
161 void CachedRawResource::allClientsRemoved()
162 {
163     if (m_loader)
164         m_loader->cancelIfNotFinishing();
165 }
166
167 void CachedRawResource::redirectReceived(ResourceRequest& request, const ResourceResponse& response)
168 {
169     CachedResourceHandle<CachedRawResource> protect(this);
170     if (!response.isNull()) {
171         CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
172         while (CachedRawResourceClient* c = w.next())
173             c->redirectReceived(this, request, response);
174         m_redirectChain.append(RedirectPair(request, response));
175     }
176     CachedResource::redirectReceived(request, response);
177 }
178
179 void CachedRawResource::responseReceived(const ResourceResponse& response)
180 {
181     CachedResourceHandle<CachedRawResource> protect(this);
182     if (!m_identifier)
183         m_identifier = m_loader->identifier();
184     CachedResource::responseReceived(response);
185     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
186     while (CachedRawResourceClient* c = w.next())
187         c->responseReceived(this, m_response);
188 }
189
190 void CachedRawResource::didSendData(unsigned long long bytesSent, unsigned long long totalBytesToBeSent)
191 {
192     CachedResourceClientWalker<CachedRawResourceClient> w(m_clients);
193     while (CachedRawResourceClient* c = w.next())
194         c->dataSent(this, bytesSent, totalBytesToBeSent);
195 }
196
197 void CachedRawResource::switchClientsToRevalidatedResource()
198 {
199     ASSERT(m_loader);
200     // If we're in the middle of a successful revalidation, responseReceived() hasn't been called, so we haven't set m_identifier.
201     ASSERT(!m_identifier);
202     downcast<CachedRawResource>(*resourceToRevalidate()).m_identifier = m_loader->identifier();
203     CachedResource::switchClientsToRevalidatedResource();
204 }
205
206 void CachedRawResource::setDefersLoading(bool defers)
207 {
208     if (m_loader)
209         m_loader->setDefersLoading(defers);
210 }
211
212 void CachedRawResource::setDataBufferingPolicy(DataBufferingPolicy dataBufferingPolicy)
213 {
214     m_options.setDataBufferingPolicy(dataBufferingPolicy);
215 }
216
217 static bool shouldIgnoreHeaderForCacheReuse(HTTPHeaderName name)
218 {
219     switch (name) {
220     // FIXME: This list of headers that don't affect cache policy almost certainly isn't complete.
221     case HTTPHeaderName::Accept:
222     case HTTPHeaderName::CacheControl:
223     case HTTPHeaderName::Pragma:
224     case HTTPHeaderName::Purpose:
225     case HTTPHeaderName::Referer:
226     case HTTPHeaderName::UserAgent:
227         return true;
228
229     default:
230         return false;
231     }
232 }
233
234 bool CachedRawResource::canReuse(const ResourceRequest& newRequest) const
235 {
236     if (dataBufferingPolicy() == DoNotBufferData)
237         return false;
238
239     if (m_resourceRequest.httpMethod() != newRequest.httpMethod())
240         return false;
241
242     if (m_resourceRequest.httpBody() != newRequest.httpBody())
243         return false;
244
245     if (m_resourceRequest.allowCookies() != newRequest.allowCookies())
246         return false;
247
248     // Ensure most headers match the existing headers before continuing.
249     // Note that the list of ignored headers includes some headers explicitly related to caching.
250     // A more detailed check of caching policy will be performed later, this is simply a list of
251     // headers that we might permit to be different and still reuse the existing CachedResource.
252     const HTTPHeaderMap& newHeaders = newRequest.httpHeaderFields();
253     const HTTPHeaderMap& oldHeaders = m_resourceRequest.httpHeaderFields();
254
255     for (const auto& header : newHeaders) {
256         if (header.keyAsHTTPHeaderName) {
257             if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value())
258                 && header.value != oldHeaders.commonHeaders().get(header.keyAsHTTPHeaderName.value()))
259                 return false;
260         } else if (header.value != oldHeaders.uncommonHeaders().get(header.key))
261             return false;
262     }
263
264     // For this second loop, we don't actually need to compare values, checking that the
265     // key is contained in newHeaders is sufficient due to the previous loop.
266     for (const auto& header : oldHeaders) {
267         if (header.keyAsHTTPHeaderName) {
268             if (!shouldIgnoreHeaderForCacheReuse(header.keyAsHTTPHeaderName.value())
269                 && !newHeaders.commonHeaders().contains(header.keyAsHTTPHeaderName.value()))
270                 return false;
271         } else if (!newHeaders.uncommonHeaders().contains(header.key))
272             return false;
273     }
274
275     return true;
276 }
277
278 void CachedRawResource::clear()
279 {
280     m_data = nullptr;
281     setEncodedSize(0);
282     if (m_loader)
283         m_loader->clearResourceData();
284 }
285
286 } // namespace WebCore