diff options
author | cindylau@google.com <cindylau@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-30 22:38:55 +0000 |
---|---|---|
committer | cindylau@google.com <cindylau@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-30 22:38:55 +0000 |
commit | f325f1e12a828a0a3fa74f75e1b2ea8cb49b0102 (patch) | |
tree | f461f2d8912ad9a1ebb4904756497d29c6dbffc7 /net/base | |
parent | 873d97bc112f5e0024e6b5855ed7868d5d8ad554 (diff) | |
download | chromium_src-f325f1e12a828a0a3fa74f75e1b2ea8cb49b0102.zip chromium_src-f325f1e12a828a0a3fa74f75e1b2ea8cb49b0102.tar.gz chromium_src-f325f1e12a828a0a3fa74f75e1b2ea8cb49b0102.tar.bz2 |
Modifies CookieMonster to ease support for the Chrome Extensions cookies API.
Refactors/extracts some methods for reuse in setting cookies programatically,
and in parsing cookie attribute tokens and values.
BUG=38398
TEST=none
Review URL: http://codereview.chromium.org/1566052
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46135 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/cookie_monster.cc | 355 | ||||
-rw-r--r-- | net/base/cookie_monster.h | 68 | ||||
-rw-r--r-- | net/base/cookie_monster_unittest.cc | 125 |
3 files changed, 441 insertions, 107 deletions
diff --git a/net/base/cookie_monster.cc b/net/base/cookie_monster.cc index f0cf00a..f623509 100644 --- a/net/base/cookie_monster.cc +++ b/net/base/cookie_monster.cc @@ -54,6 +54,7 @@ #include "base/string_tokenizer.h" #include "base/string_util.h" #include "googleurl/src/gurl.h" +#include "googleurl/src/url_canon.h" #include "net/base/net_util.h" #include "net/base/registry_controlled_domain.h" @@ -377,6 +378,10 @@ Time CookieMonster::ParseCookieTime(const std::string& time_string) { return Time(); } +bool CookieMonster::DomainIsHostOnly(const std::string& domain_string) { + return (domain_string.empty() || domain_string[0] != '.'); +} + // Returns the effective TLD+1 for a given host. This only makes sense for http // and https schemes. For other schemes, the host will be returned unchanged // (minus any leading .). @@ -385,27 +390,28 @@ static std::string GetEffectiveDomain(const std::string& scheme, if (scheme == "http" || scheme == "https") return RegistryControlledDomainService::GetDomainAndRegistry(host); - if (!host.empty() && host[0] == '.') + if (!CookieMonster::DomainIsHostOnly(host)) return host.substr(1); return host; } -// Determine the cookie domain key to use for setting the specified cookie. +// Determine the cookie domain key to use for setting a cookie with the +// specified domain attribute string. // On success returns true, and sets cookie_domain_key to either a // -host cookie key (ex: "google.com") // -domain cookie key (ex: ".google.com") -static bool GetCookieDomainKey(const GURL& url, - const CookieMonster::ParsedCookie& pc, - std::string* cookie_domain_key) { +static bool GetCookieDomainKeyWithString(const GURL& url, + const std::string& domain_string, + std::string* cookie_domain_key) { const std::string url_host(url.host()); - // If no domain was specified in the cookie, default to a host cookie. + // If no domain was specified in the domain string, default to a host cookie. // We match IE/Firefox in allowing a domain=IPADDR if it matches the url // ip address hostname exactly. It should be treated as a host cookie. - if (!pc.HasDomain() || pc.Domain().empty() || - (url.HostIsIPAddress() && url_host == pc.Domain())) { + if (domain_string.empty() || + (url.HostIsIPAddress() && url_host == domain_string)) { *cookie_domain_key = url_host; - DCHECK((*cookie_domain_key)[0] != '.'); + DCHECK(CookieMonster::DomainIsHostOnly(*cookie_domain_key)); return true; } @@ -417,7 +423,7 @@ static bool GetCookieDomainKey(const GURL& url, // also treats domain=.....my.domain.com like domain=.my.domain.com, but // neither IE nor Safari do this, and we don't either. url_canon::CanonHostInfo ignored; - std::string cookie_domain(net::CanonicalizeHost(pc.Domain(), &ignored)); + std::string cookie_domain(net::CanonicalizeHost(domain_string, &ignored)); if (cookie_domain.empty()) return false; if (cookie_domain[0] != '.') @@ -447,8 +453,18 @@ static bool GetCookieDomainKey(const GURL& url, return true; } -static std::string CanonPath(const GURL& url, - const CookieMonster::ParsedCookie& pc) { +// Determine the cookie domain key to use for setting the specified cookie. +static bool GetCookieDomainKey(const GURL& url, + const CookieMonster::ParsedCookie& pc, + std::string* cookie_domain_key) { + std::string domain_string; + if (pc.HasDomain()) + domain_string = pc.Domain(); + return GetCookieDomainKeyWithString(url, domain_string, cookie_domain_key); +} + +static std::string CanonPathWithString(const GURL& url, + const std::string& path_string) { // The RFC says the path should be a prefix of the current URL path. // However, Mozilla allows you to set any path for compatibility with // broken websites. We unfortunately will mimic this behavior. We try @@ -456,8 +472,8 @@ static std::string CanonPath(const GURL& url, // default the path to something reasonable. // The path was supplied in the cookie, we'll take it. - if (pc.HasPath() && !pc.Path().empty() && pc.Path()[0] == '/') - return pc.Path(); + if (!path_string.empty() && path_string[0] == '/') + return path_string; // The path was not supplied in the cookie or invalid, we will default // to the current URL path. @@ -477,6 +493,14 @@ static std::string CanonPath(const GURL& url, return url_path.substr(0, idx); } +static std::string CanonPath(const GURL& url, + const CookieMonster::ParsedCookie& pc) { + std::string path_string; + if (pc.HasPath()) + path_string = pc.Path(); + return CanonPathWithString(url, path_string); +} + static Time CanonExpiration(const CookieMonster::ParsedCookie& pc, const Time& current, const CookieOptions& options) { @@ -577,20 +601,59 @@ bool CookieMonster::SetCookieWithCreationTimeAndOptions( COOKIE_DLOG(WARNING) << "Failed to allocate CanonicalCookie"; return false; } + return SetCanonicalCookie(&cc, cookie_domain, creation_time, options); +} + +bool CookieMonster::SetCookieWithDetails( + const GURL& url, const std::string& name, const std::string& value, + const std::string& domain, const std::string& path, + const base::Time& expiration_time, bool secure, bool http_only) { + if (!HasCookieableScheme(url)) + return false; + + // Expect a valid domain attribute with no illegal characters. + std::string parsed_domain = ParsedCookie::ParseValueString(domain); + if (parsed_domain != domain) + return false; + std::string cookie_domain; + if (!GetCookieDomainKeyWithString(url, parsed_domain, &cookie_domain)) + return false; + + AutoLock autolock(lock_); + InitIfNecessary(); + + Time creation_time = CurrentTime(); + last_time_seen_ = creation_time; + + scoped_ptr<CanonicalCookie> cc; + cc.reset(CanonicalCookie::Create( + url, name, value, path, creation_time, expiration_time, + secure, http_only)); + + if (!cc.get()) + return false; + + CookieOptions options; + options.set_include_httponly(); + return SetCanonicalCookie(&cc, cookie_domain, creation_time, options); +} - if (DeleteAnyEquivalentCookie(cookie_domain, - *cc, +bool CookieMonster::SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, + const std::string& cookie_domain, + const Time& creation_time, + const CookieOptions& options) { + if (DeleteAnyEquivalentCookie(cookie_domain, **cc, options.exclude_httponly())) { COOKIE_DLOG(INFO) << "SetCookie() not clobbering httponly cookie"; return false; } - COOKIE_DLOG(INFO) << "SetCookie() cc: " << cc->DebugString(); + COOKIE_DLOG(INFO) << "SetCookie() cc: " << (*cc)->DebugString(); // Realize that we might be setting an expired cookie, and the only point // was to delete the cookie which we've already done. - if (!cc->IsExpired(creation_time)) - InternalInsertCookie(cookie_domain, cc.release(), true); + if (!(*cc)->IsExpired(creation_time)) + InternalInsertCookie(cookie_domain, cc->release(), true); // We assume that hopefully setting a cookie will be less common than // querying a cookie. Since setting a cookie can put us over our limits, @@ -1102,62 +1165,147 @@ static inline bool SeekBackPast(std::string::const_iterator* it, return *it == end; } +const char CookieMonster::ParsedCookie::kTerminator[] = "\n\r\0"; +const int CookieMonster::ParsedCookie::kTerminatorLen = + sizeof(kTerminator) - 1; +const char CookieMonster::ParsedCookie::kWhitespace[] = " \t"; +const char CookieMonster::ParsedCookie::kValueSeparator[] = ";"; +const char CookieMonster::ParsedCookie::kTokenSeparator[] = ";="; + +std::string::const_iterator CookieMonster::ParsedCookie::FindFirstTerminator( + const std::string& s) { + std::string::const_iterator end = s.end(); + size_t term_pos = + s.find_first_of(std::string(kTerminator, kTerminatorLen)); + if (term_pos != std::string::npos) { + // We found a character we should treat as an end of string. + end = s.begin() + term_pos; + } + return end; +} + +bool CookieMonster::ParsedCookie::ParseToken( + std::string::const_iterator* it, + const std::string::const_iterator& end, + std::string::const_iterator* token_start, + std::string::const_iterator* token_end) { + DCHECK(it && token_start && token_end); + std::string::const_iterator token_real_end; + + // Seek past any whitespace before the "token" (the name). + // token_start should point at the first character in the token + if (SeekPast(it, end, kWhitespace)) + return false; // No token, whitespace or empty. + *token_start = *it; + + // Seek over the token, to the token separator. + // token_real_end should point at the token separator, i.e. '='. + // If it == end after the seek, we probably have a token-value. + SeekTo(it, end, kTokenSeparator); + token_real_end = *it; + + // Ignore any whitespace between the token and the token separator. + // token_end should point after the last interesting token character, + // pointing at either whitespace, or at '=' (and equal to token_real_end). + if (*it != *token_start) { // We could have an empty token name. + --(*it); // Go back before the token separator. + // Skip over any whitespace to the first non-whitespace character. + SeekBackPast(it, *token_start, kWhitespace); + // Point after it. + ++(*it); + } + *token_end = *it; + + // Seek us back to the end of the token. + *it = token_real_end; + return true; +} + +void CookieMonster::ParsedCookie::ParseValue( + std::string::const_iterator* it, + const std::string::const_iterator& end, + std::string::const_iterator* value_start, + std::string::const_iterator* value_end) { + DCHECK(it && value_start && value_end); + + // Seek past any whitespace that might in-between the token and value. + SeekPast(it, end, kWhitespace); + // value_start should point at the first character of the value. + *value_start = *it; + + // It is unclear exactly how quoted string values should be handled. + // Major browsers do different things, for example, Firefox supports + // semicolons embedded in a quoted value, while IE does not. Looking at + // the specs, RFC 2109 and 2965 allow for a quoted-string as the value. + // However, these specs were apparently written after browsers had + // implemented cookies, and they seem very distant from the reality of + // what is actually implemented and used on the web. The original spec + // from Netscape is possibly what is closest to the cookies used today. + // This spec didn't have explicit support for double quoted strings, and + // states that ; is not allowed as part of a value. We had originally + // implement the Firefox behavior (A="B;C"; -> A="B;C";). However, since + // there is no standard that makes sense, we decided to follow the behavior + // of IE and Safari, which is closer to the original Netscape proposal. + // This means that A="B;C" -> A="B;. This also makes the code much simpler + // and reduces the possibility for invalid cookies, where other browsers + // like Opera currently reject those invalid cookies (ex A="B" "C";). + + // Just look for ';' to terminate ('=' allowed). + // We can hit the end, maybe they didn't terminate. + SeekTo(it, end, kValueSeparator); + + // Will be pointed at the ; seperator or the end. + *value_end = *it; + + // Ignore any unwanted whitespace after the value. + if (*value_end != *value_start) { // Could have an empty value + --(*value_end); + SeekBackPast(value_end, *value_start, kWhitespace); + ++(*value_end); + } +} + +std::string CookieMonster::ParsedCookie::ParseTokenString( + const std::string& token) { + std::string::const_iterator it = token.begin(); + std::string::const_iterator end = FindFirstTerminator(token); + + std::string::const_iterator token_start, token_end; + if (ParseToken(&it, end, &token_start, &token_end)) + return std::string(token_start, token_end); + return std::string(); +} + +std::string CookieMonster::ParsedCookie::ParseValueString( + const std::string& value) { + std::string::const_iterator it = value.begin(); + std::string::const_iterator end = FindFirstTerminator(value); + + std::string::const_iterator value_start, value_end; + ParseValue(&it, end, &value_start, &value_end); + return std::string(value_start, value_end); +} + // Parse all token/value pairs and populate pairs_. void CookieMonster::ParsedCookie::ParseTokenValuePairs( const std::string& cookie_line) { - static const char kTerminator[] = "\n\r\0"; - static const int kTerminatorLen = sizeof(kTerminator) - 1; - static const char kWhitespace[] = " \t"; - static const char kValueSeparator[] = ";"; - static const char kTokenSeparator[] = ";="; - pairs_.clear(); // Ok, here we go. We should be expecting to be starting somewhere // before the cookie line, not including any header name... std::string::const_iterator start = cookie_line.begin(); - std::string::const_iterator end = cookie_line.end(); std::string::const_iterator it = start; // TODO Make sure we're stripping \r\n in the network code. Then we // can log any unexpected terminators. - size_t term_pos = - cookie_line.find_first_of(std::string(kTerminator, kTerminatorLen)); - if (term_pos != std::string::npos) { - // We found a character we should treat as an end of string. - end = start + term_pos; - } + std::string::const_iterator end = FindFirstTerminator(cookie_line); for (int pair_num = 0; pair_num < kMaxPairs && it != end; ++pair_num) { TokenValuePair pair; - std::string::const_iterator token_start, token_real_end, token_end; - - // Seek past any whitespace before the "token" (the name). - // token_start should point at the first character in the token - if (SeekPast(&it, end, kWhitespace)) - break; // No token, whitespace or empty. - token_start = it; - - // Seek over the token, to the token separator. - // token_real_end should point at the token separator, i.e. '='. - // If it == end after the seek, we probably have a token-value. - SeekTo(&it, end, kTokenSeparator); - token_real_end = it; - - // Ignore any whitespace between the token and the token separator. - // token_end should point after the last interesting token character, - // pointing at either whitespace, or at '=' (and equal to token_real_end). - if (it != token_start) { // We could have an empty token name. - --it; // Go back before the token separator. - // Skip over any whitespace to the first non-whitespace character. - SeekBackPast(&it, token_start, kWhitespace); - // Point after it. - ++it; - } - token_end = it; - // Seek us back to the end of the token. - it = token_real_end; + std::string::const_iterator token_start, token_end; + if (!ParseToken(&it, end, &token_start, &token_end)) + break; if (it == end || *it != '=') { // We have a token-value, we didn't have any token name. @@ -1184,45 +1332,10 @@ void CookieMonster::ParsedCookie::ParseTokenValuePairs( // OK, now try to parse a value. std::string::const_iterator value_start, value_end; - - // Seek past any whitespace that might in-between the token and value. - SeekPast(&it, end, kWhitespace); - // value_start should point at the first character of the value. - value_start = it; - - // It is unclear exactly how quoted string values should be handled. - // Major browsers do different things, for example, Firefox supports - // semicolons embedded in a quoted value, while IE does not. Looking at - // the specs, RFC 2109 and 2965 allow for a quoted-string as the value. - // However, these specs were apparently written after browsers had - // implemented cookies, and they seem very distant from the reality of - // what is actually implemented and used on the web. The original spec - // from Netscape is possibly what is closest to the cookies used today. - // This spec didn't have explicit support for double quoted strings, and - // states that ; is not allowed as part of a value. We had originally - // implement the Firefox behavior (A="B;C"; -> A="B;C";). However, since - // there is no standard that makes sense, we decided to follow the behavior - // of IE and Safari, which is closer to the original Netscape proposal. - // This means that A="B;C" -> A="B;. This also makes the code much simpler - // and reduces the possibility for invalid cookies, where other browsers - // like Opera currently reject those invalid cookies (ex A="B" "C";). - - // Just look for ';' to terminate ('=' allowed). - // We can hit the end, maybe they didn't terminate. - SeekTo(&it, end, kValueSeparator); - - // Will be pointed at the ; seperator or the end. - value_end = it; - - // Ignore any unwanted whitespace after the value. - if (value_end != value_start) { // Could have an empty value - --value_end; - SeekBackPast(&value_end, value_start, kWhitespace); - ++value_end; - } - + ParseValue(&it, end, &value_start, &value_end); // OK, we're finished with a Token/Value. pair.second = std::string(value_start, value_end); + // From RFC2109: "Attributes (names) (attr) are case-insensitive." if (pair_num != 0) StringToLowerASCII(&pair.first); @@ -1245,19 +1358,21 @@ void CookieMonster::ParsedCookie::SetupAttributes() { // We skip over the first token/value, the user supplied one. for (size_t i = 1; i < pairs_.size(); ++i) { - if (pairs_[i].first == kPathTokenName) + if (pairs_[i].first == kPathTokenName) { path_index_ = i; - else if (pairs_[i].first == kDomainTokenName) + } else if (pairs_[i].first == kDomainTokenName) { domain_index_ = i; - else if (pairs_[i].first == kExpiresTokenName) + } else if (pairs_[i].first == kExpiresTokenName) { expires_index_ = i; - else if (pairs_[i].first == kMaxAgeTokenName) + } else if (pairs_[i].first == kMaxAgeTokenName) { maxage_index_ = i; - else if (pairs_[i].first == kSecureTokenName) + } else if (pairs_[i].first == kSecureTokenName) { secure_index_ = i; - else if (pairs_[i].first == kHttpOnlyTokenName) + } else if (pairs_[i].first == kHttpOnlyTokenName) { httponly_index_ = i; - else { /* some attribute we don't know or don't care about. */ } + } else { + /* some attribute we don't know or don't care about. */ + } } } @@ -1290,6 +1405,40 @@ CookieMonster::CanonicalCookie::CanonicalCookie(const GURL& url, expiry_date_ = CanonExpiration(pc, creation_date_, CookieOptions()); } +CookieMonster::CanonicalCookie* CookieMonster::CanonicalCookie::Create( + const GURL& url, const std::string& name, const std::string& value, + const std::string& path, const base::Time& creation_time, + const base::Time& expiration_time, bool secure, bool http_only) { + // Expect valid attribute tokens and values, as defined by the ParsedCookie + // logic, otherwise don't create the cookie. + std::string parsed_name = ParsedCookie::ParseTokenString(name); + if (parsed_name != name) + return NULL; + std::string parsed_value = ParsedCookie::ParseValueString(value); + if (parsed_value != value) + return NULL; + std::string parsed_path = ParsedCookie::ParseValueString(path); + if (parsed_path != path) + return NULL; + + std::string cookie_path = CanonPathWithString(url, parsed_path); + // Expect that the path was either not specified (empty), or is valid. + if (!parsed_path.empty() && cookie_path != parsed_path) + return NULL; + // Canonicalize path again to make sure it escapes characters as needed. + url_parse::Component path_component(0, cookie_path.length()); + url_canon::RawCanonOutputT<char> canon_path; + url_parse::Component canon_path_component; + url_canon::CanonicalizePath(cookie_path.data(), path_component, + &canon_path, &canon_path_component); + cookie_path = std::string(canon_path.data() + canon_path_component.begin, + canon_path_component.len); + + return new CanonicalCookie(parsed_name, parsed_value, cookie_path, + secure, http_only, creation_time, creation_time, + !expiration_time.is_null(), expiration_time); +} + bool CookieMonster::CanonicalCookie::IsOnPath( const std::string& url_path) const { diff --git a/net/base/cookie_monster.h b/net/base/cookie_monster.h index d421d6f..ec9ca16 100644 --- a/net/base/cookie_monster.h +++ b/net/base/cookie_monster.h @@ -15,6 +15,7 @@ #include "base/basictypes.h" #include "base/lock.h" #include "base/ref_counted.h" +#include "base/scoped_ptr.h" #include "base/time.h" #include "net/base/cookie_store.h" @@ -76,6 +77,10 @@ class CookieMonster : public CookieStore { // Parse the string with the cookie time (very forgivingly). static base::Time ParseCookieTime(const std::string& time_string); + // Returns true if a domain string represents a host-only cookie, + // i.e. it doesn't begin with a leading '.' character. + static bool DomainIsHostOnly(const std::string& domain_string); + // CookieStore implementation. virtual bool SetCookieWithOptions(const GURL& url, const std::string& cookie_line, @@ -85,6 +90,19 @@ class CookieMonster : public CookieStore { virtual void DeleteCookie(const GURL& url, const std::string& cookie_name); virtual CookieMonster* GetCookieMonster() { return this; } + // Sets a cookie given explicit user-provided cookie attributes. The cookie + // name, value, domain, etc. are each provided as separate strings. This + // function expects each attribute to be well-formed. It will check for + // disallowed characters (e.g. the ';' character is disallowed within the + // cookie value attribute) and will return false without setting the cookie + // if such characters are found. + bool SetCookieWithDetails(const GURL& url, + const std::string& name, + const std::string& value, + const std::string& domain, + const std::string& path, + const base::Time& expiration_time, + bool secure, bool http_only); // Exposed for unit testing. bool SetCookieWithCreationTimeAndOptions(const GURL& url, @@ -201,6 +219,13 @@ class CookieMonster : public CookieStore { CanonicalCookie* cc, bool sync_to_store); + // Helper function that sets a canonical cookie, deleting equivalents and + // performing garbage collection. + bool SetCanonicalCookie(scoped_ptr<CanonicalCookie>* cc, + const std::string& cookie_domain, + const base::Time& creation_time, + const CookieOptions& options); + void InternalUpdateCookieAccessTime(CanonicalCookie* cc); void InternalDeleteCookie(CookieMap::iterator it, bool sync_to_store); @@ -288,6 +313,13 @@ class CookieMonster::CanonicalCookie { // Supports the default copy constructor. + // Creates a canonical cookie from unparsed attribute values. May return + // NULL if an attribute value is invalid. + static CanonicalCookie* Create( + const GURL& url, const std::string& name, const std::string& value, + const std::string& path, const base::Time& creation_time, + const base::Time& expiration_time, bool secure, bool http_only); + const std::string& Name() const { return name_; } const std::string& Value() const { return value_; } const std::string& Path() const { return path_; } @@ -377,14 +409,48 @@ class CookieMonster::ParsedCookie { bool IsSecure() const { return secure_index_ != 0; } bool IsHttpOnly() const { return httponly_index_ != 0; } - // Return the number of attributes, for example, returning 2 for: + // Returns the number of attributes, for example, returning 2 for: // "BLAH=hah; path=/; domain=.google.com" size_t NumberOfAttributes() const { return pairs_.size() - 1; } // For debugging only! std::string DebugString() const; + // Returns an iterator pointing to the first terminator character found in + // the given string. + static std::string::const_iterator FindFirstTerminator(const std::string& s); + + // Given iterators pointing to the beginning and end of a string segment, + // returns as output arguments token_start and token_end to the start and end + // positions of a cookie attribute token name parsed from the segment, and + // updates the segment iterator to point to the next segment to be parsed. + // If no token is found, the function returns false. + static bool ParseToken(std::string::const_iterator* it, + const std::string::const_iterator& end, + std::string::const_iterator* token_start, + std::string::const_iterator* token_end); + + // Given iterators pointing to the beginning and end of a string segment, + // returns as output arguments value_start and value_end to the start and end + // positions of a cookie attribute value parsed from the segment, and updates + // the segment iterator to point to the next segment to be parsed. + static void ParseValue(std::string::const_iterator* it, + const std::string::const_iterator& end, + std::string::const_iterator* value_start, + std::string::const_iterator* value_end); + + // Same as the above functions, except the input is assumed to contain the + // desired token/value and nothing else. + static std::string ParseTokenString(const std::string& token); + static std::string ParseValueString(const std::string& value); + private: + static const char kTerminator[]; + static const int kTerminatorLen; + static const char kWhitespace[]; + static const char kValueSeparator[]; + static const char kTokenSeparator[]; + void ParseTokenValuePairs(const std::string& cookie_line); void SetupAttributes(); diff --git a/net/base/cookie_monster_unittest.cc b/net/base/cookie_monster_unittest.cc index c2a5cae..a814480 100644 --- a/net/base/cookie_monster_unittest.cc +++ b/net/base/cookie_monster_unittest.cc @@ -379,6 +379,33 @@ TEST(ParsedCookieTest, EmbeddedTerminator) { EXPECT_EQ("BB", pc3.Value()); } +TEST(ParsedCookieTest, ParseTokensAndValues) { + EXPECT_EQ("hello", + net::CookieMonster::ParsedCookie::ParseTokenString( + "hello\nworld")); + EXPECT_EQ("fs!!@", + net::CookieMonster::ParsedCookie::ParseTokenString( + "fs!!@;helloworld")); + EXPECT_EQ("hello world\tgood", + net::CookieMonster::ParsedCookie::ParseTokenString( + "hello world\tgood\rbye")); + EXPECT_EQ("A", + net::CookieMonster::ParsedCookie::ParseTokenString( + "A=B=C;D=E")); + EXPECT_EQ("hello", + net::CookieMonster::ParsedCookie::ParseValueString( + "hello\nworld")); + EXPECT_EQ("fs!!@", + net::CookieMonster::ParsedCookie::ParseValueString( + "fs!!@;helloworld")); + EXPECT_EQ("hello world\tgood", + net::CookieMonster::ParsedCookie::ParseValueString( + "hello world\tgood\rbye")); + EXPECT_EQ("A=B=C", + net::CookieMonster::ParsedCookie::ParseValueString( + "A=B=C;D=E")); +} + static const char kUrlGoogle[] = "http://www.google.izzle"; static const char kUrlGoogleSecure[] = "https://www.google.izzle"; static const char kUrlFtp[] = "ftp://ftp.google.izzle/"; @@ -725,6 +752,11 @@ struct CookieDateParsingCase { const time_t epoch; }; +struct DomainIsHostOnlyCase { + const char* str; + const bool is_host_only; +}; + } // namespace TEST(CookieMonsterTest, TestCookieDateParsing) { @@ -811,6 +843,19 @@ TEST(CookieMonsterTest, TestCookieDateParsing) { } } +TEST(CookieMonsterTest, TestDomainIsHostOnly) { + const DomainIsHostOnlyCase tests[] = { + { "", true }, + { "www.google.com", true }, + { ".google.com", false } + }; + + for (size_t i = 0; i < arraysize(tests); ++i) { + EXPECT_EQ(tests[i].is_host_only, + net::CookieMonster::DomainIsHostOnly(tests[i].str)); + } +} + TEST(CookieMonsterTest, TestCookieDeletion) { GURL url_google(kUrlGoogle); scoped_refptr<MockPersistentCookieStore> store( @@ -1157,7 +1202,6 @@ TEST(CookieMonsterTest, SetCookieableSchemes) { } TEST(CookieMonsterTest, GetAllCookiesForURL) { - GURL url_google(kUrlGoogle); GURL url_google_secure(kUrlGoogleSecure); @@ -1214,7 +1258,7 @@ TEST(CookieMonsterTest, GetAllCookiesForURL) { ASSERT_TRUE(++it == cookies.end()); // Reading after a short wait should not update the access date. - EXPECT_TRUE (last_access_date == GetFirstCookieAccessDate(cm)); + EXPECT_TRUE(last_access_date == GetFirstCookieAccessDate(cm)); } TEST(CookieMonsterTest, GetAllCookiesForURLPathMatching) { @@ -1261,7 +1305,6 @@ TEST(CookieMonsterTest, GetAllCookiesForURLPathMatching) { EXPECT_EQ("/", it->second.Path()); ASSERT_TRUE(++it == cookies.end()); - } TEST(CookieMonsterTest, DeleteCookieByName) { @@ -1512,3 +1555,79 @@ TEST(CookieMonsterTest, Delegate) { EXPECT_EQ("val2", delegate->changes()[1].first.second.Value()); delegate->reset(); } + +TEST(CookieMonsterTest, SetCookieWithDetails) { + GURL url_google(kUrlGoogle); + GURL url_google_foo("http://www.google.izzle/foo"); + GURL url_google_bar("http://www.google.izzle/bar"); + GURL url_google_secure(kUrlGoogleSecure); + + scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL)); + + EXPECT_TRUE(cm->SetCookieWithDetails( + url_google_foo, "A", "B", std::string(), "/foo", base::Time(), + false, false)); + EXPECT_TRUE(cm->SetCookieWithDetails( + url_google_bar, "C", "D", "google.izzle", "/bar", base::Time(), + false, true)); + EXPECT_TRUE(cm->SetCookieWithDetails( + url_google, "E", "F", std::string(), std::string(), base::Time(), + true, false)); + + // Test that malformed attributes fail to set the cookie. + EXPECT_FALSE(cm->SetCookieWithDetails( + url_google_foo, " A", "B", std::string(), "/foo", base::Time(), + false, false)); + EXPECT_FALSE(cm->SetCookieWithDetails( + url_google_foo, "A;", "B", std::string(), "/foo", base::Time(), + false, false)); + EXPECT_FALSE(cm->SetCookieWithDetails( + url_google_foo, "A=", "B", std::string(), "/foo", base::Time(), + false, false)); + EXPECT_FALSE(cm->SetCookieWithDetails( + url_google_foo, "A", "B", "google.ozzzzzzle", "foo", base::Time(), + false, false)); + EXPECT_FALSE(cm->SetCookieWithDetails( + url_google_foo, "A=", "B", std::string(), "foo", base::Time(), + false, false)); + + net::CookieMonster::CookieList cookies = + cm->GetAllCookiesForURL(url_google_foo); + net::CookieMonster::CookieList::iterator it = cookies.begin(); + + ASSERT_TRUE(it != cookies.end()); + EXPECT_EQ("A", it->second.Name()); + EXPECT_EQ("B", it->second.Value()); + EXPECT_EQ("www.google.izzle", it->first); + EXPECT_EQ("/foo", it->second.Path()); + EXPECT_FALSE(it->second.DoesExpire()); + EXPECT_FALSE(it->second.IsSecure()); + EXPECT_FALSE(it->second.IsHttpOnly()); + + ASSERT_TRUE(++it == cookies.end()); + + cookies = cm->GetAllCookiesForURL(url_google_bar); + it = cookies.begin(); + + ASSERT_TRUE(it != cookies.end()); + EXPECT_EQ("C", it->second.Name()); + EXPECT_EQ("D", it->second.Value()); + EXPECT_EQ(".google.izzle", it->first); + EXPECT_EQ("/bar", it->second.Path()); + EXPECT_FALSE(it->second.IsSecure()); + EXPECT_TRUE(it->second.IsHttpOnly()); + + ASSERT_TRUE(++it == cookies.end()); + + cookies = cm->GetAllCookiesForURL(url_google_secure); + it = cookies.begin(); + + ASSERT_TRUE(it != cookies.end()); + EXPECT_EQ("E", it->second.Name()); + EXPECT_EQ("F", it->second.Value()); + EXPECT_EQ("/", it->second.Path()); + EXPECT_TRUE(it->second.IsSecure()); + EXPECT_FALSE(it->second.IsHttpOnly()); + + ASSERT_TRUE(++it == cookies.end()); +} |