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 weak_factory_(this), 51 data_shim_(new CacheOperationDataShim()), 52 io_callback_( 53 base::Bind(&DiskCacheBasedQuicServerInfo::OnIOComplete, 54 weak_factory_.GetWeakPtr(), 55 base::Owned(data_shim_))), // Ownership assigned. 56 state_(GET_BACKEND), 57 ready_(false), 58 found_entry_(false), 59 server_id_(server_id), 60 http_cache_(http_cache), 61 backend_(NULL), 62 entry_(NULL) { 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 state_ = SET_DONE; 217 return OK; 218 } 219 220 int DiskCacheBasedQuicServerInfo::DoCreateOrOpenComplete(int rv) { 221 if (rv != OK) { 222 state_ = SET_DONE; 223 } else { 224 entry_ = data_shim_->entry; 225 state_ = WRITE; 226 } 227 return OK; 228 } 229 230 int DiskCacheBasedQuicServerInfo::DoGetBackend() { 231 state_ = GET_BACKEND_COMPLETE; 232 return http_cache_->GetBackend(&data_shim_->backend, io_callback_); 233 } 234 235 int DiskCacheBasedQuicServerInfo::DoOpen() { 236 state_ = OPEN_COMPLETE; 237 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); 238 } 239 240 int DiskCacheBasedQuicServerInfo::DoRead() { 241 const int32 size = entry_->GetDataSize(0 /* index */); 242 if (!size) { 243 state_ = WAIT_FOR_DATA_READY_DONE; 244 return OK; 245 } 246 247 read_buffer_ = new IOBuffer(size); 248 state_ = READ_COMPLETE; 249 return entry_->ReadData( 250 0 /* index */, 0 /* offset */, read_buffer_, size, io_callback_); 251 } 252 253 int DiskCacheBasedQuicServerInfo::DoWrite() { 254 write_buffer_ = new IOBuffer(new_data_.size()); 255 memcpy(write_buffer_->data(), new_data_.data(), new_data_.size()); 256 state_ = WRITE_COMPLETE; 257 258 return entry_->WriteData( 259 0 /* index */, 0 /* offset */, write_buffer_, new_data_.size(), 260 io_callback_, true /* truncate */); 261 } 262 263 int DiskCacheBasedQuicServerInfo::DoCreateOrOpen() { 264 DCHECK(entry_ == NULL); 265 state_ = CREATE_OR_OPEN_COMPLETE; 266 if (found_entry_) { 267 return backend_->OpenEntry(key(), &data_shim_->entry, io_callback_); 268 } 269 270 return backend_->CreateEntry(key(), &data_shim_->entry, io_callback_); 271 } 272 273 int DiskCacheBasedQuicServerInfo::DoWaitForDataReadyDone() { 274 DCHECK(!ready_); 275 state_ = NONE; 276 ready_ = true; 277 // We close the entry because, if we shutdown before ::Persist is called, 278 // then we might leak a cache reference, which causes a DCHECK on shutdown. 279 if (entry_) 280 entry_->Close(); 281 entry_ = NULL; 282 Parse(data_); 283 return OK; 284 } 285 286 int DiskCacheBasedQuicServerInfo::DoSetDone() { 287 if (entry_) 288 entry_->Close(); 289 entry_ = NULL; 290 new_data_.clear(); 291 state_ = NONE; 292 return OK; 293 } 294 295 } // namespace net 296