Browse Source

Rewrite CookieStorage

Tatsuhiro Tsujikawa 12 years ago
parent
commit
5a223115e0

+ 21 - 50
src/Cookie.cc

@@ -43,23 +43,23 @@
 namespace aria2 {
 
 Cookie::Cookie
-(const std::string& name,
- const std::string& value,
+(std::string name,
+ std::string value,
  time_t  expiryTime,
  bool persistent,
- const std::string& domain,
+ std::string domain,
  bool hostOnly,
- const std::string& path,
+ std::string path,
  bool secure,
  bool httpOnly,
  time_t creationTime):
-  name_(name),
-  value_(value),
+  name_(std::move(name)),
+  value_(std::move(value)),
   expiryTime_(expiryTime),
   persistent_(persistent),
-  domain_(domain),
+  domain_(std::move(domain)),
   hostOnly_(hostOnly),
-  path_(path),
+  path_(std::move(path)),
   secure_(secure),
   httpOnly_(httpOnly),
   creationTime_(creationTime),
@@ -74,40 +74,6 @@ Cookie::Cookie():
   creationTime_(0),
   lastAccessTime_(0) {}
 
-Cookie::Cookie(const Cookie& c)
-  : name_(c.name_),
-    value_(c.value_),
-    expiryTime_(c.expiryTime_),
-    persistent_(c.persistent_),
-    domain_(c.domain_),
-    hostOnly_(c.hostOnly_),
-    path_(c.path_),
-    secure_(c.secure_),
-    httpOnly_(c.httpOnly_),
-    creationTime_(c.creationTime_),
-    lastAccessTime_(c.lastAccessTime_)
-{}
-
-Cookie::~Cookie() {}
-
-Cookie& Cookie::operator=(const Cookie& c)
-{
-  if(this != &c) {
-    name_ = c.name_;
-    value_ = c.value_;
-    expiryTime_ = c.expiryTime_;
-    persistent_ = c.persistent_;
-    domain_ = c.domain_;
-    hostOnly_ = c.hostOnly_;
-    path_ = c.path_;
-    secure_ = c.secure_;
-    httpOnly_ = c.httpOnly_;
-    creationTime_ = c.creationTime_;
-    lastAccessTime_ = c.lastAccessTime_;
-  }
-  return *this;
-}
-
 std::string Cookie::toString() const
 {
   std::string s = name_;
@@ -138,6 +104,11 @@ bool Cookie::operator==(const Cookie& cookie) const
     name_ == cookie.name_;
 }
 
+bool Cookie::operator!=(const Cookie& cookie) const
+{
+  return !(*this == cookie);
+}
+
 bool Cookie::isExpired(time_t base) const
 {
   return persistent_ && base > expiryTime_;
@@ -174,24 +145,24 @@ std::string Cookie::toNsCookieFormat() const
   return ss.str();
 }
 
-void Cookie::setName(const std::string& name)
+void Cookie::setName(std::string name)
 {
-  name_ = name;
+  name_ = std::move(name);
 }
 
-void Cookie::setValue(const std::string& value)
+void Cookie::setValue(std::string value)
 {
-  value_ = value;
+  value_ = std::move(value);
 }
 
-void Cookie::setDomain(const std::string& domain)
+void Cookie::setDomain(std::string domain)
 {
-  domain_ = domain;
+  domain_ = std::move(domain);
 }
 
-void Cookie::setPath(const std::string& path)
+void Cookie::setPath(std::string path)
 {
-  path_ = path;
+  path_ = std::move(path);
 }
 
 } // namespace aria2

+ 10 - 14
src/Cookie.h

@@ -63,23 +63,17 @@ public:
   Cookie();
 
   Cookie
-  (const std::string& name,
-   const std::string& value,
+  (std::string name,
+   std::string value,
    time_t  expiryTime,
    bool persistent,
-   const std::string& domain,
+   std::string domain,
    bool hostOnly,
-   const std::string& path,
+   std::string path,
    bool secure,
    bool httpOnly,
    time_t creationTime);
 
-  Cookie(const Cookie& c);
-
-  ~Cookie();
-
-  Cookie& operator=(const Cookie& c);
-
   std::string toString() const;
 
   bool match
@@ -88,6 +82,8 @@ public:
 
   bool operator==(const Cookie& cookie) const;
 
+  bool operator!=(const Cookie& cookie) const;
+
   bool isExpired(time_t base) const;
 
   const std::string& getName() const
@@ -95,7 +91,7 @@ public:
     return name_;
   }
 
-  void setName(const std::string& name);
+  void setName(std::string name);
 
   template<typename InputIterator>
   void setName(InputIterator first, InputIterator last)
@@ -108,7 +104,7 @@ public:
     return value_;
   }
 
-  void setValue(const std::string& value);
+  void setValue(std::string value);
 
   template<typename InputIterator>
   void setValue(InputIterator first, InputIterator last)
@@ -141,7 +137,7 @@ public:
     return domain_;
   }
 
-  void setDomain(const std::string& domain);
+  void setDomain(std::string domain);
 
   template<typename InputIterator>
   void setDomain(InputIterator first, InputIterator last)
@@ -164,7 +160,7 @@ public:
     return path_;
   }
 
-  void setPath(const std::string& path);
+  void setPath(std::string path);
 
   template<typename InputIterator>
   void setPath(InputIterator first, InputIterator last)

+ 278 - 178
src/CookieStorage.cc

@@ -2,7 +2,7 @@
 /*
  * aria2 - The high speed download utility
  *
- * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ * Copyright (C) 2013 Tatsuhiro Tsujikawa
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -56,131 +56,196 @@
 
 namespace aria2 {
 
-CookieStorage::DomainEntry::DomainEntry(const std::string& domain)
-  : key_(util::isNumericHost(domain)?domain:cookie::reverseDomainLevel(domain)),
-    lastAccessTime_(0)
+DomainNode::DomainNode(std::string label, DomainNode* parent)
+  : label_{std::move(label)}, parent_{parent}, lastAccessTime_{0},
+    lruAccessTime_{0}, inLru_{false}
 {}
 
-CookieStorage::DomainEntry::DomainEntry
-(const DomainEntry& c)
-  : key_(c.key_),
-    lastAccessTime_(c.lastAccessTime_),
-    cookies_(c.cookies_)
-{}
-
-CookieStorage::DomainEntry::~DomainEntry() {}
-
-CookieStorage::DomainEntry& CookieStorage::DomainEntry::operator=
-(const DomainEntry& c)
-{
-  if(this != &c) {
-    key_ = c.key_;
-    lastAccessTime_ = c.lastAccessTime_;
-    cookies_ = c.cookies_;
-  }
-  return *this;
-}
-
-void CookieStorage::DomainEntry::swap(CookieStorage::DomainEntry& c)
-{
-  using std::swap;
-  swap(key_, c.key_);
-  swap(lastAccessTime_, c.lastAccessTime_);
-  swap(cookies_, c.cookies_);
-}
-
-void swap(CookieStorage::DomainEntry& a, CookieStorage::DomainEntry& b)
-{
-  a.swap(b);
-}
-
-void CookieStorage::DomainEntry::findCookie
-(std::vector<Cookie>& out,
+void DomainNode::findCookie
+(std::vector<const Cookie*>& out,
  const std::string& requestHost,
  const std::string& requestPath,
  time_t now, bool secure)
 {
-  for(std::deque<Cookie>::iterator i = cookies_.begin(),
-        eoi = cookies_.end(); i != eoi; ++i) {
-    if((*i).match(requestHost, requestPath, now, secure)) {
-      (*i).setLastAccessTime(now);
-      out.push_back(*i);
+  if(cookies_) {
+    for(auto& c : *cookies_) {
+      if(c->match(requestHost, requestPath, now, secure)) {
+        c->setLastAccessTime(now);
+        out.push_back(c.get());
+      }
     }
   }
 }
 
-bool CookieStorage::DomainEntry::addCookie(const Cookie& cookie, time_t now)
+bool DomainNode::addCookie(std::unique_ptr<Cookie> cookie, time_t now)
 {
   using namespace std::placeholders;
   setLastAccessTime(now);
-  std::deque<Cookie>::iterator i =
-    std::find(cookies_.begin(), cookies_.end(), cookie);
-  if(i == cookies_.end()) {
-    if(cookie.isExpired(now)) {
+  if(!cookies_) {
+    if(cookie->isExpired(now)) {
       return false;
     } else {
-      if(cookies_.size() >= CookieStorage::MAX_COOKIE_PER_DOMAIN) {
-        cookies_.erase
-          (std::remove_if(cookies_.begin(), cookies_.end(),
+      cookies_ = make_unique<std::deque<std::unique_ptr<Cookie>>>();
+      cookies_->push_back(std::move(cookie));
+      return true;
+    }
+  }
+
+  auto i = std::find_if(std::begin(*cookies_), std::end(*cookies_),
+                        [&](const std::unique_ptr<Cookie>& c)
+                        {
+                          return *c == *cookie;
+                        });
+  if(i == std::end(*cookies_)) {
+    if(cookie->isExpired(now)) {
+      return false;
+    } else {
+      if(cookies_->size() >= CookieStorage::MAX_COOKIE_PER_DOMAIN) {
+        cookies_->erase
+          (std::remove_if(std::begin(*cookies_), std::end(*cookies_),
                           std::bind(&Cookie::isExpired, _1, now)),
-           cookies_.end());
-        if(cookies_.size() >= CookieStorage::MAX_COOKIE_PER_DOMAIN) {
-          std::deque<Cookie>::iterator m = std::min_element
-            (cookies_.begin(), cookies_.end(), LeastRecentAccess<Cookie>());
-          *m = cookie;
+           std::end(*cookies_));
+        if(cookies_->size() >= CookieStorage::MAX_COOKIE_PER_DOMAIN) {
+          auto m = std::min_element(std::begin(*cookies_), std::end(*cookies_),
+                                    [](const std::unique_ptr<Cookie>& lhs,
+                                       const std::unique_ptr<Cookie>& rhs)
+                                    {
+                                      return lhs->getLastAccessTime() <
+                                      rhs->getLastAccessTime();
+                                    });
+          *m = std::move(cookie);
         } else {
-          cookies_.push_back(cookie);
+          cookies_->push_back(std::move(cookie));
         }
       } else {
-        cookies_.push_back(cookie);
+        cookies_->push_back(std::move(cookie));
       }
       return true;
     }
-  } else if(cookie.isExpired(now)) {
-    cookies_.erase(i);
+  } else if(cookie->isExpired(now)) {
+    cookies_->erase(i);
     return false;
   } else {
-    *i = cookie;
+    *i = std::move(cookie);
     return true;
   }
 }
 
-bool CookieStorage::DomainEntry::contains(const Cookie& cookie) const
+bool DomainNode::contains(const Cookie& cookie) const
 {
-  return std::find(cookies_.begin(), cookies_.end(), cookie) != cookies_.end();
+  if(cookies_) {
+    for(auto& i : *cookies_) {
+      if(*i == cookie) {
+        return true;
+      }
+    }
+  }
+  return false;
 }
 
-bool CookieStorage::DomainEntry::writeCookie(BufferedFile& fp) const
+bool DomainNode::writeCookie(BufferedFile& fp) const
 {
-  for(std::deque<Cookie>::const_iterator i = cookies_.begin(),
-        eoi = cookies_.end(); i != eoi; ++i) {
-    std::string data = (*i).toNsCookieFormat();
-    data += "\n";
-    if(fp.write(data.data(), data.size()) != data.size()) {
-      return false;
+  if(cookies_) {
+    for(const auto& c : *cookies_) {
+      std::string data = c->toNsCookieFormat();
+      data += "\n";
+      if(fp.write(data.data(), data.size()) != data.size()) {
+        return false;
+      }
     }
   }
   return true;
 }
 
-size_t CookieStorage::DomainEntry::countCookie() const
+size_t DomainNode::countCookie() const
+{
+  if(cookies_) {
+    return cookies_->size();
+  } else {
+    return 0;
+  }
+}
+
+void DomainNode::clearCookie()
+{
+  cookies_->clear();
+}
+
+void DomainNode::setLastAccessTime(time_t lastAccessTime)
 {
-  return cookies_.size();
+  lastAccessTime_ = lastAccessTime;
 }
 
-bool CookieStorage::DomainEntry::operator==(const DomainEntry& de) const
+time_t DomainNode::getLastAccessTime() const
 {
-  return key_ == de.key_;
+  return lastAccessTime_;
 }
 
-bool CookieStorage::DomainEntry::operator<(const DomainEntry& de) const
+void DomainNode::setLruAccessTime(time_t t)
 {
-  return key_ < de.key_;
+  lruAccessTime_ = t;
 }
 
-CookieStorage::CookieStorage() {}
+time_t DomainNode::getLruAccessTime() const
+{
+  return lruAccessTime_;
+}
 
-CookieStorage::~CookieStorage() {}
+bool DomainNode::empty() const
+{
+  return !cookies_ || cookies_->empty();
+}
+
+bool DomainNode::hasNext() const
+{
+  return !next_.empty();
+}
+
+DomainNode* DomainNode::getParent() const
+{
+  return parent_;
+}
+
+void DomainNode::removeNode(DomainNode* node)
+{
+  next_.erase(node->getLabel());
+}
+
+DomainNode* DomainNode::findNext(const std::string& label) const
+{
+  auto i = next_.find(label);
+  if(i == std::end(next_)) {
+    return nullptr;
+  } else {
+    return (*i).second.get();
+  }
+}
+
+DomainNode* DomainNode::addNext(std::string label,
+                                std::unique_ptr<DomainNode> node)
+{
+  auto& res = next_[std::move(label)] = std::move(node);
+  return res.get();
+}
+
+const std::string& DomainNode::getLabel() const
+{
+  return label_;
+}
+
+bool DomainNode::getInLru() const
+{
+  return inLru_;
+}
+
+void DomainNode::setInLru(bool f)
+{
+  inLru_ = f;
+}
+
+CookieStorage::CookieStorage()
+  : rootNode_{make_unique<DomainNode>("", nullptr)}
+{}
 
 namespace {
 // See CookieStorageTest::testDomainIsFull() in CookieStorageTest.cc
@@ -189,29 +254,86 @@ const size_t DOMAIN_EVICTION_TRIGGER = 2000;
 const double DOMAIN_EVICTION_RATE = 0.1;
 } // namespace
 
-bool CookieStorage::store(const Cookie& cookie, time_t now)
+namespace {
+std::vector<std::string> splitDomainLabel(const std::string& domain)
 {
-  if(domains_.size() >= DOMAIN_EVICTION_TRIGGER) {
-    std::vector<std::shared_ptr<DomainEntry> > evictions(domains_.begin(),
-                                                      domains_.end());
-    std::sort(evictions.begin(), evictions.end(),
-              LeastRecentAccess<DomainEntry>());
-    size_t delnum = (size_t)(evictions.size()*DOMAIN_EVICTION_RATE);
-    domains_.clear();
-    domains_.insert(evictions.begin()+delnum, evictions.end());
-  }
-  std::shared_ptr<DomainEntry> v(new DomainEntry(cookie.getDomain()));
-  DomainEntrySet::iterator i = domains_.lower_bound(v);
-  bool added = false;
-  if(i != domains_.end() && *(*i) == *v) {
-    added = (*i)->addCookie(cookie, now);
+  auto labels = std::vector<std::string>{};
+  if(util::isNumericHost(domain)) {
+    labels.push_back(domain);
   } else {
-    added = v->addCookie(cookie, now);
-    if(added) {
-      domains_.insert(i, v);
+    util::split(std::begin(domain), std::end(domain),
+                std::back_inserter(labels), '.');
+  }
+  return labels;
+}
+} // namespace
+
+
+size_t CookieStorage::getLruTrackerSize() const
+{
+  return lruTracker_.size();
+}
+
+void CookieStorage::evictNode(size_t delnum)
+{
+  for(; delnum > 0 && !lruTracker_.empty(); --delnum) {
+    auto node = (*lruTracker_.begin()).second;
+    lruTracker_.erase(lruTracker_.begin());
+    node->setInLru(false);
+    node->clearCookie();
+    while(node->empty() && !node->hasNext()) {
+      auto parent = node->getParent();
+      parent->removeNode(node);
+      if(!parent->empty() || parent->hasNext() ||
+         parent == rootNode_.get()) {
+        break;
+      }
+      node = parent;
+      if(node->getInLru()) {
+        lruTracker_.erase({node->getLruAccessTime(), node});
+        node->setInLru(false);
+      }
     }
   }
-  return added;
+}
+
+const DomainNode* CookieStorage::getRootNode() const
+{
+  return rootNode_.get();
+}
+
+bool CookieStorage::store(std::unique_ptr<Cookie> cookie, time_t now)
+{
+  if(lruTracker_.size() >= DOMAIN_EVICTION_TRIGGER) {
+    auto delnum = size_t(lruTracker_.size()*DOMAIN_EVICTION_RATE);
+    evictNode(delnum);
+  }
+  auto labels = splitDomainLabel(cookie->getDomain());
+  auto node = rootNode_.get();
+  for(auto i = labels.rbegin(), eoi = labels.rend(); i != eoi; ++i) {
+    auto nextNode = node->findNext(*i);
+    if(nextNode) {
+      node = nextNode;
+    } else {
+      node = node->addNext(*i, make_unique<DomainNode>(*i, node));
+    }
+  }
+  bool ok = node->addCookie(std::move(cookie), now);
+  if(ok) {
+    updateLru(node, now);
+  }
+  return ok;
+}
+
+void CookieStorage::updateLru(DomainNode* node, time_t now)
+{
+  if(node->getInLru()) {
+    lruTracker_.erase({node->getLruAccessTime(), node});
+  } else {
+    node->setInLru(true);
+  }
+  node->setLruAccessTime(now);
+  lruTracker_.insert({node->getLruAccessTime(), node});
 }
 
 bool CookieStorage::parseAndStore
@@ -220,20 +342,17 @@ bool CookieStorage::parseAndStore
  const std::string& defaultPath,
  time_t now)
 {
-  Cookie cookie;
-  if(cookie::parse(cookie, setCookieString, requestHost, defaultPath, now)) {
-    return store(cookie, now);
-  } else {
-    return false;
-  }
+  auto cookie = cookie::parse(setCookieString, requestHost, defaultPath, now);
+  return cookie && store(std::move(cookie), now);
 }
 
+namespace {
 struct CookiePathDivider {
-  Cookie cookie_;
+  const Cookie* cookie_;
   int pathDepth_;
-  CookiePathDivider(const Cookie& cookie):cookie_(cookie), pathDepth_(0)
+  CookiePathDivider(const Cookie* cookie):cookie_(cookie), pathDepth_(0)
   {
-    const std::string& path = cookie_.getPath();
+    const std::string& path = cookie_->getPath();
     if(!path.empty()) {
       for(size_t i = 1, len = path.size(); i < len; ++i) {
         if(path[i] == '/' && path[i-1] != '/') {
@@ -246,16 +365,17 @@ struct CookiePathDivider {
     }
   }
 };
+} // namespace
 
 namespace {
 class CookiePathDividerConverter {
 public:
-  CookiePathDivider operator()(const Cookie& cookie) const
+  CookiePathDivider operator()(const Cookie* cookie) const
   {
     return CookiePathDivider(cookie);
   }
 
-  Cookie operator()(const CookiePathDivider& cookiePathDivider) const
+  const Cookie* operator()(const CookiePathDivider& cookiePathDivider) const
   {
     return cookiePathDivider.cookie_;
   }
@@ -280,80 +400,69 @@ public:
     //       creation-times.
     return lhs.pathDepth_ > rhs.pathDepth_ ||
       (!(rhs.pathDepth_ > lhs.pathDepth_) &&
-       lhs.cookie_.getCreationTime() < rhs.cookie_.getCreationTime());
+       lhs.cookie_->getCreationTime() < rhs.cookie_->getCreationTime());
   }
 };
 } // namespace
 
-void CookieStorage::searchCookieByDomainSuffix
-(std::vector<Cookie>& out,
- const std::string& domain,
- const std::string& requestHost,
- const std::string& requestPath,
- time_t now, bool secure)
+namespace {
+DomainNode* findNode(const std::string& domain, DomainNode* node)
 {
-  std::shared_ptr<DomainEntry> v(new DomainEntry(domain));
-  DomainEntrySet::iterator i = domains_.lower_bound(v);
-  if(i != domains_.end() && *(*i) == *v) {
-    (*i)->setLastAccessTime(now);
-    (*i)->findCookie(out, requestHost, requestPath, now, secure);
+  auto labels = splitDomainLabel(domain);
+  for(auto i = labels.rbegin(), eoi = labels.rend(); i != eoi && node; ++i) {
+    node = node->findNext(*i);
   }
+  return node;
 }
+} // namespace
 
 bool CookieStorage::contains(const Cookie& cookie) const
 {
-  std::shared_ptr<DomainEntry> v(new DomainEntry(cookie.getDomain()));
-  DomainEntrySet::iterator i = domains_.find(v);
-  if(i != domains_.end()) {
-    return (*i)->contains(cookie);
-  } else {
-    return false;
-  }
+  auto node = findNode(cookie.getDomain(), rootNode_.get());
+  return node && node->contains(cookie);
 }
 
-std::vector<Cookie> CookieStorage::criteriaFind
+std::vector<const Cookie*> CookieStorage::criteriaFind
 (const std::string& requestHost,
  const std::string& requestPath,
  time_t now,
  bool secure)
 {
-  std::vector<Cookie> res;
+  auto res = std::vector<const Cookie*>{};
   if(requestPath.empty()) {
     return res;
   }
-  if(util::isNumericHost(requestHost)) {
-    searchCookieByDomainSuffix
-      (res, requestHost, requestHost, requestPath, now, secure);
-  } else {
-    std::vector<Scip> levels;
-    util::splitIter(requestHost.begin(), requestHost.end(),
-                    std::back_inserter(levels), '.');
-    std::string domain;
-    for(std::vector<Scip>::const_reverse_iterator i =
-          levels.rbegin(), eoi = levels.rend();
-        i != eoi; ++i, domain.insert(domain.begin(), '.')) {
-      domain.insert(domain.begin(), (*i).first, (*i).second);
-      searchCookieByDomainSuffix
-        (res, domain, requestHost, requestPath, now, secure);
+  auto labels = splitDomainLabel(requestHost);
+  auto node = rootNode_.get();
+  for(auto i = labels.rbegin(), eoi = labels.rend(); i != eoi; ++i) {
+    auto nextNode = node->findNext(*i);
+    if(nextNode) {
+      nextNode->setLastAccessTime(now);
+      if(nextNode->getInLru()) {
+        updateLru(nextNode, now);
+      }
+      nextNode->findCookie(res, requestHost, requestPath, now, secure);
+      node = nextNode;
+    } else {
+      break;
     }
   }
-  std::vector<CookiePathDivider> divs;
-  std::transform(res.begin(), res.end(), std::back_inserter(divs),
-                 CookiePathDividerConverter());
-  std::sort(divs.begin(), divs.end(), OrderByPathDepthDesc());
-  std::transform(divs.begin(), divs.end(), res.begin(),
-                 CookiePathDividerConverter());
+  auto divs = std::vector<CookiePathDivider>{};
+  std::transform(std::begin(res), std::end(res), std::back_inserter(divs),
+                 CookiePathDividerConverter{});
+  std::sort(std::begin(divs), std::end(divs), OrderByPathDepthDesc{});
+  std::transform(std::begin(divs), std::end(divs), std::begin(res),
+                 CookiePathDividerConverter{});
   return res;
 }
 
 size_t CookieStorage::size() const
 {
-  size_t numCookie = 0;
-  for(DomainEntrySet::iterator i = domains_.begin(), eoi = domains_.end();
-      i != eoi; ++i) {
-    numCookie += (*i)->countCookie();
+  size_t n = 0;
+  for(auto& p : lruTracker_) {
+    n += p.second->countCookie();
   }
-  return numCookie;
+  return n;
 }
 
 bool CookieStorage::load(const std::string& filename, time_t now)
@@ -361,7 +470,7 @@ bool CookieStorage::load(const std::string& filename, time_t now)
   char header[16]; // "SQLite format 3" plus \0
   size_t headlen;
   {
-    BufferedFile fp(filename.c_str(), BufferedFile::READ);
+    BufferedFile fp{filename.c_str(), BufferedFile::READ};
     if(!fp) {
       A2_LOG_ERROR(fmt("Failed to open cookie file %s", filename.c_str()));
       return false;
@@ -371,25 +480,28 @@ bool CookieStorage::load(const std::string& filename, time_t now)
   try {
     if(headlen == 16 && memcmp(header, "SQLite format 3\0", 16) == 0) {
 #ifdef HAVE_SQLITE3
-      std::vector<Cookie> cookies;
       try {
-        Sqlite3MozCookieParser(filename).parse(cookies);
+        auto cookies = Sqlite3MozCookieParser(filename).parse();
+        storeCookies(std::make_move_iterator(std::begin(cookies)),
+                     std::make_move_iterator(std::end(cookies)), now);
       } catch(RecoverableException& e) {
         A2_LOG_INFO_EX(EX_EXCEPTION_CAUGHT, e);
         A2_LOG_INFO("This does not look like Firefox3 cookie file."
                     " Retrying, assuming it is Chromium cookie file.");
         // Try chrome cookie format
-        Sqlite3ChromiumCookieParser(filename).parse(cookies);
+        auto cookies = Sqlite3ChromiumCookieParser(filename).parse();
+        storeCookies(std::make_move_iterator(std::begin(cookies)),
+                     std::make_move_iterator(std::end(cookies)), now);
       }
-      storeCookies(cookies.begin(), cookies.end(), now);
 #else // !HAVE_SQLITE3
       throw DL_ABORT_EX
         ("Cannot read SQLite3 database because SQLite3 support is disabled by"
          " configuration.");
 #endif // !HAVE_SQLITE3
     } else {
-      std::vector<Cookie> cookies = NsCookieParser().parse(filename, now);
-      storeCookies(cookies.begin(), cookies.end(), now);
+      auto cookies = NsCookieParser().parse(filename, now);
+      storeCookies(std::make_move_iterator(std::begin(cookies)),
+                   std::make_move_iterator(std::end(cookies)), now);
     }
     return true;
   } catch(RecoverableException& e) {
@@ -400,17 +512,16 @@ bool CookieStorage::load(const std::string& filename, time_t now)
 
 bool CookieStorage::saveNsFormat(const std::string& filename)
 {
-  std::string tempfilename = filename;
+  auto tempfilename = filename;
   tempfilename += "__temp";
   {
-    BufferedFile fp(tempfilename.c_str(), BufferedFile::WRITE);
+    BufferedFile fp{tempfilename.c_str(), BufferedFile::WRITE};
     if(!fp) {
       A2_LOG_ERROR(fmt("Cannot create cookie file %s", filename.c_str()));
       return false;
     }
-    for(DomainEntrySet::iterator i = domains_.begin(), eoi = domains_.end();
-        i != eoi; ++i) {
-      if(!(*i)->writeCookie(fp)) {
+    for(auto& p : lruTracker_) {
+      if(!p.second->writeCookie(fp)) {
         A2_LOG_ERROR(fmt("Failed to save cookies to %s", filename.c_str()));
         return false;
       }
@@ -431,14 +542,3 @@ bool CookieStorage::saveNsFormat(const std::string& filename)
 }
 
 } // namespace aria2
-
-namespace std {
-template<>
-void swap<aria2::CookieStorage::DomainEntry>
-(aria2::CookieStorage::DomainEntry& a,
- aria2::CookieStorage::DomainEntry& b)
-{
-  a.swap(b);
-}
-} // namespace std
-

+ 122 - 97
src/CookieStorage.h

@@ -2,7 +2,7 @@
 /*
  * aria2 - The high speed download utility
  *
- * Copyright (C) 2006 Tatsuhiro Tsujikawa
+ * Copyright (C) 2013 Tatsuhiro Tsujikawa
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -42,6 +42,7 @@
 #include <vector>
 #include <set>
 #include <algorithm>
+#include <unordered_map>
 
 #include "a2time.h"
 #include "Cookie.h"
@@ -51,89 +52,97 @@ namespace aria2 {
 
 class BufferedFile;
 
-class CookieStorage {
+// This object represents one domain label.
+class DomainNode {
 public:
+  DomainNode(std::string label, DomainNode* parent);
+  // Stores the matching cookies in |out|. The |now| is used to update
+  // the last access time of this node.
+  void findCookie
+  (std::vector<const Cookie*>& out,
+   const std::string& requestHost,
+   const std::string& requestPath,
+   time_t now, bool secure);
+  // Returns the number of cookies this node has.
+  size_t countCookie() const;
+  // Add |cookie| using update time |now|. Returns true if the
+  // function succeeds.
+  bool addCookie(std::unique_ptr<Cookie> cookie, time_t now);
+  // Sets the last access time of this node.
+  void setLastAccessTime(time_t lastAccessTime);
+  // Returns the last access time of this node.
+  time_t getLastAccessTime() const;
+  // Sets the time |t| as a time used as key in LRU tracker.
+  void setLruAccessTime(time_t t);
+  time_t getLruAccessTime() const;
+
+  bool writeCookie(BufferedFile& fp) const;
+  // Returns true if this node contains the |cookie|.
+  bool contains(const Cookie& cookie) const;
+  // Returns true if this node contains no cookie.
+  bool empty() const;
+  // Returns true if this node has any next nodes.
+  bool hasNext() const;
+  // Returns the parent node. If this is the root node, returns
+  // nullptr.
+  DomainNode* getParent() const;
+  // Removes the child node |node|. Nothing happens if |node| is not a
+  // child of this node.
+  void removeNode(DomainNode* node);
+  // Returns the child node having label |label. Returns nullptr if
+  // there is no such node.
+  DomainNode* findNext(const std::string& label) const;
+  // Add the |node| as a child using label |label and returns the raw
+  // pointer of |node|.
+  DomainNode* addNext(std::string label, std::unique_ptr<DomainNode> node);
+  // Returns the |label|.
+  const std::string& getLabel() const;
+  // Deletes all cookies this node has.
+  void clearCookie();
+  // Returns value set by setInLru(). This is typically used to know
+  // this node is tracked by LRU tracker or not.
+  bool getInLru() const;
+  void setInLru(bool f);
 
-  static const size_t MAX_COOKIE_PER_DOMAIN = 50;
-
-  class DomainEntry {
-  private:
-    // This is reversed domain level string.
-    // e.g. net.sourceforge.aria2
-    // e.g. 192.168.0.1
-    std::string key_;
-
-    time_t lastAccessTime_;
-
-    std::deque<Cookie> cookies_;
-  public:
-    DomainEntry(const std::string& domain);
-    DomainEntry(const DomainEntry& c);
-    ~DomainEntry();
-
-    void swap(DomainEntry& c);
-
-    DomainEntry& operator=(const DomainEntry& c);
-
-    const std::string& getKey() const
-    {
-      return key_;
-    }
-
-    void findCookie
-    (std::vector<Cookie>& out,
-     const std::string& requestHost,
-     const std::string& requestPath,
-     time_t now, bool secure);
-
-    size_t countCookie() const;
-
-    bool addCookie(const Cookie& cookie, time_t now);
-
-    void setLastAccessTime(time_t lastAccessTime)
-    {
-      lastAccessTime_ = lastAccessTime;
-    }
-
-    time_t getLastAccessTime() const
-    {
-      return lastAccessTime_;
-    }
-
-    bool writeCookie(BufferedFile& fp) const;
-
-    bool contains(const Cookie& cookie) const;
-
-    template<typename OutputIterator>
-    OutputIterator dumpCookie(OutputIterator out) const
-    {
-      return std::copy(cookies_.begin(), cookies_.end(), out);
+  template<typename OutputIterator>
+  OutputIterator dumpCookie(OutputIterator out) const
+  {
+    if(cookies_) {
+      for(auto& c : *cookies_) {
+        out++ = c.get();
+      }
     }
+    return out;
+  }
+private:
+  std::string label_;
+  DomainNode* parent_;
+  time_t lastAccessTime_;
+  time_t lruAccessTime_;
+  bool inLru_;
+  std::unique_ptr<std::deque<std::unique_ptr<Cookie>>> cookies_;
+  // domain label string to DomainNode
+  // e.g. net, sourceforge
+  // For numerical addresses, this is address itself.
+  // e.g. 192.168.0.1
+  std::unordered_map<std::string, std::unique_ptr<DomainNode>> next_;
+};
 
-    bool operator==(const DomainEntry& de) const;
-    bool operator<(const DomainEntry& de) const;
-  };
+class CookieStorage {
+public:
+  static const size_t MAX_COOKIE_PER_DOMAIN = 50;
 private:
-  typedef std::set<std::shared_ptr<DomainEntry>,
-                   DerefLess<std::shared_ptr<DomainEntry> > > DomainEntrySet;
-  DomainEntrySet domains_;
+  // typedef std::set<std::shared_ptr<DomainEntry>,
+  //                  DerefLess<std::shared_ptr<DomainEntry> > > DomainEntrySet;
+  // DomainEntrySet domains_;
 
-  template<typename InputIterator>
-  void storeCookies(InputIterator first, InputIterator last, time_t now)
-  {
-    for(; first != last; ++first) {
-      store(*first, now);
-    }
-  }
 public:
   CookieStorage();
 
-  ~CookieStorage();
-
   // Returns true if cookie is stored or updated existing cookie.
   // Returns false if cookie is expired. now is used as last access
   // time.
-  bool store(const Cookie& cookie, time_t now);
+  bool store(std::unique_ptr<Cookie> cookie, time_t now);
 
   // Returns true if cookie is stored or updated existing cookie.
   // Otherwise, returns false. now is used as creation time and last
@@ -146,9 +155,10 @@ public:
 
   // Finds cookies matched with given criteria and returns them.
   // Matched cookies' lastAccess_ property is updated.
-  std::vector<Cookie> criteriaFind(const std::string& requestHost,
-                                   const std::string& requestPath,
-                                   time_t now, bool secure);
+  std::vector<const Cookie*>
+  criteriaFind(const std::string& requestHost,
+               const std::string& requestPath,
+               time_t now, bool secure);
 
   // Loads Cookies from file denoted by filename.  If compiled with
   // libsqlite3, this method automatically detects the specified file
@@ -170,36 +180,51 @@ public:
   // satisfies.
   bool contains(const Cookie& cookie) const;
 
-  // Searches Cookie using given domain, requestHost, requestPath,
-  // current time and secure flag. The found Cookies are stored in
-  // out.
-  void searchCookieByDomainSuffix
-  (std::vector<Cookie>& out,
-   const std::string& domain,
-   const std::string& requestHost,
-   const std::string& requestPath,
-   time_t now, bool secure);
-
   template<typename OutputIterator>
   OutputIterator dumpCookie(OutputIterator out) const
   {
-    for(DomainEntrySet::iterator i = domains_.begin(), eoi = domains_.end();
-        i != eoi; ++i) {
-      out = (*i)->dumpCookie(out);
+    for(auto& i : lruTracker_) {
+      out = i.second->dumpCookie(out);
     }
     return out;
   }
-};
 
-void swap(CookieStorage::DomainEntry& a, CookieStorage::DomainEntry& b);
+  // Force eviction of delnum nodes. Exposed for unittest
+  void evictNode(size_t delnum);
+  // Returns size of LRU tracker. Exposed for unittest
+  size_t getLruTrackerSize() const;
+  // Returns root node. Exposed for unittest
+  const DomainNode* getRootNode() const;
+private:
+  template<typename InputIterator>
+  void storeCookies(InputIterator first, InputIterator last, time_t now)
+  {
+    for(; first != last; ++first) {
+      store(*first, now);
+    }
+  }
+
+  void updateLru(DomainNode* node, time_t now);
+
+  // rootNode_ is a root node of tree structure of reversed domain
+  // labels.  rootNode_ always contans no cookie. It has the child
+  // nodes of the top level domain label (e.g., net, com and org). And
+  // those top level domain nodes have 2nd doman label (e.g.,
+  // sourcforge, github), and so on. The numeric host name are always
+  // stored as a child node of rootNode_. So the domain name of a
+  // paricular node is constructed as follows. First traverse the
+  // target node from root node. The concatenation of the visited
+  // node's label in the reverse order, delimited by ".", is the
+  std::unique_ptr<DomainNode> rootNode_;
+  // This object tracks the node which has cookies or it once had. The
+  // order is sorted by the least recent updated node first. This
+  // object does not track the node which has not contan cookie. For
+  // example, adding cookies in aria2.sourceforge.net, and no node
+  // labeled "sourceforge" is present, only node labelded "aria2" is
+  // tracked and node labeled "sourceforge" and "net" are not.
+  std::set<std::pair<time_t, DomainNode*>> lruTracker_;
+};
 
 } // namespace aria2
 
-namespace std {
-template<>
-void swap<aria2::CookieStorage::DomainEntry>
-(aria2::CookieStorage::DomainEntry& a,
- aria2::CookieStorage::DomainEntry& b);
-} // namespace std
-
 #endif // D_COOKIE_STORAGE_H

+ 5 - 7
src/HttpRequest.cc

@@ -234,13 +234,11 @@ std::string HttpRequest::createRequest()
     std::string cookiesValue;
     std::string path = getDir();
     path += getFile();
-    std::vector<Cookie> cookies =
-      cookieStorage_->criteriaFind(getHost(), path,
-                                   Time().getTime(),
-                                   getProtocol() == "https");
-    for(std::vector<Cookie>::const_iterator itr = cookies.begin(),
-          eoi = cookies.end(); itr != eoi; ++itr) {
-      cookiesValue += (*itr).toString();
+    auto cookies = cookieStorage_->criteriaFind(getHost(), path,
+                                                Time().getTime(),
+                                                getProtocol() == "https");
+    for(auto c : cookies) {
+      cookiesValue += c->toString();
       cookiesValue += ";";
     }
     if(!cookiesValue.empty()) {

+ 24 - 23
src/NsCookieParser.cc

@@ -54,58 +54,59 @@ NsCookieParser::NsCookieParser() {}
 NsCookieParser::~NsCookieParser() {}
 
 namespace {
-bool parseNsCookie
-(Cookie& cookie, const std::string& cookieStr, time_t creationTime)
+std::unique_ptr<Cookie> parseNsCookie
+(const std::string& cookieStr, time_t creationTime)
 {
   std::vector<Scip> vs;
   util::splitIter(cookieStr.begin(), cookieStr.end(), std::back_inserter(vs),
                   '\t', true);
   if(vs.size() < 6) {
-    return false;
+    return std::unique_ptr<Cookie>{};
   }
   vs[0].first = util::lstripIter(vs[0].first, vs[0].second, '.');
   if(vs[5].first == vs[5].second || vs[0].first == vs[0].second ||
      !cookie::goodPath(vs[2].first, vs[2].second)) {
-    return false;
+    return std::unique_ptr<Cookie>{};
   }
   int64_t expiryTime;
   if(!util::parseLLIntNoThrow(expiryTime,
                               std::string(vs[4].first, vs[4].second))) {
-    return false;
+    return std::unique_ptr<Cookie>{};
   }
   if(std::numeric_limits<time_t>::max() < expiryTime) {
     expiryTime = std::numeric_limits<time_t>::max();
   } else if(std::numeric_limits<time_t>::min() > expiryTime) {
     expiryTime = std::numeric_limits<time_t>::min();
   }
-  cookie.setName(vs[5].first, vs[5].second);
+  auto cookie = make_unique<Cookie>();
+  cookie->setName(vs[5].first, vs[5].second);
   if(vs.size() >= 7) {
-    cookie.setValue(vs[6].first, vs[6].second);
+    cookie->setValue(vs[6].first, vs[6].second);
   } else {
-    cookie.setValue(A2STR::NIL.begin(), A2STR::NIL.end());
+    cookie->setValue("");
   }
-  cookie.setExpiryTime(expiryTime == 0?
+  cookie->setExpiryTime(expiryTime == 0?
                        std::numeric_limits<time_t>::max():expiryTime);
-  // aria2 treats expiryTime == 0 means session cookie.
-  cookie.setPersistent(expiryTime != 0);
-  cookie.setDomain(vs[0].first, vs[0].second);
-  cookie.setHostOnly(util::isNumericHost(cookie.getDomain()) ||
+  // aria2 treats expiryTime == 0 means session cookie->
+  cookie->setPersistent(expiryTime != 0);
+  cookie->setDomain(vs[0].first, vs[0].second);
+  cookie->setHostOnly(util::isNumericHost(cookie->getDomain()) ||
                      !util::streq(vs[1].first, vs[1].second, "TRUE"));
-  cookie.setPath(vs[2].first, vs[2].second);
-  cookie.setSecure(util::streq(vs[3].first, vs[3].second, "TRUE"));
-  cookie.setCreationTime(creationTime);
-  return true;
+  cookie->setPath(vs[2].first, vs[2].second);
+  cookie->setSecure(util::streq(vs[3].first, vs[3].second, "TRUE"));
+  cookie->setCreationTime(creationTime);
+  return cookie;
 }
 } // namespace
 
-std::vector<Cookie> NsCookieParser::parse
+std::vector<std::unique_ptr<Cookie>> NsCookieParser::parse
 (const std::string& filename, time_t creationTime)
 {
-  BufferedFile fp(filename.c_str(), BufferedFile::READ);
+  BufferedFile fp{filename.c_str(), BufferedFile::READ};
   if(!fp) {
     throw DL_ABORT_EX(fmt("Failed to open file %s", filename.c_str()));
   }
-  std::vector<Cookie> cookies;
+  std::vector<std::unique_ptr<Cookie>> cookies;
   while(1) {
     std::string line = fp.getLine();
     if(line.empty()) {
@@ -120,9 +121,9 @@ std::vector<Cookie> NsCookieParser::parse
     if(line[0] == '#') {
       continue;
     }
-    Cookie c;
-    if(parseNsCookie(c, line, creationTime)) {
-      cookies.push_back(c);
+    auto c = parseNsCookie(line, creationTime);
+    if(c) {
+      cookies.push_back(std::move(c));
     }
   }
   return cookies;

+ 3 - 1
src/NsCookieParser.h

@@ -39,6 +39,7 @@
 
 #include <string>
 #include <vector>
+#include <memory>
 
 namespace aria2 {
 
@@ -50,7 +51,8 @@ public:
 
   ~NsCookieParser();
 
-  std::vector<Cookie> parse(const std::string& filename, time_t creationTime);
+  std::vector<std::unique_ptr<Cookie>> parse
+  (const std::string& filename, time_t creationTime);
 };
 
 } // namespace aria2

+ 18 - 18
src/Sqlite3CookieParser.cc

@@ -102,8 +102,8 @@ int cookieRowMapper(void* data, int columns, char** values, char** names)
   if(columns != 7 || !values[0] || !values[1] || !values[4]) {
     return 0;
   }
-  std::vector<Cookie>& cookies =
-    *reinterpret_cast<std::vector<Cookie>*>(data);
+  std::vector<std::unique_ptr<Cookie>>& cookies =
+    *reinterpret_cast<std::vector<std::unique_ptr<Cookie>>*>(data);
   size_t val0len = strlen(values[0]);
   std::string cookieDomain
     (util::lstripIter(&values[0][0], &values[0][val0len], '.'),
@@ -122,29 +122,29 @@ int cookieRowMapper(void* data, int columns, char** values, char** names)
   if(!values[6] || !parseTime(lastAccessTime, values[6])) {
     return 0;
   }
-  Cookie c(cookieName,
-           toString(values[5]), // value
-           expiryTime,
-           true, // persistent
-           cookieDomain,
-           util::isNumericHost(cookieDomain) ||
-           (values[0] && values[0][0] != '.'), // hostOnly
-           cookiePath,
-           values[2] && strcmp(values[2], "1") == 0, //secure
-           false,
-           lastAccessTime // creation time. Set this later.
-           );
-  cookies.push_back(c);
+  cookies.push_back(make_unique<Cookie>
+                    (std::move(cookieName),
+                     toString(values[5]), // value
+                     expiryTime,
+                     true, // persistent
+                     std::move(cookieDomain),
+                     util::isNumericHost(cookieDomain) ||
+                     (values[0] && values[0][0] != '.'), // hostOnly
+                     std::move(cookiePath),
+                     values[2] && strcmp(values[2], "1") == 0, //secure
+                     false,
+                     lastAccessTime // creation time. Set this later.
+                     ));
   return 0;
 }
 } // namespace
 
-void Sqlite3CookieParser::parse(std::vector<Cookie>& cookies)
+std::vector<std::unique_ptr<Cookie>> Sqlite3CookieParser::parse()
 {
   if(!db_) {
     throw DL_ABORT_EX(fmt("SQLite3 database is not opened."));
   }
-  std::vector<Cookie> tcookies;
+  auto tcookies = std::vector<std::unique_ptr<Cookie>>{};
   char* sqlite3ErrMsg = 0;
   int ret = sqlite3_exec(db_, getQuery(), cookieRowMapper,
                          &tcookies, &sqlite3ErrMsg);
@@ -157,7 +157,7 @@ void Sqlite3CookieParser::parse(std::vector<Cookie>& cookies)
     throw DL_ABORT_EX
       (fmt("Failed to read SQLite3 database: %s", errMsg.c_str()));
   }
-  cookies.swap(tcookies);
+  return tcookies;
 }
 
 } // namespace aria2

+ 2 - 1
src/Sqlite3CookieParser.h

@@ -39,6 +39,7 @@
 
 #include <string>
 #include <vector>
+#include <memory>
 
 #include <sqlite3.h>
 
@@ -55,7 +56,7 @@ public:
   // Loads cookies from sqlite3 database and stores them in cookies.
   // When loading is successful, cookies stored in cookies initially
   // are removed. Otherwise, the content of cookies is unchanged.
-  void parse(std::vector<Cookie>& cookies);
+  std::vector<std::unique_ptr<Cookie>> parse();
 protected:
   // Returns SQL select statement to get 1 record of cookie.  The sql
   // must return 6 columns in the following order: host, path,

+ 24 - 24
src/cookie_helper.cc

@@ -216,9 +216,8 @@ bool parseDate
   return time != -1;
 }
 
-bool parse
-(Cookie& cookie,
- const std::string& cookieStr,
+std::unique_ptr<Cookie> parse
+(const std::string& cookieStr,
  const std::string& requestHost,
  const std::string& defaultPath,
  time_t creationTime)
@@ -231,13 +230,13 @@ bool parse
   std::string::const_iterator eq = cookieStr.begin();
   for(; eq != nvEnd && *eq != '='; ++eq);
   if(eq == nvEnd) {
-    return false;
+    return std::unique_ptr<Cookie>{};
   }
   std::pair<std::string::const_iterator,
             std::string::const_iterator> p =
     util::stripIter(cookieStr.begin(), eq);
   if(p.first == p.second) {
-    return false;
+    return std::unique_ptr<Cookie>{};
   }
   Scip cookieName(p.first, p.second);
   p = util::stripIter(eq+1, nvEnd);
@@ -276,18 +275,18 @@ bool parse
       if(parseDate(expiryTime, attrp.first, attrp.second)) {
         foundExpires = true;
       } else {
-        return false;
+        return std::unique_ptr<Cookie>{};
       }
     } else if(util::strieq(p.first, p.second, "max-age")) {
       if(attrp.first == attrp.second ||
          (!in(static_cast<unsigned char>(*attrp.first), 0x30u, 0x39u) &&
           *attrp.first != '-')) {
-        return false;
+        return std::unique_ptr<Cookie>{};
       }
       for(std::string::const_iterator s = attrp.first+1,
             eos = attrp.second; s != eos; ++s) {
         if(!in(static_cast<unsigned char>(*s), 0x30u, 0x39u)) {
-          return false;
+          return std::unique_ptr<Cookie>{};
         }
       }
       int64_t delta;
@@ -306,17 +305,17 @@ bool parse
           }
         }
       } else {
-        return false;
+        return std::unique_ptr<Cookie>{};
       }
     } else if(util::strieq(p.first, p.second, "domain")) {
       if(attrp.first == attrp.second) {
-        return false;
+        return std::unique_ptr<Cookie>{};
       }
       std::string::const_iterator noDot = attrp.first;
       std::string::const_iterator end = attrp.second;
       for(; noDot != end && *noDot == '.'; ++noDot);
       if(noDot == end) {
-        return false;
+        return std::unique_ptr<Cookie>{};
       }
       cookieDomain.assign(noDot, end);
     } else if(util::strieq(p.first, p.second, "path")) {
@@ -349,26 +348,27 @@ bool parse
   } else if(domainMatch(canonicalizedHost, cookieDomain)) {
     hostOnly = util::isNumericHost(canonicalizedHost);
   } else {
-    return false;
+    return std::unique_ptr<Cookie>{};
   }
 
   if(cookiePath.empty()) {
     cookiePath = defaultPath;
   }
 
-  cookie.setName(cookieName.first, cookieName.second);
-  cookie.setValue(cookieValue.first, cookieValue.second);
-  cookie.setExpiryTime(expiryTime);
-  cookie.setPersistent(persistent);
-  cookie.setDomain(cookieDomain);
-  cookie.setHostOnly(hostOnly);
-  cookie.setPath(cookiePath);
-  cookie.setSecure(secure);
-  cookie.setHttpOnly(httpOnly);
-  cookie.setCreationTime(creationTime);
-  cookie.setLastAccessTime(creationTime);
+  auto cookie = make_unique<Cookie>();
+  cookie->setName(cookieName.first, cookieName.second);
+  cookie->setValue(cookieValue.first, cookieValue.second);
+  cookie->setExpiryTime(expiryTime);
+  cookie->setPersistent(persistent);
+  cookie->setDomain(std::move(cookieDomain));
+  cookie->setHostOnly(hostOnly);
+  cookie->setPath(std::move(cookiePath));
+  cookie->setSecure(secure);
+  cookie->setHttpOnly(httpOnly);
+  cookie->setCreationTime(creationTime);
+  cookie->setLastAccessTime(creationTime);
 
-  return true;
+  return cookie;
 }
 
 bool goodPath

+ 3 - 3
src/cookie_helper.h

@@ -38,6 +38,7 @@
 #include "common.h"
 
 #include <string>
+#include <memory>
 
 #include "a2time.h"
 
@@ -52,9 +53,8 @@ bool parseDate
  std::string::const_iterator first,
  std::string::const_iterator last);
 
-bool parse
-(Cookie& cookie,
- const std::string& cookieStr,
+std::unique_ptr<Cookie> parse
+(const std::string& cookieStr,
  const std::string& requestHost,
  const std::string& defaultPath,
  time_t creationTime);

+ 75 - 75
test/CookieHelperTest.cc

@@ -88,145 +88,145 @@ void CookieHelperTest::testParse()
   {
     std::string str = "ID=123456789; expires=Sun, 10-Jun-2007 11:00:00 GMT;"
       "path=/foo; domain=localhost; secure;httpOnly   ";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
-    CPPUNIT_ASSERT_EQUAL(std::string("ID"), c.getName());
-    CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
-    CPPUNIT_ASSERT_EQUAL((time_t)1181473200, c.getExpiryTime());
-    CPPUNIT_ASSERT(c.getPersistent());
-    CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
-    CPPUNIT_ASSERT(!c.getHostOnly());
-    CPPUNIT_ASSERT_EQUAL(std::string("/foo"), c.getPath());
-    CPPUNIT_ASSERT(c.getSecure());
-    CPPUNIT_ASSERT(c.getHttpOnly());
-    CPPUNIT_ASSERT_EQUAL((time_t)141, c.getCreationTime());
-    CPPUNIT_ASSERT_EQUAL((time_t)141, c.getLastAccessTime());
+    auto c = cookie::parse(str, "localhost", "/", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL(std::string("ID"), c->getName());
+    CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c->getValue());
+    CPPUNIT_ASSERT_EQUAL((time_t)1181473200, c->getExpiryTime());
+    CPPUNIT_ASSERT(c->getPersistent());
+    CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c->getDomain());
+    CPPUNIT_ASSERT(!c->getHostOnly());
+    CPPUNIT_ASSERT_EQUAL(std::string("/foo"), c->getPath());
+    CPPUNIT_ASSERT(c->getSecure());
+    CPPUNIT_ASSERT(c->getHttpOnly());
+    CPPUNIT_ASSERT_EQUAL((time_t)141, c->getCreationTime());
+    CPPUNIT_ASSERT_EQUAL((time_t)141, c->getLastAccessTime());
   }
   {
     std::string str = "id=; Max-Age=0;";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
-    CPPUNIT_ASSERT_EQUAL(std::string("id"), c.getName());
-    CPPUNIT_ASSERT_EQUAL((time_t)0, c.getExpiryTime());
-    CPPUNIT_ASSERT(c.getPersistent());
-    CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
-    CPPUNIT_ASSERT(c.getHostOnly());
-    CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-    CPPUNIT_ASSERT(!c.getSecure());
-    CPPUNIT_ASSERT(!c.getHttpOnly());
+    auto c = cookie::parse(str, "localhost", "/", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL(std::string("id"), c->getName());
+    CPPUNIT_ASSERT_EQUAL((time_t)0, c->getExpiryTime());
+    CPPUNIT_ASSERT(c->getPersistent());
+    CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c->getDomain());
+    CPPUNIT_ASSERT(c->getHostOnly());
+    CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
+    CPPUNIT_ASSERT(!c->getSecure());
+    CPPUNIT_ASSERT(!c->getHttpOnly());
   }
   {
     std::string str = "id=; Max-Age=-100;";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
-    CPPUNIT_ASSERT_EQUAL((time_t)0, c.getExpiryTime());
-    CPPUNIT_ASSERT(c.getPersistent());
+    auto c = cookie::parse(str, "localhost", "/", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL((time_t)0, c->getExpiryTime());
+    CPPUNIT_ASSERT(c->getPersistent());
   }
   {
     std::string str = "id=; Max-Age=100;";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
-    CPPUNIT_ASSERT_EQUAL((time_t)creationDate+100, c.getExpiryTime());
-    CPPUNIT_ASSERT(c.getPersistent());
+    auto c = cookie::parse(str, "localhost", "/", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL((time_t)creationDate+100, c->getExpiryTime());
+    CPPUNIT_ASSERT(c->getPersistent());
   }
   {
     std::string str = "id=; Max-Age=9223372036854775807;";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
-    CPPUNIT_ASSERT_EQUAL(std::numeric_limits<time_t>::max(), c.getExpiryTime());
-    CPPUNIT_ASSERT(c.getPersistent());
+    auto c = cookie::parse(str, "localhost", "/", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL(std::numeric_limits<time_t>::max(),
+                         c->getExpiryTime());
+    CPPUNIT_ASSERT(c->getPersistent());
   }
   {
     std::string str = "id=; Max-Age=X;";
-    Cookie c;
-    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT(!cookie::parse(str, "localhost", "/", creationDate));
   }
   {
     std::string str = "id=; Max-Age=100garbage;";
-    Cookie c;
-    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT(!cookie::parse(str, "localhost", "/", creationDate));
   }
   {
     std::string str = "id=; Max-Age=100;expires=Sun, 10-Jun-2007 11:00:00 GMT;";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
-    CPPUNIT_ASSERT_EQUAL((time_t)creationDate+100, c.getExpiryTime());
-    CPPUNIT_ASSERT(c.getPersistent());
+    auto c = cookie::parse(str, "localhost", "/", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL((time_t)creationDate+100, c->getExpiryTime());
+    CPPUNIT_ASSERT(c->getPersistent());
   }
   {
     // Cookie data cannot be parsed.
     std::string str = "id=; expires=2007-10-01 11:00:00 GMT;";
-    Cookie c;
-    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/", creationDate));
+    CPPUNIT_ASSERT(!cookie::parse(str, "localhost", "/", creationDate));
   }
   {
     std::string str = "id=;";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
-    CPPUNIT_ASSERT(!c.getPersistent());
+    auto c = cookie::parse(str, "localhost", "/", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT(!c->getPersistent());
   }
   {
     std::string str = "id=; path=abc";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
-    CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
+    auto c = cookie::parse(str, "localhost", "/", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
   }
   {
     std::string str = "id=; domain=.example.org";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "www.example.org", "/",creationDate));
+    CPPUNIT_ASSERT(cookie::parse(str, "www.example.org", "/",creationDate));
   }
   {
     // Fails because request host does not domain-match with cookie
     // domain.
     std::string str = "id=; domain=www.example.org";
-    Cookie c;
-    CPPUNIT_ASSERT(!cookie::parse(c, str, "example.org", "/", creationDate));
+    CPPUNIT_ASSERT(!cookie::parse(str, "example.org", "/", creationDate));
   }
   {
     std::string str = "id=; domain=.";
-    Cookie c;
-    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/",creationDate));
+    CPPUNIT_ASSERT(!cookie::parse(str, "localhost", "/",creationDate));
   }
   {
     std::string str = "";
-    Cookie c;
-    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/",creationDate));
+    CPPUNIT_ASSERT(!cookie::parse(str, "localhost", "/",creationDate));
   }
   {
     std::string str = "=";
-    Cookie c;
-    CPPUNIT_ASSERT(!cookie::parse(c, str, "localhost", "/",creationDate));
+    CPPUNIT_ASSERT(!cookie::parse(str, "localhost", "/",creationDate));
   }
   {
     // Use domain last time seen.
     std::string str = "id=;domain=a.example.org;domain=.example.org";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "b.example.org", "/",creationDate));
-    CPPUNIT_ASSERT_EQUAL(std::string("example.org"), c.getDomain());
+    auto c = cookie::parse(str, "b.example.org", "/",creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL(std::string("example.org"), c->getDomain());
   }
   {
     // numeric host
     std::string str = "id=;";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "192.168.0.1", "/",creationDate));
-    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c.getDomain());
-    CPPUNIT_ASSERT(c.getHostOnly());
+    auto c = cookie::parse(str, "192.168.0.1", "/",creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c->getDomain());
+    CPPUNIT_ASSERT(c->getHostOnly());
   }
   {
     // numeric host
     std::string str = "id=; domain=192.168.0.1";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "192.168.0.1", "/",creationDate));
-    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c.getDomain());
-    CPPUNIT_ASSERT(c.getHostOnly());
+    auto c = cookie::parse(str, "192.168.0.1", "/",creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c->getDomain());
+    CPPUNIT_ASSERT(c->getHostOnly());
   }
   {
     // DQUOTE around cookie-value
     std::string str = "id=\"foo\";";
-    Cookie c;
-    CPPUNIT_ASSERT(cookie::parse(c, str, "localhost", "/", creationDate));
-    CPPUNIT_ASSERT_EQUAL(std::string("foo"), c.getValue());
+    auto c = cookie::parse(str, "localhost", "/", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL(std::string("foo"), c->getValue());
+  }
+  {
+    // Default path
+    std::string str = "id=;";
+    auto c = cookie::parse(str, "localhost", "/foo", creationDate);
+    CPPUNIT_ASSERT(c);
+    CPPUNIT_ASSERT_EQUAL(std::string("/foo"), c->getPath());
   }
 }
 

+ 274 - 207
test/CookieStorageTest.cc

@@ -12,6 +12,7 @@
 #include "RecoverableException.h"
 #include "File.h"
 #include "TestUtil.h"
+#include "TimerA2.h"
 
 namespace aria2 {
 
@@ -29,14 +30,13 @@ class CookieStorageTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testSaveNsFormat_fail);
   CPPUNIT_TEST(testCookieIsFull);
   CPPUNIT_TEST(testDomainIsFull);
+  CPPUNIT_TEST(testEviction);
   CPPUNIT_TEST_SUITE_END();
 public:
   void setUp() {}
 
   void tearDown() {}
 
-  void dumpCookie(std::vector<Cookie>& cookies, const CookieStorage& cs);
-
   void testStore();
   void testParseAndStore();
   void testCriteriaFind();
@@ -48,56 +48,67 @@ public:
   void testSaveNsFormat_fail();
   void testCookieIsFull();
   void testDomainIsFull();
+  void testEviction();
 };
 
 
 CPPUNIT_TEST_SUITE_REGISTRATION(CookieStorageTest);
 
-void CookieStorageTest::dumpCookie
-(std::vector<Cookie>& cookies, const CookieStorage& st)
+namespace {
+std::vector<const Cookie*> dumpCookie(const CookieStorage& st)
 {
-  st.dumpCookie(std::back_inserter(cookies));
-  std::sort(cookies.begin(), cookies.end(), CookieSorter());
+  auto res = std::vector<const Cookie*>{};
+  st.dumpCookie(std::back_inserter(res));
+  std::sort(res.begin(), res.end(), CookieSorter());
+  return res;
 }
+} // namespace
 
 void CookieStorageTest::testStore()
 {
   time_t now = 1000;
-  CookieStorage st;
-  Cookie goodCookie(createCookie("k", "v", "localhost", true, "/", false));
-  CPPUNIT_ASSERT(st.store(goodCookie, now));
+  auto st = CookieStorage{};
+  auto goodCookie = []() {
+    return createCookie("k", "v", "localhost", true, "/", false);
+  };
+  CPPUNIT_ASSERT(st.store(goodCookie(), now));
   CPPUNIT_ASSERT_EQUAL((size_t)1, st.size());
-  CPPUNIT_ASSERT(st.contains(goodCookie));
+  CPPUNIT_ASSERT(st.contains(*goodCookie()));
 
-  Cookie anotherCookie(createCookie("k", "v", "mirror",  true, "/", true));
-  CPPUNIT_ASSERT(st.store(anotherCookie, now));
+  auto anotherCookie = []() {
+    return createCookie("k", "v", "mirror",  true, "/", true);
+  };
+  CPPUNIT_ASSERT(st.store(anotherCookie(), now));
   CPPUNIT_ASSERT_EQUAL((size_t)2, st.size());
-  CPPUNIT_ASSERT(st.contains(anotherCookie));
-  CPPUNIT_ASSERT(st.contains(goodCookie));
+  CPPUNIT_ASSERT(st.contains(*anotherCookie()));
+  CPPUNIT_ASSERT(st.contains(*goodCookie()));
 
-  Cookie updateGoodCookie(createCookie("k", "v2", "localhost",  true,
-                                       "/", false));
-  CPPUNIT_ASSERT(st.store(updateGoodCookie, now));
+  auto updateGoodCookie = []() {
+    return createCookie("k", "v2", "localhost",  true, "/", false);
+  };
+  CPPUNIT_ASSERT(st.store(updateGoodCookie(), now));
   CPPUNIT_ASSERT_EQUAL((size_t)2, st.size());
-  CPPUNIT_ASSERT(st.contains(updateGoodCookie));
-  CPPUNIT_ASSERT(st.contains(anotherCookie));
+  CPPUNIT_ASSERT(st.contains(*updateGoodCookie()));
+  CPPUNIT_ASSERT(st.contains(*anotherCookie()));
 
-  Cookie expireGoodCookie(createCookie("k", "v3", 0, "localhost", true,
-                                       "/", false));
-  CPPUNIT_ASSERT(!st.store(expireGoodCookie, now));
+  auto expireGoodCookie = []() {
+    return createCookie("k", "v3", 0, "localhost", true, "/", false);
+  };
+  CPPUNIT_ASSERT(!st.store(expireGoodCookie(), now));
   CPPUNIT_ASSERT_EQUAL((size_t)1, st.size());
-  CPPUNIT_ASSERT(st.contains(anotherCookie));
+  CPPUNIT_ASSERT(st.contains(*anotherCookie()));
 
-  Cookie fromNumericHost(createCookie("k", "v", "192.168.1.1", true,
-                                      "/", false));
-  CPPUNIT_ASSERT(st.store(fromNumericHost, now));
+  auto fromNumericHost = []() {
+    return createCookie("k", "v", "192.168.1.1", true, "/", false);
+  };
+  CPPUNIT_ASSERT(st.store(fromNumericHost(), now));
   CPPUNIT_ASSERT_EQUAL((size_t)2, st.size());
-  CPPUNIT_ASSERT(st.contains(fromNumericHost));
+  CPPUNIT_ASSERT(st.contains(*fromNumericHost()));
 }
 
 void CookieStorageTest::testParseAndStore()
 {
-  CookieStorage st;
+  auto st = CookieStorage{};
   time_t now = 1000;
   std::string localhostCookieStr = "k=v;"
     " expires=Fri, 01 Jan 2038 00:00:00 GMT; path=/; domain=localhost;";
@@ -122,221 +133,233 @@ void CookieStorageTest::testParseAndStore()
 
 void CookieStorageTest::testCriteriaFind()
 {
-  CookieStorage st;
+  auto st = CookieStorage{};
   time_t now = 1000;
 
-  Cookie alpha(createCookie("alpha", "ALPHA", "aria2.org", false,  "/", false));
-  Cookie bravo(createCookie("bravo", "BRAVO", 1060, "aria2.org", false,
-                            "/foo", false));
-  Cookie charlie(createCookie("charlie", "CHARLIE", "aria2.org", false,
-                              "/", true));
-  Cookie delta(createCookie("delta", "DELTA", "aria2.org", false,
-                            "/foo/bar", false));
-  Cookie echo(createCookie("echo", "ECHO", "www.dl.aria2.org", false,
-                           "/", false));
-  Cookie foxtrot(createCookie("foxtrot", "FOXTROT", "sf.net", false,
-                              "/", false));
-  Cookie golf(createCookie("golf", "GOLF", "192.168.1.1",  true,
-                           "/", false));
-  Cookie hotel1(createCookie("hotel", "HOTEL1", "samename.x", false,
-                             "/", false));
-  Cookie hotel2(createCookie("hotel", "HOTEL2", "samename.x", false,
-                             "/hotel", false));
-  Cookie hotel3(createCookie("hotel", "HOTEL3", "samename.x", false,
-                             "/bar/wine", false));
-  Cookie hotel4(createCookie("hotel", "HOTEL4", "samename.x", false,
-                             "/bar/", false));
-  Cookie india1(createCookie("india", "INDIA1", "default.domain",  true,
-                             "/foo", false));
-  Cookie india2(createCookie("india", "INDIA2", "default.domain", false,
-                             "/",  false));
-  Cookie juliet1(createCookie("juliet", "JULIET1", "localhost", true,
-                              "/foo", false));
-
-  CPPUNIT_ASSERT(st.store(alpha, now));
-  CPPUNIT_ASSERT(st.store(bravo, now));
-  CPPUNIT_ASSERT(st.store(charlie, now));
-  CPPUNIT_ASSERT(st.store(delta, now));
-  CPPUNIT_ASSERT(st.store(echo, now));
-  CPPUNIT_ASSERT(st.store(foxtrot, now));
-  CPPUNIT_ASSERT(st.store(golf, now));
-  CPPUNIT_ASSERT(st.store(hotel1, now));
-  CPPUNIT_ASSERT(st.store(hotel2, now));
-  CPPUNIT_ASSERT(st.store(hotel3, now));
-  CPPUNIT_ASSERT(st.store(hotel4, now));
-  CPPUNIT_ASSERT(st.store(india1, now));
-  CPPUNIT_ASSERT(st.store(india2, now));
-  CPPUNIT_ASSERT(st.store(juliet1, now));
-
-  std::vector<Cookie> aria2Slash = st.criteriaFind("www.dl.aria2.org", "/",
-                                                   0, false);
+  auto alpha = []() {
+    return createCookie("alpha", "ALPHA", "aria2.org", false,  "/", false);
+  };
+  auto bravo = []() {
+    return createCookie("bravo", "BRAVO", 1060, "aria2.org", false,
+                        "/foo", false);
+  };
+  auto charlie = []() {
+    return createCookie("charlie", "CHARLIE", "aria2.org", false,
+                        "/", true);
+  };
+  auto delta = []() {
+    return createCookie("delta", "DELTA", "aria2.org", false,
+                        "/foo/bar", false);
+  };
+  auto echo = []() {
+    return createCookie("echo", "ECHO", "www.dl.aria2.org", false,
+                        "/", false);
+  };
+  auto foxtrot = []() {
+    return createCookie("foxtrot", "FOXTROT", "sf.net", false,
+                        "/", false);
+  };
+  auto golf = []() {
+    return createCookie("golf", "GOLF", "192.168.1.1",  true,
+                        "/", false);
+  };
+  auto hotel1 = []() {
+    return createCookie("hotel", "HOTEL1", "samename.x", false,
+                        "/", false);
+  };
+  auto hotel2 = []() {
+    return createCookie("hotel", "HOTEL2", "samename.x", false,
+                        "/hotel", false);
+  };
+  auto hotel3 = []() {
+    return createCookie("hotel", "HOTEL3", "samename.x", false,
+                        "/bar/wine", false);
+  };
+  auto hotel4 = []() {
+    return createCookie("hotel", "HOTEL4", "samename.x", false,
+                        "/bar/", false);
+  };
+  auto india1 = []() {
+    return createCookie("india", "INDIA1", "default.domain",  true,
+                        "/foo", false);
+  };
+  auto india2 = []() {
+    return createCookie("india", "INDIA2", "default.domain", false,
+                        "/",  false);
+  };
+  auto juliet1 = []() {
+    return createCookie("juliet", "JULIET1", "localhost", true,
+                        "/foo", false);
+  };
+
+  CPPUNIT_ASSERT(st.store(alpha(), now));
+  CPPUNIT_ASSERT(st.store(bravo(), now));
+  CPPUNIT_ASSERT(st.store(charlie(), now));
+  CPPUNIT_ASSERT(st.store(delta(), now));
+  CPPUNIT_ASSERT(st.store(echo(), now));
+  CPPUNIT_ASSERT(st.store(foxtrot(), now));
+  CPPUNIT_ASSERT(st.store(golf(), now));
+  CPPUNIT_ASSERT(st.store(hotel1(), now));
+  CPPUNIT_ASSERT(st.store(hotel2(), now));
+  CPPUNIT_ASSERT(st.store(hotel3(), now));
+  CPPUNIT_ASSERT(st.store(hotel4(), now));
+  CPPUNIT_ASSERT(st.store(india1(), now));
+  CPPUNIT_ASSERT(st.store(india2(), now));
+  CPPUNIT_ASSERT(st.store(juliet1(), now));
+
+  auto aria2Slash = st.criteriaFind("www.dl.aria2.org", "/", 0, false);
   CPPUNIT_ASSERT_EQUAL((size_t)2, aria2Slash.size());
-  CPPUNIT_ASSERT(std::find(aria2Slash.begin(), aria2Slash.end(), alpha)
-                 != aria2Slash.end());
-  CPPUNIT_ASSERT(std::find(aria2Slash.begin(), aria2Slash.end(), echo)
-                 != aria2Slash.end());
+  CPPUNIT_ASSERT(derefFind(aria2Slash, alpha()));
+  CPPUNIT_ASSERT(derefFind(aria2Slash, echo()));
 
-  std::vector<Cookie> aria2SlashFoo = st.criteriaFind("www.dl.aria2.org","/foo",
-                                                      0, false);
+  auto aria2SlashFoo = st.criteriaFind("www.dl.aria2.org","/foo", 0, false);
   CPPUNIT_ASSERT_EQUAL((size_t)3, aria2SlashFoo.size());
-  CPPUNIT_ASSERT_EQUAL(std::string("bravo"), aria2SlashFoo[0].getName());
-  CPPUNIT_ASSERT(std::find(aria2SlashFoo.begin(), aria2SlashFoo.end(), alpha)
-                 != aria2SlashFoo.end());
-  CPPUNIT_ASSERT(std::find(aria2SlashFoo.begin(), aria2SlashFoo.end(), echo)
-                 != aria2SlashFoo.end());
-
-  std::vector<Cookie> aria2Expires = st.criteriaFind("www.dl.aria2.org", "/foo",
-                                                     1120,
-                                                     false);
+  CPPUNIT_ASSERT_EQUAL(std::string("bravo"), aria2SlashFoo[0]->getName());
+  CPPUNIT_ASSERT(derefFind(aria2SlashFoo, alpha()));
+  CPPUNIT_ASSERT(derefFind(aria2SlashFoo, echo()));
+
+  auto aria2Expires = st.criteriaFind("www.dl.aria2.org", "/foo", 1120, false);
   CPPUNIT_ASSERT_EQUAL((size_t)2, aria2Expires.size());
-  CPPUNIT_ASSERT(std::find(aria2Expires.begin(), aria2Expires.end(), alpha)
-                 != aria2Expires.end());
-  CPPUNIT_ASSERT(std::find(aria2Expires.begin(), aria2Expires.end(), echo)
-                 != aria2Expires.end());
+  CPPUNIT_ASSERT(derefFind(aria2Expires, alpha()));
+  CPPUNIT_ASSERT(derefFind(aria2Expires, echo()));
 
-  std::vector<Cookie> dlAria2 = st.criteriaFind("dl.aria2.org", "/", 0, false);
+  auto dlAria2 = st.criteriaFind("dl.aria2.org", "/", 0, false);
   CPPUNIT_ASSERT_EQUAL((size_t)1, dlAria2.size());
-  CPPUNIT_ASSERT_EQUAL(std::string("alpha"), dlAria2[0].getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("alpha"), dlAria2[0]->getName());
 
-  std::vector<Cookie> tailmatchAria2 = st.criteriaFind("myaria2.org", "/", 0,
-                                                       false);
+  auto tailmatchAria2 = st.criteriaFind("myaria2.org", "/", 0, false);
   CPPUNIT_ASSERT(tailmatchAria2.empty());
 
-  std::vector<Cookie> numericHostCookies = st.criteriaFind("192.168.1.1", "/",0,
-                                                           false);
+  auto numericHostCookies = st.criteriaFind("192.168.1.1", "/",0, false);
   CPPUNIT_ASSERT_EQUAL((size_t)1, numericHostCookies.size());
-  CPPUNIT_ASSERT_EQUAL(std::string("golf"), numericHostCookies[0].getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("golf"), numericHostCookies[0]->getName());
 
-  std::vector<Cookie> sameNameCookies =
-    st.criteriaFind("samename.x", "/bar/wine", 0, false);
+  auto sameNameCookies = st.criteriaFind("samename.x", "/bar/wine", 0, false);
   CPPUNIT_ASSERT_EQUAL((size_t)3, sameNameCookies.size());
-  CPPUNIT_ASSERT_EQUAL(std::string("HOTEL3"), sameNameCookies[0].getValue());
-  CPPUNIT_ASSERT_EQUAL(std::string("HOTEL4"), sameNameCookies[1].getValue());
-  CPPUNIT_ASSERT_EQUAL(std::string("HOTEL1"), sameNameCookies[2].getValue());
+  CPPUNIT_ASSERT_EQUAL(std::string("HOTEL3"), sameNameCookies[0]->getValue());
+  CPPUNIT_ASSERT_EQUAL(std::string("HOTEL4"), sameNameCookies[1]->getValue());
+  CPPUNIT_ASSERT_EQUAL(std::string("HOTEL1"), sameNameCookies[2]->getValue());
 
-  std::vector<Cookie> defaultDomainCookies =
-    st.criteriaFind("default.domain", "/foo", 0, false);
+  auto defaultDomainCookies = st.criteriaFind("default.domain", "/foo", 0,
+                                              false);
   CPPUNIT_ASSERT_EQUAL((size_t)2, defaultDomainCookies.size());
   CPPUNIT_ASSERT_EQUAL(std::string("INDIA1"),
-                       defaultDomainCookies[0].getValue());
+                       defaultDomainCookies[0]->getValue());
   CPPUNIT_ASSERT_EQUAL(std::string("INDIA2"),
-                       defaultDomainCookies[1].getValue());
+                       defaultDomainCookies[1]->getValue());
   defaultDomainCookies =
     st.criteriaFind("sub.default.domain", "/foo", 0, false);
   CPPUNIT_ASSERT_EQUAL((size_t)1, defaultDomainCookies.size());
   CPPUNIT_ASSERT_EQUAL(std::string("INDIA2"),
-                       defaultDomainCookies[0].getValue());
+                       defaultDomainCookies[0]->getValue());
 
   // localhost.local case
-  std::vector<Cookie> localDomainCookies =
-    st.criteriaFind("localhost", "/foo", 0, false);
+  auto localDomainCookies = st.criteriaFind("localhost", "/foo", 0, false);
   CPPUNIT_ASSERT_EQUAL((size_t)1, localDomainCookies.size());
   CPPUNIT_ASSERT_EQUAL(std::string("JULIET1"),
-                       localDomainCookies[0].getValue());
+                       localDomainCookies[0]->getValue());
 }
 
 void CookieStorageTest::testCriteriaFind_cookieOrder()
 {
-  CookieStorage st;
-  Cookie a(createCookie("a", "0", "host", true, "/", false));
-  a.setCreationTime(1000);
-  Cookie b(createCookie("b", "0", "host", true, "/foo", false));
-  b.setCreationTime(5000);
-  Cookie c(createCookie("c", "0", "host", true, "/foo", false));
-  c.setCreationTime(4000);
-  Cookie d(createCookie("d", "0", "host", true, "/foo/bar", false));
-  d.setCreationTime(6000);
-
-  st.store(a, 0);
-  st.store(b, 0);
-  st.store(c, 0);
-  st.store(d, 0);
-
-  std::vector<Cookie> cookies = st.criteriaFind("host", "/foo/bar", 0, false);
+  auto st = CookieStorage{};
+  auto a = createCookie("a", "0", "host", true, "/", false);
+  a->setCreationTime(1000);
+  auto b = createCookie("b", "0", "host", true, "/foo", false);
+  b->setCreationTime(5000);
+  auto c = createCookie("c", "0", "host", true, "/foo", false);
+  c->setCreationTime(4000);
+  auto d = createCookie("d", "0", "host", true, "/foo/bar", false);
+  d->setCreationTime(6000);
+
+  st.store(std::move(a), 0);
+  st.store(std::move(b), 0);
+  st.store(std::move(c), 0);
+  st.store(std::move(d), 0);
+
+  auto cookies = st.criteriaFind("host", "/foo/bar", 0, false);
   CPPUNIT_ASSERT_EQUAL((size_t)4, cookies.size());
-  CPPUNIT_ASSERT_EQUAL(std::string("d"), cookies[0].getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("c"), cookies[1].getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("b"), cookies[2].getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("a"), cookies[3].getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("d"), cookies[0]->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("c"), cookies[1]->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("b"), cookies[2]->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("a"), cookies[3]->getName());
 }
 
 void CookieStorageTest::testLoad()
 {
-  CookieStorage st;
+  auto st = CookieStorage{};
 
   st.load(A2_TEST_DIR"/nscookietest.txt", 1001);
 
   CPPUNIT_ASSERT_EQUAL((size_t)4, st.size());
 
-  std::vector<Cookie> cookies;
-  dumpCookie(cookies, st);
+  auto cookies = dumpCookie(st);
 
-  Cookie c = cookies[0];
-  CPPUNIT_ASSERT_EQUAL(std::string("passwd"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("secret"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL(std::numeric_limits<time_t>::max(), c.getExpiryTime());
-  CPPUNIT_ASSERT(!c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("/cgi-bin"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c.getDomain());
-  CPPUNIT_ASSERT(c.getHostOnly());
-  CPPUNIT_ASSERT(!c.getSecure());
+  auto c = cookies[0];
+  CPPUNIT_ASSERT_EQUAL(std::string("passwd"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("secret"), c->getValue());
+  CPPUNIT_ASSERT_EQUAL(std::numeric_limits<time_t>::max(), c->getExpiryTime());
+  CPPUNIT_ASSERT(!c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("/cgi-bin"), c->getPath());
+  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c->getDomain());
+  CPPUNIT_ASSERT(c->getHostOnly());
+  CPPUNIT_ASSERT(!c->getSecure());
 
   c = cookies[1];
-  CPPUNIT_ASSERT_EQUAL(std::string("novalue"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string(""), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiryTime());
-  CPPUNIT_ASSERT(c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("example.org"), c.getDomain());
-  CPPUNIT_ASSERT(!c.getHostOnly());
-  CPPUNIT_ASSERT(!c.getSecure());
+  CPPUNIT_ASSERT_EQUAL(std::string("novalue"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string(""), c->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c->getExpiryTime());
+  CPPUNIT_ASSERT(c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
+  CPPUNIT_ASSERT_EQUAL(std::string("example.org"), c->getDomain());
+  CPPUNIT_ASSERT(!c->getHostOnly());
+  CPPUNIT_ASSERT(!c->getSecure());
 
   c = cookies[2];
-  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c.getExpiryTime());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
-  CPPUNIT_ASSERT(c.getHostOnly());
-  CPPUNIT_ASSERT(c.getSecure());
+  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)2147483647, c->getExpiryTime());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
+  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c->getDomain());
+  CPPUNIT_ASSERT(c->getHostOnly());
+  CPPUNIT_ASSERT(c->getSecure());
 
   c = cookies[3];
-  CPPUNIT_ASSERT_EQUAL(std::string("TAX"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("1000"), c.getValue());
-  CPPUNIT_ASSERT((time_t)INT32_MAX <= c.getExpiryTime());
-  CPPUNIT_ASSERT(c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT_EQUAL(std::string("overflow"), c.getDomain());
-  CPPUNIT_ASSERT(!c.getSecure());
+  CPPUNIT_ASSERT_EQUAL(std::string("TAX"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("1000"), c->getValue());
+  CPPUNIT_ASSERT((time_t)INT32_MAX <= c->getExpiryTime());
+  CPPUNIT_ASSERT(c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
+  CPPUNIT_ASSERT_EQUAL(std::string("overflow"), c->getDomain());
+  CPPUNIT_ASSERT(!c->getSecure());
 }
 
 void CookieStorageTest::testLoad_sqlite3()
 {
-  CookieStorage st;
+  auto st = CookieStorage{};
 #ifdef HAVE_SQLITE3
   st.load(A2_TEST_DIR"/cookies.sqlite", 1000);
   CPPUNIT_ASSERT_EQUAL((size_t)2, st.size());
-  std::vector<Cookie> cookies;
-  dumpCookie(cookies, st);
-  Cookie c = cookies[0];
-  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c.getExpiryTime());
-  CPPUNIT_ASSERT(c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
-  CPPUNIT_ASSERT(c.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT(c.getSecure());
+  auto cookies = dumpCookie(st);
+  auto c = cookies[0];
+  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c->getExpiryTime());
+  CPPUNIT_ASSERT(c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c->getDomain());
+  CPPUNIT_ASSERT(c->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
+  CPPUNIT_ASSERT(c->getSecure());
 
   c = cookies[1];
-  CPPUNIT_ASSERT_EQUAL(std::string("foo"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("bar"), c.getValue());
-  CPPUNIT_ASSERT((time_t)INT32_MAX <= c.getExpiryTime());
-  CPPUNIT_ASSERT(c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("overflow.time_t.org"), c.getDomain());
-  CPPUNIT_ASSERT(!c.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), c.getPath());
-  CPPUNIT_ASSERT(!c.getSecure());
+  CPPUNIT_ASSERT_EQUAL(std::string("foo"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("bar"), c->getValue());
+  CPPUNIT_ASSERT((time_t)INT32_MAX <= c->getExpiryTime());
+  CPPUNIT_ASSERT(c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("overflow.time_t.org"), c->getDomain());
+  CPPUNIT_ASSERT(!c->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), c->getPath());
+  CPPUNIT_ASSERT(!c->getSecure());
 
 #else // !HAVE_SQLITE3
   CPPUNIT_ASSERT(!st.load(A2_TEST_DIR"/cookies.sqlite", 1000));
@@ -345,7 +368,7 @@ void CookieStorageTest::testLoad_sqlite3()
 
 void CookieStorageTest::testLoad_fileNotfound()
 {
-  CookieStorage st;
+  auto st = CookieStorage{};
   CPPUNIT_ASSERT(!st.load("./aria2_CookieStorageTest_testLoad_fileNotfound",0));
 }
 
@@ -354,22 +377,22 @@ void CookieStorageTest::testSaveNsFormat()
   // TODO add cookie with default domain
   std::string filename = A2_TEST_OUT_DIR"/aria2_CookieStorageTest_testSaveNsFormat";
   File(filename).remove();
-  CookieStorage st;
+  auto st = CookieStorage{};
   time_t now = 1000;
-  st.store(Cookie(createCookie("favorite", "classic", "domain.org", false,
-                               "/config",true)), now);
-  st.store(Cookie(createCookie("uid", "tujikawa", now, "domain.org", true,
-                               "/",false)), now);
+  st.store(createCookie("favorite", "classic", "domain.org", false,
+                        "/config",true), now);
+  st.store(createCookie("uid", "tujikawa", now, "domain.org", true,
+                        "/",false), now);
   CPPUNIT_ASSERT(st.saveNsFormat(filename));
-  CookieStorage loadst;
+  auto loadst = CookieStorage{};
   loadst.load(filename, now);
   CPPUNIT_ASSERT_EQUAL((size_t)2, loadst.size());
 
-  std::vector<Cookie> cookies;
-  dumpCookie(cookies, loadst);
+  auto cookies = dumpCookie(loadst);
 
-  CPPUNIT_ASSERT_EQUAL(std::string("favorite"), cookies[0].getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("uid"), cookies[1].getName());
+  CPPUNIT_ASSERT_EQUAL((size_t)2, cookies.size());
+  CPPUNIT_ASSERT_EQUAL(std::string("favorite"), cookies[0]->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("uid"), cookies[1]->getName());
 }
 
 void CookieStorageTest::testSaveNsFormat_fail()
@@ -380,17 +403,16 @@ void CookieStorageTest::testSaveNsFormat_fail()
   f.remove();
   f.mkdirs();
   CPPUNIT_ASSERT(f.isDir());
-  CookieStorage st;
+  auto st = CookieStorage{};
   CPPUNIT_ASSERT(!st.saveNsFormat(filename));
 }
 
 void CookieStorageTest::testCookieIsFull()
 {
-  CookieStorage st;
+  auto st = CookieStorage{};
   for(size_t i = 0; i < CookieStorage::MAX_COOKIE_PER_DOMAIN+1; ++i) {
-    Cookie c(createCookie("k"+util::itos(i), "v", "aria2.org", false,
-                          "/", false));
-    st.store(c, 0);
+    st.store(createCookie("k"+util::itos(i), "v", "aria2.org", false,
+                          "/", false), 0);
   }
   CPPUNIT_ASSERT_EQUAL((size_t)CookieStorage::MAX_COOKIE_PER_DOMAIN, st.size());
 }
@@ -399,13 +421,58 @@ void CookieStorageTest::testDomainIsFull()
 {
   // See DOMAIN_EVICTION_TRIGGER and DOMAIN_EVICTION_RATE in
   // CookieStorage.cc
-  CookieStorage st;
+  auto st = CookieStorage{};
   for(size_t i = 0; i < 2001; ++i) {
-    Cookie c(createCookie("k", "v", "domain"+util::itos(i), true,
-                          "/", false));
-    st.store(c, 0);
+    st.store(createCookie("k", "v", "domain"+util::itos(i), true,
+                          "/", false), 0);
   }
   CPPUNIT_ASSERT_EQUAL((size_t)1801, st.size());
 }
 
+void CookieStorageTest::testEviction()
+{
+  auto st = CookieStorage{};
+  auto alpha = []() {
+    return createCookie("a", "alpha", "aria2.sf.net", false, "/", false);
+  };
+  auto bravo = []() {
+    return createCookie("b", "bravo", "d.aria2.sf.net", false, "/", false);
+  };
+  auto charlie = []() {
+    return createCookie("c", "charlie", "a2.github.com", false, "/", false);
+  };
+  auto delta = []() {
+    return createCookie("d", "delta", "aria2.sf.net", false, "/", false);
+  };
+  st.store(alpha(), 0);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, st.getLruTrackerSize());
+  st.store(bravo(), 1);
+  CPPUNIT_ASSERT_EQUAL((size_t)2, st.getLruTrackerSize());
+  st.store(charlie(), 2);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, st.getLruTrackerSize());
+  st.store(delta(), 0);
+  CPPUNIT_ASSERT_EQUAL((size_t)3, st.getLruTrackerSize());
+
+  // aria2.sf.net will be evicted
+  st.evictNode(1);
+  CPPUNIT_ASSERT_EQUAL((size_t)2, st.getLruTrackerSize());
+  CPPUNIT_ASSERT(!st.contains(*alpha()));
+  CPPUNIT_ASSERT(st.contains(*bravo()));
+  CPPUNIT_ASSERT(st.contains(*charlie()));
+  CPPUNIT_ASSERT(!st.contains(*delta()));
+
+  // d.aria2.sf.net will be evicted
+  st.evictNode(1);
+  CPPUNIT_ASSERT_EQUAL((size_t)1, st.getLruTrackerSize());
+  CPPUNIT_ASSERT(!st.contains(*bravo()));
+  CPPUNIT_ASSERT(st.contains(*charlie()));
+
+  // a2.github.com will be evicted
+  st.evictNode(1);
+  CPPUNIT_ASSERT_EQUAL((size_t)0, st.getLruTrackerSize());
+  CPPUNIT_ASSERT(!st.contains(*charlie()));
+  CPPUNIT_ASSERT_EQUAL((size_t)0, st.size());
+  CPPUNIT_ASSERT(!st.getRootNode()->hasNext());
+}
+
 } // namespace aria2

+ 54 - 55
test/CookieTest.cc

@@ -35,67 +35,66 @@ CPPUNIT_TEST_SUITE_REGISTRATION(CookieTest);
 
 void CookieTest::testOperatorEqual()
 {
-  Cookie a(createCookie("k", "v", "localhost", true, "/", false));
-  Cookie b(createCookie("k", "v", "localhost", true, "/", true));
-  Cookie wrongPath(createCookie("k", "v", "localhost", true, "/a", false));
-  Cookie wrongDomain(createCookie("k", "v", "mydomain", true, "/", false));
-  Cookie wrongName(createCookie("h", "v", "localhost", true, "/a", false));
-  Cookie caseSensitiveName(createCookie("K", "v", "localhost", true,
-                                        "/a", false));
-  CPPUNIT_ASSERT(a == b);
-  CPPUNIT_ASSERT(!(a == wrongPath));
-  CPPUNIT_ASSERT(!(a == wrongDomain));
-  CPPUNIT_ASSERT(!(a == wrongName));
-  CPPUNIT_ASSERT(!(a == caseSensitiveName));
+  auto a = createCookie("k", "v", "localhost", true, "/", false);
+  auto b = createCookie("k", "v", "localhost", true, "/", true);
+  auto wrongPath = createCookie("k", "v", "localhost", true, "/a", false);
+  auto wrongDomain = createCookie("k", "v", "mydomain", true, "/", false);
+  auto wrongName = createCookie("h", "v", "localhost", true, "/a", false);
+  auto caseSensitiveName = createCookie("K", "v", "localhost", true,
+                                        "/a", false);
+  CPPUNIT_ASSERT(*a == *b);
+  CPPUNIT_ASSERT(!(*a == *wrongPath));
+  CPPUNIT_ASSERT(!(*a == *wrongDomain));
+  CPPUNIT_ASSERT(!(*a == *wrongName));
+  CPPUNIT_ASSERT(!(*a == *caseSensitiveName));
 }
 
 void CookieTest::testMatch()
 {
-  Cookie c(createCookie("k", "v", "aria2.org", false, "/downloads", false));
-  Cookie c2(createCookie("k", "v", "aria2.org", false, "/downloads", false));
-  Cookie c3(createCookie("k", "v", "aria2.org", true, "/downloads", false));
-  Cookie c4(createCookie("k", "v", "localhost", true, "/downloads", false));
-  CPPUNIT_ASSERT(c.match("www.aria2.org", "/downloads", 0, false));
-  CPPUNIT_ASSERT(c2.match("www.aria2.org", "/downloads", 0, false));
-  CPPUNIT_ASSERT(!c.match("www.aria.org", "/downloads", 0, false));
-  CPPUNIT_ASSERT(!c.match("www.aria2.org", "/examples", 0, false));
-  CPPUNIT_ASSERT(c.match("www.aria2.org", "/downloads", 0, true));
-  CPPUNIT_ASSERT(c.match("www.aria2.org", "/downloads/latest", 0, false));
-  CPPUNIT_ASSERT(!c.match("www.aria2.org", "/downloadss/latest", 0, false));
-  CPPUNIT_ASSERT(!c.match("www.aria2.org", "/DOWNLOADS", 0, false));
-  CPPUNIT_ASSERT(!c3.match("www.aria2.org", "/downloads", 0, false));
-  CPPUNIT_ASSERT(c4.match("localhost", "/downloads", 0, false));
-
-  Cookie secureCookie(createCookie("k", "v", "secure.aria2.org", false,
-                                   "/", true));
-  CPPUNIT_ASSERT(secureCookie.match("secure.aria2.org", "/", 0, true));
-  CPPUNIT_ASSERT(!secureCookie.match("secure.aria2.org", "/", 0, false));
-  CPPUNIT_ASSERT(!secureCookie.match("ssecure.aria2.org", "/", 0, true));
-  CPPUNIT_ASSERT(secureCookie.match("www.secure.aria2.org", "/", 0, true));
-
-  Cookie expireTest(createCookie("k", "v", 1000, "aria2.org", false,
-                                 "/", false));
-  CPPUNIT_ASSERT(expireTest.match("www.aria2.org", "/", 999, false));
-  CPPUNIT_ASSERT(expireTest.match("www.aria2.org", "/", 1000, false));
-  CPPUNIT_ASSERT(!expireTest.match("www.aria2.org", "/", 1001, false));
-
-  Cookie fromNumericHost(createCookie("k", "v", "192.168.1.1", true,
-                                      "/foo", false));
-  CPPUNIT_ASSERT(fromNumericHost.match("192.168.1.1", "/foo", 0, false));
-  CPPUNIT_ASSERT(!fromNumericHost.match("www.aria2.org", "/foo", 0, false));
-  CPPUNIT_ASSERT(!fromNumericHost.match("1.192.168.1.1", "/foo", 0, false));
-  CPPUNIT_ASSERT(!fromNumericHost.match("192.168.1.1", "/", 0, false));
+  auto c = createCookie("k", "v", "aria2.org", false, "/downloads", false);
+  auto c2 = createCookie("k", "v", "aria2.org", false, "/downloads", false);
+  auto c3 = createCookie("k", "v", "aria2.org", true, "/downloads", false);
+  auto c4 = createCookie("k", "v", "localhost", true, "/downloads", false);
+  CPPUNIT_ASSERT(c->match("www.aria2.org", "/downloads", 0, false));
+  CPPUNIT_ASSERT(c2->match("www.aria2.org", "/downloads", 0, false));
+  CPPUNIT_ASSERT(!c->match("www.aria.org", "/downloads", 0, false));
+  CPPUNIT_ASSERT(!c->match("www.aria2.org", "/examples", 0, false));
+  CPPUNIT_ASSERT(c->match("www.aria2.org", "/downloads", 0, true));
+  CPPUNIT_ASSERT(c->match("www.aria2.org", "/downloads/latest", 0, false));
+  CPPUNIT_ASSERT(!c->match("www.aria2.org", "/downloadss/latest", 0, false));
+  CPPUNIT_ASSERT(!c->match("www.aria2.org", "/DOWNLOADS", 0, false));
+  CPPUNIT_ASSERT(!c3->match("www.aria2.org", "/downloads", 0, false));
+  CPPUNIT_ASSERT(c4->match("localhost", "/downloads", 0, false));
+
+  auto secureCookie = createCookie("k", "v", "secure.aria2.org", false,
+                                   "/", true);
+  CPPUNIT_ASSERT(secureCookie->match("secure.aria2.org", "/", 0, true));
+  CPPUNIT_ASSERT(!secureCookie->match("secure.aria2.org", "/", 0, false));
+  CPPUNIT_ASSERT(!secureCookie->match("ssecure.aria2.org", "/", 0, true));
+  CPPUNIT_ASSERT(secureCookie->match("www.secure.aria2.org", "/", 0, true));
+
+  auto expireTest = createCookie("k", "v", 1000, "aria2.org", false,
+                                 "/", false);
+  CPPUNIT_ASSERT(expireTest->match("www.aria2.org", "/", 999, false));
+  CPPUNIT_ASSERT(expireTest->match("www.aria2.org", "/", 1000, false));
+  CPPUNIT_ASSERT(!expireTest->match("www.aria2.org", "/", 1001, false));
+
+  auto fromNumericHost = createCookie("k", "v", "192.168.1.1", true,
+                                      "/foo", false);
+  CPPUNIT_ASSERT(fromNumericHost->match("192.168.1.1", "/foo", 0, false));
+  CPPUNIT_ASSERT(!fromNumericHost->match("www.aria2.org", "/foo", 0, false));
+  CPPUNIT_ASSERT(!fromNumericHost->match("1.192.168.1.1", "/foo", 0, false));
+  CPPUNIT_ASSERT(!fromNumericHost->match("192.168.1.1", "/", 0, false));
 }
 
 void CookieTest::testIsExpired()
 {
-  Cookie cookie(createCookie("k", "v", 1000, "localhost", true,
-                                    "/", false));
-  CPPUNIT_ASSERT(cookie.isExpired(1001));
-  CPPUNIT_ASSERT(!cookie.isExpired(1000));
-  CPPUNIT_ASSERT(!cookie.isExpired(999));
-  Cookie sessionCookie(createCookie("k", "v", "localhost", true, "/", false));
-  CPPUNIT_ASSERT(!sessionCookie.isExpired(INT32_MAX));
+  auto cookie = createCookie("k", "v", 1000, "localhost", true, "/", false);
+  CPPUNIT_ASSERT(cookie->isExpired(1001));
+  CPPUNIT_ASSERT(!cookie->isExpired(1000));
+  CPPUNIT_ASSERT(!cookie->isExpired(999));
+  auto sessionCookie = createCookie("k", "v", "localhost", true, "/", false);
+  CPPUNIT_ASSERT(!sessionCookie->isExpired(INT32_MAX));
 }
 
 void CookieTest::testToNsCookieFormat()
@@ -103,12 +102,12 @@ void CookieTest::testToNsCookieFormat()
   CPPUNIT_ASSERT_EQUAL
     (std::string(".domain.org\tTRUE\t/\tFALSE\t12345678\thello\tworld"),
      createCookie("hello", "world", 12345678, "domain.org", false, "/",false)
-     .toNsCookieFormat());
+     ->toNsCookieFormat());
   // Session cookie
   CPPUNIT_ASSERT_EQUAL
     (std::string("domain.org\tFALSE\t/\tTRUE\t0\thello\tworld"),
      createCookie("hello", "world", "domain.org", true, "/", true)
-     .toNsCookieFormat());
+     ->toNsCookieFormat());
 }
 
 } // namespace aria2

+ 18 - 13
test/HttpRequestTest.cc

@@ -368,6 +368,13 @@ void HttpRequestTest::testCreateRequest_ftp()
   CPPUNIT_ASSERT_EQUAL(expectedText, httpRequest.createRequest());
 }
 
+template<typename InputIterator>
+void foo(CookieStorage& st, InputIterator first, InputIterator last, time_t t)
+{
+  for(; first != last; ++first) {
+    st.store(*first, t);
+  }
+}
 void HttpRequestTest::testCreateRequest_with_cookie()
 {
   auto request = std::make_shared<Request>();
@@ -376,19 +383,17 @@ void HttpRequestTest::testCreateRequest_with_cookie()
   auto segment = std::make_shared<PiecedSegment>(1024*1024, p);
   auto fileEntry = std::make_shared<FileEntry>("file", 1024*1024*10, 0);
 
-  std::vector<Cookie> cookies {
-    createCookie("name1", "value1", "localhost", true, "/archives", false),
-    createCookie("name2", "value2", "localhost", true,
-                 "/archives/download",  false),
-    createCookie("name3", "value3", "aria2.org", false,
-                 "/archives/download",  false),
-    createCookie("name4", "value4", "aria2.org", false, "/archives/", true),
-    createCookie("name5", "value5", "example.org", false, "/", false)
-  };
-  CookieStorage st;
-  for(auto c : cookies) {
-    CPPUNIT_ASSERT(st.store(c, 0));
-  }
+  auto st = CookieStorage{};
+  CPPUNIT_ASSERT(st.store(createCookie("name1", "value1", "localhost", true,
+                                       "/archives", false), 0));
+  CPPUNIT_ASSERT(st.store(createCookie("name2", "value2", "localhost", true,
+                                       "/archives/download",  false), 0));
+  CPPUNIT_ASSERT(st.store(createCookie("name3", "value3", "aria2.org", false,
+                                       "/archives/download",  false), 0));
+  CPPUNIT_ASSERT(st.store(createCookie("name4", "value4", "aria2.org", false,
+                                       "/archives/", true), 0));
+  CPPUNIT_ASSERT(st.store(createCookie("name5", "value5", "example.org", false,
+                                       "/", false), 0));
 
   HttpRequest httpRequest;
 

+ 5 - 3
test/HttpResponseTest.cc

@@ -4,6 +4,7 @@
 
 #include <cppunit/extensions/HelperMacros.h>
 
+#include "TestUtil.h"
 #include "prefs.h"
 #include "PiecedSegment.h"
 #include "Piece.h"
@@ -511,10 +512,11 @@ void HttpResponseTest::testRetrieveCookie()
 
   CPPUNIT_ASSERT_EQUAL((size_t)2, st.size());
 
-  std::vector<Cookie> cookies;
+  auto cookies = std::vector<const Cookie*>{};
   st.dumpCookie(std::back_inserter(cookies));
-  CPPUNIT_ASSERT_EQUAL(std::string("k2=v2"), cookies[0].toString());
-  CPPUNIT_ASSERT_EQUAL(std::string("k3=v3"), cookies[1].toString());
+  std::sort(std::begin(cookies), std::end(cookies), CookieSorter());
+  CPPUNIT_ASSERT_EQUAL(std::string("k2=v2"), cookies[0]->toString());
+  CPPUNIT_ASSERT_EQUAL(std::string("k3=v3"), cookies[1]->toString());
 }
 
 void HttpResponseTest::testSupportsPersistentConnection()

+ 50 - 50
test/NsCookieParserTest.cc

@@ -33,58 +33,58 @@ void NsCookieParserTest::testParse()
 {
   NsCookieParser parser;
   time_t now = 0;
-  std::vector<Cookie> cookies = parser.parse(A2_TEST_DIR"/nscookietest.txt", now);
+  auto cookies = parser.parse(A2_TEST_DIR"/nscookietest.txt", now);
   CPPUNIT_ASSERT_EQUAL((size_t)5, cookies.size());
 
-  Cookie c = cookies[0];
-  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c.getExpiryTime());
-  CPPUNIT_ASSERT(c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c.getDomain());
-  CPPUNIT_ASSERT(c.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT(c.getSecure());
-
-  c = cookies[1];
-  CPPUNIT_ASSERT_EQUAL(std::string("user"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("me"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)1000, c.getExpiryTime());
-  CPPUNIT_ASSERT(c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("expired"), c.getDomain());
-  CPPUNIT_ASSERT(c.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT(!c.getSecure());
-
-  c = cookies[2];
-  CPPUNIT_ASSERT_EQUAL(std::string("passwd"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("secret"), c.getValue());
-  CPPUNIT_ASSERT_EQUAL(std::numeric_limits<time_t>::max(), c.getExpiryTime());
-  CPPUNIT_ASSERT(!c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c.getDomain());
-  CPPUNIT_ASSERT(c.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/cgi-bin"), c.getPath());
-  CPPUNIT_ASSERT(!c.getSecure());
-
-  c = cookies[3];
-  CPPUNIT_ASSERT_EQUAL(std::string("TAX"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("1000"), c.getValue());
-  CPPUNIT_ASSERT((time_t)INT32_MAX <= c.getExpiryTime());
-  CPPUNIT_ASSERT(c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("overflow"), c.getDomain());
-  CPPUNIT_ASSERT(c.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT(!c.getSecure());
-
-  c = cookies[4];
-  CPPUNIT_ASSERT_EQUAL(std::string("novalue"), c.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string(""), c.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c.getExpiryTime());
-  CPPUNIT_ASSERT(c.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("example.org"), c.getDomain());
-  CPPUNIT_ASSERT(!c.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), c.getPath());
-  CPPUNIT_ASSERT(!c.getSecure());
+  auto c = cookies[0].get();
+  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), c->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c->getExpiryTime());
+  CPPUNIT_ASSERT(c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), c->getDomain());
+  CPPUNIT_ASSERT(c->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
+  CPPUNIT_ASSERT(c->getSecure());
+
+  c = cookies[1].get();
+  CPPUNIT_ASSERT_EQUAL(std::string("user"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("me"), c->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)1000, c->getExpiryTime());
+  CPPUNIT_ASSERT(c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("expired"), c->getDomain());
+  CPPUNIT_ASSERT(c->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
+  CPPUNIT_ASSERT(!c->getSecure());
+
+  c = cookies[2].get();
+  CPPUNIT_ASSERT_EQUAL(std::string("passwd"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("secret"), c->getValue());
+  CPPUNIT_ASSERT_EQUAL(std::numeric_limits<time_t>::max(), c->getExpiryTime());
+  CPPUNIT_ASSERT(!c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), c->getDomain());
+  CPPUNIT_ASSERT(c->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/cgi-bin"), c->getPath());
+  CPPUNIT_ASSERT(!c->getSecure());
+
+  c = cookies[3].get();
+  CPPUNIT_ASSERT_EQUAL(std::string("TAX"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("1000"), c->getValue());
+  CPPUNIT_ASSERT((time_t)INT32_MAX <= c->getExpiryTime());
+  CPPUNIT_ASSERT(c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("overflow"), c->getDomain());
+  CPPUNIT_ASSERT(c->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
+  CPPUNIT_ASSERT(!c->getSecure());
+
+  c = cookies[4].get();
+  CPPUNIT_ASSERT_EQUAL(std::string("novalue"), c->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string(""), c->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, c->getExpiryTime());
+  CPPUNIT_ASSERT(c->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("example.org"), c->getDomain());
+  CPPUNIT_ASSERT(!c->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), c->getPath());
+  CPPUNIT_ASSERT(!c->getSecure());
 }
 
 void NsCookieParserTest::testParse_fileNotFound()

+ 64 - 67
test/Sqlite3CookieParserTest.cc

@@ -35,55 +35,53 @@ CPPUNIT_TEST_SUITE_REGISTRATION(Sqlite3CookieParserTest);
 
 void Sqlite3CookieParserTest::testMozParse()
 {
-  Sqlite3MozCookieParser parser(A2_TEST_DIR"/cookies.sqlite");
-  std::vector<Cookie> cookies;
-  parser.parse(cookies);
+  auto parser = Sqlite3MozCookieParser{A2_TEST_DIR"/cookies.sqlite"};
+  auto cookies = parser.parse();
   CPPUNIT_ASSERT_EQUAL((size_t)3, cookies.size());
 
-  const Cookie& localhost = cookies[0];
-  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), localhost.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), localhost.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, localhost.getExpiryTime());
-  CPPUNIT_ASSERT(localhost.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), localhost.getDomain());
-  CPPUNIT_ASSERT(localhost.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), localhost.getPath());
-  CPPUNIT_ASSERT(localhost.getSecure());
-  CPPUNIT_ASSERT_EQUAL((time_t)3000, localhost.getLastAccessTime());
-  CPPUNIT_ASSERT_EQUAL((time_t)3000, localhost.getCreationTime());
-
-  const Cookie& nullValue = cookies[1];
-  CPPUNIT_ASSERT_EQUAL(std::string("uid"), nullValue.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string(""), nullValue.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)0, nullValue.getExpiryTime());
-  CPPUNIT_ASSERT(nullValue.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("null_value.com"), nullValue.getDomain());
-  CPPUNIT_ASSERT(!nullValue.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), nullValue.getPath());
-  CPPUNIT_ASSERT(!nullValue.getSecure());
+  auto& localhost = cookies[0];
+  CPPUNIT_ASSERT_EQUAL(std::string("JSESSIONID"), localhost->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("123456789"), localhost->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)INT32_MAX, localhost->getExpiryTime());
+  CPPUNIT_ASSERT(localhost->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("localhost"), localhost->getDomain());
+  CPPUNIT_ASSERT(localhost->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), localhost->getPath());
+  CPPUNIT_ASSERT(localhost->getSecure());
+  CPPUNIT_ASSERT_EQUAL((time_t)3000, localhost->getLastAccessTime());
+  CPPUNIT_ASSERT_EQUAL((time_t)3000, localhost->getCreationTime());
+
+  auto& nullValue = cookies[1];
+  CPPUNIT_ASSERT_EQUAL(std::string("uid"), nullValue->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string(""), nullValue->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)0, nullValue->getExpiryTime());
+  CPPUNIT_ASSERT(nullValue->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("null_value.com"), nullValue->getDomain());
+  CPPUNIT_ASSERT(!nullValue->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), nullValue->getPath());
+  CPPUNIT_ASSERT(!nullValue->getSecure());
 
   // See row id=3 has no name, so it is skipped.
 
-  const Cookie& overflowTime = cookies[2];
-  CPPUNIT_ASSERT_EQUAL(std::string("foo"), overflowTime.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("bar"), overflowTime.getValue());
-  CPPUNIT_ASSERT((time_t)INT32_MAX <= overflowTime.getExpiryTime());
-  CPPUNIT_ASSERT(overflowTime.getPersistent());
+  auto& overflowTime = cookies[2];
+  CPPUNIT_ASSERT_EQUAL(std::string("foo"), overflowTime->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("bar"), overflowTime->getValue());
+  CPPUNIT_ASSERT((time_t)INT32_MAX <= overflowTime->getExpiryTime());
+  CPPUNIT_ASSERT(overflowTime->getPersistent());
   CPPUNIT_ASSERT_EQUAL(std::string("overflow.time_t.org"),
-                       overflowTime.getDomain());
-  CPPUNIT_ASSERT(!overflowTime.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), overflowTime.getPath());
-  CPPUNIT_ASSERT(!overflowTime.getSecure());
+                       overflowTime->getDomain());
+  CPPUNIT_ASSERT(!overflowTime->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/path/to"), overflowTime->getPath());
+  CPPUNIT_ASSERT(!overflowTime->getSecure());
 
   // See row id=5 has bad path, so it is skipped.
 }
 
 void Sqlite3CookieParserTest::testMozParse_fileNotFound()
 {
-  Sqlite3MozCookieParser parser("fileNotFound");
+  auto parser = Sqlite3MozCookieParser{"fileNotFound"};
   try {
-    std::vector<Cookie> cookies;
-    parser.parse(cookies);
+    parser.parse();
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(RecoverableException& e) {
     // SUCCESS
@@ -95,10 +93,9 @@ void Sqlite3CookieParserTest::testMozParse_fileNotFound()
 
 void Sqlite3CookieParserTest::testMozParse_badfile()
 {
-  Sqlite3MozCookieParser parser(A2_TEST_DIR"/badcookies.sqlite");
+  auto parser = Sqlite3MozCookieParser{A2_TEST_DIR"/badcookies.sqlite"};
   try {
-    std::vector<Cookie> cookies;
-    parser.parse(cookies);
+    parser.parse();
     CPPUNIT_FAIL("exception must be thrown.");
   } catch(RecoverableException& e) {
     // SUCCESS
@@ -107,37 +104,37 @@ void Sqlite3CookieParserTest::testMozParse_badfile()
 
 void Sqlite3CookieParserTest::testChromumParse()
 {
-  Sqlite3ChromiumCookieParser parser(A2_TEST_DIR"/chromium_cookies.sqlite");
-  std::vector<Cookie> cookies;
-  parser.parse(cookies);
+  auto parser = Sqlite3ChromiumCookieParser
+    {A2_TEST_DIR"/chromium_cookies.sqlite"};
+  auto cookies = parser.parse();
   CPPUNIT_ASSERT_EQUAL((size_t)3, cookies.size());
 
-  const Cookie& sfnet = cookies[0];
-  CPPUNIT_ASSERT_EQUAL(std::string("mykey"), sfnet.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("pass"), sfnet.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)12345679, sfnet.getExpiryTime());
-  CPPUNIT_ASSERT(sfnet.getPersistent());
+  auto& sfnet = cookies[0];
+  CPPUNIT_ASSERT_EQUAL(std::string("mykey"), sfnet->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("pass"), sfnet->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)12345679, sfnet->getExpiryTime());
+  CPPUNIT_ASSERT(sfnet->getPersistent());
   CPPUNIT_ASSERT_EQUAL(std::string("aria2.sourceforge.net"),
-                       sfnet.getDomain());
-  CPPUNIT_ASSERT(!sfnet.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/"), sfnet.getPath());
-  CPPUNIT_ASSERT(!sfnet.getSecure());
-
-  const Cookie& sfjp = cookies[1];
-  CPPUNIT_ASSERT_EQUAL(std::string("myseckey"), sfjp.getName());
-  CPPUNIT_ASSERT_EQUAL(std::string("pass2"), sfjp.getValue());
-  CPPUNIT_ASSERT_EQUAL((time_t)0, sfjp.getExpiryTime());
-  CPPUNIT_ASSERT(sfjp.getPersistent());
-  CPPUNIT_ASSERT_EQUAL(std::string("aria2.sourceforge.jp"), sfjp.getDomain());
-  CPPUNIT_ASSERT(sfjp.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL(std::string("/profile"), sfjp.getPath());
-  CPPUNIT_ASSERT(sfjp.getSecure());
-
-  const Cookie& localnet = cookies[2];
-  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), localnet.getDomain());
-  CPPUNIT_ASSERT(sfjp.getHostOnly());
-  CPPUNIT_ASSERT_EQUAL((time_t)3000, localnet.getLastAccessTime());
-  CPPUNIT_ASSERT_EQUAL((time_t)3000, localnet.getCreationTime());
+                       sfnet->getDomain());
+  CPPUNIT_ASSERT(!sfnet->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/"), sfnet->getPath());
+  CPPUNIT_ASSERT(!sfnet->getSecure());
+
+  auto& sfjp = cookies[1];
+  CPPUNIT_ASSERT_EQUAL(std::string("myseckey"), sfjp->getName());
+  CPPUNIT_ASSERT_EQUAL(std::string("pass2"), sfjp->getValue());
+  CPPUNIT_ASSERT_EQUAL((time_t)0, sfjp->getExpiryTime());
+  CPPUNIT_ASSERT(sfjp->getPersistent());
+  CPPUNIT_ASSERT_EQUAL(std::string("aria2.sourceforge.jp"), sfjp->getDomain());
+  CPPUNIT_ASSERT(sfjp->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL(std::string("/profile"), sfjp->getPath());
+  CPPUNIT_ASSERT(sfjp->getSecure());
+
+  auto& localnet = cookies[2];
+  CPPUNIT_ASSERT_EQUAL(std::string("192.168.0.1"), localnet->getDomain());
+  CPPUNIT_ASSERT(localnet->getHostOnly());
+  CPPUNIT_ASSERT_EQUAL((time_t)3000, localnet->getLastAccessTime());
+  CPPUNIT_ASSERT_EQUAL((time_t)3000, localnet->getCreationTime());
 }
 
 } // namespace aria2

+ 4 - 4
test/TestUtil.cc

@@ -50,7 +50,7 @@ std::string readFile(const std::string& path)
   return ss.str();
 }
 
-Cookie createCookie
+std::unique_ptr<Cookie> createCookie
 (const std::string& name,
  const std::string& value,
  const std::string& domain,
@@ -58,11 +58,11 @@ Cookie createCookie
  const std::string& path,
  bool secure)
 {
-  return Cookie
+  return make_unique<Cookie>
     (name, value, 0, false, domain, hostOnly, path, secure, false, 0);
 }
 
-Cookie createCookie
+std::unique_ptr<Cookie> createCookie
 (const std::string& name,
  const std::string& value,
  time_t expiryTime,
@@ -71,7 +71,7 @@ Cookie createCookie
  const std::string& path,
  bool secure)
 {
-  return Cookie
+  return make_unique<Cookie>
     (name, value, expiryTime, true, domain, hostOnly, path, secure, false, 0);
 }
 

+ 19 - 6
test/TestUtil.h

@@ -21,17 +21,17 @@ std::string readFile(const std::string& path);
 
 class CookieSorter {
 public:
-  bool operator()(const Cookie& lhs, const Cookie& rhs) const
+  bool operator()(const Cookie* lhs, const Cookie* rhs) const
   {
-    if(lhs.getDomain() == rhs.getDomain()) {
-      return lhs.getName() < rhs.getName();
+    if(lhs->getDomain() == rhs->getDomain()) {
+      return lhs->getName() < rhs->getName();
     } else {
-      return lhs.getDomain() < rhs.getDomain();
+      return lhs->getDomain() < rhs->getDomain();
     }
   }
 };
 
-Cookie createCookie
+std::unique_ptr<Cookie> createCookie
 (const std::string& name,
  const std::string& value,
  const std::string& domain,
@@ -39,7 +39,7 @@ Cookie createCookie
  const std::string& path,
  bool secure);
 
-Cookie createCookie
+std::unique_ptr<Cookie> createCookie
 (const std::string& name,
  const std::string& value,
  time_t expiryTime,
@@ -75,4 +75,17 @@ std::shared_ptr<RequestGroup> createRequestGroup(int32_t pieceLength,
 std::shared_ptr<DownloadResult> createDownloadResult
 (error_code::Value result, const std::string& uri);
 
+namespace {
+template<typename V, typename T>
+bool derefFind(const V& v, const T& t)
+{
+  for(auto i : v) {
+    if(*i == *t) {
+      return true;
+    }
+  }
+  return false;
+}
+} // namespace
+
 } // namespace aria2