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