OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "webkit/browser/appcache/appcache_response.h" | |
6 | |
7 #include "base/bind.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 "net/base/completion_callback.h" | |
15 #include "net/base/io_buffer.h" | |
16 #include "net/base/net_errors.h" | |
17 #include "webkit/browser/appcache/appcache_storage.h" | |
18 | |
19 namespace appcache { | |
20 | |
21 namespace { | |
22 | |
23 // Disk cache entry data indices. | |
24 enum { | |
25 kResponseInfoIndex, | |
26 kResponseContentIndex | |
27 }; | |
28 | |
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 { | |
32 public: | |
33 explicit WrappedPickleIOBuffer(const Pickle* pickle) : | |
34 net::WrappedIOBuffer(reinterpret_cast<const char*>(pickle->data())), | |
35 pickle_(pickle) { | |
36 DCHECK(pickle->data()); | |
37 } | |
38 | |
39 private: | |
40 virtual ~WrappedPickleIOBuffer() {} | |
41 | |
42 scoped_ptr<const Pickle> pickle_; | |
43 }; | |
44 | |
45 } // anon namespace | |
46 | |
47 | |
48 // AppCacheResponseInfo ---------------------------------------------- | |
49 | |
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), | |
56 storage_(storage) { | |
57 DCHECK(http_info); | |
58 DCHECK(response_id != kAppCacheNoResponseId); | |
59 storage_->working_set()->AddResponseInfo(this); | |
60 } | |
61 | |
62 AppCacheResponseInfo::~AppCacheResponseInfo() { | |
63 storage_->working_set()->RemoveResponseInfo(this); | |
64 } | |
65 | |
66 // HttpResponseInfoIOBuffer ------------------------------------------ | |
67 | |
68 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer() | |
69 : response_data_size(kUnkownResponseDataSize) {} | |
70 | |
71 HttpResponseInfoIOBuffer::HttpResponseInfoIOBuffer(net::HttpResponseInfo* info) | |
72 : http_info(info), response_data_size(kUnkownResponseDataSize) {} | |
73 | |
74 HttpResponseInfoIOBuffer::~HttpResponseInfoIOBuffer() {} | |
75 | |
76 // AppCacheResponseIO ---------------------------------------------- | |
77 | |
78 AppCacheResponseIO::AppCacheResponseIO( | |
79 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache) | |
80 : response_id_(response_id), | |
81 group_id_(group_id), | |
82 disk_cache_(disk_cache), | |
83 entry_(NULL), | |
84 buffer_len_(0), | |
85 weak_factory_(this) { | |
86 } | |
87 | |
88 AppCacheResponseIO::~AppCacheResponseIO() { | |
89 if (entry_) | |
90 entry_->Close(); | |
91 } | |
92 | |
93 void AppCacheResponseIO::ScheduleIOCompletionCallback(int result) { | |
94 base::MessageLoop::current()->PostTask( | |
95 FROM_HERE, base::Bind(&AppCacheResponseIO::OnIOComplete, | |
96 weak_factory_.GetWeakPtr(), result)); | |
97 } | |
98 | |
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. | |
102 buffer_ = NULL; | |
103 info_buffer_ = NULL; | |
104 net::CompletionCallback cb = callback_; | |
105 callback_.Reset(); | |
106 cb.Run(result); | |
107 } | |
108 | |
109 void AppCacheResponseIO::ReadRaw(int index, int offset, | |
110 net::IOBuffer* buf, int buf_len) { | |
111 DCHECK(entry_); | |
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); | |
118 } | |
119 | |
120 void AppCacheResponseIO::WriteRaw(int index, int offset, | |
121 net::IOBuffer* buf, int buf_len) { | |
122 DCHECK(entry_); | |
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); | |
129 } | |
130 | |
131 void AppCacheResponseIO::OnRawIOComplete(int result) { | |
132 DCHECK_NE(net::ERR_IO_PENDING, result); | |
133 OnIOComplete(result); | |
134 } | |
135 | |
136 | |
137 // AppCacheResponseReader ---------------------------------------------- | |
138 | |
139 AppCacheResponseReader::AppCacheResponseReader( | |
140 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache) | |
141 : AppCacheResponseIO(response_id, group_id, disk_cache), | |
142 range_offset_(0), | |
143 range_length_(kint32max), | |
144 read_position_(0), | |
145 weak_factory_(this) { | |
146 } | |
147 | |
148 AppCacheResponseReader::~AppCacheResponseReader() { | |
149 } | |
150 | |
151 void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer* info_buf, | |
152 const net::CompletionCallback& callback) { | |
153 DCHECK(!callback.is_null()); | |
154 DCHECK(!IsReadPending()); | |
155 DCHECK(info_buf); | |
156 DCHECK(!info_buf->http_info.get()); | |
157 DCHECK(!buffer_.get()); | |
158 DCHECK(!info_buffer_.get()); | |
159 | |
160 info_buffer_ = info_buf; | |
161 callback_ = callback; // cleared on completion | |
162 OpenEntryIfNeededAndContinue(); | |
163 } | |
164 | |
165 void AppCacheResponseReader::ContinueReadInfo() { | |
166 if (!entry_) { | |
167 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS); | |
168 return; | |
169 } | |
170 | |
171 int size = entry_->GetSize(kResponseInfoIndex); | |
172 if (size <= 0) { | |
173 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS); | |
174 return; | |
175 } | |
176 | |
177 buffer_ = new net::IOBuffer(size); | |
178 ReadRaw(kResponseInfoIndex, 0, buffer_.get(), size); | |
179 } | |
180 | |
181 void AppCacheResponseReader::ReadData(net::IOBuffer* buf, int buf_len, | |
182 const net::CompletionCallback& callback) { | |
183 DCHECK(!callback.is_null()); | |
184 DCHECK(!IsReadPending()); | |
185 DCHECK(buf); | |
186 DCHECK(buf_len >= 0); | |
187 DCHECK(!buffer_.get()); | |
188 DCHECK(!info_buffer_.get()); | |
189 | |
190 buffer_ = buf; | |
191 buffer_len_ = buf_len; | |
192 callback_ = callback; // cleared on completion | |
193 OpenEntryIfNeededAndContinue(); | |
194 } | |
195 | |
196 void AppCacheResponseReader::ContinueReadData() { | |
197 if (!entry_) { | |
198 ScheduleIOCompletionCallback(net::ERR_CACHE_MISS); | |
199 return; | |
200 } | |
201 | |
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_; | |
206 } | |
207 ReadRaw(kResponseContentIndex, | |
208 range_offset_ + read_position_, | |
209 buffer_.get(), | |
210 buffer_len_); | |
211 } | |
212 | |
213 void AppCacheResponseReader::SetReadRange(int offset, int length) { | |
214 DCHECK(!IsReadPending() && !read_position_); | |
215 range_offset_ = offset; | |
216 range_length_ = length; | |
217 } | |
218 | |
219 void AppCacheResponseReader::OnIOComplete(int result) { | |
220 if (result >= 0) { | |
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); | |
229 return; | |
230 } | |
231 DCHECK(!response_truncated); | |
232 info_buffer_->http_info.reset(info.release()); | |
233 | |
234 // Also return the size of the response body | |
235 DCHECK(entry_); | |
236 info_buffer_->response_data_size = | |
237 entry_->GetSize(kResponseContentIndex); | |
238 } else { | |
239 read_position_ += result; | |
240 } | |
241 } | |
242 InvokeUserCompletionCallback(result); | |
243 } | |
244 | |
245 void AppCacheResponseReader::OpenEntryIfNeededAndContinue() { | |
246 int rv; | |
247 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL; | |
248 if (entry_) { | |
249 rv = net::OK; | |
250 } else if (!disk_cache_) { | |
251 rv = net::ERR_FAILED; | |
252 } else { | |
253 entry_ptr = new AppCacheDiskCacheInterface::Entry*; | |
254 open_callback_ = | |
255 base::Bind(&AppCacheResponseReader::OnOpenEntryComplete, | |
256 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr)); | |
257 rv = disk_cache_->OpenEntry(response_id_, entry_ptr, open_callback_); | |
258 } | |
259 | |
260 if (rv != net::ERR_IO_PENDING) | |
261 OnOpenEntryComplete(entry_ptr, rv); | |
262 } | |
263 | |
264 void AppCacheResponseReader::OnOpenEntryComplete( | |
265 AppCacheDiskCacheInterface::Entry** entry, int rv) { | |
266 DCHECK(info_buffer_.get() || buffer_.get()); | |
267 | |
268 if (!open_callback_.is_null()) { | |
269 if (rv == net::OK) { | |
270 DCHECK(entry); | |
271 entry_ = *entry; | |
272 } | |
273 open_callback_.Reset(); | |
274 } | |
275 | |
276 if (info_buffer_.get()) | |
277 ContinueReadInfo(); | |
278 else | |
279 ContinueReadData(); | |
280 } | |
281 | |
282 // AppCacheResponseWriter ---------------------------------------------- | |
283 | |
284 AppCacheResponseWriter::AppCacheResponseWriter( | |
285 int64 response_id, int64 group_id, AppCacheDiskCacheInterface* disk_cache) | |
286 : AppCacheResponseIO(response_id, group_id, disk_cache), | |
287 info_size_(0), | |
288 write_position_(0), | |
289 write_amount_(0), | |
290 creation_phase_(INITIAL_ATTEMPT), | |
291 weak_factory_(this) { | |
292 } | |
293 | |
294 AppCacheResponseWriter::~AppCacheResponseWriter() { | |
295 } | |
296 | |
297 void AppCacheResponseWriter::WriteInfo( | |
298 HttpResponseInfoIOBuffer* info_buf, | |
299 const net::CompletionCallback& callback) { | |
300 DCHECK(!callback.is_null()); | |
301 DCHECK(!IsWritePending()); | |
302 DCHECK(info_buf); | |
303 DCHECK(info_buf->http_info.get()); | |
304 DCHECK(!buffer_.get()); | |
305 DCHECK(!info_buffer_.get()); | |
306 DCHECK(info_buf->http_info->headers.get()); | |
307 | |
308 info_buffer_ = info_buf; | |
309 callback_ = callback; // cleared on completion | |
310 CreateEntryIfNeededAndContinue(); | |
311 } | |
312 | |
313 void AppCacheResponseWriter::ContinueWriteInfo() { | |
314 if (!entry_) { | |
315 ScheduleIOCompletionCallback(net::ERR_FAILED); | |
316 return; | |
317 } | |
318 | |
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_); | |
326 } | |
327 | |
328 void AppCacheResponseWriter::WriteData( | |
329 net::IOBuffer* buf, int buf_len, const net::CompletionCallback& callback) { | |
330 DCHECK(!callback.is_null()); | |
331 DCHECK(!IsWritePending()); | |
332 DCHECK(buf); | |
333 DCHECK(buf_len >= 0); | |
334 DCHECK(!buffer_.get()); | |
335 DCHECK(!info_buffer_.get()); | |
336 | |
337 buffer_ = buf; | |
338 write_amount_ = buf_len; | |
339 callback_ = callback; // cleared on completion | |
340 CreateEntryIfNeededAndContinue(); | |
341 } | |
342 | |
343 void AppCacheResponseWriter::ContinueWriteData() { | |
344 if (!entry_) { | |
345 ScheduleIOCompletionCallback(net::ERR_FAILED); | |
346 return; | |
347 } | |
348 WriteRaw( | |
349 kResponseContentIndex, write_position_, buffer_.get(), write_amount_); | |
350 } | |
351 | |
352 void AppCacheResponseWriter::OnIOComplete(int result) { | |
353 if (result >= 0) { | |
354 DCHECK(write_amount_ == result); | |
355 if (!info_buffer_.get()) | |
356 write_position_ += result; | |
357 else | |
358 info_size_ = result; | |
359 } | |
360 InvokeUserCompletionCallback(result); | |
361 } | |
362 | |
363 void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() { | |
364 int rv; | |
365 AppCacheDiskCacheInterface::Entry** entry_ptr = NULL; | |
366 if (entry_) { | |
367 creation_phase_ = NO_ATTEMPT; | |
368 rv = net::OK; | |
369 } else if (!disk_cache_) { | |
370 creation_phase_ = NO_ATTEMPT; | |
371 rv = net::ERR_FAILED; | |
372 } else { | |
373 creation_phase_ = INITIAL_ATTEMPT; | |
374 entry_ptr = new AppCacheDiskCacheInterface::Entry*; | |
375 create_callback_ = | |
376 base::Bind(&AppCacheResponseWriter::OnCreateEntryComplete, | |
377 weak_factory_.GetWeakPtr(), base::Owned(entry_ptr)); | |
378 rv = disk_cache_->CreateEntry(response_id_, entry_ptr, create_callback_); | |
379 } | |
380 if (rv != net::ERR_IO_PENDING) | |
381 OnCreateEntryComplete(entry_ptr, rv); | |
382 } | |
383 | |
384 void AppCacheResponseWriter::OnCreateEntryComplete( | |
385 AppCacheDiskCacheInterface::Entry** entry, int rv) { | |
386 DCHECK(info_buffer_.get() || buffer_.get()); | |
387 | |
388 if (creation_phase_ == INITIAL_ATTEMPT) { | |
389 if (rv != net::OK) { | |
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); | |
395 return; | |
396 } | |
397 } else if (creation_phase_ == DOOM_EXISTING) { | |
398 creation_phase_ = SECOND_ATTEMPT; | |
399 AppCacheDiskCacheInterface::Entry** entry_ptr = | |
400 new AppCacheDiskCacheInterface::Entry*; | |
401 create_callback_ = | |
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); | |
407 return; | |
408 } | |
409 | |
410 if (!create_callback_.is_null()) { | |
411 if (rv == net::OK) | |
412 entry_ = *entry; | |
413 | |
414 create_callback_.Reset(); | |
415 } | |
416 | |
417 if (info_buffer_.get()) | |
418 ContinueWriteInfo(); | |
419 else | |
420 ContinueWriteData(); | |
421 } | |
422 | |
423 } // namespace appcache | |
OLD | NEW |