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"
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"
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
{
34 explicit WrappedPickleIOBuffer(const base::Pickle
* pickle
)
35 : net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle
->data())),
37 DCHECK(pickle
->data());
41 ~WrappedPickleIOBuffer() override
{}
43 scoped_ptr
<const base::Pickle
> pickle_
;
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
),
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
),
83 disk_cache_(disk_cache
),
89 AppCacheResponseIO::~AppCacheResponseIO() {
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.
105 net::CompletionCallback cb
= callback_
;
110 void AppCacheResponseIO::ReadRaw(int index
, int offset
,
111 net::IOBuffer
* buf
, int buf_len
) {
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
) {
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() {
144 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
147 } else if (!disk_cache_
) {
148 rv
= net::ERR_FAILED
;
150 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
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()) {
170 open_callback_
.Reset();
172 OnOpenEntryComplete();
176 // AppCacheResponseReader ----------------------------------------------
178 AppCacheResponseReader::AppCacheResponseReader(
181 AppCacheDiskCacheInterface
* disk_cache
)
182 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
184 range_length_(kint32max
),
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());
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
207 void AppCacheResponseReader::ContinueReadInfo() {
208 int size
= entry_
->GetSize(kResponseInfoIndex
);
210 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
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());
223 DCHECK(buf_len
>= 0);
224 DCHECK(!buffer_
.get());
225 DCHECK(!info_buffer_
.get());
228 buffer_len_
= buf_len
;
229 callback_
= callback
; // cleared on completion
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_
,
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
) {
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
);
267 DCHECK(!response_truncated
);
268 info_buffer_
->http_info
.reset(info
.release());
270 // Also return the size of the response body
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
);
285 read_position_
+= result
;
288 InvokeUserCompletionCallback(result
);
291 void AppCacheResponseReader::OnOpenEntryComplete() {
293 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
296 if (info_buffer_
.get())
302 // AppCacheResponseWriter ----------------------------------------------
304 AppCacheResponseWriter::AppCacheResponseWriter(
305 int64 response_id
, int64 group_id
, AppCacheDiskCacheInterface
* disk_cache
)
306 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
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());
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() {
335 ScheduleIOCompletionCallback(net::ERR_FAILED
);
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());
353 DCHECK(buf_len
>= 0);
354 DCHECK(!buffer_
.get());
355 DCHECK(!info_buffer_
.get());
358 write_amount_
= buf_len
;
359 callback_
= callback
; // cleared on completion
360 CreateEntryIfNeededAndContinue();
363 void AppCacheResponseWriter::ContinueWriteData() {
365 ScheduleIOCompletionCallback(net::ERR_FAILED
);
369 kResponseContentIndex
, write_position_
, buffer_
.get(), write_amount_
);
372 void AppCacheResponseWriter::OnIOComplete(int result
) {
374 DCHECK(write_amount_
== result
);
375 if (!info_buffer_
.get())
376 write_position_
+= result
;
380 InvokeUserCompletionCallback(result
);
383 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
385 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
387 creation_phase_
= NO_ATTEMPT
;
389 } else if (!disk_cache_
) {
390 creation_phase_
= NO_ATTEMPT
;
391 rv
= net::ERR_FAILED
;
393 creation_phase_
= INITIAL_ATTEMPT
;
394 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
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
) {
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
);
417 } else if (creation_phase_
== DOOM_EXISTING
) {
418 creation_phase_
= SECOND_ATTEMPT
;
419 AppCacheDiskCacheInterface::Entry
** entry_ptr
=
420 new AppCacheDiskCacheInterface::Entry
*;
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
);
430 if (!create_callback_
.is_null()) {
434 create_callback_
.Reset();
437 if (info_buffer_
.get())
443 // AppCacheResponseMetadataWriter ----------------------------------------------
445 AppCacheResponseMetadataWriter::AppCacheResponseMetadataWriter(
448 AppCacheDiskCacheInterface
* disk_cache
)
449 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
451 weak_factory_(this) {
454 AppCacheResponseMetadataWriter::~AppCacheResponseMetadataWriter() {
457 void AppCacheResponseMetadataWriter::WriteMetadata(
460 const net::CompletionCallback
& callback
) {
461 DCHECK(!callback
.is_null());
462 DCHECK(!IsIOPending());
464 DCHECK(buf_len
>= 0);
465 DCHECK(!buffer_
.get());
468 write_amount_
= buf_len
;
469 callback_
= callback
; // cleared on completion
473 void AppCacheResponseMetadataWriter::OnOpenEntryComplete() {
475 ScheduleIOCompletionCallback(net::ERR_FAILED
);
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