Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / webkit / appcache / appcache_response.cc
blob30ae5cdef6664f4bea3eccad4ca95d10b001dd7f
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 "webkit/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.h"
12 #include "base/pickle.h"
13 #include "base/string_util.h"
14 #include "net/base/completion_callback.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/io_buffer.h"
17 #include "webkit/appcache/appcache_service.h"
19 namespace appcache {
21 namespace {
23 // Disk cache entry data indices.
24 enum {
25 kResponseInfoIndex,
26 kResponseContentIndex
29 // An IOBuffer that wraps a pickle's data. Ownership of the
30 // pickle is transfered to the WrappedPickleIOBuffer object.
31 class WrappedPickleIOBuffer : public net::WrappedIOBuffer {
32 public:
33 explicit WrappedPickleIOBuffer(const Pickle* pickle) :
34 net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle->data())),
35 pickle_(pickle) {
36 DCHECK(pickle->data());
39 private:
40 virtual ~WrappedPickleIOBuffer() {}
42 scoped_ptr<const Pickle> pickle_;
45 } // anon namespace
48 // AppCacheResponseInfo ----------------------------------------------
50 AppCacheResponseInfo::AppCacheResponseInfo(
51 AppCacheService* service, const GURL& manifest_url,
52 int64 response_id, net::HttpResponseInfo* http_info,
53 int64 response_data_size)
54 : manifest_url_(manifest_url), response_id_(response_id),
55 http_response_info_(http_info), response_data_size_(response_data_size),
56 service_(service) {
57 DCHECK(http_info);
58 DCHECK(response_id != kNoResponseId);
59 service_->storage()->working_set()->AddResponseInfo(this);
62 AppCacheResponseInfo::~AppCacheResponseInfo() {
63 service_->storage()->working_set()->RemoveResponseInfo(this);
66 // HttpResponseInfoIOBuffer ------------------------------------------
68 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer()
69 : response_data_size(kUnkownResponseDataSize) {}
71 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo* info)
72 : http_info(info), response_data_size(kUnkownResponseDataSize) {}
74 HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {}
76 // AppCacheResponseIO ----------------------------------------------
78 AppCacheResponseIO::AppCacheResponseIO(
79 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
80 : response_id_(response_id),
81 group_id_(group_id),
82 disk_cache_(disk_cache),
83 entry_(NULL),
84 buffer_len_(0),
85 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
88 AppCacheResponseIO::~AppCacheResponseIO() {
89 if (entry_)
90 entry_->Close();
93 void AppCacheResponseIO::ScheduleIOCompletionCallback(int result) {
94 MessageLoop::current()->PostTask(
95 FROM_HERE, base::Bind(&AppCacheResponseIO::OnIOComplete,
96 weak_factory_.GetWeakPtr(), result));
99 void AppCacheResponseIO::InvokeUserCompletionCallback(int result) {
100 // Clear the user callback and buffers prior to invoking the callback
101 // so the caller can schedule additional operations in the callback.
102 buffer_ = NULL;
103 info_buffer_ = NULL;
104 net::CompletionCallback cb = callback_;
105 callback_.Reset();
106 cb.Run(result);
109 void AppCacheResponseIO::ReadRaw(int index, int offset,
110 net::IOBuffer* buf, int buf_len) {
111 DCHECK(entry_);
112 int rv = entry_->Read(
113 index, offset, buf, buf_len,
114 base::Bind(&AppCacheResponseIO::OnRawIOComplete,
115 weak_factory_.GetWeakPtr()));
116 if (rv != net::ERR_IO_PENDING)
117 ScheduleIOCompletionCallback(rv);
120 void AppCacheResponseIO::WriteRaw(int index, int offset,
121 net::IOBuffer* buf, int buf_len) {
122 DCHECK(entry_);
123 int rv = entry_->Write(
124 index, offset, buf, buf_len,
125 base::Bind(&AppCacheResponseIO::OnRawIOComplete,
126 weak_factory_.GetWeakPtr()));
127 if (rv != net::ERR_IO_PENDING)
128 ScheduleIOCompletionCallback(rv);
131 void AppCacheResponseIO::OnRawIOComplete(int result) {
132 DCHECK_NE(net::ERR_IO_PENDING, result);
133 OnIOComplete(result);
137 // AppCacheResponseReader ----------------------------------------------
139 AppCacheResponseReader::AppCacheResponseReader(
140 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
141 : AppCacheResponseIO(response_id, group_id, disk_cache),
142 range_offset_(0),
143 range_length_(kint32max),
144 read_position_(0),
145 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
148 AppCacheResponseReader::~AppCacheResponseReader() {
151 void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer* info_buf,
152 const net::CompletionCallback& callback) {
153 DCHECK(!callback.is_null());
154 DCHECK(!IsReadPending());
155 DCHECK(info_buf);
156 DCHECK(!info_buf->http_info.get());
157 DCHECK(!buffer_.get());
158 DCHECK(!info_buffer_.get());
160 info_buffer_ = info_buf;
161 callback_ = callback; // cleared on completion
162 OpenEntryIfNeededAndContinue();
165 void AppCacheResponseReader::ContinueReadInfo() {
166 if (!entry_) {
167 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
168 return;
171 int size = entry_->GetSize(kResponseInfoIndex);
172 if (size <= 0) {
173 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
174 return;
177 buffer_ = new net::IOBuffer(size);
178 ReadRaw(kResponseInfoIndex, 0, buffer_.get(), size);
181 void AppCacheResponseReader::ReadData(net::IOBuffer* buf, int buf_len,
182 const net::CompletionCallback& callback) {
183 DCHECK(!callback.is_null());
184 DCHECK(!IsReadPending());
185 DCHECK(buf);
186 DCHECK(buf_len >= 0);
187 DCHECK(!buffer_.get());
188 DCHECK(!info_buffer_.get());
190 buffer_ = buf;
191 buffer_len_ = buf_len;
192 callback_ = callback; // cleared on completion
193 OpenEntryIfNeededAndContinue();
196 void AppCacheResponseReader::ContinueReadData() {
197 if (!entry_) {
198 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
199 return;
202 if (read_position_ + buffer_len_ > range_length_) {
203 // TODO(michaeln): What about integer overflows?
204 DCHECK(range_length_ >= read_position_);
205 buffer_len_ = range_length_ - read_position_;
207 ReadRaw(kResponseContentIndex, range_offset_ + read_position_,
208 buffer_, buffer_len_);
211 void AppCacheResponseReader::SetReadRange(int offset, int length) {
212 DCHECK(!IsReadPending() && !read_position_);
213 range_offset_ = offset;
214 range_length_ = length;
217 void AppCacheResponseReader::OnIOComplete(int result) {
218 if (result >= 0) {
219 if (info_buffer_.get()) {
220 // Deserialize the http info structure, ensuring we got headers.
221 Pickle pickle(buffer_->data(), result);
222 scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
223 bool response_truncated = false;
224 if (!info->InitFromPickle(pickle, &response_truncated) ||
225 !info->headers) {
226 InvokeUserCompletionCallback(net::ERR_FAILED);
227 return;
229 DCHECK(!response_truncated);
230 info_buffer_->http_info.reset(info.release());
232 // Also return the size of the response body
233 DCHECK(entry_);
234 info_buffer_->response_data_size =
235 entry_->GetSize(kResponseContentIndex);
236 } else {
237 read_position_ += result;
240 InvokeUserCompletionCallback(result);
243 void AppCacheResponseReader::OpenEntryIfNeededAndContinue() {
244 int rv;
245 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
246 if (entry_) {
247 rv = net::OK;
248 } else if (!disk_cache_) {
249 rv = net::ERR_FAILED;
250 } else {
251 entry_ptr = new AppCacheDiskCacheInterface::Entry*;
252 open_callback_ =
253 base::Bind(&AppCacheResponseReader::OnOpenEntryComplete,
254 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
255 rv = disk_cache_->OpenEntry(response_id_, entry_ptr, open_callback_);
258 if (rv != net::ERR_IO_PENDING)
259 OnOpenEntryComplete(entry_ptr, rv);
262 void AppCacheResponseReader::OnOpenEntryComplete(
263 AppCacheDiskCacheInterface::Entry** entry, int rv) {
264 DCHECK(info_buffer_.get() || buffer_.get());
266 if (!open_callback_.is_null()) {
267 if (rv == net::OK) {
268 DCHECK(entry);
269 entry_ = *entry;
271 open_callback_.Reset();
274 if (info_buffer_)
275 ContinueReadInfo();
276 else
277 ContinueReadData();
280 // AppCacheResponseWriter ----------------------------------------------
282 AppCacheResponseWriter::AppCacheResponseWriter(
283 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
284 : AppCacheResponseIO(response_id, group_id, disk_cache),
285 info_size_(0),
286 write_position_(0),
287 write_amount_(0),
288 creation_phase_(INITIAL_ATTEMPT),
289 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
292 AppCacheResponseWriter::~AppCacheResponseWriter() {
295 void AppCacheResponseWriter::WriteInfo(
296 HttpResponseInfoIOBuffer* info_buf,
297 const net::CompletionCallback& callback) {
298 DCHECK(!callback.is_null());
299 DCHECK(!IsWritePending());
300 DCHECK(info_buf);
301 DCHECK(info_buf->http_info.get());
302 DCHECK(!buffer_.get());
303 DCHECK(!info_buffer_.get());
304 DCHECK(info_buf->http_info->headers);
306 info_buffer_ = info_buf;
307 callback_ = callback; // cleared on completion
308 CreateEntryIfNeededAndContinue();
311 void AppCacheResponseWriter::ContinueWriteInfo() {
312 if (!entry_) {
313 ScheduleIOCompletionCallback(net::ERR_FAILED);
314 return;
317 const bool kSkipTransientHeaders = true;
318 const bool kTruncated = false;
319 Pickle* pickle = new Pickle;
320 info_buffer_->http_info->Persist(pickle, kSkipTransientHeaders, kTruncated);
321 write_amount_ = static_cast<int>(pickle->size());
322 buffer_ = new WrappedPickleIOBuffer(pickle); // takes ownership of pickle
323 WriteRaw(kResponseInfoIndex, 0, buffer_, write_amount_);
326 void AppCacheResponseWriter::WriteData(
327 net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) {
328 DCHECK(!callback.is_null());
329 DCHECK(!IsWritePending());
330 DCHECK(buf);
331 DCHECK(buf_len >= 0);
332 DCHECK(!buffer_.get());
333 DCHECK(!info_buffer_.get());
335 buffer_ = buf;
336 write_amount_ = buf_len;
337 callback_ = callback; // cleared on completion
338 CreateEntryIfNeededAndContinue();
341 void AppCacheResponseWriter::ContinueWriteData() {
342 if (!entry_) {
343 ScheduleIOCompletionCallback(net::ERR_FAILED);
344 return;
346 WriteRaw(kResponseContentIndex, write_position_, buffer_, write_amount_);
349 void AppCacheResponseWriter::OnIOComplete(int result) {
350 if (result >= 0) {
351 DCHECK(write_amount_ == result);
352 if (!info_buffer_.get())
353 write_position_ += result;
354 else
355 info_size_ = result;
357 InvokeUserCompletionCallback(result);
360 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
361 int rv;
362 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
363 if (entry_) {
364 creation_phase_ = NO_ATTEMPT;
365 rv = net::OK;
366 } else if (!disk_cache_) {
367 creation_phase_ = NO_ATTEMPT;
368 rv = net::ERR_FAILED;
369 } else {
370 creation_phase_ = INITIAL_ATTEMPT;
371 entry_ptr = new AppCacheDiskCacheInterface::Entry*;
372 create_callback_ =
373 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
374 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
375 rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
377 if (rv != net::ERR_IO_PENDING)
378 OnCreateEntryComplete(entry_ptr, rv);
381 void AppCacheResponseWriter::OnCreateEntryComplete(
382 AppCacheDiskCacheInterface::Entry** entry, int rv) {
383 DCHECK(info_buffer_.get() || buffer_.get());
385 if (creation_phase_ == INITIAL_ATTEMPT) {
386 if (rv != net::OK) {
387 // We may try to overwrite existing entries.
388 creation_phase_ = DOOM_EXISTING;
389 rv = disk_cache_->DoomEntry(response_id_, create_callback_);
390 if (rv != net::ERR_IO_PENDING)
391 OnCreateEntryComplete(NULL, rv);
392 return;
394 } else if (creation_phase_ == DOOM_EXISTING) {
395 creation_phase_ = SECOND_ATTEMPT;
396 AppCacheDiskCacheInterface::Entry** entry_ptr =
397 new AppCacheDiskCacheInterface::Entry*;
398 create_callback_ =
399 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
400 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
401 rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
402 if (rv != net::ERR_IO_PENDING)
403 OnCreateEntryComplete(entry_ptr, rv);
404 return;
407 if (!create_callback_.is_null()) {
408 if (rv == net::OK)
409 entry_ = *entry;
411 create_callback_.Reset();
414 if (info_buffer_)
415 ContinueWriteInfo();
416 else
417 ContinueWriteData();
420 } // namespace appcache