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 "webkit/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.h"
12 #include "base/pickle.h"
13 #include "base/string_util.h"
14 #include "net/base/completion_callback.h"
15 #include "net/base/net_errors.h"
16 #include "net/base/io_buffer.h"
17 #include "webkit/appcache/appcache_service.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 AppCacheService
* service
, 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
!= kNoResponseId
);
59 service_
->storage()->working_set()->AddResponseInfo(this);
62 AppCacheResponseInfo::~AppCacheResponseInfo() {
63 service_
->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
),
85 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
88 AppCacheResponseIO::~AppCacheResponseIO() {
93 void AppCacheResponseIO::ScheduleIOCompletionCallback(int result
) {
94 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 ALLOW_THIS_IN_INITIALIZER_LIST(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
, range_offset_
+ read_position_
,
208 buffer_
, buffer_len_
);
211 void AppCacheResponseReader::SetReadRange(int offset
, int length
) {
212 DCHECK(!IsReadPending() && !read_position_
);
213 range_offset_
= offset
;
214 range_length_
= length
;
217 void AppCacheResponseReader::OnIOComplete(int result
) {
219 if (info_buffer_
.get()) {
220 // Deserialize the http info structure, ensuring we got headers.
221 Pickle
pickle(buffer_
->data(), result
);
222 scoped_ptr
<net::HttpResponseInfo
> info(new net::HttpResponseInfo
);
223 bool response_truncated
= false;
224 if (!info
->InitFromPickle(pickle
, &response_truncated
) ||
226 InvokeUserCompletionCallback(net::ERR_FAILED
);
229 DCHECK(!response_truncated
);
230 info_buffer_
->http_info
.reset(info
.release());
232 // Also return the size of the response body
234 info_buffer_
->response_data_size
=
235 entry_
->GetSize(kResponseContentIndex
);
237 read_position_
+= result
;
240 InvokeUserCompletionCallback(result
);
243 void AppCacheResponseReader::OpenEntryIfNeededAndContinue() {
245 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
248 } else if (!disk_cache_
) {
249 rv
= net::ERR_FAILED
;
251 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
253 base::Bind(&AppCacheResponseReader::OnOpenEntryComplete
,
254 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
255 rv
= disk_cache_
->OpenEntry(response_id_
, entry_ptr
, open_callback_
);
258 if (rv
!= net::ERR_IO_PENDING
)
259 OnOpenEntryComplete(entry_ptr
, rv
);
262 void AppCacheResponseReader::OnOpenEntryComplete(
263 AppCacheDiskCacheInterface::Entry
** entry
, int rv
) {
264 DCHECK(info_buffer_
.get() || buffer_
.get());
266 if (!open_callback_
.is_null()) {
271 open_callback_
.Reset();
280 // AppCacheResponseWriter ----------------------------------------------
282 AppCacheResponseWriter::AppCacheResponseWriter(
283 int64 response_id
, int64 group_id
, AppCacheDiskCacheInterface
* disk_cache
)
284 : AppCacheResponseIO(response_id
, group_id
, disk_cache
),
288 creation_phase_(INITIAL_ATTEMPT
),
289 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
292 AppCacheResponseWriter::~AppCacheResponseWriter() {
295 void AppCacheResponseWriter::WriteInfo(
296 HttpResponseInfoIOBuffer
* info_buf
,
297 const net::CompletionCallback
& callback
) {
298 DCHECK(!callback
.is_null());
299 DCHECK(!IsWritePending());
301 DCHECK(info_buf
->http_info
.get());
302 DCHECK(!buffer_
.get());
303 DCHECK(!info_buffer_
.get());
304 DCHECK(info_buf
->http_info
->headers
);
306 info_buffer_
= info_buf
;
307 callback_
= callback
; // cleared on completion
308 CreateEntryIfNeededAndContinue();
311 void AppCacheResponseWriter::ContinueWriteInfo() {
313 ScheduleIOCompletionCallback(net::ERR_FAILED
);
317 const bool kSkipTransientHeaders
= true;
318 const bool kTruncated
= false;
319 Pickle
* pickle
= new Pickle
;
320 info_buffer_
->http_info
->Persist(pickle
, kSkipTransientHeaders
, kTruncated
);
321 write_amount_
= static_cast<int>(pickle
->size());
322 buffer_
= new WrappedPickleIOBuffer(pickle
); // takes ownership of pickle
323 WriteRaw(kResponseInfoIndex
, 0, buffer_
, write_amount_
);
326 void AppCacheResponseWriter::WriteData(
327 net::IOBuffer
* buf
, int buf_len
, const net::CompletionCallback
& callback
) {
328 DCHECK(!callback
.is_null());
329 DCHECK(!IsWritePending());
331 DCHECK(buf_len
>= 0);
332 DCHECK(!buffer_
.get());
333 DCHECK(!info_buffer_
.get());
336 write_amount_
= buf_len
;
337 callback_
= callback
; // cleared on completion
338 CreateEntryIfNeededAndContinue();
341 void AppCacheResponseWriter::ContinueWriteData() {
343 ScheduleIOCompletionCallback(net::ERR_FAILED
);
346 WriteRaw(kResponseContentIndex
, write_position_
, buffer_
, write_amount_
);
349 void AppCacheResponseWriter::OnIOComplete(int result
) {
351 DCHECK(write_amount_
== result
);
352 if (!info_buffer_
.get())
353 write_position_
+= result
;
357 InvokeUserCompletionCallback(result
);
360 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() {
362 AppCacheDiskCacheInterface::Entry
** entry_ptr
= NULL
;
364 creation_phase_
= NO_ATTEMPT
;
366 } else if (!disk_cache_
) {
367 creation_phase_
= NO_ATTEMPT
;
368 rv
= net::ERR_FAILED
;
370 creation_phase_
= INITIAL_ATTEMPT
;
371 entry_ptr
= new AppCacheDiskCacheInterface::Entry
*;
373 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete
,
374 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
375 rv
= disk_cache_
->CreateEntry(response_id_
, entry_ptr
, create_callback_
);
377 if (rv
!= net::ERR_IO_PENDING
)
378 OnCreateEntryComplete(entry_ptr
, rv
);
381 void AppCacheResponseWriter::OnCreateEntryComplete(
382 AppCacheDiskCacheInterface::Entry
** entry
, int rv
) {
383 DCHECK(info_buffer_
.get() || buffer_
.get());
385 if (creation_phase_
== INITIAL_ATTEMPT
) {
387 // We may try to overwrite existing entries.
388 creation_phase_
= DOOM_EXISTING
;
389 rv
= disk_cache_
->DoomEntry(response_id_
, create_callback_
);
390 if (rv
!= net::ERR_IO_PENDING
)
391 OnCreateEntryComplete(NULL
, rv
);
394 } else if (creation_phase_
== DOOM_EXISTING
) {
395 creation_phase_
= SECOND_ATTEMPT
;
396 AppCacheDiskCacheInterface::Entry
** entry_ptr
=
397 new AppCacheDiskCacheInterface::Entry
*;
399 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete
,
400 weak_factory_
.GetWeakPtr(), base::Owned(entry_ptr
));
401 rv
= disk_cache_
->CreateEntry(response_id_
, entry_ptr
, create_callback_
);
402 if (rv
!= net::ERR_IO_PENDING
)
403 OnCreateEntryComplete(entry_ptr
, rv
);
407 if (!create_callback_
.is_null()) {
411 create_callback_
.Reset();
420 } // namespace appcache