| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 // Copyright 2014 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 "net/http/disk_cache_based_quic_server_info.h" |  | 
| 6 |  | 
| 7 #include "base/bind.h" |  | 
| 8 #include "base/callback.h" |  | 
| 9 #include "base/callback_helpers.h" |  | 
| 10 #include "base/logging.h" |  | 
| 11 #include "base/metrics/histogram_macros.h" |  | 
| 12 #include "base/stl_util.h" |  | 
| 13 #include "base/trace_event/memory_usage_estimator.h" |  | 
| 14 #include "net/base/completion_callback.h" |  | 
| 15 #include "net/base/io_buffer.h" |  | 
| 16 #include "net/base/net_errors.h" |  | 
| 17 #include "net/http/http_cache.h" |  | 
| 18 #include "net/http/http_network_session.h" |  | 
| 19 #include "net/quic/core/quic_server_id.h" |  | 
| 20 |  | 
| 21 namespace net { |  | 
| 22 |  | 
| 23 // Some APIs inside disk_cache take a handle that the caller must keep alive |  | 
| 24 // until the API has finished its asynchronous execution. |  | 
| 25 // |  | 
| 26 // Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the |  | 
| 27 // operation completes causing a use-after-free. |  | 
| 28 // |  | 
| 29 // This data shim struct is meant to provide a location for the disk_cache |  | 
| 30 // APIs to write into even if the originating DiskCacheBasedQuicServerInfo |  | 
| 31 // object has been deleted.  The lifetime for instances of this struct |  | 
| 32 // should be bound to the CompletionCallback that is passed to the disk_cache |  | 
| 33 // API.  We do this by binding an instance of this struct to an unused |  | 
| 34 // parameter for OnIOComplete() using base::Owned(). |  | 
| 35 // |  | 
| 36 // This is a hack. A better fix is to make it so that the disk_cache APIs |  | 
| 37 // take a Callback to a mutator for setting the output value rather than |  | 
| 38 // writing into a raw handle. Then the caller can just pass in a Callback |  | 
| 39 // bound to WeakPtr for itself. This callback would correctly "no-op" itself |  | 
| 40 // when the DiskCacheBasedQuicServerInfo object is deleted. |  | 
| 41 // |  | 
| 42 // TODO(ajwong): Change disk_cache's API to return results via Callback. |  | 
| 43 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim { |  | 
| 44   CacheOperationDataShim() : backend(NULL), entry(NULL) {} |  | 
| 45 |  | 
| 46   disk_cache::Backend* backend; |  | 
| 47   disk_cache::Entry* entry; |  | 
| 48 }; |  | 
| 49 |  | 
| 50 DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo( |  | 
| 51     const QuicServerId& server_id, |  | 
| 52     HttpCache* http_cache) |  | 
| 53     : QuicServerInfo(server_id), |  | 
| 54       data_shim_(new CacheOperationDataShim()), |  | 
| 55       state_(GET_BACKEND), |  | 
| 56       ready_(false), |  | 
| 57       found_entry_(false), |  | 
| 58       server_id_(server_id), |  | 
| 59       http_cache_(http_cache), |  | 
| 60       backend_(NULL), |  | 
| 61       entry_(NULL), |  | 
| 62       last_failure_(NO_FAILURE), |  | 
| 63       weak_factory_(this) { |  | 
| 64       io_callback_ = |  | 
| 65           base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete, |  | 
| 66                      weak_factory_.GetWeakPtr(), |  | 
| 67                      base::Owned(data_shim_));  // Ownership assigned. |  | 
| 68 } |  | 
| 69 |  | 
| 70 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() { |  | 
| 71   DCHECK(wait_for_ready_callback_.is_null()); |  | 
| 72   if (entry_) |  | 
| 73     entry_->Close(); |  | 
| 74 } |  | 
| 75 |  | 
| 76 void DiskCacheBasedQuicServerInfo::Start() { |  | 
| 77   DCHECK(CalledOnValidThread()); |  | 
| 78   DCHECK_EQ(GET_BACKEND, state_); |  | 
| 79   DCHECK_EQ(last_failure_, NO_FAILURE); |  | 
| 80   RecordQuicServerInfoStatus(QUIC_SERVER_INFO_START); |  | 
| 81   load_start_time_ = base::TimeTicks::Now(); |  | 
| 82   DoLoop(OK); |  | 
| 83 } |  | 
| 84 |  | 
| 85 int DiskCacheBasedQuicServerInfo::WaitForDataReady( |  | 
| 86     const CompletionCallback& callback) { |  | 
| 87   DCHECK(CalledOnValidThread()); |  | 
| 88   DCHECK_NE(GET_BACKEND, state_); |  | 
| 89   wait_for_data_start_time_ = base::TimeTicks::Now(); |  | 
| 90 |  | 
| 91   RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY); |  | 
| 92   if (ready_) { |  | 
| 93     wait_for_data_end_time_ = base::TimeTicks::Now(); |  | 
| 94     RecordLastFailure(); |  | 
| 95     return OK; |  | 
| 96   } |  | 
| 97 |  | 
| 98   if (!callback.is_null()) { |  | 
| 99     // Prevent a new callback for WaitForDataReady overwriting an existing |  | 
| 100     // pending callback (|wait_for_ready_callback_|). |  | 
| 101     if (!wait_for_ready_callback_.is_null()) { |  | 
| 102       RecordQuicServerInfoFailure(WAIT_FOR_DATA_READY_INVALID_ARGUMENT_FAILURE); |  | 
| 103       return ERR_INVALID_ARGUMENT; |  | 
| 104     } |  | 
| 105     wait_for_ready_callback_ = callback; |  | 
| 106   } |  | 
| 107 |  | 
| 108   return ERR_IO_PENDING; |  | 
| 109 } |  | 
| 110 |  | 
| 111 void DiskCacheBasedQuicServerInfo::ResetWaitForDataReadyCallback() { |  | 
| 112   DCHECK(CalledOnValidThread()); |  | 
| 113   wait_for_ready_callback_.Reset(); |  | 
| 114 } |  | 
| 115 |  | 
| 116 void DiskCacheBasedQuicServerInfo::CancelWaitForDataReadyCallback() { |  | 
| 117   DCHECK(CalledOnValidThread()); |  | 
| 118 |  | 
| 119   RecordQuicServerInfoStatus(QUIC_SERVER_INFO_WAIT_FOR_DATA_READY_CANCEL); |  | 
| 120   if (!wait_for_ready_callback_.is_null()) { |  | 
| 121     RecordLastFailure(); |  | 
| 122     wait_for_ready_callback_.Reset(); |  | 
| 123   } |  | 
| 124 } |  | 
| 125 |  | 
| 126 bool DiskCacheBasedQuicServerInfo::IsDataReady() { |  | 
| 127   return ready_; |  | 
| 128 } |  | 
| 129 |  | 
| 130 bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() { |  | 
| 131   // The data can be persisted if it has been loaded from the disk cache |  | 
| 132   // and there are no pending writes. |  | 
| 133   RecordQuicServerInfoStatus(QUIC_SERVER_INFO_READY_TO_PERSIST); |  | 
| 134   if (ready_ && new_data_.empty()) |  | 
| 135     return true; |  | 
| 136   RecordQuicServerInfoFailure(READY_TO_PERSIST_FAILURE); |  | 
| 137   return false; |  | 
| 138 } |  | 
| 139 |  | 
| 140 void DiskCacheBasedQuicServerInfo::Persist() { |  | 
| 141   DCHECK(CalledOnValidThread()); |  | 
| 142   if (!IsReadyToPersist()) { |  | 
| 143     // Handle updates while a write is pending or if we haven't loaded from disk |  | 
| 144     // cache. Save the data to be written into a temporary buffer and then |  | 
| 145     // persist that data when we are ready to persist. |  | 
| 146     pending_write_data_ = Serialize(); |  | 
| 147     return; |  | 
| 148   } |  | 
| 149   PersistInternal(); |  | 
| 150 } |  | 
| 151 |  | 
| 152 void DiskCacheBasedQuicServerInfo::PersistInternal() { |  | 
| 153   DCHECK(CalledOnValidThread()); |  | 
| 154   DCHECK_NE(GET_BACKEND, state_); |  | 
| 155   DCHECK(new_data_.empty()); |  | 
| 156   CHECK(ready_); |  | 
| 157   DCHECK(wait_for_ready_callback_.is_null()); |  | 
| 158 |  | 
| 159   if (pending_write_data_.empty()) { |  | 
| 160     new_data_ = Serialize(); |  | 
| 161   } else { |  | 
| 162     new_data_ = pending_write_data_; |  | 
| 163     base::STLClearObject(&pending_write_data_); |  | 
| 164   } |  | 
| 165 |  | 
| 166   RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PERSIST); |  | 
| 167   if (!backend_) { |  | 
| 168     RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE); |  | 
| 169     return; |  | 
| 170   } |  | 
| 171 |  | 
| 172   state_ = CREATE_OR_OPEN; |  | 
| 173   DoLoop(OK); |  | 
| 174 } |  | 
| 175 |  | 
| 176 void DiskCacheBasedQuicServerInfo::OnExternalCacheHit() { |  | 
| 177   DCHECK(CalledOnValidThread()); |  | 
| 178   DCHECK_NE(GET_BACKEND, state_); |  | 
| 179 |  | 
| 180   RecordQuicServerInfoStatus(QUIC_SERVER_INFO_EXTERNAL_CACHE_HIT); |  | 
| 181   if (!backend_) { |  | 
| 182     RecordQuicServerInfoFailure(PERSIST_NO_BACKEND_FAILURE); |  | 
| 183     return; |  | 
| 184   } |  | 
| 185 |  | 
| 186   backend_->OnExternalCacheHit(key()); |  | 
| 187 } |  | 
| 188 |  | 
| 189 size_t DiskCacheBasedQuicServerInfo::EstimateMemoryUsage() const { |  | 
| 190   return base::trace_event::EstimateMemoryUsage(new_data_) + |  | 
| 191          base::trace_event::EstimateMemoryUsage(pending_write_data_) + |  | 
| 192          base::trace_event::EstimateMemoryUsage(server_id_) + |  | 
| 193          (read_buffer_ == nullptr ? 0 : read_buffer_->size()) + |  | 
| 194          (write_buffer_ == nullptr ? 0 : write_buffer_->size()) + |  | 
| 195          base::trace_event::EstimateMemoryUsage(data_); |  | 
| 196 } |  | 
| 197 |  | 
| 198 std::string DiskCacheBasedQuicServerInfo::key() const { |  | 
| 199   return "quicserverinfo:" + server_id_.ToString(); |  | 
| 200 } |  | 
| 201 |  | 
| 202 void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused, |  | 
| 203                                                 int rv) { |  | 
| 204   DCHECK_NE(NONE, state_); |  | 
| 205   rv = DoLoop(rv); |  | 
| 206   if (rv == ERR_IO_PENDING) |  | 
| 207     return; |  | 
| 208 |  | 
| 209   base::WeakPtr<DiskCacheBasedQuicServerInfo> weak_this = |  | 
| 210       weak_factory_.GetWeakPtr(); |  | 
| 211 |  | 
| 212   if (!wait_for_ready_callback_.is_null()) { |  | 
| 213     wait_for_data_end_time_ = base::TimeTicks::Now(); |  | 
| 214     RecordLastFailure(); |  | 
| 215     base::ResetAndReturn(&wait_for_ready_callback_).Run(rv); |  | 
| 216   } |  | 
| 217   // |wait_for_ready_callback_| could delete the object if there is an error. |  | 
| 218   // Check if |weak_this| still exists before accessing it. |  | 
| 219   if (weak_this.get() && ready_ && !pending_write_data_.empty()) { |  | 
| 220     DCHECK_EQ(NONE, state_); |  | 
| 221     PersistInternal(); |  | 
| 222   } |  | 
| 223 } |  | 
| 224 |  | 
| 225 int DiskCacheBasedQuicServerInfo::DoLoop(int rv) { |  | 
| 226   do { |  | 
| 227     switch (state_) { |  | 
| 228       case GET_BACKEND: |  | 
| 229         rv = DoGetBackend(); |  | 
| 230         break; |  | 
| 231       case GET_BACKEND_COMPLETE: |  | 
| 232         rv = DoGetBackendComplete(rv); |  | 
| 233         break; |  | 
| 234       case OPEN: |  | 
| 235         rv = DoOpen(); |  | 
| 236         break; |  | 
| 237       case OPEN_COMPLETE: |  | 
| 238         rv = DoOpenComplete(rv); |  | 
| 239         break; |  | 
| 240       case READ: |  | 
| 241         rv = DoRead(); |  | 
| 242         break; |  | 
| 243       case READ_COMPLETE: |  | 
| 244         rv = DoReadComplete(rv); |  | 
| 245         break; |  | 
| 246       case WAIT_FOR_DATA_READY_DONE: |  | 
| 247         rv = DoWaitForDataReadyDone(); |  | 
| 248         break; |  | 
| 249       case CREATE_OR_OPEN: |  | 
| 250         rv = DoCreateOrOpen(); |  | 
| 251         break; |  | 
| 252       case CREATE_OR_OPEN_COMPLETE: |  | 
| 253         rv = DoCreateOrOpenComplete(rv); |  | 
| 254         break; |  | 
| 255       case WRITE: |  | 
| 256         rv = DoWrite(); |  | 
| 257         break; |  | 
| 258       case WRITE_COMPLETE: |  | 
| 259         rv = DoWriteComplete(rv); |  | 
| 260         break; |  | 
| 261       case SET_DONE: |  | 
| 262         rv = DoSetDone(); |  | 
| 263         break; |  | 
| 264       default: |  | 
| 265         rv = OK; |  | 
| 266         NOTREACHED(); |  | 
| 267     } |  | 
| 268   } while (rv != ERR_IO_PENDING && state_ != NONE); |  | 
| 269 |  | 
| 270   return rv; |  | 
| 271 } |  | 
| 272 |  | 
| 273 int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) { |  | 
| 274   if (rv == OK) { |  | 
| 275     backend_ = data_shim_->backend; |  | 
| 276     state_ = OPEN; |  | 
| 277   } else { |  | 
| 278     RecordQuicServerInfoFailure(GET_BACKEND_FAILURE); |  | 
| 279     state_ = WAIT_FOR_DATA_READY_DONE; |  | 
| 280   } |  | 
| 281   return OK; |  | 
| 282 } |  | 
| 283 |  | 
| 284 int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) { |  | 
| 285   if (rv == OK) { |  | 
| 286     entry_ = data_shim_->entry; |  | 
| 287     state_ = READ; |  | 
| 288     found_entry_ = true; |  | 
| 289   } else { |  | 
| 290     RecordQuicServerInfoFailure(OPEN_FAILURE); |  | 
| 291     state_ = WAIT_FOR_DATA_READY_DONE; |  | 
| 292   } |  | 
| 293 |  | 
| 294   return OK; |  | 
| 295 } |  | 
| 296 |  | 
| 297 int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) { |  | 
| 298   if (rv > 0) |  | 
| 299     data_.assign(read_buffer_->data(), rv); |  | 
| 300   else if (rv < 0) |  | 
| 301     RecordQuicServerInfoFailure(READ_FAILURE); |  | 
| 302 |  | 
| 303   read_buffer_ = nullptr; |  | 
| 304   state_ = WAIT_FOR_DATA_READY_DONE; |  | 
| 305   return OK; |  | 
| 306 } |  | 
| 307 |  | 
| 308 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) { |  | 
| 309   if (rv < 0) |  | 
| 310     RecordQuicServerInfoFailure(WRITE_FAILURE); |  | 
| 311   write_buffer_ = nullptr; |  | 
| 312   state_ = SET_DONE; |  | 
| 313   return OK; |  | 
| 314 } |  | 
| 315 |  | 
| 316 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) { |  | 
| 317   if (rv != OK) { |  | 
| 318     RecordQuicServerInfoFailure(CREATE_OR_OPEN_FAILURE); |  | 
| 319     state_ = SET_DONE; |  | 
| 320   } else { |  | 
| 321     if (!entry_) { |  | 
| 322       entry_ = data_shim_->entry; |  | 
| 323       found_entry_ = true; |  | 
| 324     } |  | 
| 325     DCHECK(entry_); |  | 
| 326     state_ = WRITE; |  | 
| 327   } |  | 
| 328   return OK; |  | 
| 329 } |  | 
| 330 |  | 
| 331 int DiskCacheBasedQuicServerInfo::DoGetBackend() { |  | 
| 332   state_ = GET_BACKEND_COMPLETE; |  | 
| 333   return http_cache_->GetBackend(&data_shim_->backend, io_callback_); |  | 
| 334 } |  | 
| 335 |  | 
| 336 int DiskCacheBasedQuicServerInfo::DoOpen() { |  | 
| 337   state_ = OPEN_COMPLETE; |  | 
| 338   return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); |  | 
| 339 } |  | 
| 340 |  | 
| 341 int DiskCacheBasedQuicServerInfo::DoRead() { |  | 
| 342   const int32_t size = entry_->GetDataSize(0 /* index */); |  | 
| 343   if (!size) { |  | 
| 344     state_ = WAIT_FOR_DATA_READY_DONE; |  | 
| 345     return OK; |  | 
| 346   } |  | 
| 347 |  | 
| 348   read_buffer_ = new IOBufferWithSize(size); |  | 
| 349   state_ = READ_COMPLETE; |  | 
| 350   return entry_->ReadData( |  | 
| 351       0 /* index */, 0 /* offset */, read_buffer_.get(), size, io_callback_); |  | 
| 352 } |  | 
| 353 |  | 
| 354 int DiskCacheBasedQuicServerInfo::DoWrite() { |  | 
| 355   write_buffer_ = new IOBufferWithSize(new_data_.size()); |  | 
| 356   memcpy(write_buffer_->data(), new_data_.data(), new_data_.size()); |  | 
| 357   state_ = WRITE_COMPLETE; |  | 
| 358 |  | 
| 359   return entry_->WriteData(0 /* index */, |  | 
| 360                            0 /* offset */, |  | 
| 361                            write_buffer_.get(), |  | 
| 362                            new_data_.size(), |  | 
| 363                            io_callback_, |  | 
| 364                            true /* truncate */); |  | 
| 365 } |  | 
| 366 |  | 
| 367 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() { |  | 
| 368   state_ = CREATE_OR_OPEN_COMPLETE; |  | 
| 369   if (entry_) |  | 
| 370     return OK; |  | 
| 371 |  | 
| 372   if (found_entry_) { |  | 
| 373     return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); |  | 
| 374   } |  | 
| 375 |  | 
| 376   return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_); |  | 
| 377 } |  | 
| 378 |  | 
| 379 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() { |  | 
| 380   DCHECK(!ready_); |  | 
| 381   state_ = NONE; |  | 
| 382   ready_ = true; |  | 
| 383   // We close the entry because, if we shutdown before ::Persist is called, |  | 
| 384   // then we might leak a cache reference, which causes a DCHECK on shutdown. |  | 
| 385   if (entry_) |  | 
| 386     entry_->Close(); |  | 
| 387   entry_ = NULL; |  | 
| 388 |  | 
| 389   RecordQuicServerInfoStatus(QUIC_SERVER_INFO_PARSE); |  | 
| 390   if (!Parse(data_)) { |  | 
| 391     if (data_.empty()) |  | 
| 392       RecordQuicServerInfoFailure(PARSE_NO_DATA_FAILURE); |  | 
| 393     else |  | 
| 394       RecordQuicServerInfoFailure(PARSE_FAILURE); |  | 
| 395   } |  | 
| 396 |  | 
| 397   UMA_HISTOGRAM_TIMES("Net.QuicServerInfo.DiskCacheLoadTime", |  | 
| 398                       base::TimeTicks::Now() - load_start_time_); |  | 
| 399   return OK; |  | 
| 400 } |  | 
| 401 |  | 
| 402 int DiskCacheBasedQuicServerInfo::DoSetDone() { |  | 
| 403   if (entry_) |  | 
| 404     entry_->Close(); |  | 
| 405   entry_ = NULL; |  | 
| 406   base::STLClearObject(&new_data_); |  | 
| 407   state_ = NONE; |  | 
| 408   return OK; |  | 
| 409 } |  | 
| 410 |  | 
| 411 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoStatus( |  | 
| 412     QuicServerInfoAPICall call) { |  | 
| 413   if (!backend_) { |  | 
| 414     UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.NoBackend", call, |  | 
| 415                               QUIC_SERVER_INFO_NUM_OF_API_CALLS); |  | 
| 416   } else if (backend_->GetCacheType() == MEMORY_CACHE) { |  | 
| 417     UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.MemoryCache", call, |  | 
| 418                               QUIC_SERVER_INFO_NUM_OF_API_CALLS); |  | 
| 419   } else { |  | 
| 420     UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.APICall.DiskCache", call, |  | 
| 421                               QUIC_SERVER_INFO_NUM_OF_API_CALLS); |  | 
| 422   } |  | 
| 423 } |  | 
| 424 |  | 
| 425 void DiskCacheBasedQuicServerInfo::RecordLastFailure() { |  | 
| 426   if (last_failure_ != NO_FAILURE) { |  | 
| 427     UMA_HISTOGRAM_ENUMERATION( |  | 
| 428         "Net.QuicDiskCache.FailureReason.WaitForDataReady", |  | 
| 429         last_failure_, NUM_OF_FAILURES); |  | 
| 430   } |  | 
| 431   last_failure_ = NO_FAILURE; |  | 
| 432 } |  | 
| 433 |  | 
| 434 void DiskCacheBasedQuicServerInfo::RecordQuicServerInfoFailure( |  | 
| 435     FailureReason failure) { |  | 
| 436   last_failure_ = failure; |  | 
| 437 |  | 
| 438   if (!backend_) { |  | 
| 439     UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.NoBackend", |  | 
| 440                               failure, NUM_OF_FAILURES); |  | 
| 441   } else if (backend_->GetCacheType() == MEMORY_CACHE) { |  | 
| 442     UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.MemoryCache", |  | 
| 443                               failure, NUM_OF_FAILURES); |  | 
| 444   } else { |  | 
| 445     UMA_HISTOGRAM_ENUMERATION("Net.QuicDiskCache.FailureReason.DiskCache", |  | 
| 446                               failure, NUM_OF_FAILURES); |  | 
| 447   } |  | 
| 448 } |  | 
| 449 |  | 
| 450 }  // namespace net |  | 
| OLD | NEW | 
|---|