Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / appcache / appcache_response.cc
blobf113adf940d2168084003f41897bb5f25c8080e9
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/location.h"
11 #include "base/logging.h"
12 #include "base/numerics/safe_math.h"
13 #include "base/pickle.h"
14 #include "base/profiler/scoped_tracker.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/strings/string_util.h"
17 #include "base/thread_task_runner_handle.h"
18 #include "content/browser/appcache/appcache_storage.h"
19 #include "net/base/completion_callback.h"
20 #include "net/base/io_buffer.h"
21 #include "net/base/net_errors.h"
23 namespace content {
25 namespace {
27 // Disk cache entry data indices.
28 enum { kResponseInfoIndex, kResponseContentIndex, kResponseMetadataIndex };
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 base::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 base::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::ThreadTaskRunnerHandle::Get()->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(rtenneti): 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);
142 void AppCacheResponseIO::OpenEntryIfNeeded() {
143 int rv;
144 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
145 if (entry_) {
146 rv = net::OK;
147 } else if (!disk_cache_) {
148 rv = net::ERR_FAILED;
149 } else {
150 entry_ptr = new AppCacheDiskCacheInterface::Entry*;
151 open_callback_ =
152 base::Bind(&AppCacheResponseIO::OpenEntryCallback,
153 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
154 rv = disk_cache_->OpenEntry(response_id_, entry_ptr, open_callback_);
157 if (rv != net::ERR_IO_PENDING)
158 OpenEntryCallback(entry_ptr, rv);
161 void AppCacheResponseIO::OpenEntryCallback(
162 AppCacheDiskCacheInterface::Entry** entry, int rv) {
163 DCHECK(info_buffer_.get() || buffer_.get());
165 if (!open_callback_.is_null()) {
166 if (rv == net::OK) {
167 DCHECK(entry);
168 entry_ = *entry;
170 open_callback_.Reset();
172 OnOpenEntryComplete();
176 // AppCacheResponseReader ----------------------------------------------
178 AppCacheResponseReader::AppCacheResponseReader(
179 int64 response_id,
180 int64 group_id,
181 AppCacheDiskCacheInterface* disk_cache)
182 : AppCacheResponseIO(response_id, group_id, disk_cache),
183 range_offset_(0),
184 range_length_(kint32max),
185 read_position_(0),
186 reading_metadata_size_(0),
187 weak_factory_(this) {
190 AppCacheResponseReader::~AppCacheResponseReader() {
193 void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer* info_buf,
194 const net::CompletionCallback& callback) {
195 DCHECK(!callback.is_null());
196 DCHECK(!IsReadPending());
197 DCHECK(info_buf);
198 DCHECK(!info_buf->http_info.get());
199 DCHECK(!buffer_.get());
200 DCHECK(!info_buffer_.get());
202 info_buffer_ = info_buf;
203 callback_ = callback; // cleared on completion
204 OpenEntryIfNeeded();
207 void AppCacheResponseReader::ContinueReadInfo() {
208 int size = entry_->GetSize(kResponseInfoIndex);
209 if (size <= 0) {
210 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
211 return;
214 buffer_ = new net::IOBuffer(size);
215 ReadRaw(kResponseInfoIndex, 0, buffer_.get(), size);
218 void AppCacheResponseReader::ReadData(net::IOBuffer* buf, int buf_len,
219 const net::CompletionCallback& callback) {
220 DCHECK(!callback.is_null());
221 DCHECK(!IsReadPending());
222 DCHECK(buf);
223 DCHECK(buf_len >= 0);
224 DCHECK(!buffer_.get());
225 DCHECK(!info_buffer_.get());
227 buffer_ = buf;
228 buffer_len_ = buf_len;
229 callback_ = callback; // cleared on completion
230 OpenEntryIfNeeded();
233 void AppCacheResponseReader::ContinueReadData() {
234 if (read_position_ + buffer_len_ > range_length_) {
235 // TODO(michaeln): What about integer overflows?
236 DCHECK(range_length_ >= read_position_);
237 buffer_len_ = range_length_ - read_position_;
239 ReadRaw(kResponseContentIndex,
240 range_offset_ + read_position_,
241 buffer_.get(),
242 buffer_len_);
245 void AppCacheResponseReader::SetReadRange(int offset, int length) {
246 DCHECK(!IsReadPending() && !read_position_);
247 range_offset_ = offset;
248 range_length_ = length;
251 void AppCacheResponseReader::OnIOComplete(int result) {
252 if (result >= 0) {
253 if (reading_metadata_size_) {
254 DCHECK(reading_metadata_size_ == result);
255 DCHECK(info_buffer_->http_info->metadata);
256 reading_metadata_size_ = 0;
257 } else if (info_buffer_.get()) {
258 // Deserialize the http info structure, ensuring we got headers.
259 base::Pickle pickle(buffer_->data(), result);
260 scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
261 bool response_truncated = false;
262 if (!info->InitFromPickle(pickle, &response_truncated) ||
263 !info->headers.get()) {
264 InvokeUserCompletionCallback(net::ERR_FAILED);
265 return;
267 DCHECK(!response_truncated);
268 info_buffer_->http_info.reset(info.release());
270 // Also return the size of the response body
271 DCHECK(entry_);
272 info_buffer_->response_data_size =
273 entry_->GetSize(kResponseContentIndex);
275 int64 metadata_size = entry_->GetSize(kResponseMetadataIndex);
276 if (metadata_size > 0) {
277 reading_metadata_size_ = metadata_size;
278 info_buffer_->http_info->metadata = new net::IOBufferWithSize(
279 base::CheckedNumeric<size_t>(metadata_size).ValueOrDie());
280 ReadRaw(kResponseMetadataIndex, 0,
281 info_buffer_->http_info->metadata.get(), metadata_size);
282 return;
284 } else {
285 read_position_ += result;
288 InvokeUserCompletionCallback(result);
291 void AppCacheResponseReader::OnOpenEntryComplete() {
292 if (!entry_) {
293 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
294 return;
296 if (info_buffer_.get())
297 ContinueReadInfo();
298 else
299 ContinueReadData();
302 // AppCacheResponseWriter ----------------------------------------------
304 AppCacheResponseWriter::AppCacheResponseWriter(
305 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
306 : AppCacheResponseIO(response_id, group_id, disk_cache),
307 info_size_(0),
308 write_position_(0),
309 write_amount_(0),
310 creation_phase_(INITIAL_ATTEMPT),
311 weak_factory_(this) {
314 AppCacheResponseWriter::~AppCacheResponseWriter() {
317 void AppCacheResponseWriter::WriteInfo(
318 HttpResponseInfoIOBuffer* info_buf,
319 const net::CompletionCallback& callback) {
320 DCHECK(!callback.is_null());
321 DCHECK(!IsWritePending());
322 DCHECK(info_buf);
323 DCHECK(info_buf->http_info.get());
324 DCHECK(!buffer_.get());
325 DCHECK(!info_buffer_.get());
326 DCHECK(info_buf->http_info->headers.get());
328 info_buffer_ = info_buf;
329 callback_ = callback; // cleared on completion
330 CreateEntryIfNeededAndContinue();
333 void AppCacheResponseWriter::ContinueWriteInfo() {
334 if (!entry_) {
335 ScheduleIOCompletionCallback(net::ERR_FAILED);
336 return;
339 const bool kSkipTransientHeaders = true;
340 const bool kTruncated = false;
341 base::Pickle* pickle = new base::Pickle;
342 info_buffer_->http_info->Persist(pickle, kSkipTransientHeaders, kTruncated);
343 write_amount_ = static_cast<int>(pickle->size());
344 buffer_ = new WrappedPickleIOBuffer(pickle); // takes ownership of pickle
345 WriteRaw(kResponseInfoIndex, 0, buffer_.get(), write_amount_);
348 void AppCacheResponseWriter::WriteData(
349 net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) {
350 DCHECK(!callback.is_null());
351 DCHECK(!IsWritePending());
352 DCHECK(buf);
353 DCHECK(buf_len >= 0);
354 DCHECK(!buffer_.get());
355 DCHECK(!info_buffer_.get());
357 buffer_ = buf;
358 write_amount_ = buf_len;
359 callback_ = callback; // cleared on completion
360 CreateEntryIfNeededAndContinue();
363 void AppCacheResponseWriter::ContinueWriteData() {
364 if (!entry_) {
365 ScheduleIOCompletionCallback(net::ERR_FAILED);
366 return;
368 WriteRaw(
369 kResponseContentIndex, write_position_, buffer_.get(), write_amount_);
372 void AppCacheResponseWriter::OnIOComplete(int result) {
373 if (result >= 0) {
374 DCHECK(write_amount_ == result);
375 if (!info_buffer_.get())
376 write_position_ += result;
377 else
378 info_size_ = result;
380 InvokeUserCompletionCallback(result);
383 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
384 int rv;
385 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
386 if (entry_) {
387 creation_phase_ = NO_ATTEMPT;
388 rv = net::OK;
389 } else if (!disk_cache_) {
390 creation_phase_ = NO_ATTEMPT;
391 rv = net::ERR_FAILED;
392 } else {
393 creation_phase_ = INITIAL_ATTEMPT;
394 entry_ptr = new AppCacheDiskCacheInterface::Entry*;
395 create_callback_ =
396 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
397 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
398 rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
400 if (rv != net::ERR_IO_PENDING)
401 OnCreateEntryComplete(entry_ptr, rv);
404 void AppCacheResponseWriter::OnCreateEntryComplete(
405 AppCacheDiskCacheInterface::Entry** entry, int rv) {
406 DCHECK(info_buffer_.get() || buffer_.get());
408 if (creation_phase_ == INITIAL_ATTEMPT) {
409 if (rv != net::OK) {
410 // We may try to overwrite existing entries.
411 creation_phase_ = DOOM_EXISTING;
412 rv = disk_cache_->DoomEntry(response_id_, create_callback_);
413 if (rv != net::ERR_IO_PENDING)
414 OnCreateEntryComplete(NULL, rv);
415 return;
417 } else if (creation_phase_ == DOOM_EXISTING) {
418 creation_phase_ = SECOND_ATTEMPT;
419 AppCacheDiskCacheInterface::Entry** entry_ptr =
420 new AppCacheDiskCacheInterface::Entry*;
421 create_callback_ =
422 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
423 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
424 rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
425 if (rv != net::ERR_IO_PENDING)
426 OnCreateEntryComplete(entry_ptr, rv);
427 return;
430 if (!create_callback_.is_null()) {
431 if (rv == net::OK)
432 entry_ = *entry;
434 create_callback_.Reset();
437 if (info_buffer_.get())
438 ContinueWriteInfo();
439 else
440 ContinueWriteData();
443 // AppCacheResponseMetadataWriter ----------------------------------------------
445 AppCacheResponseMetadataWriter::AppCacheResponseMetadataWriter(
446 int64 response_id,
447 int64 group_id,
448 AppCacheDiskCacheInterface* disk_cache)
449 : AppCacheResponseIO(response_id, group_id, disk_cache),
450 write_amount_(0),
451 weak_factory_(this) {
454 AppCacheResponseMetadataWriter::~AppCacheResponseMetadataWriter() {
457 void AppCacheResponseMetadataWriter::WriteMetadata(
458 net::IOBuffer* buf,
459 int buf_len,
460 const net::CompletionCallback& callback) {
461 DCHECK(!callback.is_null());
462 DCHECK(!IsIOPending());
463 DCHECK(buf);
464 DCHECK(buf_len >= 0);
465 DCHECK(!buffer_.get());
467 buffer_ = buf;
468 write_amount_ = buf_len;
469 callback_ = callback; // cleared on completion
470 OpenEntryIfNeeded();
473 void AppCacheResponseMetadataWriter::OnOpenEntryComplete() {
474 if (!entry_) {
475 ScheduleIOCompletionCallback(net::ERR_FAILED);
476 return;
478 WriteRaw(kResponseMetadataIndex, 0, buffer_.get(), write_amount_);
481 void AppCacheResponseMetadataWriter::OnIOComplete(int result) {
482 DCHECK(result < 0 || write_amount_ == result);
483 InvokeUserCompletionCallback(result);
486 } // namespace content