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>>& ) |
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>>& ) |
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 = !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 (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 = 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 ; |
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 = true; |
241 | for (auto& : 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 | |