Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2012 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/mock_http_cache.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/message_loop/message_loop.h"
      9 #include "net/base/completion_callback.h"
     10 #include "net/base/net_errors.h"
     11 #include "testing/gtest/include/gtest/gtest.h"
     12 
     13 namespace {
     14 
     15 // We can override the test mode for a given operation by setting this global
     16 // variable.
     17 int g_test_mode = 0;
     18 
     19 int GetTestModeForEntry(const std::string& key) {
     20   // 'key' is prefixed with an identifier if it corresponds to a cached POST.
     21   // Skip past that to locate the actual URL.
     22   //
     23   // TODO(darin): It breaks the abstraction a bit that we assume 'key' is an
     24   // URL corresponding to a registered MockTransaction.  It would be good to
     25   // have another way to access the test_mode.
     26   GURL url;
     27   if (isdigit(key[0])) {
     28     size_t slash = key.find('/');
     29     DCHECK(slash != std::string::npos);
     30     url = GURL(key.substr(slash + 1));
     31   } else {
     32     url = GURL(key);
     33   }
     34   const MockTransaction* t = FindMockTransaction(url);
     35   DCHECK(t);
     36   return t->test_mode;
     37 }
     38 
     39 void CallbackForwader(const net::CompletionCallback& callback, int result) {
     40   callback.Run(result);
     41 }
     42 
     43 }  // namespace
     44 
     45 //-----------------------------------------------------------------------------
     46 
     47 struct MockDiskEntry::CallbackInfo {
     48   scoped_refptr<MockDiskEntry> entry;
     49   net::CompletionCallback callback;
     50   int result;
     51 };
     52 
     53 MockDiskEntry::MockDiskEntry(const std::string& key)
     54     : key_(key), doomed_(false), sparse_(false),
     55       fail_requests_(false), fail_sparse_requests_(false), busy_(false),
     56       delayed_(false) {
     57   test_mode_ = GetTestModeForEntry(key);
     58 }
     59 
     60 void MockDiskEntry::Doom() {
     61   doomed_ = true;
     62 }
     63 
     64 void MockDiskEntry::Close() {
     65   Release();
     66 }
     67 
     68 std::string MockDiskEntry::GetKey() const {
     69   return key_;
     70 }
     71 
     72 base::Time MockDiskEntry::GetLastUsed() const {
     73   return base::Time::FromInternalValue(0);
     74 }
     75 
     76 base::Time MockDiskEntry::GetLastModified() const {
     77   return base::Time::FromInternalValue(0);
     78 }
     79 
     80 int32 MockDiskEntry::GetDataSize(int index) const {
     81   DCHECK(index >= 0 && index < kNumCacheEntryDataIndices);
     82   return static_cast<int32>(data_[index].size());
     83 }
     84 
     85 int MockDiskEntry::ReadData(
     86     int index, int offset, net::IOBuffer* buf, int buf_len,
     87     const net::CompletionCallback& callback) {
     88   DCHECK(index >= 0 && index < kNumCacheEntryDataIndices);
     89   DCHECK(!callback.is_null());
     90 
     91   if (fail_requests_)
     92     return net::ERR_CACHE_READ_FAILURE;
     93 
     94   if (offset < 0 || offset > static_cast<int>(data_[index].size()))
     95     return net::ERR_FAILED;
     96   if (static_cast<size_t>(offset) == data_[index].size())
     97     return 0;
     98 
     99   int num = std::min(buf_len, static_cast<int>(data_[index].size()) - offset);
    100   memcpy(buf->data(), &data_[index][offset], num);
    101 
    102   if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ)
    103     return num;
    104 
    105   CallbackLater(callback, num);
    106   return net::ERR_IO_PENDING;
    107 }
    108 
    109 int MockDiskEntry::WriteData(
    110     int index, int offset, net::IOBuffer* buf, int buf_len,
    111     const net::CompletionCallback& callback, bool truncate) {
    112   DCHECK(index >= 0 && index < kNumCacheEntryDataIndices);
    113   DCHECK(!callback.is_null());
    114   DCHECK(truncate);
    115 
    116   if (fail_requests_) {
    117     CallbackLater(callback, net::ERR_CACHE_READ_FAILURE);
    118     return net::ERR_IO_PENDING;
    119   }
    120 
    121   if (offset < 0 || offset > static_cast<int>(data_[index].size()))
    122     return net::ERR_FAILED;
    123 
    124   data_[index].resize(offset + buf_len);
    125   if (buf_len)
    126     memcpy(&data_[index][offset], buf->data(), buf_len);
    127 
    128   if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE)
    129     return buf_len;
    130 
    131   CallbackLater(callback, buf_len);
    132   return net::ERR_IO_PENDING;
    133 }
    134 
    135 int MockDiskEntry::ReadSparseData(int64 offset, net::IOBuffer* buf, int buf_len,
    136                                   const net::CompletionCallback& callback) {
    137   DCHECK(!callback.is_null());
    138   if (fail_sparse_requests_)
    139     return net::ERR_NOT_IMPLEMENTED;
    140   if (!sparse_ || busy_)
    141     return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
    142   if (offset < 0)
    143     return net::ERR_FAILED;
    144 
    145   if (fail_requests_)
    146     return net::ERR_CACHE_READ_FAILURE;
    147 
    148   DCHECK(offset < kint32max);
    149   int real_offset = static_cast<int>(offset);
    150   if (!buf_len)
    151     return 0;
    152 
    153   int num = std::min(static_cast<int>(data_[1].size()) - real_offset,
    154                      buf_len);
    155   memcpy(buf->data(), &data_[1][real_offset], num);
    156 
    157   if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ)
    158     return num;
    159 
    160   CallbackLater(callback, num);
    161   busy_ = true;
    162   delayed_ = false;
    163   return net::ERR_IO_PENDING;
    164 }
    165 
    166 int MockDiskEntry::WriteSparseData(int64 offset, net::IOBuffer* buf,
    167                                    int buf_len,
    168                                    const net::CompletionCallback& callback) {
    169   DCHECK(!callback.is_null());
    170   if (fail_sparse_requests_)
    171     return net::ERR_NOT_IMPLEMENTED;
    172   if (busy_)
    173     return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
    174   if (!sparse_) {
    175     if (data_[1].size())
    176       return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
    177     sparse_ = true;
    178   }
    179   if (offset < 0)
    180     return net::ERR_FAILED;
    181   if (!buf_len)
    182     return 0;
    183 
    184   if (fail_requests_)
    185     return net::ERR_CACHE_READ_FAILURE;
    186 
    187   DCHECK(offset < kint32max);
    188   int real_offset = static_cast<int>(offset);
    189 
    190   if (static_cast<int>(data_[1].size()) < real_offset + buf_len)
    191     data_[1].resize(real_offset + buf_len);
    192 
    193   memcpy(&data_[1][real_offset], buf->data(), buf_len);
    194   if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE)
    195     return buf_len;
    196 
    197   CallbackLater(callback, buf_len);
    198   return net::ERR_IO_PENDING;
    199 }
    200 
    201 int MockDiskEntry::GetAvailableRange(int64 offset, int len, int64* start,
    202                                      const net::CompletionCallback& callback) {
    203   DCHECK(!callback.is_null());
    204   if (!sparse_ || busy_)
    205     return net::ERR_CACHE_OPERATION_NOT_SUPPORTED;
    206   if (offset < 0)
    207     return net::ERR_FAILED;
    208 
    209   if (fail_requests_)
    210     return net::ERR_CACHE_READ_FAILURE;
    211 
    212   *start = offset;
    213   DCHECK(offset < kint32max);
    214   int real_offset = static_cast<int>(offset);
    215   if (static_cast<int>(data_[1].size()) < real_offset)
    216     return 0;
    217 
    218   int num = std::min(static_cast<int>(data_[1].size()) - real_offset, len);
    219   int count = 0;
    220   for (; num > 0; num--, real_offset++) {
    221     if (!count) {
    222       if (data_[1][real_offset]) {
    223         count++;
    224         *start = real_offset;
    225       }
    226     } else {
    227       if (!data_[1][real_offset])
    228         break;
    229       count++;
    230     }
    231   }
    232   if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_WRITE)
    233     return count;
    234 
    235   CallbackLater(callback, count);
    236   return net::ERR_IO_PENDING;
    237 }
    238 
    239 bool MockDiskEntry::CouldBeSparse() const {
    240   if (fail_sparse_requests_)
    241     return false;
    242   return sparse_;
    243 }
    244 
    245 void MockDiskEntry::CancelSparseIO() {
    246   cancel_ = true;
    247 }
    248 
    249 int MockDiskEntry::ReadyForSparseIO(const net::CompletionCallback& callback) {
    250   if (fail_sparse_requests_)
    251     return net::ERR_NOT_IMPLEMENTED;
    252   if (!cancel_)
    253     return net::OK;
    254 
    255   cancel_ = false;
    256   DCHECK(!callback.is_null());
    257   if (MockHttpCache::GetTestMode(test_mode_) & TEST_MODE_SYNC_CACHE_READ)
    258     return net::OK;
    259 
    260   // The pending operation is already in the message loop (and hopefully
    261   // already in the second pass).  Just notify the caller that it finished.
    262   CallbackLater(callback, 0);
    263   return net::ERR_IO_PENDING;
    264 }
    265 
    266 // If |value| is true, don't deliver any completion callbacks until called
    267 // again with |value| set to false.  Caution: remember to enable callbacks
    268 // again or all subsequent tests will fail.
    269 // Static.
    270 void MockDiskEntry::IgnoreCallbacks(bool value) {
    271   if (ignore_callbacks_ == value)
    272     return;
    273   ignore_callbacks_ = value;
    274   if (!value)
    275     StoreAndDeliverCallbacks(false, NULL, net::CompletionCallback(), 0);
    276 }
    277 
    278 MockDiskEntry::~MockDiskEntry() {
    279 }
    280 
    281 // Unlike the callbacks for MockHttpTransaction, we want this one to run even
    282 // if the consumer called Close on the MockDiskEntry.  We achieve that by
    283 // leveraging the fact that this class is reference counted.
    284 void MockDiskEntry::CallbackLater(const net::CompletionCallback& callback,
    285                                   int result) {
    286   if (ignore_callbacks_)
    287     return StoreAndDeliverCallbacks(true, this, callback, result);
    288   base::MessageLoop::current()->PostTask(
    289       FROM_HERE,
    290       base::Bind(&MockDiskEntry::RunCallback, this, callback, result));
    291 }
    292 
    293 void MockDiskEntry::RunCallback(
    294     const net::CompletionCallback& callback, int result) {
    295   if (busy_) {
    296     // This is kind of hacky, but controlling the behavior of just this entry
    297     // from a test is sort of complicated.  What we really want to do is
    298     // delay the delivery of a sparse IO operation a little more so that the
    299     // request start operation (async) will finish without seeing the end of
    300     // this operation (already posted to the message loop)... and without
    301     // just delaying for n mS (which may cause trouble with slow bots).  So
    302     // we re-post this operation (all async sparse IO operations will take two
    303     // trips through the message loop instead of one).
    304     if (!delayed_) {
    305       delayed_ = true;
    306       return CallbackLater(callback, result);
    307     }
    308   }
    309   busy_ = false;
    310   callback.Run(result);
    311 }
    312 
    313 // When |store| is true, stores the callback to be delivered later; otherwise
    314 // delivers any callback previously stored.
    315 // Static.
    316 void MockDiskEntry::StoreAndDeliverCallbacks(
    317     bool store, MockDiskEntry* entry, const net::CompletionCallback& callback,
    318     int result) {
    319   static std::vector<CallbackInfo> callback_list;
    320   if (store) {
    321     CallbackInfo c = {entry, callback, result};
    322     callback_list.push_back(c);
    323   } else {
    324     for (size_t i = 0; i < callback_list.size(); i++) {
    325       CallbackInfo& c = callback_list[i];
    326       c.entry->CallbackLater(c.callback, c.result);
    327     }
    328     callback_list.clear();
    329   }
    330 }
    331 
    332 // Statics.
    333 bool MockDiskEntry::cancel_ = false;
    334 bool MockDiskEntry::ignore_callbacks_ = false;
    335 
    336 //-----------------------------------------------------------------------------
    337 
    338 MockDiskCache::MockDiskCache()
    339     : open_count_(0), create_count_(0), fail_requests_(false),
    340       soft_failures_(false), double_create_check_(true),
    341       fail_sparse_requests_(false) {
    342 }
    343 
    344 MockDiskCache::~MockDiskCache() {
    345   ReleaseAll();
    346 }
    347 
    348 net::CacheType MockDiskCache::GetCacheType() const {
    349   return net::DISK_CACHE;
    350 }
    351 
    352 int32 MockDiskCache::GetEntryCount() const {
    353   return static_cast<int32>(entries_.size());
    354 }
    355 
    356 int MockDiskCache::OpenEntry(const std::string& key, disk_cache::Entry** entry,
    357                              const net::CompletionCallback& callback) {
    358   DCHECK(!callback.is_null());
    359   if (fail_requests_)
    360     return net::ERR_CACHE_OPEN_FAILURE;
    361 
    362   EntryMap::iterator it = entries_.find(key);
    363   if (it == entries_.end())
    364     return net::ERR_CACHE_OPEN_FAILURE;
    365 
    366   if (it->second->is_doomed()) {
    367     it->second->Release();
    368     entries_.erase(it);
    369     return net::ERR_CACHE_OPEN_FAILURE;
    370   }
    371 
    372   open_count_++;
    373 
    374   it->second->AddRef();
    375   *entry = it->second;
    376 
    377   if (soft_failures_)
    378     it->second->set_fail_requests();
    379 
    380   if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START)
    381     return net::OK;
    382 
    383   CallbackLater(callback, net::OK);
    384   return net::ERR_IO_PENDING;
    385 }
    386 
    387 int MockDiskCache::CreateEntry(const std::string& key,
    388                                disk_cache::Entry** entry,
    389                                const net::CompletionCallback& callback) {
    390   DCHECK(!callback.is_null());
    391   if (fail_requests_)
    392     return net::ERR_CACHE_CREATE_FAILURE;
    393 
    394   EntryMap::iterator it = entries_.find(key);
    395   if (it != entries_.end()) {
    396     if (!it->second->is_doomed()) {
    397       if (double_create_check_)
    398         NOTREACHED();
    399       else
    400         return net::ERR_CACHE_CREATE_FAILURE;
    401     }
    402     it->second->Release();
    403     entries_.erase(it);
    404   }
    405 
    406   create_count_++;
    407 
    408   MockDiskEntry* new_entry = new MockDiskEntry(key);
    409 
    410   new_entry->AddRef();
    411   entries_[key] = new_entry;
    412 
    413   new_entry->AddRef();
    414   *entry = new_entry;
    415 
    416   if (soft_failures_)
    417     new_entry->set_fail_requests();
    418 
    419   if (fail_sparse_requests_)
    420     new_entry->set_fail_sparse_requests();
    421 
    422   if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START)
    423     return net::OK;
    424 
    425   CallbackLater(callback, net::OK);
    426   return net::ERR_IO_PENDING;
    427 }
    428 
    429 int MockDiskCache::DoomEntry(const std::string& key,
    430                              const net::CompletionCallback& callback) {
    431   DCHECK(!callback.is_null());
    432   EntryMap::iterator it = entries_.find(key);
    433   if (it != entries_.end()) {
    434     it->second->Release();
    435     entries_.erase(it);
    436   }
    437 
    438   if (GetTestModeForEntry(key) & TEST_MODE_SYNC_CACHE_START)
    439     return net::OK;
    440 
    441   CallbackLater(callback, net::OK);
    442   return net::ERR_IO_PENDING;
    443 }
    444 
    445 int MockDiskCache::DoomAllEntries(const net::CompletionCallback& callback) {
    446   return net::ERR_NOT_IMPLEMENTED;
    447 }
    448 
    449 int MockDiskCache::DoomEntriesBetween(const base::Time initial_time,
    450                                       const base::Time end_time,
    451                                       const net::CompletionCallback& callback) {
    452   return net::ERR_NOT_IMPLEMENTED;
    453 }
    454 
    455 int MockDiskCache::DoomEntriesSince(const base::Time initial_time,
    456                                     const net::CompletionCallback& callback) {
    457   return net::ERR_NOT_IMPLEMENTED;
    458 }
    459 
    460 class MockDiskCache::NotImplementedIterator : public Iterator {
    461  public:
    462   virtual int OpenNextEntry(disk_cache::Entry** next_entry,
    463                             const net::CompletionCallback& callback) OVERRIDE {
    464     return net::ERR_NOT_IMPLEMENTED;
    465   }
    466 };
    467 
    468 scoped_ptr<disk_cache::Backend::Iterator> MockDiskCache::CreateIterator() {
    469   return scoped_ptr<Iterator>(new NotImplementedIterator());
    470 }
    471 
    472 void MockDiskCache::GetStats(
    473     std::vector<std::pair<std::string, std::string> >* stats) {
    474 }
    475 
    476 void MockDiskCache::OnExternalCacheHit(const std::string& key) {
    477 }
    478 
    479 void MockDiskCache::ReleaseAll() {
    480   EntryMap::iterator it = entries_.begin();
    481   for (; it != entries_.end(); ++it)
    482     it->second->Release();
    483   entries_.clear();
    484 }
    485 
    486 void MockDiskCache::CallbackLater(const net::CompletionCallback& callback,
    487                                   int result) {
    488   base::MessageLoop::current()->PostTask(
    489       FROM_HERE, base::Bind(&CallbackForwader, callback, result));
    490 }
    491 
    492 //-----------------------------------------------------------------------------
    493 
    494 int MockBackendFactory::CreateBackend(net::NetLog* net_log,
    495                                       scoped_ptr<disk_cache::Backend>* backend,
    496                                       const net::CompletionCallback& callback) {
    497   backend->reset(new MockDiskCache());
    498   return net::OK;
    499 }
    500 
    501 //-----------------------------------------------------------------------------
    502 
    503 MockHttpCache::MockHttpCache()
    504     : http_cache_(new MockNetworkLayer(), NULL, new MockBackendFactory()) {
    505 }
    506 
    507 MockHttpCache::MockHttpCache(net::HttpCache::BackendFactory* disk_cache_factory)
    508     : http_cache_(new MockNetworkLayer(), NULL, disk_cache_factory) {
    509 }
    510 
    511 MockDiskCache* MockHttpCache::disk_cache() {
    512   net::TestCompletionCallback cb;
    513   disk_cache::Backend* backend;
    514   int rv = http_cache_.GetBackend(&backend, cb.callback());
    515   rv = cb.GetResult(rv);
    516   return (rv == net::OK) ? static_cast<MockDiskCache*>(backend) : NULL;
    517 }
    518 
    519 int MockHttpCache::CreateTransaction(scoped_ptr<net::HttpTransaction>* trans) {
    520   return http_cache_.CreateTransaction(net::DEFAULT_PRIORITY, trans);
    521 }
    522 
    523 void MockHttpCache::BypassCacheLock() {
    524   http_cache_.BypassLockForTest();
    525 }
    526 
    527 bool MockHttpCache::ReadResponseInfo(disk_cache::Entry* disk_entry,
    528                                      net::HttpResponseInfo* response_info,
    529                                      bool* response_truncated) {
    530   int size = disk_entry->GetDataSize(0);
    531 
    532   net::TestCompletionCallback cb;
    533   scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(size));
    534   int rv = disk_entry->ReadData(0, 0, buffer.get(), size, cb.callback());
    535   rv = cb.GetResult(rv);
    536   EXPECT_EQ(size, rv);
    537 
    538   return net::HttpCache::ParseResponseInfo(buffer->data(), size,
    539                                            response_info,
    540                                            response_truncated);
    541 }
    542 
    543 bool MockHttpCache::WriteResponseInfo(
    544     disk_cache::Entry* disk_entry, const net::HttpResponseInfo* response_info,
    545     bool skip_transient_headers, bool response_truncated) {
    546   Pickle pickle;
    547   response_info->Persist(
    548       &pickle, skip_transient_headers, response_truncated);
    549 
    550   net::TestCompletionCallback cb;
    551   scoped_refptr<net::WrappedIOBuffer> data(new net::WrappedIOBuffer(
    552       reinterpret_cast<const char*>(pickle.data())));
    553   int len = static_cast<int>(pickle.size());
    554 
    555   int rv = disk_entry->WriteData(0, 0, data.get(), len, cb.callback(), true);
    556   rv = cb.GetResult(rv);
    557   return (rv == len);
    558 }
    559 
    560 bool MockHttpCache::OpenBackendEntry(const std::string& key,
    561                                      disk_cache::Entry** entry) {
    562   net::TestCompletionCallback cb;
    563   int rv = disk_cache()->OpenEntry(key, entry, cb.callback());
    564   return (cb.GetResult(rv) == net::OK);
    565 }
    566 
    567 bool MockHttpCache::CreateBackendEntry(const std::string& key,
    568                                        disk_cache::Entry** entry,
    569                                        net::NetLog* net_log) {
    570   net::TestCompletionCallback cb;
    571   int rv = disk_cache()->CreateEntry(key, entry, cb.callback());
    572   return (cb.GetResult(rv) == net::OK);
    573 }
    574 
    575 // Static.
    576 int MockHttpCache::GetTestMode(int test_mode) {
    577   if (!g_test_mode)
    578     return test_mode;
    579 
    580   return g_test_mode;
    581 }
    582 
    583 // Static.
    584 void MockHttpCache::SetTestMode(int test_mode) {
    585   g_test_mode = test_mode;
    586 }
    587 
    588 //-----------------------------------------------------------------------------
    589 
    590 int MockDiskCacheNoCB::CreateEntry(const std::string& key,
    591                                    disk_cache::Entry** entry,
    592                                    const net::CompletionCallback& callback) {
    593   return net::ERR_IO_PENDING;
    594 }
    595 
    596 //-----------------------------------------------------------------------------
    597 
    598 int MockBackendNoCbFactory::CreateBackend(
    599     net::NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend,
    600     const net::CompletionCallback& callback) {
    601   backend->reset(new MockDiskCacheNoCB());
    602   return net::OK;
    603 }
    604 
    605 //-----------------------------------------------------------------------------
    606 
    607 MockBlockingBackendFactory::MockBlockingBackendFactory()
    608     : backend_(NULL),
    609       block_(true),
    610       fail_(false) {
    611 }
    612 
    613 MockBlockingBackendFactory::~MockBlockingBackendFactory() {
    614 }
    615 
    616 int MockBlockingBackendFactory::CreateBackend(
    617     net::NetLog* net_log, scoped_ptr<disk_cache::Backend>* backend,
    618     const net::CompletionCallback& callback) {
    619   if (!block_) {
    620     if (!fail_)
    621       backend->reset(new MockDiskCache());
    622     return Result();
    623   }
    624 
    625   backend_ =  backend;
    626   callback_ = callback;
    627   return net::ERR_IO_PENDING;
    628 }
    629 
    630 void MockBlockingBackendFactory::FinishCreation() {
    631   block_ = false;
    632   if (!callback_.is_null()) {
    633     if (!fail_)
    634       backend_->reset(new MockDiskCache());
    635     net::CompletionCallback cb = callback_;
    636     callback_.Reset();
    637     cb.Run(Result());  // This object can be deleted here.
    638   }
    639 }
    640