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 17 namespace 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. 39 struct DiskCacheBasedQuicServerInfo::CacheOperationDataShim { 40 CacheOperationDataShim() : backend(NULL), entry(NULL) {} 41 42 disk_cache::Backend* backend; 43 disk_cache::Entry* entry; 44 }; 45 46 DiskCacheBasedQuicServerInfo::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 65 void DiskCacheBasedQuicServerInfo::Start() { 66 DCHECK(CalledOnValidThread()); 67 DCHECK_EQ(GET_BACKEND, state_); 68 DoLoop(OK); 69 } 70 71 int 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 90 bool DiskCacheBasedQuicServerInfo::IsDataReady() { 91 return ready_; 92 } 93 94 bool 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 100 void 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 116 DiskCacheBasedQuicServerInfo::~DiskCacheBasedQuicServerInfo() { 117 DCHECK(user_callback_.is_null()); 118 if (entry_) 119 entry_->Close(); 120 } 121 122 std::string DiskCacheBasedQuicServerInfo::key() const { 123 return "quicserverinfo:" + server_id_.ToString(); 124 } 125 126 void 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 137 int 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 185 int 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 195 int 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 207 int 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 215 int DiskCacheBasedQuicServerInfo::DoWriteComplete(int rv) { 216 // Keep the entry open for future writes. 217 new_data_.clear(); 218 state_ = NONE; 219 return OK; 220 } 221 222 int 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 233 int DiskCacheBasedQuicServerInfo::DoGetBackend() { 234 state_ = GET_BACKEND_COMPLETE; 235 return http_cache_->GetBackend(&data_shim_->backend, io_callback_); 236 } 237 238 int DiskCacheBasedQuicServerInfo::DoOpen() { 239 state_ = OPEN_COMPLETE; 240 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); 241 } 242 243 int 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 256 int 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 269 int 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 281 int 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 294 int 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