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
37namespace WebKit {
38namespace NetworkCache {
39
40Entry::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
50Entry::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
64Entry::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
75Entry::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
83Storage::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
110std::unique_ptr<Entry> Entry::decodeStorageRecord(const Storage::Record& storageEntry)
111{
112 auto entry = std::make_unique<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)
149bool Entry::hasReachedPrevalentResourceAgeCap() const
150{
151 return m_maxAgeCap && WebCore::computeCurrentAge(response(), timeStamp()) > m_maxAgeCap;
152}
153
154void Entry::capMaxAge(const Seconds seconds)
155{
156 m_maxAgeCap = seconds;
157}
158#endif
159
160#if ENABLE(SHAREABLE_RESOURCE)
161void 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
172void 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
184WebCore::SharedBuffer* Entry::buffer() const
185{
186 if (!m_buffer)
187 initializeBufferFromStorageRecord();
188
189 return m_buffer.get();
190}
191
192#if ENABLE(SHAREABLE_RESOURCE)
193ShareableResource::Handle& Entry::shareableResourceHandle() const
194{
195 if (m_shareableResourceHandle.isNull())
196 initializeShareableResourceHandleFromStorageRecord();
197
198 return m_shareableResourceHandle;
199}
200#endif
201
202bool Entry::needsValidation() const
203{
204 return m_response.source() == WebCore::ResourceResponse::Source::DiskCacheAfterValidation;
205}
206
207void Entry::setNeedsValidation(bool value)
208{
209 m_response.setSource(value ? WebCore::ResourceResponse::Source::DiskCacheAfterValidation : WebCore::ResourceResponse::Source::DiskCache);
210}
211
212void 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}
256