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 |