[WTF] Add makeUnique<T>, which ensures T is fast-allocated, makeUnique / makeUniqueWi...
[WebKit-https.git] / Source / WebKit / NetworkProcess / cache / NetworkCacheEntry.cpp
1 /*
2  * Copyright (C) 2015 Apple 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 INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "NetworkCache.h"
28
29 #include "Logging.h"
30 #include "NetworkCacheCoders.h"
31 #include "NetworkProcess.h"
32 #include "WebCoreArgumentCoders.h"
33 #include <WebCore/ResourceRequest.h>
34 #include <WebCore/SharedBuffer.h>
35 #include <wtf/text/StringBuilder.h>
36
37 namespace WebKit {
38 namespace NetworkCache {
39
40 Entry::Entry(const Key& key, const WebCore::ResourceResponse& response, RefPtr<WebCore::SharedBuffer>&& buffer, const Vector<std::pair<String, String>>& varyingRequestHeaders)
41     : m_key(key)
42     , m_timeStamp(WallTime::now())
43     , m_response(response)
44     , m_varyingRequestHeaders(varyingRequestHeaders)
45     , m_buffer(WTFMove(buffer))
46 {
47     ASSERT(m_key.type() == "Resource");
48 }
49
50 Entry::Entry(const Key& key, const WebCore::ResourceResponse& response, const WebCore::ResourceRequest& redirectRequest, const Vector<std::pair<String, String>>& varyingRequestHeaders)
51     : m_key(key)
52     , m_timeStamp(WallTime::now())
53     , m_response(response)
54     , m_varyingRequestHeaders(varyingRequestHeaders)
55 {
56     ASSERT(m_key.type() == "Resource");
57
58     m_redirectRequest.emplace();
59     m_redirectRequest->setAsIsolatedCopy(redirectRequest);
60     // Redirect body is not needed even if exists.
61     m_redirectRequest->setHTTPBody(nullptr);
62 }
63
64 Entry::Entry(const Entry& other)
65     : m_key(other.m_key)
66     , m_timeStamp(other.m_timeStamp)
67     , m_response(other.m_response)
68     , m_varyingRequestHeaders(other.m_varyingRequestHeaders)
69     , m_redirectRequest(other.m_redirectRequest)
70     , m_buffer(other.m_buffer)
71     , m_sourceStorageRecord(other.m_sourceStorageRecord)
72 {
73 }
74
75 Entry::Entry(const Storage::Record& storageEntry)
76     : m_key(storageEntry.key)
77     , m_timeStamp(storageEntry.timeStamp)
78     , m_sourceStorageRecord(storageEntry)
79 {
80     ASSERT(m_key.type() == "Resource");
81 }
82
83 Storage::Record Entry::encodeAsStorageRecord() const
84 {
85     WTF::Persistence::Encoder encoder;
86     encoder << m_response;
87
88     bool hasVaryingRequestHeaders = !m_varyingRequestHeaders.isEmpty();
89     encoder << hasVaryingRequestHeaders;
90     if (hasVaryingRequestHeaders)
91         encoder << m_varyingRequestHeaders;
92
93     bool isRedirect = !!m_redirectRequest;
94     encoder << isRedirect;
95     if (isRedirect)
96         m_redirectRequest->encodeWithoutPlatformData(encoder);
97
98     encoder << m_maxAgeCap;
99     
100     encoder.encodeChecksum();
101
102     Data header(encoder.buffer(), encoder.bufferSize());
103     Data body;
104     if (m_buffer)
105         body = { reinterpret_cast<const uint8_t*>(m_buffer->data()), m_buffer->size() };
106
107     return { m_key, m_timeStamp, header, body, { } };
108 }
109
110 std::unique_ptr<Entry> Entry::decodeStorageRecord(const Storage::Record& storageEntry)
111 {
112     auto entry = makeUnique<Entry>(storageEntry);
113
114     WTF::Persistence::Decoder decoder(storageEntry.header.data(), storageEntry.header.size());
115     if (!decoder.decode(entry->m_response))
116         return nullptr;
117     entry->m_response.setSource(WebCore::ResourceResponse::Source::DiskCache);
118
119     bool hasVaryingRequestHeaders;
120     if (!decoder.decode(hasVaryingRequestHeaders))
121         return nullptr;
122
123     if (hasVaryingRequestHeaders) {
124         if (!decoder.decode(entry->m_varyingRequestHeaders))
125             return nullptr;
126     }
127
128     bool isRedirect;
129     if (!decoder.decode(isRedirect))
130         return nullptr;
131
132     if (isRedirect) {
133         entry->m_redirectRequest.emplace();
134         if (!entry->m_redirectRequest->decodeWithoutPlatformData(decoder))
135             return nullptr;
136     }
137
138     decoder.decode(entry->m_maxAgeCap);
139     
140     if (!decoder.verifyChecksum()) {
141         LOG(NetworkCache, "(NetworkProcess) checksum verification failure\n");
142         return nullptr;
143     }
144
145     return entry;
146 }
147
148 #if ENABLE(RESOURCE_LOAD_STATISTICS)
149 bool Entry::hasReachedPrevalentResourceAgeCap() const
150 {
151     return m_maxAgeCap && WebCore::computeCurrentAge(response(), timeStamp()) > m_maxAgeCap;
152 }
153
154 void Entry::capMaxAge(const Seconds seconds)
155 {
156     m_maxAgeCap = seconds;
157 }
158 #endif
159
160 #if ENABLE(SHAREABLE_RESOURCE)
161 void Entry::initializeShareableResourceHandleFromStorageRecord() const
162 {
163     auto sharedMemory = m_sourceStorageRecord.body.tryCreateSharedMemory();
164     if (!sharedMemory)
165         return;
166
167     auto shareableResource = ShareableResource::create(sharedMemory.releaseNonNull(), 0, m_sourceStorageRecord.body.size());
168     shareableResource->createHandle(m_shareableResourceHandle);
169 }
170 #endif
171
172 void Entry::initializeBufferFromStorageRecord() const
173 {
174 #if ENABLE(SHAREABLE_RESOURCE)
175     if (!shareableResourceHandle().isNull()) {
176         m_buffer = m_shareableResourceHandle.tryWrapInSharedBuffer();
177         if (m_buffer)
178             return;
179     }
180 #endif
181     m_buffer = WebCore::SharedBuffer::create(m_sourceStorageRecord.body.data(), m_sourceStorageRecord.body.size());
182 }
183
184 WebCore::SharedBuffer* Entry::buffer() const
185 {
186     if (!m_buffer)
187         initializeBufferFromStorageRecord();
188
189     return m_buffer.get();
190 }
191
192 #if ENABLE(SHAREABLE_RESOURCE)
193 ShareableResource::Handle& Entry::shareableResourceHandle() const
194 {
195     if (m_shareableResourceHandle.isNull())
196         initializeShareableResourceHandleFromStorageRecord();
197
198     return m_shareableResourceHandle;
199 }
200 #endif
201
202 bool Entry::needsValidation() const
203 {
204     return m_response.source() == WebCore::ResourceResponse::Source::DiskCacheAfterValidation;
205 }
206
207 void Entry::setNeedsValidation(bool value)
208 {
209     m_response.setSource(value ? WebCore::ResourceResponse::Source::DiskCacheAfterValidation : WebCore::ResourceResponse::Source::DiskCache);
210 }
211
212 void Entry::asJSON(StringBuilder& json, const Storage::RecordInfo& info) const
213 {
214     json.appendLiteral("{\n");
215     json.appendLiteral("\"hash\": ");
216     json.appendQuotedJSONString(m_key.hashAsString());
217     json.appendLiteral(",\n");
218     json.appendLiteral("\"bodySize\": ");
219     json.appendNumber(info.bodySize);
220     json.appendLiteral(",\n");
221     json.appendLiteral("\"worth\": ");
222     json.appendFixedPrecisionNumber(info.worth);
223     json.appendLiteral(",\n");
224     json.appendLiteral("\"partition\": ");
225     json.appendQuotedJSONString(m_key.partition());
226     json.appendLiteral(",\n");
227     json.appendLiteral("\"timestamp\": ");
228     json.appendFixedPrecisionNumber(m_timeStamp.secondsSinceEpoch().milliseconds());
229     json.appendLiteral(",\n");
230     json.appendLiteral("\"URL\": ");
231     json.appendQuotedJSONString(m_response.url().string());
232     json.appendLiteral(",\n");
233     json.appendLiteral("\"bodyHash\": ");
234     json.appendQuotedJSONString(info.bodyHash);
235     json.appendLiteral(",\n");
236     json.appendLiteral("\"bodyShareCount\": ");
237     json.appendNumber(info.bodyShareCount);
238     json.appendLiteral(",\n");
239     json.appendLiteral("\"headers\": {\n");
240     bool firstHeader = true;
241     for (auto& header : m_response.httpHeaderFields()) {
242         if (!firstHeader)
243             json.appendLiteral(",\n");
244         firstHeader = false;
245         json.appendLiteral("    ");
246         json.appendQuotedJSONString(header.key);
247         json.appendLiteral(": ");
248         json.appendQuotedJSONString(header.value);
249     }
250     json.appendLiteral("\n}\n");
251     json.appendLiteral("}");
252 }
253
254 }
255 }