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.
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 Pickle
* pickle
) :
35 net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle
->data())),
37 DCHECK(pickle
->data());
41 ~WrappedPickleIOBuffer() override
{}
43 scoped_ptr
<const 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::MessageLoop::current()->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(vadimt): 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
);
143 // AppCacheResponseReader ----------------------------------------------
145 AppCacheResponseReader::AppCacheResponseReader(
146 int64 response_id
, int64 group_id
, AppCacheDiskCacheInterface
* disk_cache
)
147 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
149 range_length_(kint32max
),
151 weak_factory_(this) {
154 AppCacheResponseReader::~AppCacheResponseReader() {
157 void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer
* info_buf
,
158 const net::CompletionCallback
& callback
) {
159 DCHECK(!callback
.is_null());
160 DCHECK(!IsReadPending());
162 DCHECK(!info_buf
->http_info
.get());
163 DCHECK(!buffer_
.get());
164 DCHECK(!info_buffer_
.get());
166 info_buffer_
= info_buf
;
167 callback_
= callback
; // cleared on completion
168 OpenEntryIfNeededAndContinue();
171 void AppCacheResponseReader::ContinueReadInfo() {
173 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
177 int size
= entry_
->GetSize(kResponseInfoIndex
);
179 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
183 buffer_
= new net::IOBuffer(size
);
184 ReadRaw(kResponseInfoIndex
, 0, buffer_
.get(), size
);
187 void AppCacheResponseReader::ReadData(net::IOBuffer
* buf
, int buf_len
,
188 const net::CompletionCallback
& callback
) {
189 DCHECK(!callback
.is_null());
190 DCHECK(!IsReadPending());
192 DCHECK(buf_len
>= 0);
193 DCHECK(!buffer_
.get());
194 DCHECK(!info_buffer_
.get());
197 buffer_len_
= buf_len
;
198 callback_
= callback
; // cleared on completion
199 OpenEntryIfNeededAndContinue();
202 void AppCacheResponseReader::ContinueReadData() {
204 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
208 if (read_position_
+ buffer_len_
> range_length_
) {
209 // TODO(michaeln): What about integer overflows?
210 DCHECK(range_length_
>= read_position_
);
211 buffer_len_
= range_length_
- read_position_
;
213 ReadRaw(kResponseContentIndex
,
214 range_offset_
+ read_position_
,
219 void AppCacheResponseReader::SetReadRange(int offset
, int length
) {
220 DCHECK(!IsReadPending() && !read_position_
);
221 range_offset_
= offset
;
222 range_length_
= length
;
225 void AppCacheResponseReader::OnIOComplete(int result
) {
227 if (info_buffer_
.get()) {
228 // Deserialize the http info structure, ensuring we got headers.
229 Pickle
pickle(buffer_
->data(), result
);
230 scoped_ptr
<net::HttpResponseInfo
> info(new net::HttpResponseInfo
);
231 bool response_truncated
= false;
232 if (!info
->InitFromPickle(pickle
, &response_truncated
) ||
233 !info
->headers
.get()) {
234 InvokeUserCompletionCallback(net::ERR_FAILED
);
237 DCHECK(!response_truncated
);
238 info_buffer_
->http_info
.reset(info
.release());
240 // Also return the size of the response body
242 info_buffer_
->response_data_size
=
243 entry_
->GetSize(kResponseContentIndex
);
245 read_position_
+= result
;
248 InvokeUserCompletionCallback(result
);
251 void AppCacheResponseReader::OpenEntryIfNeededAndContinue() {
253 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
256 } else if (!disk_cache_
) {
257 rv
= net::ERR_FAILED
;
259 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
261 base::Bind(&AppCacheResponseReader::OnOpenEntryComplete
,
262 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
263 rv
= disk_cache_
->OpenEntry(response_id_
, entry_ptr
, open_callback_
);
266 if (rv
!= net::ERR_IO_PENDING
)
267 OnOpenEntryComplete(entry_ptr
, rv
);
270 void AppCacheResponseReader::OnOpenEntryComplete(
271 AppCacheDiskCacheInterface::Entry
** entry
, int rv
) {
272 DCHECK(info_buffer_
.get() || buffer_
.get());
274 if (!open_callback_
.is_null()) {
279 open_callback_
.Reset();
282 if (info_buffer_
.get())
288 // AppCacheResponseWriter ----------------------------------------------
290 AppCacheResponseWriter::AppCacheResponseWriter(
291 int64 response_id
, int64 group_id
, AppCacheDiskCacheInterface
* disk_cache
)
292 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
296 creation_phase_(INITIAL_ATTEMPT
),
297 weak_factory_(this) {
300 AppCacheResponseWriter::~AppCacheResponseWriter() {
303 void AppCacheResponseWriter::WriteInfo(
304 HttpResponseInfoIOBuffer
* info_buf
,
305 const net::CompletionCallback
& callback
) {
306 DCHECK(!callback
.is_null());
307 DCHECK(!IsWritePending());
309 DCHECK(info_buf
->http_info
.get());
310 DCHECK(!buffer_
.get());
311 DCHECK(!info_buffer_
.get());
312 DCHECK(info_buf
->http_info
->headers
.get());
314 info_buffer_
= info_buf
;
315 callback_
= callback
; // cleared on completion
316 CreateEntryIfNeededAndContinue();
319 void AppCacheResponseWriter::ContinueWriteInfo() {
321 ScheduleIOCompletionCallback(net::ERR_FAILED
);
325 const bool kSkipTransientHeaders
= true;
326 const bool kTruncated
= false;
327 Pickle
* pickle
= new Pickle
;
328 info_buffer_
->http_info
->Persist(pickle
, kSkipTransientHeaders
, kTruncated
);
329 write_amount_
= static_cast<int>(pickle
->size());
330 buffer_
= new WrappedPickleIOBuffer(pickle
); // takes ownership of pickle
331 WriteRaw(kResponseInfoIndex
, 0, buffer_
.get(), write_amount_
);
334 void AppCacheResponseWriter::WriteData(
335 net::IOBuffer
* buf
, int buf_len
, const net::CompletionCallback
& callback
) {
336 DCHECK(!callback
.is_null());
337 DCHECK(!IsWritePending());
339 DCHECK(buf_len
>= 0);
340 DCHECK(!buffer_
.get());
341 DCHECK(!info_buffer_
.get());
344 write_amount_
= buf_len
;
345 callback_
= callback
; // cleared on completion
346 CreateEntryIfNeededAndContinue();
349 void AppCacheResponseWriter::ContinueWriteData() {
351 ScheduleIOCompletionCallback(net::ERR_FAILED
);
355 kResponseContentIndex
, write_position_
, buffer_
.get(), write_amount_
);
358 void AppCacheResponseWriter::OnIOComplete(int result
) {
360 DCHECK(write_amount_
== result
);
361 if (!info_buffer_
.get())
362 write_position_
+= result
;
366 InvokeUserCompletionCallback(result
);
369 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
371 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
373 creation_phase_
= NO_ATTEMPT
;
375 } else if (!disk_cache_
) {
376 creation_phase_
= NO_ATTEMPT
;
377 rv
= net::ERR_FAILED
;
379 creation_phase_
= INITIAL_ATTEMPT
;
380 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
382 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete
,
383 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
384 rv
= disk_cache_
->CreateEntry(response_id_
, entry_ptr
, create_callback_
);
386 if (rv
!= net::ERR_IO_PENDING
)
387 OnCreateEntryComplete(entry_ptr
, rv
);
390 void AppCacheResponseWriter::OnCreateEntryComplete(
391 AppCacheDiskCacheInterface::Entry
** entry
, int rv
) {
392 DCHECK(info_buffer_
.get() || buffer_
.get());
394 if (creation_phase_
== INITIAL_ATTEMPT
) {
396 // We may try to overwrite existing entries.
397 creation_phase_
= DOOM_EXISTING
;
398 rv
= disk_cache_
->DoomEntry(response_id_
, create_callback_
);
399 if (rv
!= net::ERR_IO_PENDING
)
400 OnCreateEntryComplete(NULL
, rv
);
403 } else if (creation_phase_
== DOOM_EXISTING
) {
404 creation_phase_
= SECOND_ATTEMPT
;
405 AppCacheDiskCacheInterface::Entry
** entry_ptr
=
406 new AppCacheDiskCacheInterface::Entry
*;
408 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete
,
409 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
410 rv
= disk_cache_
->CreateEntry(response_id_
, entry_ptr
, create_callback_
);
411 if (rv
!= net::ERR_IO_PENDING
)
412 OnCreateEntryComplete(entry_ptr
, rv
);
416 if (!create_callback_
.is_null()) {
420 create_callback_
.Reset();
423 if (info_buffer_
.get())
429 } // namespace content