| 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 |