• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 namespace content {
20 
21 namespace {
22 
23 // Disk cache entry data indices.
24 enum {
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.
31 class WrappedPickleIOBuffer : public net::WrappedIOBuffer {
32  public:
WrappedPickleIOBuffer(const Pickle * pickle)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:
~WrappedPickleIOBuffer()40   virtual ~WrappedPickleIOBuffer() {}
41 
42   scoped_ptr<const Pickle> pickle_;
43 };
44 
45 }  // anon namespace
46 
47 
48 // AppCacheResponseInfo ----------------------------------------------
49 
AppCacheResponseInfo(AppCacheStorage * storage,const GURL & manifest_url,int64 response_id,net::HttpResponseInfo * http_info,int64 response_data_size)50 AppCacheResponseInfo::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 
~AppCacheResponseInfo()62 AppCacheResponseInfo::~AppCacheResponseInfo() {
63   storage_->working_set()->RemoveResponseInfo(this);
64 }
65 
66 // HttpResponseInfoIOBuffer ------------------------------------------
67 
HttpResponseInfoIOBuffer()68 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer()
69     : response_data_size(kUnkownResponseDataSize) {}
70 
HttpResponseInfoIOBuffer(net::HttpResponseInfo * info)71 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo* info)
72     : http_info(info), response_data_size(kUnkownResponseDataSize) {}
73 
~HttpResponseInfoIOBuffer()74 HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {}
75 
76 // AppCacheResponseIO ----------------------------------------------
77 
AppCacheResponseIO(int64 response_id,int64 group_id,AppCacheDiskCacheInterface * disk_cache)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       weak_factory_(this) {
86 }
87 
~AppCacheResponseIO()88 AppCacheResponseIO::~AppCacheResponseIO() {
89   if (entry_)
90     entry_->Close();
91 }
92 
ScheduleIOCompletionCallback(int result)93 void AppCacheResponseIO::ScheduleIOCompletionCallback(int result) {
94   base::MessageLoop::current()->PostTask(
95       FROM_HERE, base::Bind(&AppCacheResponseIO::OnIOComplete,
96                             weak_factory_.GetWeakPtr(), result));
97 }
98 
InvokeUserCompletionCallback(int 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);
107 }
108 
ReadRaw(int index,int offset,net::IOBuffer * buf,int buf_len)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);
118 }
119 
WriteRaw(int index,int offset,net::IOBuffer * buf,int buf_len)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);
129 }
130 
OnRawIOComplete(int result)131 void AppCacheResponseIO::OnRawIOComplete(int result) {
132   DCHECK_NE(net::ERR_IO_PENDING, result);
133   OnIOComplete(result);
134 }
135 
136 
137 // AppCacheResponseReader ----------------------------------------------
138 
AppCacheResponseReader(int64 response_id,int64 group_id,AppCacheDiskCacheInterface * disk_cache)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       weak_factory_(this) {
146 }
147 
~AppCacheResponseReader()148 AppCacheResponseReader::~AppCacheResponseReader() {
149 }
150 
ReadInfo(HttpResponseInfoIOBuffer * info_buf,const net::CompletionCallback & callback)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());
159 
160   info_buffer_ = info_buf;
161   callback_ = callback;  // cleared on completion
162   OpenEntryIfNeededAndContinue();
163 }
164 
ContinueReadInfo()165 void 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 
ReadData(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)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());
189 
190   buffer_ = buf;
191   buffer_len_ = buf_len;
192   callback_ = callback;  // cleared on completion
193   OpenEntryIfNeededAndContinue();
194 }
195 
ContinueReadData()196 void 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 
SetReadRange(int offset,int length)213 void AppCacheResponseReader::SetReadRange(int offset, int length) {
214   DCHECK(!IsReadPending() && !read_position_);
215   range_offset_ = offset;
216   range_length_ = length;
217 }
218 
OnIOComplete(int result)219 void 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 
OpenEntryIfNeededAndContinue()245 void 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 
OnOpenEntryComplete(AppCacheDiskCacheInterface::Entry ** entry,int rv)264 void 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 
AppCacheResponseWriter(int64 response_id,int64 group_id,AppCacheDiskCacheInterface * disk_cache)284 AppCacheResponseWriter::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 
~AppCacheResponseWriter()294 AppCacheResponseWriter::~AppCacheResponseWriter() {
295 }
296 
WriteInfo(HttpResponseInfoIOBuffer * info_buf,const net::CompletionCallback & callback)297 void 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 
ContinueWriteInfo()313 void 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 
WriteData(net::IOBuffer * buf,int buf_len,const net::CompletionCallback & callback)328 void 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 
ContinueWriteData()343 void 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 
OnIOComplete(int result)352 void 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 
CreateEntryIfNeededAndContinue()363 void 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 
OnCreateEntryComplete(AppCacheDiskCacheInterface::Entry ** entry,int rv)384 void 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