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/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"
23 // Disk cache entry data indices.
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
{
33 explicit WrappedPickleIOBuffer(const Pickle
* pickle
) :
34 net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle
->data())),
36 DCHECK(pickle
->data());
40 virtual ~WrappedPickleIOBuffer() {}
42 scoped_ptr
<const Pickle
> pickle_
;
48 // AppCacheResponseInfo ----------------------------------------------
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
),
58 DCHECK(response_id
!= kAppCacheNoResponseId
);
59 storage_
->working_set()->AddResponseInfo(this);
62 AppCacheResponseInfo::~AppCacheResponseInfo() {
63 storage_
->working_set()->RemoveResponseInfo(this);
66 // HttpResponseInfoIOBuffer ------------------------------------------
68 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer()
69 : response_data_size(kUnkownResponseDataSize
) {}
71 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo
* info
)
72 : http_info(info
), response_data_size(kUnkownResponseDataSize
) {}
74 HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {}
76 // AppCacheResponseIO ----------------------------------------------
78 AppCacheResponseIO::AppCacheResponseIO(
79 int64 response_id
, int64 group_id
, AppCacheDiskCacheInterface
* disk_cache
)
80 : response_id_(response_id
),
82 disk_cache_(disk_cache
),
88 AppCacheResponseIO::~AppCacheResponseIO() {
93 void AppCacheResponseIO::ScheduleIOCompletionCallback(int result
) {
94 base::MessageLoop::current()->PostTask(
95 FROM_HERE
, base::Bind(&AppCacheResponseIO::OnIOComplete
,
96 weak_factory_
.GetWeakPtr(), 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.
104 net::CompletionCallback cb
= callback_
;
109 void AppCacheResponseIO::ReadRaw(int index
, int offset
,
110 net::IOBuffer
* buf
, int buf_len
) {
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
);
120 void AppCacheResponseIO::WriteRaw(int index
, int offset
,
121 net::IOBuffer
* buf
, int buf_len
) {
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
);
131 void AppCacheResponseIO::OnRawIOComplete(int result
) {
132 DCHECK_NE(net::ERR_IO_PENDING
, result
);
133 OnIOComplete(result
);
137 // AppCacheResponseReader ----------------------------------------------
139 AppCacheResponseReader::AppCacheResponseReader(
140 int64 response_id
, int64 group_id
, AppCacheDiskCacheInterface
* disk_cache
)
141 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
143 range_length_(kint32max
),
145 weak_factory_(this) {
148 AppCacheResponseReader::~AppCacheResponseReader() {
151 void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer
* info_buf
,
152 const net::CompletionCallback
& callback
) {
153 DCHECK(!callback
.is_null());
154 DCHECK(!IsReadPending());
156 DCHECK(!info_buf
->http_info
.get());
157 DCHECK(!buffer_
.get());
158 DCHECK(!info_buffer_
.get());
160 info_buffer_
= info_buf
;
161 callback_
= callback
; // cleared on completion
162 OpenEntryIfNeededAndContinue();
165 void AppCacheResponseReader::ContinueReadInfo() {
167 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
171 int size
= entry_
->GetSize(kResponseInfoIndex
);
173 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
177 buffer_
= new net::IOBuffer(size
);
178 ReadRaw(kResponseInfoIndex
, 0, buffer_
.get(), size
);
181 void AppCacheResponseReader::ReadData(net::IOBuffer
* buf
, int buf_len
,
182 const net::CompletionCallback
& callback
) {
183 DCHECK(!callback
.is_null());
184 DCHECK(!IsReadPending());
186 DCHECK(buf_len
>= 0);
187 DCHECK(!buffer_
.get());
188 DCHECK(!info_buffer_
.get());
191 buffer_len_
= buf_len
;
192 callback_
= callback
; // cleared on completion
193 OpenEntryIfNeededAndContinue();
196 void AppCacheResponseReader::ContinueReadData() {
198 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS
);
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_
;
207 ReadRaw(kResponseContentIndex
,
208 range_offset_
+ read_position_
,
213 void AppCacheResponseReader::SetReadRange(int offset
, int length
) {
214 DCHECK(!IsReadPending() && !read_position_
);
215 range_offset_
= offset
;
216 range_length_
= length
;
219 void AppCacheResponseReader::OnIOComplete(int result
) {
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
);
231 DCHECK(!response_truncated
);
232 info_buffer_
->http_info
.reset(info
.release());
234 // Also return the size of the response body
236 info_buffer_
->response_data_size
=
237 entry_
->GetSize(kResponseContentIndex
);
239 read_position_
+= result
;
242 InvokeUserCompletionCallback(result
);
245 void AppCacheResponseReader::OpenEntryIfNeededAndContinue() {
247 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
250 } else if (!disk_cache_
) {
251 rv
= net::ERR_FAILED
;
253 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
255 base::Bind(&AppCacheResponseReader::OnOpenEntryComplete
,
256 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
257 rv
= disk_cache_
->OpenEntry(response_id_
, entry_ptr
, open_callback_
);
260 if (rv
!= net::ERR_IO_PENDING
)
261 OnOpenEntryComplete(entry_ptr
, rv
);
264 void AppCacheResponseReader::OnOpenEntryComplete(
265 AppCacheDiskCacheInterface::Entry
** entry
, int rv
) {
266 DCHECK(info_buffer_
.get() || buffer_
.get());
268 if (!open_callback_
.is_null()) {
273 open_callback_
.Reset();
276 if (info_buffer_
.get())
282 // AppCacheResponseWriter ----------------------------------------------
284 AppCacheResponseWriter::AppCacheResponseWriter(
285 int64 response_id
, int64 group_id
, AppCacheDiskCacheInterface
* disk_cache
)
286 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
290 creation_phase_(INITIAL_ATTEMPT
),
291 weak_factory_(this) {
294 AppCacheResponseWriter::~AppCacheResponseWriter() {
297 void AppCacheResponseWriter::WriteInfo(
298 HttpResponseInfoIOBuffer
* info_buf
,
299 const net::CompletionCallback
& callback
) {
300 DCHECK(!callback
.is_null());
301 DCHECK(!IsWritePending());
303 DCHECK(info_buf
->http_info
.get());
304 DCHECK(!buffer_
.get());
305 DCHECK(!info_buffer_
.get());
306 DCHECK(info_buf
->http_info
->headers
.get());
308 info_buffer_
= info_buf
;
309 callback_
= callback
; // cleared on completion
310 CreateEntryIfNeededAndContinue();
313 void AppCacheResponseWriter::ContinueWriteInfo() {
315 ScheduleIOCompletionCallback(net::ERR_FAILED
);
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_
);
328 void AppCacheResponseWriter::WriteData(
329 net::IOBuffer
* buf
, int buf_len
, const net::CompletionCallback
& callback
) {
330 DCHECK(!callback
.is_null());
331 DCHECK(!IsWritePending());
333 DCHECK(buf_len
>= 0);
334 DCHECK(!buffer_
.get());
335 DCHECK(!info_buffer_
.get());
338 write_amount_
= buf_len
;
339 callback_
= callback
; // cleared on completion
340 CreateEntryIfNeededAndContinue();
343 void AppCacheResponseWriter::ContinueWriteData() {
345 ScheduleIOCompletionCallback(net::ERR_FAILED
);
349 kResponseContentIndex
, write_position_
, buffer_
.get(), write_amount_
);
352 void AppCacheResponseWriter::OnIOComplete(int result
) {
354 DCHECK(write_amount_
== result
);
355 if (!info_buffer_
.get())
356 write_position_
+= result
;
360 InvokeUserCompletionCallback(result
);
363 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
365 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
367 creation_phase_
= NO_ATTEMPT
;
369 } else if (!disk_cache_
) {
370 creation_phase_
= NO_ATTEMPT
;
371 rv
= net::ERR_FAILED
;
373 creation_phase_
= INITIAL_ATTEMPT
;
374 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
376 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete
,
377 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
378 rv
= disk_cache_
->CreateEntry(response_id_
, entry_ptr
, create_callback_
);
380 if (rv
!= net::ERR_IO_PENDING
)
381 OnCreateEntryComplete(entry_ptr
, rv
);
384 void AppCacheResponseWriter::OnCreateEntryComplete(
385 AppCacheDiskCacheInterface::Entry
** entry
, int rv
) {
386 DCHECK(info_buffer_
.get() || buffer_
.get());
388 if (creation_phase_
== INITIAL_ATTEMPT
) {
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
);
397 } else if (creation_phase_
== DOOM_EXISTING
) {
398 creation_phase_
= SECOND_ATTEMPT
;
399 AppCacheDiskCacheInterface::Entry
** entry_ptr
=
400 new AppCacheDiskCacheInterface::Entry
*;
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
);
410 if (!create_callback_
.is_null()) {
414 create_callback_
.Reset();
417 if (info_buffer_
.get())
423 } // namespace content