Home | History | Annotate | Download | only in http
      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