Home | History | Annotate | Download | only in http
      1 // Copyright (c) 2008 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "net/http/http_auth_cache.h"
      6 
      7 #include "base/logging.h"
      8 #include "base/string_util.h"
      9 
     10 namespace {
     11 
     12 // Helper to find the containing directory of path. In RFC 2617 this is what
     13 // they call the "last symbolic element in the absolute path".
     14 // Examples:
     15 //   "/foo/bar.txt" --> "/foo/"
     16 //   "/foo/" --> "/foo/"
     17 std::string GetParentDirectory(const std::string& path) {
     18   std::string::size_type last_slash = path.rfind("/");
     19   if (last_slash == std::string::npos) {
     20     // No slash (absolute paths always start with slash, so this must be
     21     // the proxy case which uses empty string).
     22     DCHECK(path.empty());
     23     return path;
     24   }
     25   return path.substr(0, last_slash + 1);
     26 }
     27 
     28 // Debug helper to check that |path| arguments are properly formed.
     29 // (should be absolute path, or empty string).
     30 void CheckPathIsValid(const std::string& path) {
     31   DCHECK(path.empty() || path[0] == '/');
     32 }
     33 
     34 // Return true if |path| is a subpath of |container|. In other words, is
     35 // |container| an ancestor of |path|?
     36 bool IsEnclosingPath(const std::string& container, const std::string& path) {
     37   DCHECK(container.empty() || *(container.end() - 1) == '/');
     38   return (container.empty() && path.empty()) ||
     39          (!container.empty() && StartsWithASCII(path, container, true));
     40 }
     41 
     42 // Debug helper to check that |origin| arguments are properly formed.
     43 void CheckOriginIsValid(const GURL& origin) {
     44   DCHECK(origin.is_valid());
     45   DCHECK(origin.SchemeIs("http") || origin.SchemeIs("https"));
     46   DCHECK(origin.GetOrigin() == origin);
     47 }
     48 
     49 // Functor used by remove_if.
     50 struct IsEnclosedBy {
     51   IsEnclosedBy(const std::string& path) : path(path) { }
     52   bool operator() (const std::string& x) {
     53     return IsEnclosingPath(path, x);
     54   }
     55   const std::string& path;
     56 };
     57 
     58 } // namespace
     59 
     60 namespace net {
     61 
     62 // Performance: O(n), where n is the number of realm entries.
     63 HttpAuthCache::Entry* HttpAuthCache::LookupByRealm(const GURL& origin,
     64                                                    const std::string& realm) {
     65   CheckOriginIsValid(origin);
     66 
     67   // Linear scan through the realm entries.
     68   for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) {
     69     if (it->origin() == origin && it->realm() == realm)
     70       return &(*it);
     71   }
     72   return NULL; // No realm entry found.
     73 }
     74 
     75 // Performance: O(n*m), where n is the number of realm entries, m is the number
     76 // of path entries per realm. Both n amd m are expected to be small; m is
     77 // kept small because AddPath() only keeps the shallowest entry.
     78 HttpAuthCache::Entry* HttpAuthCache::LookupByPath(const GURL& origin,
     79                                                   const std::string& path) {
     80   CheckOriginIsValid(origin);
     81   CheckPathIsValid(path);
     82 
     83   // RFC 2617 section 2:
     84   // A client SHOULD assume that all paths at or deeper than the depth of
     85   // the last symbolic element in the path field of the Request-URI also are
     86   // within the protection space ...
     87   std::string parent_dir = GetParentDirectory(path);
     88 
     89   // Linear scan through the realm entries.
     90   for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) {
     91     if (it->origin() == origin && it->HasEnclosingPath(parent_dir))
     92       return &(*it);
     93   }
     94   return NULL; // No entry found.
     95 }
     96 
     97 HttpAuthCache::Entry* HttpAuthCache::Add(const GURL& origin,
     98                                          HttpAuthHandler* handler,
     99                                          const std::wstring& username,
    100                                          const std::wstring& password,
    101                                          const std::string& path) {
    102   CheckOriginIsValid(origin);
    103   CheckPathIsValid(path);
    104 
    105   // Check for existing entry (we will re-use it if present).
    106   HttpAuthCache::Entry* entry = LookupByRealm(origin, handler->realm());
    107 
    108   if (!entry) {
    109     // Failsafe to prevent unbounded memory growth of the cache.
    110     if (entries_.size() >= kMaxNumRealmEntries) {
    111       LOG(WARNING) << "Num auth cache entries reached limit -- evicting";
    112       entries_.pop_back();
    113     }
    114 
    115     entries_.push_front(Entry());
    116     entry = &entries_.front();
    117     entry->origin_ = origin;
    118   }
    119 
    120   entry->username_ = username;
    121   entry->password_ = password;
    122   entry->handler_ = handler;
    123   entry->AddPath(path);
    124 
    125   return entry;
    126 }
    127 
    128 void HttpAuthCache::Entry::AddPath(const std::string& path) {
    129   std::string parent_dir = GetParentDirectory(path);
    130   if (!HasEnclosingPath(parent_dir)) {
    131     // Remove any entries that have been subsumed by the new entry.
    132     paths_.remove_if(IsEnclosedBy(parent_dir));
    133 
    134     // Failsafe to prevent unbounded memory growth of the cache.
    135     if (paths_.size() >= kMaxNumPathsPerRealmEntry) {
    136       LOG(WARNING) << "Num path entries for " << origin()
    137                    << " has grown too large -- evicting";
    138       paths_.pop_back();
    139     }
    140 
    141     // Add new path.
    142     paths_.push_front(parent_dir);
    143   }
    144 }
    145 
    146 bool HttpAuthCache::Entry::HasEnclosingPath(const std::string& dir) {
    147   DCHECK(GetParentDirectory(dir) == dir);
    148   for (PathList::const_iterator it = paths_.begin(); it != paths_.end();
    149        ++it) {
    150     if (IsEnclosingPath(*it, dir))
    151       return true;
    152   }
    153   return false;
    154 }
    155 
    156 bool HttpAuthCache::Remove(const GURL& origin,
    157                            const std::string& realm,
    158                            const std::wstring& username,
    159                            const std::wstring& password) {
    160   for (EntryList::iterator it = entries_.begin(); it != entries_.end(); ++it) {
    161     if (it->origin() == origin && it->realm() == realm) {
    162       if (username == it->username() && password == it->password()) {
    163         entries_.erase(it);
    164         return true;
    165       }
    166       return false;
    167     }
    168   }
    169   return false;
    170 }
    171 
    172 } // namespace net
    173