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/logging.h" 10#include "net/base/completion_callback.h" 11#include "net/base/io_buffer.h" 12#include "net/base/net_errors.h" 13#include "net/http/http_cache.h" 14#include "net/http/http_network_session.h" 15#include "net/quic/quic_server_id.h" 16 17namespace net { 18 19// Some APIs inside disk_cache take a handle that the caller must keep alive 20// until the API has finished its asynchronous execution. 21// 22// Unfortunately, DiskCacheBasedQuicServerInfo may be deleted before the 23// operation completes causing a use-after-free. 24// 25// This data shim struct is meant to provide a location for the disk_cache 26// APIs to write into even if the originating DiskCacheBasedQuicServerInfo 27// object has been deleted. The lifetime for instances of this struct 28// should be bound to the CompletionCallback that is passed to the disk_cache 29// API. We do this by binding an instance of this struct to an unused 30// parameter for OnIOComplete() using base::Owned(). 31// 32// This is a hack. A better fix is to make it so that the disk_cache APIs 33// take a Callback to a mutator for setting the output value rather than 34// writing into a raw handle. Then the caller can just pass in a Callback 35// bound to WeakPtr for itself. This callback would correctly "no-op" itself 36// when the DiskCacheBasedQuicServerInfo object is deleted. 37// 38// TODO(ajwong): Change disk_cache's API to return results via Callback. 39struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim { 40 CacheOperationDataShim() : backend(NULL), entry(NULL) {} 41 42 disk_cache::Backend* backend; 43 disk_cache::Entry* entry; 44}; 45 46DiskCacheBasedQuicServerInfo::DiskCacheBasedQuicServerInfo( 47 const QuicServerId& server_id, 48 HttpCache* http_cache) 49 : QuicServerInfo(server_id), 50 data_shim_(new CacheOperationDataShim()), 51 state_(GET_BACKEND), 52 ready_(false), 53 found_entry_(false), 54 server_id_(server_id), 55 http_cache_(http_cache), 56 backend_(NULL), 57 entry_(NULL), 58 weak_factory_(this) { 59 io_callback_ = 60 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete, 61 weak_factory_.GetWeakPtr(), 62 base::Owned(data_shim_)); // Ownership assigned. 63} 64 65void DiskCacheBasedQuicServerInfo::Start() { 66 DCHECK(CalledOnValidThread()); 67 DCHECK_EQ(GET_BACKEND, state_); 68 DoLoop(OK); 69} 70 71int DiskCacheBasedQuicServerInfo::WaitForDataReady( 72 const CompletionCallback& callback) { 73 DCHECK(CalledOnValidThread()); 74 DCHECK_NE(GET_BACKEND, state_); 75 76 if (ready_) 77 return OK; 78 79 if (!callback.is_null()) { 80 // Prevent a new callback for WaitForDataReady overwriting an existing 81 // pending callback (|user_callback_|). 82 if (!user_callback_.is_null()) 83 return ERR_INVALID_ARGUMENT; 84 user_callback_ = callback; 85 } 86 87 return ERR_IO_PENDING; 88} 89 90bool DiskCacheBasedQuicServerInfo::IsDataReady() { 91 return ready_; 92} 93 94bool DiskCacheBasedQuicServerInfo::IsReadyToPersist() { 95 // The data can be persisted if it has been loaded from the disk cache 96 // and there are no pending writes. 97 return ready_ && new_data_.empty(); 98} 99 100void DiskCacheBasedQuicServerInfo::Persist() { 101 DCHECK(CalledOnValidThread()); 102 DCHECK_NE(GET_BACKEND, state_); 103 104 DCHECK(new_data_.empty()); 105 CHECK(ready_); 106 DCHECK(user_callback_.is_null()); 107 new_data_ = Serialize(); 108 109 if (!backend_) 110 return; 111 112 state_ = CREATE_OR_OPEN; 113 DoLoop(OK); 114} 115 116DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() { 117 DCHECK(user_callback_.is_null()); 118 if (entry_) 119 entry_->Close(); 120} 121 122std::string DiskCacheBasedQuicServerInfo::key() const { 123 return "quicserverinfo:" + server_id_.ToString(); 124} 125 126void DiskCacheBasedQuicServerInfo::OnIOComplete(CacheOperationDataShim* unused, 127 int rv) { 128 DCHECK_NE(NONE, state_); 129 rv = DoLoop(rv); 130 if (rv != ERR_IO_PENDING && !user_callback_.is_null()) { 131 CompletionCallback callback = user_callback_; 132 user_callback_.Reset(); 133 callback.Run(rv); 134 } 135} 136 137int DiskCacheBasedQuicServerInfo::DoLoop(int rv) { 138 do { 139 switch (state_) { 140 case GET_BACKEND: 141 rv = DoGetBackend(); 142 break; 143 case GET_BACKEND_COMPLETE: 144 rv = DoGetBackendComplete(rv); 145 break; 146 case OPEN: 147 rv = DoOpen(); 148 break; 149 case OPEN_COMPLETE: 150 rv = DoOpenComplete(rv); 151 break; 152 case READ: 153 rv = DoRead(); 154 break; 155 case READ_COMPLETE: 156 rv = DoReadComplete(rv); 157 break; 158 case WAIT_FOR_DATA_READY_DONE: 159 rv = DoWaitForDataReadyDone(); 160 break; 161 case CREATE_OR_OPEN: 162 rv = DoCreateOrOpen(); 163 break; 164 case CREATE_OR_OPEN_COMPLETE: 165 rv = DoCreateOrOpenComplete(rv); 166 break; 167 case WRITE: 168 rv = DoWrite(); 169 break; 170 case WRITE_COMPLETE: 171 rv = DoWriteComplete(rv); 172 break; 173 case SET_DONE: 174 rv = DoSetDone(); 175 break; 176 default: 177 rv = OK; 178 NOTREACHED(); 179 } 180 } while (rv != ERR_IO_PENDING && state_ != NONE); 181 182 return rv; 183} 184 185int DiskCacheBasedQuicServerInfo::DoGetBackendComplete(int rv) { 186 if (rv == OK) { 187 backend_ = data_shim_->backend; 188 state_ = OPEN; 189 } else { 190 state_ = WAIT_FOR_DATA_READY_DONE; 191 } 192 return OK; 193} 194 195int DiskCacheBasedQuicServerInfo::DoOpenComplete(int rv) { 196 if (rv == OK) { 197 entry_ = data_shim_->entry; 198 state_ = READ; 199 found_entry_ = true; 200 } else { 201 state_ = WAIT_FOR_DATA_READY_DONE; 202 } 203 204 return OK; 205} 206 207int DiskCacheBasedQuicServerInfo::DoReadComplete(int rv) { 208 if (rv > 0) 209 data_.assign(read_buffer_->data(), rv); 210 211 state_ = WAIT_FOR_DATA_READY_DONE; 212 return OK; 213} 214 215int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) { 216 // Keep the entry open for future writes. 217 new_data_.clear(); 218 state_ = NONE; 219 return OK; 220} 221 222int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) { 223 if (rv != OK) { 224 state_ = SET_DONE; 225 } else { 226 if (!entry_) 227 entry_ = data_shim_->entry; 228 state_ = WRITE; 229 } 230 return OK; 231} 232 233int DiskCacheBasedQuicServerInfo::DoGetBackend() { 234 state_ = GET_BACKEND_COMPLETE; 235 return http_cache_->GetBackend(&data_shim_->backend, io_callback_); 236} 237 238int DiskCacheBasedQuicServerInfo::DoOpen() { 239 state_ = OPEN_COMPLETE; 240 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); 241} 242 243int DiskCacheBasedQuicServerInfo::DoRead() { 244 const int32 size = entry_->GetDataSize(0 /* index */); 245 if (!size) { 246 state_ = WAIT_FOR_DATA_READY_DONE; 247 return OK; 248 } 249 250 read_buffer_ = new IOBuffer(size); 251 state_ = READ_COMPLETE; 252 return entry_->ReadData( 253 0 /* index */, 0 /* offset */, read_buffer_.get(), size, io_callback_); 254} 255 256int DiskCacheBasedQuicServerInfo::DoWrite() { 257 write_buffer_ = new IOBuffer(new_data_.size()); 258 memcpy(write_buffer_->data(), new_data_.data(), new_data_.size()); 259 state_ = WRITE_COMPLETE; 260 261 return entry_->WriteData(0 /* index */, 262 0 /* offset */, 263 write_buffer_.get(), 264 new_data_.size(), 265 io_callback_, 266 true /* truncate */); 267} 268 269int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() { 270 state_ = CREATE_OR_OPEN_COMPLETE; 271 if (entry_) 272 return OK; 273 274 if (found_entry_) { 275 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); 276 } 277 278 return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_); 279} 280 281int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() { 282 DCHECK(!ready_); 283 state_ = NONE; 284 ready_ = true; 285 // We close the entry because, if we shutdown before ::Persist is called, 286 // then we might leak a cache reference, which causes a DCHECK on shutdown. 287 if (entry_) 288 entry_->Close(); 289 entry_ = NULL; 290 Parse(data_); 291 return OK; 292} 293 294int DiskCacheBasedQuicServerInfo::DoSetDone() { 295 if (entry_) 296 entry_->Close(); 297 entry_ = NULL; 298 new_data_.clear(); 299 state_ = NONE; 300 return OK; 301} 302 303} // namespace net 304