[Storage] Blob Storage Refactoring pt 1:
[chromium-blink-merge.git] / content / browser / appcache / appcache_response.cc
blobc38d3afa29305830e595b4c4adeb95207bf50f02
1 // Copyright (c) 2012 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.
5 #include "content/browser/appcache/appcache_response.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/pickle.h"
13 #include "base/profiler/scoped_tracker.h"
14 #include "base/strings/string_util.h"
15 #include "content/browser/appcache/appcache_storage.h"
16 #include "net/base/completion_callback.h"
17 #include "net/base/io_buffer.h"
18 #include "net/base/net_errors.h"
20 namespace content {
22 namespace {
24 // Disk cache entry data indices.
25 enum {
26 kResponseInfoIndex,
27 kResponseContentIndex
30 // An IOBuffer that wraps a pickle's data. Ownership of the
31 // pickle is transfered to the WrappedPickleIOBuffer object.
32 class WrappedPickleIOBuffer : public net::WrappedIOBuffer {
33 public:
34 explicit WrappedPickleIOBuffer(const Pickle* pickle) :
35 net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle->data())),
36 pickle_(pickle) {
37 DCHECK(pickle->data());
40 private:
41 ~WrappedPickleIOBuffer() override {}
43 scoped_ptr<const Pickle> pickle_;
46 } // anon namespace
49 // AppCacheResponseInfo ----------------------------------------------
51 AppCacheResponseInfo::AppCacheResponseInfo(
52 AppCacheStorage* storage, const GURL& manifest_url,
53 int64 response_id, net::HttpResponseInfo* http_info,
54 int64 response_data_size)
55 : manifest_url_(manifest_url), response_id_(response_id),
56 http_response_info_(http_info), response_data_size_(response_data_size),
57 storage_(storage) {
58 DCHECK(http_info);
59 DCHECK(response_id != kAppCacheNoResponseId);
60 storage_->working_set()->AddResponseInfo(this);
63 AppCacheResponseInfo::~AppCacheResponseInfo() {
64 storage_->working_set()->RemoveResponseInfo(this);
67 // HttpResponseInfoIOBuffer ------------------------------------------
69 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer()
70 : response_data_size(kUnkownResponseDataSize) {}
72 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo* info)
73 : http_info(info), response_data_size(kUnkownResponseDataSize) {}
75 HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {}
77 // AppCacheResponseIO ----------------------------------------------
79 AppCacheResponseIO::AppCacheResponseIO(
80 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
81 : response_id_(response_id),
82 group_id_(group_id),
83 disk_cache_(disk_cache),
84 entry_(NULL),
85 buffer_len_(0),
86 weak_factory_(this) {
89 AppCacheResponseIO::~AppCacheResponseIO() {
90 if (entry_)
91 entry_->Close();
94 void AppCacheResponseIO::ScheduleIOCompletionCallback(int result) {
95 base::MessageLoop::current()->PostTask(
96 FROM_HERE, base::Bind(&AppCacheResponseIO::OnIOComplete,
97 weak_factory_.GetWeakPtr(), result));
100 void AppCacheResponseIO::InvokeUserCompletionCallback(int result) {
101 // Clear the user callback and buffers prior to invoking the callback
102 // so the caller can schedule additional operations in the callback.
103 buffer_ = NULL;
104 info_buffer_ = NULL;
105 net::CompletionCallback cb = callback_;
106 callback_.Reset();
107 cb.Run(result);
110 void AppCacheResponseIO::ReadRaw(int index, int offset,
111 net::IOBuffer* buf, int buf_len) {
112 DCHECK(entry_);
113 int rv = entry_->Read(
114 index, offset, buf, buf_len,
115 base::Bind(&AppCacheResponseIO::OnRawIOComplete,
116 weak_factory_.GetWeakPtr()));
117 if (rv != net::ERR_IO_PENDING)
118 ScheduleIOCompletionCallback(rv);
121 void AppCacheResponseIO::WriteRaw(int index, int offset,
122 net::IOBuffer* buf, int buf_len) {
123 DCHECK(entry_);
124 int rv = entry_->Write(
125 index, offset, buf, buf_len,
126 base::Bind(&AppCacheResponseIO::OnRawIOComplete,
127 weak_factory_.GetWeakPtr()));
128 if (rv != net::ERR_IO_PENDING)
129 ScheduleIOCompletionCallback(rv);
132 void AppCacheResponseIO::OnRawIOComplete(int result) {
133 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
134 tracked_objects::ScopedTracker tracking_profile(
135 FROM_HERE_WITH_EXPLICIT_FUNCTION(
136 "422516 AppCacheResponseIO::OnRawIOComplete"));
138 DCHECK_NE(net::ERR_IO_PENDING, result);
139 OnIOComplete(result);
143 // AppCacheResponseReader ----------------------------------------------
145 AppCacheResponseReader::AppCacheResponseReader(
146 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
147 : AppCacheResponseIO(response_id, group_id, disk_cache),
148 range_offset_(0),
149 range_length_(kint32max),
150 read_position_(0),
151 weak_factory_(this) {
154 AppCacheResponseReader::~AppCacheResponseReader() {
157 void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer* info_buf,
158 const net::CompletionCallback& callback) {
159 DCHECK(!callback.is_null());
160 DCHECK(!IsReadPending());
161 DCHECK(info_buf);
162 DCHECK(!info_buf->http_info.get());
163 DCHECK(!buffer_.get());
164 DCHECK(!info_buffer_.get());
166 info_buffer_ = info_buf;
167 callback_ = callback; // cleared on completion
168 OpenEntryIfNeededAndContinue();
171 void AppCacheResponseReader::ContinueReadInfo() {
172 if (!entry_) {
173 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
174 return;
177 int size = entry_->GetSize(kResponseInfoIndex);
178 if (size <= 0) {
179 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
180 return;
183 buffer_ = new net::IOBuffer(size);
184 ReadRaw(kResponseInfoIndex, 0, buffer_.get(), size);
187 void AppCacheResponseReader::ReadData(net::IOBuffer* buf, int buf_len,
188 const net::CompletionCallback& callback) {
189 DCHECK(!callback.is_null());
190 DCHECK(!IsReadPending());
191 DCHECK(buf);
192 DCHECK(buf_len >= 0);
193 DCHECK(!buffer_.get());
194 DCHECK(!info_buffer_.get());
196 buffer_ = buf;
197 buffer_len_ = buf_len;
198 callback_ = callback; // cleared on completion
199 OpenEntryIfNeededAndContinue();
202 void AppCacheResponseReader::ContinueReadData() {
203 if (!entry_) {
204 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
205 return;
208 if (read_position_ + buffer_len_ > range_length_) {
209 // TODO(michaeln): What about integer overflows?
210 DCHECK(range_length_ >= read_position_);
211 buffer_len_ = range_length_ - read_position_;
213 ReadRaw(kResponseContentIndex,
214 range_offset_ + read_position_,
215 buffer_.get(),
216 buffer_len_);
219 void AppCacheResponseReader::SetReadRange(int offset, int length) {
220 DCHECK(!IsReadPending() && !read_position_);
221 range_offset_ = offset;
222 range_length_ = length;
225 void AppCacheResponseReader::OnIOComplete(int result) {
226 if (result >= 0) {
227 if (info_buffer_.get()) {
228 // Deserialize the http info structure, ensuring we got headers.
229 Pickle pickle(buffer_->data(), result);
230 scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
231 bool response_truncated = false;
232 if (!info->InitFromPickle(pickle, &response_truncated) ||
233 !info->headers.get()) {
234 InvokeUserCompletionCallback(net::ERR_FAILED);
235 return;
237 DCHECK(!response_truncated);
238 info_buffer_->http_info.reset(info.release());
240 // Also return the size of the response body
241 DCHECK(entry_);
242 info_buffer_->response_data_size =
243 entry_->GetSize(kResponseContentIndex);
244 } else {
245 read_position_ += result;
248 InvokeUserCompletionCallback(result);
251 void AppCacheResponseReader::OpenEntryIfNeededAndContinue() {
252 int rv;
253 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
254 if (entry_) {
255 rv = net::OK;
256 } else if (!disk_cache_) {
257 rv = net::ERR_FAILED;
258 } else {
259 entry_ptr = new AppCacheDiskCacheInterface::Entry*;
260 open_callback_ =
261 base::Bind(&AppCacheResponseReader::OnOpenEntryComplete,
262 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
263 rv = disk_cache_->OpenEntry(response_id_, entry_ptr, open_callback_);
266 if (rv != net::ERR_IO_PENDING)
267 OnOpenEntryComplete(entry_ptr, rv);
270 void AppCacheResponseReader::OnOpenEntryComplete(
271 AppCacheDiskCacheInterface::Entry** entry, int rv) {
272 DCHECK(info_buffer_.get() || buffer_.get());
274 if (!open_callback_.is_null()) {
275 if (rv == net::OK) {
276 DCHECK(entry);
277 entry_ = *entry;
279 open_callback_.Reset();
282 if (info_buffer_.get())
283 ContinueReadInfo();
284 else
285 ContinueReadData();
288 // AppCacheResponseWriter ----------------------------------------------
290 AppCacheResponseWriter::AppCacheResponseWriter(
291 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
292 : AppCacheResponseIO(response_id, group_id, disk_cache),
293 info_size_(0),
294 write_position_(0),
295 write_amount_(0),
296 creation_phase_(INITIAL_ATTEMPT),
297 weak_factory_(this) {
300 AppCacheResponseWriter::~AppCacheResponseWriter() {
303 void AppCacheResponseWriter::WriteInfo(
304 HttpResponseInfoIOBuffer* info_buf,
305 const net::CompletionCallback& callback) {
306 DCHECK(!callback.is_null());
307 DCHECK(!IsWritePending());
308 DCHECK(info_buf);
309 DCHECK(info_buf->http_info.get());
310 DCHECK(!buffer_.get());
311 DCHECK(!info_buffer_.get());
312 DCHECK(info_buf->http_info->headers.get());
314 info_buffer_ = info_buf;
315 callback_ = callback; // cleared on completion
316 CreateEntryIfNeededAndContinue();
319 void AppCacheResponseWriter::ContinueWriteInfo() {
320 if (!entry_) {
321 ScheduleIOCompletionCallback(net::ERR_FAILED);
322 return;
325 const bool kSkipTransientHeaders = true;
326 const bool kTruncated = false;
327 Pickle* pickle = new Pickle;
328 info_buffer_->http_info->Persist(pickle, kSkipTransientHeaders, kTruncated);
329 write_amount_ = static_cast<int>(pickle->size());
330 buffer_ = new WrappedPickleIOBuffer(pickle); // takes ownership of pickle
331 WriteRaw(kResponseInfoIndex, 0, buffer_.get(), write_amount_);
334 void AppCacheResponseWriter::WriteData(
335 net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) {
336 DCHECK(!callback.is_null());
337 DCHECK(!IsWritePending());
338 DCHECK(buf);
339 DCHECK(buf_len >= 0);
340 DCHECK(!buffer_.get());
341 DCHECK(!info_buffer_.get());
343 buffer_ = buf;
344 write_amount_ = buf_len;
345 callback_ = callback; // cleared on completion
346 CreateEntryIfNeededAndContinue();
349 void AppCacheResponseWriter::ContinueWriteData() {
350 if (!entry_) {
351 ScheduleIOCompletionCallback(net::ERR_FAILED);
352 return;
354 WriteRaw(
355 kResponseContentIndex, write_position_, buffer_.get(), write_amount_);
358 void AppCacheResponseWriter::OnIOComplete(int result) {
359 if (result >= 0) {
360 DCHECK(write_amount_ == result);
361 if (!info_buffer_.get())
362 write_position_ += result;
363 else
364 info_size_ = result;
366 InvokeUserCompletionCallback(result);
369 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
370 int rv;
371 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
372 if (entry_) {
373 creation_phase_ = NO_ATTEMPT;
374 rv = net::OK;
375 } else if (!disk_cache_) {
376 creation_phase_ = NO_ATTEMPT;
377 rv = net::ERR_FAILED;
378 } else {
379 creation_phase_ = INITIAL_ATTEMPT;
380 entry_ptr = new AppCacheDiskCacheInterface::Entry*;
381 create_callback_ =
382 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
383 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
384 rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
386 if (rv != net::ERR_IO_PENDING)
387 OnCreateEntryComplete(entry_ptr, rv);
390 void AppCacheResponseWriter::OnCreateEntryComplete(
391 AppCacheDiskCacheInterface::Entry** entry, int rv) {
392 DCHECK(info_buffer_.get() || buffer_.get());
394 if (creation_phase_ == INITIAL_ATTEMPT) {
395 if (rv != net::OK) {
396 // We may try to overwrite existing entries.
397 creation_phase_ = DOOM_EXISTING;
398 rv = disk_cache_->DoomEntry(response_id_, create_callback_);
399 if (rv != net::ERR_IO_PENDING)
400 OnCreateEntryComplete(NULL, rv);
401 return;
403 } else if (creation_phase_ == DOOM_EXISTING) {
404 creation_phase_ = SECOND_ATTEMPT;
405 AppCacheDiskCacheInterface::Entry** entry_ptr =
406 new AppCacheDiskCacheInterface::Entry*;
407 create_callback_ =
408 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
409 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
410 rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
411 if (rv != net::ERR_IO_PENDING)
412 OnCreateEntryComplete(entry_ptr, rv);
413 return;
416 if (!create_callback_.is_null()) {
417 if (rv == net::OK)
418 entry_ = *entry;
420 create_callback_.Reset();
423 if (info_buffer_.get())
424 ContinueWriteInfo();
425 else
426 ContinueWriteData();
429 } // namespace content