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.
4
5#include "content/browser/appcache/appcache_response.h"
6
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/strings/string_util.h"
14#include "content/browser/appcache/appcache_storage.h"
15#include "net/base/completion_callback.h"
16#include "net/base/io_buffer.h"
17#include "net/base/net_errors.h"
18
19namespace content {
20
21namespace {
22
23// Disk cache entry data indices.
24enum {
25  kResponseInfoIndex,
26  kResponseContentIndex
27};
28
29// An IOBuffer that wraps a pickle's data. Ownership of the
30// pickle is transfered to the WrappedPickleIOBuffer object.
31class 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());
37  }
38
39 private:
40  virtual ~WrappedPickleIOBuffer() {}
41
42  scoped_ptr<const Pickle> pickle_;
43};
44
45}  // anon namespace
46
47
48// AppCacheResponseInfo ----------------------------------------------
49
50AppCacheResponseInfo::AppCacheResponseInfo(
51    AppCacheStorage* storage, 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      storage_(storage) {
57  DCHECK(http_info);
58  DCHECK(response_id != kAppCacheNoResponseId);
59  storage_->working_set()->AddResponseInfo(this);
60}
61
62AppCacheResponseInfo::~AppCacheResponseInfo() {
63  storage_->working_set()->RemoveResponseInfo(this);
64}
65
66// HttpResponseInfoIOBuffer ------------------------------------------
67
68HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer()
69    : response_data_size(kUnkownResponseDataSize) {}
70
71HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo* info)
72    : http_info(info), response_data_size(kUnkownResponseDataSize) {}
73
74HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {}
75
76// AppCacheResponseIO ----------------------------------------------
77
78AppCacheResponseIO::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      weak_factory_(this) {
86}
87
88AppCacheResponseIO::~AppCacheResponseIO() {
89  if (entry_)
90    entry_->Close();
91}
92
93void AppCacheResponseIO::ScheduleIOCompletionCallback(int result) {
94  base::MessageLoop::current()->PostTask(
95      FROM_HERE, base::Bind(&AppCacheResponseIO::OnIOComplete,
96                            weak_factory_.GetWeakPtr(), result));
97}
98
99void 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);
107}
108
109void 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);
118}
119
120void 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);
129}
130
131void AppCacheResponseIO::OnRawIOComplete(int result) {
132  DCHECK_NE(net::ERR_IO_PENDING, result);
133  OnIOComplete(result);
134}
135
136
137// AppCacheResponseReader ----------------------------------------------
138
139AppCacheResponseReader::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      weak_factory_(this) {
146}
147
148AppCacheResponseReader::~AppCacheResponseReader() {
149}
150
151void 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());
159
160  info_buffer_ = info_buf;
161  callback_ = callback;  // cleared on completion
162  OpenEntryIfNeededAndContinue();
163}
164
165void AppCacheResponseReader::ContinueReadInfo() {
166  if (!entry_)  {
167    ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
168    return;
169  }
170
171  int size = entry_->GetSize(kResponseInfoIndex);
172  if (size <= 0) {
173    ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
174    return;
175  }
176
177  buffer_ = new net::IOBuffer(size);
178  ReadRaw(kResponseInfoIndex, 0, buffer_.get(), size);
179}
180
181void 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());
189
190  buffer_ = buf;
191  buffer_len_ = buf_len;
192  callback_ = callback;  // cleared on completion
193  OpenEntryIfNeededAndContinue();
194}
195
196void AppCacheResponseReader::ContinueReadData() {
197  if (!entry_)  {
198    ScheduleIOCompletionCallback(net::ERR_CACHE_MISS);
199    return;
200  }
201
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_;
206  }
207  ReadRaw(kResponseContentIndex,
208          range_offset_ + read_position_,
209          buffer_.get(),
210          buffer_len_);
211}
212
213void AppCacheResponseReader::SetReadRange(int offset, int length) {
214  DCHECK(!IsReadPending() && !read_position_);
215  range_offset_ = offset;
216  range_length_ = length;
217}
218
219void AppCacheResponseReader::OnIOComplete(int result) {
220  if (result >= 0) {
221    if (info_buffer_.get()) {
222      // Deserialize the http info structure, ensuring we got headers.
223      Pickle pickle(buffer_->data(), result);
224      scoped_ptr<net::HttpResponseInfo> info(new net::HttpResponseInfo);
225      bool response_truncated = false;
226      if (!info->InitFromPickle(pickle, &response_truncated) ||
227          !info->headers.get()) {
228        InvokeUserCompletionCallback(net::ERR_FAILED);
229        return;
230      }
231      DCHECK(!response_truncated);
232      info_buffer_->http_info.reset(info.release());
233
234      // Also return the size of the response body
235      DCHECK(entry_);
236      info_buffer_->response_data_size =
237          entry_->GetSize(kResponseContentIndex);
238    } else {
239      read_position_ += result;
240    }
241  }
242  InvokeUserCompletionCallback(result);
243}
244
245void AppCacheResponseReader::OpenEntryIfNeededAndContinue() {
246  int rv;
247  AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
248  if (entry_) {
249    rv = net::OK;
250  } else if (!disk_cache_) {
251    rv = net::ERR_FAILED;
252  } else {
253    entry_ptr = new AppCacheDiskCacheInterface::Entry*;
254    open_callback_ =
255        base::Bind(&AppCacheResponseReader::OnOpenEntryComplete,
256                   weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
257    rv = disk_cache_->OpenEntry(response_id_, entry_ptr, open_callback_);
258  }
259
260  if (rv != net::ERR_IO_PENDING)
261    OnOpenEntryComplete(entry_ptr, rv);
262}
263
264void AppCacheResponseReader::OnOpenEntryComplete(
265    AppCacheDiskCacheInterface::Entry** entry, int rv) {
266  DCHECK(info_buffer_.get() || buffer_.get());
267
268  if (!open_callback_.is_null()) {
269    if (rv == net::OK) {
270      DCHECK(entry);
271      entry_ = *entry;
272    }
273    open_callback_.Reset();
274  }
275
276  if (info_buffer_.get())
277    ContinueReadInfo();
278  else
279    ContinueReadData();
280}
281
282// AppCacheResponseWriter ----------------------------------------------
283
284AppCacheResponseWriter::AppCacheResponseWriter(
285    int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache)
286    : AppCacheResponseIO(response_id, group_id, disk_cache),
287      info_size_(0),
288      write_position_(0),
289      write_amount_(0),
290      creation_phase_(INITIAL_ATTEMPT),
291      weak_factory_(this) {
292}
293
294AppCacheResponseWriter::~AppCacheResponseWriter() {
295}
296
297void AppCacheResponseWriter::WriteInfo(
298    HttpResponseInfoIOBuffer* info_buf,
299    const net::CompletionCallback& callback) {
300  DCHECK(!callback.is_null());
301  DCHECK(!IsWritePending());
302  DCHECK(info_buf);
303  DCHECK(info_buf->http_info.get());
304  DCHECK(!buffer_.get());
305  DCHECK(!info_buffer_.get());
306  DCHECK(info_buf->http_info->headers.get());
307
308  info_buffer_ = info_buf;
309  callback_ = callback;  // cleared on completion
310  CreateEntryIfNeededAndContinue();
311}
312
313void AppCacheResponseWriter::ContinueWriteInfo() {
314  if (!entry_) {
315    ScheduleIOCompletionCallback(net::ERR_FAILED);
316    return;
317  }
318
319  const bool kSkipTransientHeaders = true;
320  const bool kTruncated = false;
321  Pickle* pickle = new Pickle;
322  info_buffer_->http_info->Persist(pickle, kSkipTransientHeaders, kTruncated);
323  write_amount_ = static_cast<int>(pickle->size());
324  buffer_ = new WrappedPickleIOBuffer(pickle);  // takes ownership of pickle
325  WriteRaw(kResponseInfoIndex, 0, buffer_.get(), write_amount_);
326}
327
328void AppCacheResponseWriter::WriteData(
329    net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) {
330  DCHECK(!callback.is_null());
331  DCHECK(!IsWritePending());
332  DCHECK(buf);
333  DCHECK(buf_len >= 0);
334  DCHECK(!buffer_.get());
335  DCHECK(!info_buffer_.get());
336
337  buffer_ = buf;
338  write_amount_ = buf_len;
339  callback_ = callback;  // cleared on completion
340  CreateEntryIfNeededAndContinue();
341}
342
343void AppCacheResponseWriter::ContinueWriteData() {
344  if (!entry_) {
345    ScheduleIOCompletionCallback(net::ERR_FAILED);
346    return;
347  }
348  WriteRaw(
349      kResponseContentIndex, write_position_, buffer_.get(), write_amount_);
350}
351
352void AppCacheResponseWriter::OnIOComplete(int result) {
353  if (result >= 0) {
354    DCHECK(write_amount_ == result);
355    if (!info_buffer_.get())
356      write_position_ += result;
357    else
358      info_size_ = result;
359  }
360  InvokeUserCompletionCallback(result);
361}
362
363void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
364  int rv;
365  AppCacheDiskCacheInterface::Entry** entry_ptr = NULL;
366  if (entry_) {
367    creation_phase_ = NO_ATTEMPT;
368    rv = net::OK;
369  } else if (!disk_cache_) {
370    creation_phase_ = NO_ATTEMPT;
371    rv = net::ERR_FAILED;
372  } else {
373    creation_phase_ = INITIAL_ATTEMPT;
374    entry_ptr = new AppCacheDiskCacheInterface::Entry*;
375    create_callback_ =
376        base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
377                   weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
378    rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
379  }
380  if (rv != net::ERR_IO_PENDING)
381    OnCreateEntryComplete(entry_ptr, rv);
382}
383
384void AppCacheResponseWriter::OnCreateEntryComplete(
385    AppCacheDiskCacheInterface::Entry** entry, int rv) {
386  DCHECK(info_buffer_.get() || buffer_.get());
387
388  if (creation_phase_ == INITIAL_ATTEMPT) {
389    if (rv != net::OK) {
390      // We may try to overwrite existing entries.
391      creation_phase_ = DOOM_EXISTING;
392      rv = disk_cache_->DoomEntry(response_id_, create_callback_);
393      if (rv != net::ERR_IO_PENDING)
394        OnCreateEntryComplete(NULL, rv);
395      return;
396    }
397  } else if (creation_phase_ == DOOM_EXISTING) {
398    creation_phase_ = SECOND_ATTEMPT;
399    AppCacheDiskCacheInterface::Entry** entry_ptr =
400        new AppCacheDiskCacheInterface::Entry*;
401    create_callback_ =
402        base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete,
403                   weak_factory_.GetWeakPtr(), base::Owned(entry_ptr));
404    rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_);
405    if (rv != net::ERR_IO_PENDING)
406      OnCreateEntryComplete(entry_ptr, rv);
407    return;
408  }
409
410  if (!create_callback_.is_null()) {
411    if (rv == net::OK)
412      entry_ = *entry;
413
414    create_callback_.Reset();
415  }
416
417  if (info_buffer_.get())
418    ContinueWriteInfo();
419  else
420    ContinueWriteData();
421}
422
423}  // namespace content
424