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