OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "content/browser/service_worker/service_worker_disk_cache_migrator.h" | |
6 | |
7 #include "base/memory/ref_counted.h" | |
8 #include "base/strings/string_number_conversions.h" | |
9 #include "content/common/service_worker/service_worker_types.h" | |
10 #include "net/base/io_buffer.h" | |
11 #include "net/base/net_errors.h" | |
12 #include "net/disk_cache/disk_cache.h" | |
13 | |
14 namespace content { | |
15 | |
16 // A task to move a cached resource from the src DiskCache to the dest | |
17 // DiskCache. This is owned by ServiceWorkerDiskCacheMigrator. | |
18 class ServiceWorkerDiskCacheMigrator::Task { | |
19 public: | |
20 Task(InflightTaskMap::KeyType task_id, | |
21 int64 resource_id, | |
22 int32 data_size, | |
23 ServiceWorkerDiskCache* src, | |
24 ServiceWorkerDiskCache* dest, | |
25 const base::WeakPtr<ServiceWorkerDiskCacheMigrator>& owner); | |
26 ~Task(); | |
27 | |
28 void Run(); | |
29 | |
30 InflightTaskMap::KeyType task_id() const { return task_id_; } | |
31 | |
32 private: | |
33 void ReadResponseInfo(); | |
34 void OnReadResponseInfo( | |
35 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer, | |
36 int result); | |
37 void OnWriteResponseInfo( | |
38 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer, | |
39 int result); | |
40 void WriteResponseMetadata( | |
41 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer); | |
42 void OnWriteResponseMetadata( | |
43 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer, | |
44 int result); | |
45 void ReadResponseData(); | |
46 void OnReadResponseData(const scoped_refptr<net::IOBuffer>& buffer, | |
47 int result); | |
48 void OnWriteResponseData(int result); | |
49 void Finish(ServiceWorkerStatusCode status); | |
50 | |
51 InflightTaskMap::KeyType task_id_; | |
52 int64 resource_id_; | |
53 int32 data_size_; | |
54 base::WeakPtr<ServiceWorkerDiskCacheMigrator> owner_; | |
55 | |
56 scoped_ptr<ServiceWorkerResponseReader> reader_; | |
57 scoped_ptr<ServiceWorkerResponseWriter> writer_; | |
58 scoped_ptr<ServiceWorkerResponseMetadataWriter> metadata_writer_; | |
59 | |
60 base::WeakPtrFactory<Task> weak_factory_; | |
61 | |
62 DISALLOW_COPY_AND_ASSIGN(Task); | |
63 }; | |
64 | |
65 // A wrapper class for disk_cache::Entry. This is used for holding an open entry | |
66 // and ensuring that the entry gets closed on the dtor. | |
67 class ServiceWorkerDiskCacheMigrator::WrappedEntry { | |
68 public: | |
69 WrappedEntry() {} | |
70 | |
71 ~WrappedEntry() { | |
72 if (entry_) | |
73 entry_->Close(); | |
74 } | |
75 | |
76 disk_cache::Entry* Unwrap() { | |
77 disk_cache::Entry* entry = entry_; | |
78 entry_ = nullptr; | |
79 return entry; | |
80 } | |
81 | |
82 disk_cache::Entry** GetPtr() { return &entry_; } | |
83 | |
84 private: | |
85 disk_cache::Entry* entry_ = nullptr; | |
86 | |
87 DISALLOW_COPY_AND_ASSIGN(WrappedEntry); | |
88 }; | |
89 | |
90 ServiceWorkerDiskCacheMigrator::Task::Task( | |
91 InflightTaskMap::KeyType task_id, | |
92 int64 resource_id, | |
93 int32 data_size, | |
94 ServiceWorkerDiskCache* src, | |
95 ServiceWorkerDiskCache* dest, | |
96 const base::WeakPtr<ServiceWorkerDiskCacheMigrator>& owner) | |
97 : task_id_(task_id), | |
98 resource_id_(resource_id), | |
99 data_size_(data_size), | |
100 owner_(owner), | |
101 weak_factory_(this) { | |
102 DCHECK_LT(0, data_size_); | |
103 reader_.reset(new ServiceWorkerResponseReader(resource_id, src)); | |
104 writer_.reset(new ServiceWorkerResponseWriter(resource_id, dest)); | |
105 metadata_writer_.reset( | |
106 new ServiceWorkerResponseMetadataWriter(resource_id, dest)); | |
107 } | |
108 | |
109 ServiceWorkerDiskCacheMigrator::Task::~Task() { | |
110 } | |
111 | |
112 void ServiceWorkerDiskCacheMigrator::Task::Run() { | |
113 ReadResponseInfo(); | |
114 } | |
115 | |
116 void ServiceWorkerDiskCacheMigrator::Task::ReadResponseInfo() { | |
117 scoped_refptr<HttpResponseInfoIOBuffer> info_buffer( | |
118 new HttpResponseInfoIOBuffer); | |
119 reader_->ReadInfo(info_buffer.get(), | |
120 base::Bind(&Task::OnReadResponseInfo, | |
121 weak_factory_.GetWeakPtr(), info_buffer)); | |
122 } | |
123 | |
124 void ServiceWorkerDiskCacheMigrator::Task::OnReadResponseInfo( | |
125 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer, | |
126 int result) { | |
127 if (result < 0) { | |
128 LOG(ERROR) << "Failed to read the response info"; | |
129 Finish(SERVICE_WORKER_ERROR_FAILED); | |
130 return; | |
131 } | |
132 writer_->WriteInfo(info_buffer.get(), | |
133 base::Bind(&Task::OnWriteResponseInfo, | |
134 weak_factory_.GetWeakPtr(), info_buffer)); | |
135 } | |
136 | |
137 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseInfo( | |
138 const scoped_refptr<HttpResponseInfoIOBuffer>& buffer, | |
139 int result) { | |
140 if (result < 0) { | |
141 LOG(ERROR) << "Failed to write the response info"; | |
142 Finish(SERVICE_WORKER_ERROR_FAILED); | |
143 return; | |
144 } | |
145 | |
146 const net::HttpResponseInfo* http_info = buffer->http_info.get(); | |
147 if (http_info->metadata && http_info->metadata->size()) { | |
148 WriteResponseMetadata(buffer); | |
149 return; | |
150 } | |
151 ReadResponseData(); | |
152 } | |
153 | |
154 void ServiceWorkerDiskCacheMigrator::Task::WriteResponseMetadata( | |
155 const scoped_refptr<HttpResponseInfoIOBuffer>& info_buffer) { | |
156 const net::HttpResponseInfo* http_info = info_buffer->http_info.get(); | |
157 DCHECK(http_info->metadata); | |
158 DCHECK(http_info->metadata->size()); | |
159 | |
160 // |wrapped_buffer| does not own the given metadata buffer, so a callback | |
161 // for WriteMetadata keeps |info_buffer| which is the real owner of the | |
162 // metadata buffer. | |
163 scoped_refptr<net::WrappedIOBuffer> wrapped_buffer = | |
164 new net::WrappedIOBuffer(http_info->metadata->data()); | |
165 metadata_writer_->WriteMetadata( | |
166 wrapped_buffer.get(), http_info->metadata->size(), | |
167 base::Bind(&Task::OnWriteResponseMetadata, weak_factory_.GetWeakPtr(), | |
168 info_buffer)); | |
169 } | |
170 | |
171 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseMetadata( | |
172 const scoped_refptr<HttpResponseInfoIOBuffer>& protect, | |
173 int result) { | |
174 if (result < 0) { | |
175 LOG(ERROR) << "Failed to write the response metadata"; | |
176 Finish(SERVICE_WORKER_ERROR_FAILED); | |
177 return; | |
178 } | |
179 ReadResponseData(); | |
180 } | |
181 | |
182 void ServiceWorkerDiskCacheMigrator::Task::ReadResponseData() { | |
183 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_size_); | |
184 reader_->ReadData(buffer.get(), data_size_, | |
185 base::Bind(&Task::OnReadResponseData, | |
186 weak_factory_.GetWeakPtr(), buffer)); | |
187 } | |
188 | |
189 void ServiceWorkerDiskCacheMigrator::Task::OnReadResponseData( | |
190 const scoped_refptr<net::IOBuffer>& buffer, | |
191 int result) { | |
192 if (result < 0) { | |
193 LOG(ERROR) << "Failed to read the response data"; | |
194 Finish(SERVICE_WORKER_ERROR_FAILED); | |
195 return; | |
196 } | |
197 writer_->WriteData( | |
198 buffer.get(), result, | |
199 base::Bind(&Task::OnWriteResponseData, weak_factory_.GetWeakPtr())); | |
200 } | |
201 | |
202 void ServiceWorkerDiskCacheMigrator::Task::OnWriteResponseData(int result) { | |
203 if (result < 0) { | |
204 LOG(ERROR) << "Failed to write the response data"; | |
205 Finish(SERVICE_WORKER_ERROR_FAILED); | |
206 return; | |
207 } | |
208 Finish(SERVICE_WORKER_OK); | |
209 } | |
210 | |
211 void ServiceWorkerDiskCacheMigrator::Task::Finish( | |
212 ServiceWorkerStatusCode status) { | |
213 DCHECK(owner_); | |
214 owner_->OnEntryMigrated(task_id_, status); | |
215 } | |
216 | |
217 ServiceWorkerDiskCacheMigrator::ServiceWorkerDiskCacheMigrator( | |
218 ServiceWorkerDiskCache* src, | |
219 ServiceWorkerDiskCache* dest) | |
220 : src_(src), dest_(dest), weak_factory_(this) { | |
221 DCHECK(!src_->is_disabled()); | |
222 DCHECK(!dest_->is_disabled()); | |
223 } | |
224 | |
225 ServiceWorkerDiskCacheMigrator::~ServiceWorkerDiskCacheMigrator() { | |
226 } | |
227 | |
228 void ServiceWorkerDiskCacheMigrator::Start(const StatusCallback& callback) { | |
229 callback_ = callback; | |
230 iterator_ = src_->disk_cache()->CreateIterator(); | |
231 OpenNextEntry(); | |
232 } | |
233 | |
234 void ServiceWorkerDiskCacheMigrator::OpenNextEntry() { | |
235 DCHECK(!pending_task_); | |
236 DCHECK(!is_iterating_); | |
237 is_iterating_ = true; | |
238 | |
239 scoped_ptr<WrappedEntry> wrapped_entry(new WrappedEntry); | |
240 disk_cache::Entry** entry_ptr = wrapped_entry->GetPtr(); | |
241 | |
242 net::CompletionCallback callback = base::Bind( | |
243 &ServiceWorkerDiskCacheMigrator::OnNextEntryOpened, | |
244 weak_factory_.GetWeakPtr(), base::Passed(wrapped_entry.Pass())); | |
245 int result = iterator_->OpenNextEntry(entry_ptr, callback); | |
246 if (result == net::ERR_IO_PENDING) | |
247 return; | |
248 callback.Run(result); | |
249 } | |
250 | |
251 void ServiceWorkerDiskCacheMigrator::OnNextEntryOpened( | |
252 scoped_ptr<WrappedEntry> wrapped_entry, | |
253 int result) { | |
254 DCHECK(!pending_task_); | |
255 is_iterating_ = false; | |
256 | |
257 if (result == net::ERR_FAILED) { | |
258 // ERR_FAILED means the iterator reaches the end of the enumeration. | |
falken
2015/06/03 06:45:33
nit: reached
nhiroki
2015/06/03 08:51:22
Done.
| |
259 if (inflight_tasks_.IsEmpty()) | |
260 Complete(SERVICE_WORKER_OK); | |
261 return; | |
262 } | |
263 | |
264 if (result != net::OK) { | |
265 LOG(ERROR) << "Failed to open the next entry"; | |
266 inflight_tasks_.Clear(); | |
267 Complete(SERVICE_WORKER_ERROR_FAILED); | |
268 return; | |
269 } | |
270 | |
271 disk_cache::ScopedEntryPtr scoped_entry(wrapped_entry->Unwrap()); | |
272 DCHECK(scoped_entry); | |
273 | |
274 int64 resource_id = kInvalidServiceWorkerResourceId; | |
275 if (!base::StringToInt64(scoped_entry->GetKey(), &resource_id)) { | |
276 LOG(ERROR) << "Failed to read the resource id"; | |
277 inflight_tasks_.Clear(); | |
278 Complete(SERVICE_WORKER_ERROR_FAILED); | |
279 return; | |
280 } | |
281 | |
282 InflightTaskMap::KeyType task_id = next_task_id_++; | |
283 pending_task_.reset(new Task(task_id, resource_id, | |
284 scoped_entry->GetDataSize(0), src_, dest_, | |
falken
2015/06/03 06:45:33
What's the 0 from?
nhiroki
2015/06/03 08:51:22
This is the data type and '0' means "ResponseInfo"
| |
285 weak_factory_.GetWeakPtr())); | |
286 if (inflight_tasks_.size() < max_number_of_inflight_tasks_) { | |
287 RunPendingTask(); | |
288 OpenNextEntry(); | |
289 return; | |
290 } | |
291 // |pending_task_| will run when an inflight task is completed. | |
292 } | |
293 | |
294 void ServiceWorkerDiskCacheMigrator::RunPendingTask() { | |
295 DCHECK(pending_task_); | |
296 DCHECK_GT(max_number_of_inflight_tasks_, inflight_tasks_.size()); | |
297 InflightTaskMap::KeyType task_id = pending_task_->task_id(); | |
298 pending_task_->Run(); | |
299 inflight_tasks_.AddWithID(pending_task_.release(), task_id); | |
300 } | |
301 | |
302 void ServiceWorkerDiskCacheMigrator::OnEntryMigrated( | |
303 InflightTaskMap::KeyType task_id, | |
304 ServiceWorkerStatusCode status) { | |
305 DCHECK(inflight_tasks_.Lookup(task_id)); | |
306 inflight_tasks_.Remove(task_id); | |
307 | |
308 if (status != SERVICE_WORKER_OK) { | |
309 inflight_tasks_.Clear(); | |
310 Complete(status); | |
311 return; | |
312 } | |
313 | |
314 if (pending_task_) { | |
315 RunPendingTask(); | |
316 OpenNextEntry(); | |
317 return; | |
318 } | |
319 | |
320 if (is_iterating_) | |
321 return; | |
322 | |
323 if (inflight_tasks_.IsEmpty()) { | |
324 Complete(SERVICE_WORKER_OK); | |
325 return; | |
falken
2015/06/03 06:45:33
nit: dont' need this return
nhiroki
2015/06/03 08:51:22
Done.
| |
326 } | |
327 } | |
328 | |
329 void ServiceWorkerDiskCacheMigrator::Complete(ServiceWorkerStatusCode status) { | |
330 DCHECK(inflight_tasks_.IsEmpty()); | |
331 // TODO(nhiroki): Add UMA for the result of migration. | |
332 callback_.Run(status); | |
333 } | |
334 | |
335 } // namespace content | |
OLD | NEW |