Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / appcache / appcache_response.cc
blobd53f94174bad0fa5f1935f3a2b3050355371834f
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 { kResponseInfoIndex, kResponseContentIndex, kResponseMetadataIndex };
27 // An IOBuffer that wraps a pickle's data. Ownership of the
28 // pickle is transfered to the WrappedPickleIOBuffer object.
29 class WrappedPickleIOBuffer : public net::WrappedIOBuffer {
30 public:
31 explicit WrappedPickleIOBuffer(const Pickle* pickle) :
32 net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle->data())),
33 pickle_(pickle) {
34 DCHECK(pickle->data());
37 private:
38 ~WrappedPickleIOBuffer() override {}
40 scoped_ptr<const Pickle> pickle_;
43 } // anon namespace
46 // AppCacheResponseInfo ----------------------------------------------
48 AppCacheResponseInfo::AppCacheResponseInfo(
49 AppCacheStorage* storage, const GURL& manifest_url,
50 int64 response_id, net::HttpResponseInfo* http_info,
51 int64 response_data_size)
52 : manifest_url_(manifest_url), response_id_(response_id),
53 http_response_info_(http_info), response_data_size_(response_data_size),
54 storage_(storage) {
55 DCHECK(http_info);
56 DCHECK(response_id != kAppCacheNoResponseId);
57 storage_->working_set()->AddResponseInfo(this);
60 AppCacheResponseInfo::~AppCacheResponseInfo() {
61 storage_->working_set()->RemoveResponseInfo(this);
64 // HttpResponseInfoIOBuffer ------------------------------------------
66 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer()
67 : response_data_size(kUnkownResponseDataSize) {}
69 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo* info)
70 : http_info(info), response_data_size(kUnkownResponseDataSize) {}
72 HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {}
74 // AppCacheResponseIO ----------------------------------------------
76 AppCacheResponseIO::AppCacheResponseIO(
77 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
78 : response_id_(response_id),
79 group_id_(group_id),
80 disk_cache_(disk_cache),
81 entry_(NULL),
82 buffer_len_(0),
83 weak_factory_(this) {
86 AppCacheResponseIO::~AppCacheResponseIO() {
87 if (entry_)
88 entry_->Close();
91 void AppCacheResponseIO::ScheduleIOCompletionCallback(int result) {
92 base::MessageLoop::current()->PostTask(
93 FROM_HERE, base::Bind(&AppCacheResponseIO::OnIOComplete,
94 weak_factory_.GetWeakPtr(), result));
97 void AppCacheResponseIO::InvokeUserCompletionCallback(int result) {
98 // Clear the user callback and buffers prior to invoking the callback
99 // so the caller can schedule additional operations in the callback.
100 buffer_ = NULL;
101 info_buffer_ = NULL;
102 net::CompletionCallback cb = callback_;
103 callback_.Reset();
104 cb.Run(result);
107 void AppCacheResponseIO::ReadRaw(int index, int offset,
108 net::IOBuffer* buf, int buf_len) {
109 DCHECK(entry_);
110 int rv = entry_->Read(
111 index, offset, buf, buf_len,
112 base::Bind(&AppCacheResponseIO::OnRawIOComplete,
113 weak_factory_.GetWeakPtr()));
114 if (rv != net::ERR_IO_PENDING)
115 ScheduleIOCompletionCallback(rv);
118 void AppCacheResponseIO::WriteRaw(int index, int offset,
119 net::IOBuffer* buf, int buf_len) {
120 DCHECK(entry_);
121 int rv = entry_->Write(
122 index, offset, buf, buf_len,
123 base::Bind(&AppCacheResponseIO::OnRawIOComplete,
124 weak_factory_.GetWeakPtr()));
125 if (rv != net::ERR_IO_PENDING)
126 ScheduleIOCompletionCallback(rv);
129 void AppCacheResponseIO::OnRawIOComplete(int result) {
130 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
131 tracked_objects::ScopedTracker tracking_profile(
132 FROM_HERE_WITH_EXPLICIT_FUNCTION(
133 "422516 AppCacheResponseIO::OnRawIOComplete"));
135 DCHECK_NE(net::ERR_IO_PENDING, result);
136 OnIOComplete(result);
139 void AppCacheResponseIO::OpenEntryIfNeeded() {
140 int rv;
141 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
142 if (entry_) {
143 rv = net::OK;
144 } else if (!disk_cache_) {
145 rv = net::ERR_FAILED;
146 } else {
147 entry_ptr = new AppCacheDiskCacheInterface::Entry*;
148 open_callback_ =
149 base::Bind(&AppCacheResponseIO::OpenEntryCallback,
150 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
151 rv = disk_cache_->OpenEntry(response_id_, entry_ptr, open_callback_);
154 if (rv != net::ERR_IO_PENDING)
155 OpenEntryCallback(entry_ptr, rv);
158 void AppCacheResponseIO::OpenEntryCallback(
159 AppCacheDiskCacheInterface::Entry** entry, int rv) {
160 DCHECK(info_buffer_.get() || buffer_.get());
162 if (!open_callback_.is_null()) {
163 if (rv == net::OK) {
164 DCHECK(entry);
165 entry_ = *entry;
167 open_callback_.Reset();
169 OnOpenEntryComplete();
173 // AppCacheResponseReader ----------------------------------------------
175 AppCacheResponseReader::AppCacheResponseReader(
176 int64 response_id,
177 int64 group_id,
178 AppCacheDiskCacheInterface* disk_cache)
179 : AppCacheResponseIO(response_id, group_id, disk_cache),
180 range_offset_(0),
181 range_length_(kint32max),
182 read_position_(0),
183 reading_metadata_size_(0),
184 weak_factory_(this) {
187 AppCacheResponseReader::~AppCacheResponseReader() {
190 void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer* info_buf,
191 const net::CompletionCallback& callback) {
192 DCHECK(!callback.is_null());
193 DCHECK(!IsReadPending());
194 DCHECK(info_buf);
195 DCHECK(!info_buf->http_info.get());
196 DCHECK(!buffer_.get());
197 DCHECK(!info_buffer_.get());
199 info_buffer_ = info_buf;
200 callback_ = callback; // cleared on completion
201 OpenEntryIfNeeded();
204 void AppCacheResponseReader::ContinueReadInfo() {
205 int size = entry_->GetSize(kResponseInfoIndex);
206 if (size <= 0) {
207 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
208 return;
211 buffer_ = new net::IOBuffer(size);
212 ReadRaw(kResponseInfoIndex, 0, buffer_.get(), size);
215 void AppCacheResponseReader::ReadData(net::IOBuffer* buf, int buf_len,
216 const net::CompletionCallback& callback) {
217 DCHECK(!callback.is_null());
218 DCHECK(!IsReadPending());
219 DCHECK(buf);
220 DCHECK(buf_len >= 0);
221 DCHECK(!buffer_.get());
222 DCHECK(!info_buffer_.get());
224 buffer_ = buf;
225 buffer_len_ = buf_len;
226 callback_ = callback; // cleared on completion
227 OpenEntryIfNeeded();
230 void AppCacheResponseReader::ContinueReadData() {
231 if (read_position_ + buffer_len_ > range_length_) {
232 // TODO(michaeln): What about integer overflows?
233 DCHECK(range_length_ >= read_position_);
234 buffer_len_ = range_length_ - read_position_;
236 ReadRaw(kResponseContentIndex,
237 range_offset_ + read_position_,
238 buffer_.get(),
239 buffer_len_);
242 void AppCacheResponseReader::SetReadRange(int offset, int length) {
243 DCHECK(!IsReadPending() && !read_position_);
244 range_offset_ = offset;
245 range_length_ = length;
248 void AppCacheResponseReader::OnIOComplete(int result) {
249 if (result >= 0) {
250 if (reading_metadata_size_) {
251 DCHECK(reading_metadata_size_ == result);
252 DCHECK(info_buffer_->http_info->metadata);
253 reading_metadata_size_ = 0;
254 } else if (info_buffer_.get()) {
255 // Deserialize the http info structure, ensuring we got headers.
256 Pickle pickle(buffer_->data(), result);
257 scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
258 bool response_truncated = false;
259 if (!info->InitFromPickle(pickle, &response_truncated) ||
260 !info->headers.get()) {
261 InvokeUserCompletionCallback(net::ERR_FAILED);
262 return;
264 DCHECK(!response_truncated);
265 info_buffer_->http_info.reset(info.release());
267 // Also return the size of the response body
268 DCHECK(entry_);
269 info_buffer_->response_data_size =
270 entry_->GetSize(kResponseContentIndex);
272 int64 metadata_size = entry_->GetSize(kResponseMetadataIndex);
273 if (metadata_size > 0) {
274 reading_metadata_size_ = metadata_size;
275 info_buffer_->http_info->metadata =
276 new net::IOBufferWithSize(metadata_size);
277 ReadRaw(kResponseMetadataIndex, 0,
278 info_buffer_->http_info->metadata.get(), metadata_size);
279 return;
281 } else {
282 read_position_ += result;
285 InvokeUserCompletionCallback(result);
288 void AppCacheResponseReader::OnOpenEntryComplete() {
289 if (!entry_) {
290 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
291 return;
293 if (info_buffer_.get())
294 ContinueReadInfo();
295 else
296 ContinueReadData();
299 // AppCacheResponseWriter ----------------------------------------------
301 AppCacheResponseWriter::AppCacheResponseWriter(
302 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
303 : AppCacheResponseIO(response_id, group_id, disk_cache),
304 info_size_(0),
305 write_position_(0),
306 write_amount_(0),
307 creation_phase_(INITIAL_ATTEMPT),
308 weak_factory_(this) {
311 AppCacheResponseWriter::~AppCacheResponseWriter() {
314 void AppCacheResponseWriter::WriteInfo(
315 HttpResponseInfoIOBuffer* info_buf,
316 const net::CompletionCallback& callback) {
317 DCHECK(!callback.is_null());
318 DCHECK(!IsWritePending());
319 DCHECK(info_buf);
320 DCHECK(info_buf->http_info.get());
321 DCHECK(!buffer_.get());
322 DCHECK(!info_buffer_.get());
323 DCHECK(info_buf->http_info->headers.get());
325 info_buffer_ = info_buf;
326 callback_ = callback; // cleared on completion
327 CreateEntryIfNeededAndContinue();
330 void AppCacheResponseWriter::ContinueWriteInfo() {
331 if (!entry_) {
332 ScheduleIOCompletionCallback(net::ERR_FAILED);
333 return;
336 const bool kSkipTransientHeaders = true;
337 const bool kTruncated = false;
338 Pickle* pickle = new Pickle;
339 info_buffer_->http_info->Persist(pickle, kSkipTransientHeaders, kTruncated);
340 write_amount_ = static_cast<int>(pickle->size());
341 buffer_ = new WrappedPickleIOBuffer(pickle); // takes ownership of pickle
342 WriteRaw(kResponseInfoIndex, 0, buffer_.get(), write_amount_);
345 void AppCacheResponseWriter::WriteData(
346 net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) {
347 DCHECK(!callback.is_null());
348 DCHECK(!IsWritePending());
349 DCHECK(buf);
350 DCHECK(buf_len >= 0);
351 DCHECK(!buffer_.get());
352 DCHECK(!info_buffer_.get());
354 buffer_ = buf;
355 write_amount_ = buf_len;
356 callback_ = callback; // cleared on completion
357 CreateEntryIfNeededAndContinue();
360 void AppCacheResponseWriter::ContinueWriteData() {
361 if (!entry_) {
362 ScheduleIOCompletionCallback(net::ERR_FAILED);
363 return;
365 WriteRaw(
366 kResponseContentIndex, write_position_, buffer_.get(), write_amount_);
369 void AppCacheResponseWriter::OnIOComplete(int result) {
370 if (result >= 0) {
371 DCHECK(write_amount_ == result);
372 if (!info_buffer_.get())
373 write_position_ += result;
374 else
375 info_size_ = result;
377 InvokeUserCompletionCallback(result);
380 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
381 int rv;
382 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
383 if (entry_) {
384 creation_phase_ = NO_ATTEMPT;
385 rv = net::OK;
386 } else if (!disk_cache_) {
387 creation_phase_ = NO_ATTEMPT;
388 rv = net::ERR_FAILED;
389 } else {
390 creation_phase_ = INITIAL_ATTEMPT;
391 entry_ptr = new AppCacheDiskCacheInterface::Entry*;
392 create_callback_ =
393 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
394 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
395 rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
397 if (rv != net::ERR_IO_PENDING)
398 OnCreateEntryComplete(entry_ptr, rv);
401 void AppCacheResponseWriter::OnCreateEntryComplete(
402 AppCacheDiskCacheInterface::Entry** entry, int rv) {
403 DCHECK(info_buffer_.get() || buffer_.get());
405 if (creation_phase_ == INITIAL_ATTEMPT) {
406 if (rv != net::OK) {
407 // We may try to overwrite existing entries.
408 creation_phase_ = DOOM_EXISTING;
409 rv = disk_cache_->DoomEntry(response_id_, create_callback_);
410 if (rv != net::ERR_IO_PENDING)
411 OnCreateEntryComplete(NULL, rv);
412 return;
414 } else if (creation_phase_ == DOOM_EXISTING) {
415 creation_phase_ = SECOND_ATTEMPT;
416 AppCacheDiskCacheInterface::Entry** entry_ptr =
417 new AppCacheDiskCacheInterface::Entry*;
418 create_callback_ =
419 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
420 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
421 rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
422 if (rv != net::ERR_IO_PENDING)
423 OnCreateEntryComplete(entry_ptr, rv);
424 return;
427 if (!create_callback_.is_null()) {
428 if (rv == net::OK)
429 entry_ = *entry;
431 create_callback_.Reset();
434 if (info_buffer_.get())
435 ContinueWriteInfo();
436 else
437 ContinueWriteData();
440 // AppCacheResponseMetadataWriter ----------------------------------------------
442 AppCacheResponseMetadataWriter::AppCacheResponseMetadataWriter(
443 int64 response_id,
444 int64 group_id,
445 AppCacheDiskCacheInterface* disk_cache)
446 : AppCacheResponseIO(response_id, group_id, disk_cache),
447 write_amount_(0),
448 weak_factory_(this) {
451 AppCacheResponseMetadataWriter::~AppCacheResponseMetadataWriter() {
454 void AppCacheResponseMetadataWriter::WriteMetadata(
455 net::IOBuffer* buf,
456 int buf_len,
457 const net::CompletionCallback& callback) {
458 DCHECK(!callback.is_null());
459 DCHECK(!IsIOPending());
460 DCHECK(buf);
461 DCHECK(buf_len >= 0);
462 DCHECK(!buffer_.get());
464 buffer_ = buf;
465 write_amount_ = buf_len;
466 callback_ = callback; // cleared on completion
467 OpenEntryIfNeeded();
470 void AppCacheResponseMetadataWriter::OnOpenEntryComplete() {
471 if (!entry_) {
472 ScheduleIOCompletionCallback(net::ERR_FAILED);
473 return;
475 WriteRaw(kResponseMetadataIndex, 0, buffer_.get(), write_amount_);
478 void AppCacheResponseMetadataWriter::OnIOComplete(int result) {
479 DCHECK(result < 0 || write_amount_ == result);
480 InvokeUserCompletionCallback(result);
483 } // namespace content