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/logging.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/pickle.h"
13 #include "base/profiler/scoped_tracker.h"
14 #include "base/strings/string_util.h"
15 #include "content/browser/appcache/appcache_storage.h"
16 #include "net/base/completion_callback.h"
17 #include "net/base/io_buffer.h"
18 #include "net/base/net_errors.h"
24 // Disk cache entry data indices.
25 enum { kResponseInfoIndex
, kResponseContentIndex
, kResponseMetadataIndex
};
27 // An IOBuffer that wraps a pickle's data. Ownership of the
28 // pickle is transfered to the WrappedPickleIOBuffer object.
29 class WrappedPickleIOBuffer
: public net::WrappedIOBuffer
{
31 explicit WrappedPickleIOBuffer(const Pickle
* pickle
) :
32 net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle
->data())),
34 DCHECK(pickle
->data());
38 ~WrappedPickleIOBuffer() override
{}
40 scoped_ptr
<const Pickle
> pickle_
;
46 // AppCacheResponseInfo ----------------------------------------------
48 AppCacheResponseInfo::AppCacheResponseInfo(
49 AppCacheStorage
* storage
, const GURL
& manifest_url
,
50 int64 response_id
, net::HttpResponseInfo
* http_info
,
51 int64 response_data_size
)
52 : manifest_url_(manifest_url
), response_id_(response_id
),
53 http_response_info_(http_info
), response_data_size_(response_data_size
),
56 DCHECK(response_id
!= kAppCacheNoResponseId
);
57 storage_
->working_set()->AddResponseInfo(this);
60 AppCacheResponseInfo::~AppCacheResponseInfo() {
61 storage_
->working_set()->RemoveResponseInfo(this);
64 // HttpResponseInfoIOBuffer ------------------------------------------
66 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer()
67 : response_data_size(kUnkownResponseDataSize
) {}
69 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo
* info
)
70 : http_info(info
), response_data_size(kUnkownResponseDataSize
) {}
72 HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {}
74 // AppCacheResponseIO ----------------------------------------------
76 AppCacheResponseIO::AppCacheResponseIO(
77 int64 response_id
, int64 group_id
, AppCacheDiskCacheInterface
* disk_cache
)
78 : response_id_(response_id
),
80 disk_cache_(disk_cache
),
86 AppCacheResponseIO::~AppCacheResponseIO() {
91 void AppCacheResponseIO::ScheduleIOCompletionCallback(int result
) {
92 base::MessageLoop::current()->PostTask(
93 FROM_HERE
, base::Bind(&AppCacheResponseIO::OnIOComplete
,
94 weak_factory_
.GetWeakPtr(), result
));
97 void AppCacheResponseIO::InvokeUserCompletionCallback(int result
) {
98 // Clear the user callback and buffers prior to invoking the callback
99 // so the caller can schedule additional operations in the callback.
102 net::CompletionCallback cb
= callback_
;
107 void AppCacheResponseIO::ReadRaw(int index
, int offset
,
108 net::IOBuffer
* buf
, int buf_len
) {
110 int rv
= entry_
->Read(
111 index
, offset
, buf
, buf_len
,
112 base::Bind(&AppCacheResponseIO::OnRawIOComplete
,
113 weak_factory_
.GetWeakPtr()));
114 if (rv
!= net::ERR_IO_PENDING
)
115 ScheduleIOCompletionCallback(rv
);
118 void AppCacheResponseIO::WriteRaw(int index
, int offset
,
119 net::IOBuffer
* buf
, int buf_len
) {
121 int rv
= entry_
->Write(
122 index
, offset
, buf
, buf_len
,
123 base::Bind(&AppCacheResponseIO::OnRawIOComplete
,
124 weak_factory_
.GetWeakPtr()));
125 if (rv
!= net::ERR_IO_PENDING
)
126 ScheduleIOCompletionCallback(rv
);
129 void AppCacheResponseIO::OnRawIOComplete(int result
) {
130 // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is fixed.
131 tracked_objects::ScopedTracker
tracking_profile(
132 FROM_HERE_WITH_EXPLICIT_FUNCTION(
133 "422516 AppCacheResponseIO::OnRawIOComplete"));
135 DCHECK_NE(net::ERR_IO_PENDING
, result
);
136 OnIOComplete(result
);
139 void AppCacheResponseIO::OpenEntryIfNeeded() {
141 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
144 } else if (!disk_cache_
) {
145 rv
= net::ERR_FAILED
;
147 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
149 base::Bind(&AppCacheResponseIO::OpenEntryCallback
,
150 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
151 rv
= disk_cache_
->OpenEntry(response_id_
, entry_ptr
, open_callback_
);
154 if (rv
!= net::ERR_IO_PENDING
)
155 OpenEntryCallback(entry_ptr
, rv
);
158 void AppCacheResponseIO::OpenEntryCallback(
159 AppCacheDiskCacheInterface::Entry
** entry
, int rv
) {
160 DCHECK(info_buffer_
.get() || buffer_
.get());
162 if (!open_callback_
.is_null()) {
167 open_callback_
.Reset();
169 OnOpenEntryComplete();
173 // AppCacheResponseReader ----------------------------------------------
175 AppCacheResponseReader::AppCacheResponseReader(
178 AppCacheDiskCacheInterface
* disk_cache
)
179 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
181 range_length_(kint32max
),
183 reading_metadata_size_(0),
184 weak_factory_(this) {
187 AppCacheResponseReader::~AppCacheResponseReader() {
190 void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer
* info_buf
,
191 const net::CompletionCallback
& callback
) {
192 DCHECK(!callback
.is_null());
193 DCHECK(!IsReadPending());
195 DCHECK(!info_buf
->http_info
.get());
196 DCHECK(!buffer_
.get());
197 DCHECK(!info_buffer_
.get());
199 info_buffer_
= info_buf
;
200 callback_
= callback
; // cleared on completion
204 void AppCacheResponseReader::ContinueReadInfo() {
205 int size
= entry_
->GetSize(kResponseInfoIndex
);
207 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
211 buffer_
= new net::IOBuffer(size
);
212 ReadRaw(kResponseInfoIndex
, 0, buffer_
.get(), size
);
215 void AppCacheResponseReader::ReadData(net::IOBuffer
* buf
, int buf_len
,
216 const net::CompletionCallback
& callback
) {
217 DCHECK(!callback
.is_null());
218 DCHECK(!IsReadPending());
220 DCHECK(buf_len
>= 0);
221 DCHECK(!buffer_
.get());
222 DCHECK(!info_buffer_
.get());
225 buffer_len_
= buf_len
;
226 callback_
= callback
; // cleared on completion
230 void AppCacheResponseReader::ContinueReadData() {
231 if (read_position_
+ buffer_len_
> range_length_
) {
232 // TODO(michaeln): What about integer overflows?
233 DCHECK(range_length_
>= read_position_
);
234 buffer_len_
= range_length_
- read_position_
;
236 ReadRaw(kResponseContentIndex
,
237 range_offset_
+ read_position_
,
242 void AppCacheResponseReader::SetReadRange(int offset
, int length
) {
243 DCHECK(!IsReadPending() && !read_position_
);
244 range_offset_
= offset
;
245 range_length_
= length
;
248 void AppCacheResponseReader::OnIOComplete(int result
) {
250 if (reading_metadata_size_
) {
251 DCHECK(reading_metadata_size_
== result
);
252 DCHECK(info_buffer_
->http_info
->metadata
);
253 reading_metadata_size_
= 0;
254 } else if (info_buffer_
.get()) {
255 // Deserialize the http info structure, ensuring we got headers.
256 Pickle
pickle(buffer_
->data(), result
);
257 scoped_ptr
<net::HttpResponseInfo
> info(new net::HttpResponseInfo
);
258 bool response_truncated
= false;
259 if (!info
->InitFromPickle(pickle
, &response_truncated
) ||
260 !info
->headers
.get()) {
261 InvokeUserCompletionCallback(net::ERR_FAILED
);
264 DCHECK(!response_truncated
);
265 info_buffer_
->http_info
.reset(info
.release());
267 // Also return the size of the response body
269 info_buffer_
->response_data_size
=
270 entry_
->GetSize(kResponseContentIndex
);
272 int64 metadata_size
= entry_
->GetSize(kResponseMetadataIndex
);
273 if (metadata_size
> 0) {
274 reading_metadata_size_
= metadata_size
;
275 info_buffer_
->http_info
->metadata
=
276 new net::IOBufferWithSize(metadata_size
);
277 ReadRaw(kResponseMetadataIndex
, 0,
278 info_buffer_
->http_info
->metadata
.get(), metadata_size
);
282 read_position_
+= result
;
285 InvokeUserCompletionCallback(result
);
288 void AppCacheResponseReader::OnOpenEntryComplete() {
290 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
293 if (info_buffer_
.get())
299 // AppCacheResponseWriter ----------------------------------------------
301 AppCacheResponseWriter::AppCacheResponseWriter(
302 int64 response_id
, int64 group_id
, AppCacheDiskCacheInterface
* disk_cache
)
303 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
307 creation_phase_(INITIAL_ATTEMPT
),
308 weak_factory_(this) {
311 AppCacheResponseWriter::~AppCacheResponseWriter() {
314 void AppCacheResponseWriter::WriteInfo(
315 HttpResponseInfoIOBuffer
* info_buf
,
316 const net::CompletionCallback
& callback
) {
317 DCHECK(!callback
.is_null());
318 DCHECK(!IsWritePending());
320 DCHECK(info_buf
->http_info
.get());
321 DCHECK(!buffer_
.get());
322 DCHECK(!info_buffer_
.get());
323 DCHECK(info_buf
->http_info
->headers
.get());
325 info_buffer_
= info_buf
;
326 callback_
= callback
; // cleared on completion
327 CreateEntryIfNeededAndContinue();
330 void AppCacheResponseWriter::ContinueWriteInfo() {
332 ScheduleIOCompletionCallback(net::ERR_FAILED
);
336 const bool kSkipTransientHeaders
= true;
337 const bool kTruncated
= false;
338 Pickle
* pickle
= new Pickle
;
339 info_buffer_
->http_info
->Persist(pickle
, kSkipTransientHeaders
, kTruncated
);
340 write_amount_
= static_cast<int>(pickle
->size());
341 buffer_
= new WrappedPickleIOBuffer(pickle
); // takes ownership of pickle
342 WriteRaw(kResponseInfoIndex
, 0, buffer_
.get(), write_amount_
);
345 void AppCacheResponseWriter::WriteData(
346 net::IOBuffer
* buf
, int buf_len
, const net::CompletionCallback
& callback
) {
347 DCHECK(!callback
.is_null());
348 DCHECK(!IsWritePending());
350 DCHECK(buf_len
>= 0);
351 DCHECK(!buffer_
.get());
352 DCHECK(!info_buffer_
.get());
355 write_amount_
= buf_len
;
356 callback_
= callback
; // cleared on completion
357 CreateEntryIfNeededAndContinue();
360 void AppCacheResponseWriter::ContinueWriteData() {
362 ScheduleIOCompletionCallback(net::ERR_FAILED
);
366 kResponseContentIndex
, write_position_
, buffer_
.get(), write_amount_
);
369 void AppCacheResponseWriter::OnIOComplete(int result
) {
371 DCHECK(write_amount_
== result
);
372 if (!info_buffer_
.get())
373 write_position_
+= result
;
377 InvokeUserCompletionCallback(result
);
380 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
382 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
384 creation_phase_
= NO_ATTEMPT
;
386 } else if (!disk_cache_
) {
387 creation_phase_
= NO_ATTEMPT
;
388 rv
= net::ERR_FAILED
;
390 creation_phase_
= INITIAL_ATTEMPT
;
391 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
393 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete
,
394 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
395 rv
= disk_cache_
->CreateEntry(response_id_
, entry_ptr
, create_callback_
);
397 if (rv
!= net::ERR_IO_PENDING
)
398 OnCreateEntryComplete(entry_ptr
, rv
);
401 void AppCacheResponseWriter::OnCreateEntryComplete(
402 AppCacheDiskCacheInterface::Entry
** entry
, int rv
) {
403 DCHECK(info_buffer_
.get() || buffer_
.get());
405 if (creation_phase_
== INITIAL_ATTEMPT
) {
407 // We may try to overwrite existing entries.
408 creation_phase_
= DOOM_EXISTING
;
409 rv
= disk_cache_
->DoomEntry(response_id_
, create_callback_
);
410 if (rv
!= net::ERR_IO_PENDING
)
411 OnCreateEntryComplete(NULL
, rv
);
414 } else if (creation_phase_
== DOOM_EXISTING
) {
415 creation_phase_
= SECOND_ATTEMPT
;
416 AppCacheDiskCacheInterface::Entry
** entry_ptr
=
417 new AppCacheDiskCacheInterface::Entry
*;
419 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete
,
420 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
421 rv
= disk_cache_
->CreateEntry(response_id_
, entry_ptr
, create_callback_
);
422 if (rv
!= net::ERR_IO_PENDING
)
423 OnCreateEntryComplete(entry_ptr
, rv
);
427 if (!create_callback_
.is_null()) {
431 create_callback_
.Reset();
434 if (info_buffer_
.get())
440 // AppCacheResponseMetadataWriter ----------------------------------------------
442 AppCacheResponseMetadataWriter::AppCacheResponseMetadataWriter(
445 AppCacheDiskCacheInterface
* disk_cache
)
446 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
448 weak_factory_(this) {
451 AppCacheResponseMetadataWriter::~AppCacheResponseMetadataWriter() {
454 void AppCacheResponseMetadataWriter::WriteMetadata(
457 const net::CompletionCallback
& callback
) {
458 DCHECK(!callback
.is_null());
459 DCHECK(!IsIOPending());
461 DCHECK(buf_len
>= 0);
462 DCHECK(!buffer_
.get());
465 write_amount_
= buf_len
;
466 callback_
= callback
; // cleared on completion
470 void AppCacheResponseMetadataWriter::OnOpenEntryComplete() {
472 ScheduleIOCompletionCallback(net::ERR_FAILED
);
475 WriteRaw(kResponseMetadataIndex
, 0, buffer_
.get(), write_amount_
);
478 void AppCacheResponseMetadataWriter::OnIOComplete(int result
) {
479 DCHECK(result
< 0 || write_amount_
== result
);
480 InvokeUserCompletionCallback(result
);
483 } // namespace content