зеркало из https://github.com/mozilla/gecko-dev.git
change cookie eviction so that purges only take place when the limit on max cookies is reached, and the oldest cookie is greater than a set age. b=444600, r=sdwilsh,
sr=mconnor.
This commit is contained in:
Родитель
82c041d890
Коммит
ef83e1d68c
|
@ -71,6 +71,7 @@ _TEST_FILES = \
|
|||
test_same_base_domain_5.html \
|
||||
test_same_base_domain_6.html \
|
||||
file_loopback_inner.html \
|
||||
test_eviction.html \
|
||||
$(NULL)
|
||||
|
||||
# XXX see bug 454857
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for cookie eviction</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body onload="setupTest()">
|
||||
<p id="display"></p>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
function setupTest() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
// twiddle prefs to convenient values for this test
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
prefs.setIntPref("network.cookie.purgeAge", 1);
|
||||
prefs.setIntPref("network.cookie.maxNumber", 1000);
|
||||
|
||||
var cm = Components.classes["@mozilla.org/cookiemanager;1"]
|
||||
.getService(Components.interfaces.nsICookieManager2);
|
||||
|
||||
// eviction is performed based on two limits: when the total number of cookies
|
||||
// exceeds maxNumber + 10% (1100), and when cookies are older than purgeAge
|
||||
// (1 second). purging is done when both conditions are satisfied, and only
|
||||
// those cookies are purged.
|
||||
|
||||
// we test the following cases of eviction:
|
||||
// 1) excess and age are satisfied, but only some of the excess are old enough
|
||||
// to be purged.
|
||||
is(testEviction(cm, 1101, 2, 50, 1051), 1051, "incorrect number of cookies");
|
||||
|
||||
// 2) excess and age are satisfied, and all of the excess are old enough
|
||||
// to be purged.
|
||||
is(testEviction(cm, 1101, 2, 100, 1001), 1001, "incorrect number of cookies");
|
||||
|
||||
// 3) excess and age are satisfied, and more than the excess are old enough
|
||||
// to be purged.
|
||||
is(testEviction(cm, 1101, 2, 500, 1001), 1001, "incorrect number of cookies");
|
||||
|
||||
// 4) excess but not age are satisfied.
|
||||
is(testEviction(cm, 2000, 0, 0, 2000), 2000, "incorrect number of cookies");
|
||||
|
||||
// 5) age but not excess are satisfied.
|
||||
is(testEviction(cm, 1100, 2, 200, 1100), 1100, "incorrect number of cookies");
|
||||
|
||||
cm.removeAll();
|
||||
|
||||
// reset prefs to defaults
|
||||
prefs.setIntPref("network.cookie.purgeAge", 30 * 24 * 60 * 60 * 1000000);
|
||||
prefs.setIntPref("network.cookie.maxNumber", 2000);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
// test that cookies are evicted by order of lastAccessed time, if both the limit
|
||||
// on total cookies (maxNumber + 10%) and the purge age are exceeded
|
||||
function
|
||||
testEviction(aCM, aNumberTotal, aSleepDuration, aNumberAfter, aNumberToExpect)
|
||||
{
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
aCM.removeAll();
|
||||
|
||||
var i;
|
||||
for (i = 0; i < aNumberTotal; ++i) {
|
||||
var host = "eviction." + i + ".tests";
|
||||
aCM.add(host, "", "test", "eviction", false, false, false, Math.pow(2, 62));
|
||||
|
||||
if ((i == aNumberAfter - 1) && aSleepDuration) {
|
||||
// sleep a while, to make sure the first batch of cookies is older than
|
||||
// the second (timer resolution varies on different platforms).
|
||||
sleep(aSleepDuration * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
var enumerator = aCM.enumerator;
|
||||
|
||||
i = 0;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
var cookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
|
||||
++i;
|
||||
|
||||
if (aNumberTotal != aNumberToExpect) {
|
||||
var hostNumber = new Number(cookie.rawHost.split(".")[1]);
|
||||
if (hostNumber < (aNumberTotal - aNumberToExpect)) break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
// delay for a number of milliseconds
|
||||
function sleep(delay)
|
||||
{
|
||||
var start = new Date().getTime();
|
||||
while (new Date().getTime() < start + delay);
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -52,18 +52,23 @@ interface nsIChannel;
|
|||
* list is changed, or a cookie is rejected:
|
||||
*
|
||||
* topic : "cookie-changed"
|
||||
* broadcast whenever the cookie list changes in some way. there
|
||||
* are four possible data strings for this notification; one
|
||||
* notification will be broadcast for each change, and will involve
|
||||
* a single cookie.
|
||||
* subject: an nsICookie2 interface pointer representing the cookie object
|
||||
* that changed.
|
||||
* broadcast whenever the cookie list changes in some way. see
|
||||
* explanation of data strings below.
|
||||
* subject: see below.
|
||||
* data : "deleted"
|
||||
* a cookie was deleted. the subject is the deleted cookie.
|
||||
* a cookie was deleted. the subject is an nsICookie2 representing
|
||||
* the deleted cookie.
|
||||
* "added"
|
||||
* a cookie was added. the subject is the added cookie.
|
||||
* a cookie was added. the subject is an nsICookie2 representing
|
||||
* the added cookie.
|
||||
* "changed"
|
||||
* a cookie was changed. the subject is the new cookie.
|
||||
* a cookie was changed. the subject is an nsICookie2 representing
|
||||
* the new cookie. (note that host, path, and name are invariant
|
||||
* for a given cookie; other parameters may change.)
|
||||
* "batch-deleted"
|
||||
* a batch of cookies was deleted (for instance, as part of a purging
|
||||
* operation). the subject is an nsIArray of nsICookie2's representing
|
||||
* the deleted cookies.
|
||||
* "cleared"
|
||||
* the entire cookie list was cleared. the subject is null.
|
||||
* "reload"
|
||||
|
|
|
@ -55,7 +55,9 @@
|
|||
#include "nsILineInputStream.h"
|
||||
#include "nsIEffectiveTLDService.h"
|
||||
|
||||
#include "nsTArray.h"
|
||||
#include "nsCOMArray.h"
|
||||
#include "nsIMutableArray.h"
|
||||
#include "nsArrayEnumerator.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsReadableUtils.h"
|
||||
|
@ -86,6 +88,7 @@ static const char kCookieFileName[] = "cookies.sqlite";
|
|||
#define COOKIES_SCHEMA_VERSION 2
|
||||
|
||||
static const PRInt64 kCookieStaleThreshold = 60 * PR_USEC_PER_SEC; // 1 minute in microseconds
|
||||
static const PRInt64 kCookiePurgeAge = 30 * 24 * 60 * 60 * PR_USEC_PER_SEC; // 30 days in microseconds
|
||||
|
||||
static const char kOldCookieFileName[] = "cookies.txt";
|
||||
|
||||
|
@ -117,6 +120,7 @@ static const PRUint32 BEHAVIOR_REJECT = 2;
|
|||
static const char kPrefCookiesPermissions[] = "network.cookie.cookieBehavior";
|
||||
static const char kPrefMaxNumberOfCookies[] = "network.cookie.maxNumber";
|
||||
static const char kPrefMaxCookiesPerHost[] = "network.cookie.maxPerHost";
|
||||
static const char kPrefCookiePurgeAge[] = "network.cookie.purgeAge";
|
||||
|
||||
// struct for temporarily storing cookie attributes during header parsing
|
||||
struct nsCookieAttributes
|
||||
|
@ -206,7 +210,13 @@ static PRLogModuleInfo *sCookieLog = PR_NewLogModule("cookie");
|
|||
|
||||
#define COOKIE_LOGFAILURE(a, b, c, d) LogFailure(a, b, c, d)
|
||||
#define COOKIE_LOGSUCCESS(a, b, c, d, e) LogSuccess(a, b, c, d, e)
|
||||
#define COOKIE_LOGEVICTED(a) LogEvicted(a)
|
||||
|
||||
#define COOKIE_LOGEVICTED(a) \
|
||||
PR_BEGIN_MACRO \
|
||||
if (PR_LOG_TEST(sCookieLog, PR_LOG_DEBUG)) \
|
||||
LogEvicted(a); \
|
||||
PR_END_MACRO
|
||||
|
||||
#define COOKIE_LOGSTRING(lvl, fmt) \
|
||||
PR_BEGIN_MACRO \
|
||||
PR_LOG(sCookieLog, lvl, fmt); \
|
||||
|
@ -299,11 +309,6 @@ LogSuccess(PRBool aSetCookie, nsIURI *aHostURI, const char *aCookieString, nsCoo
|
|||
static void
|
||||
LogEvicted(nsCookie *aCookie)
|
||||
{
|
||||
// if logging isn't enabled, return now to save cycles
|
||||
if (!PR_LOG_TEST(sCookieLog, PR_LOG_DEBUG)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PR_LOG(sCookieLog, PR_LOG_DEBUG,("===== COOKIE EVICTED =====\n"));
|
||||
|
||||
LogCookie(aCookie);
|
||||
|
@ -377,10 +382,12 @@ NS_IMPL_ISUPPORTS5(nsCookieService,
|
|||
|
||||
nsCookieService::nsCookieService()
|
||||
: mHostTable(&mDefaultHostTable)
|
||||
, mCookieOldestTime(LL_MAXINT)
|
||||
, mCookieCount(0)
|
||||
, mCookiesPermissions(BEHAVIOR_ACCEPT)
|
||||
, mMaxNumberOfCookies(kMaxNumberOfCookies)
|
||||
, mMaxCookiesPerHost(kMaxCookiesPerHost)
|
||||
, mCookiePurgeAge(kCookiePurgeAge)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -401,6 +408,7 @@ nsCookieService::Init()
|
|||
prefBranch->AddObserver(kPrefCookiesPermissions, this, PR_TRUE);
|
||||
prefBranch->AddObserver(kPrefMaxNumberOfCookies, this, PR_TRUE);
|
||||
prefBranch->AddObserver(kPrefMaxCookiesPerHost, this, PR_TRUE);
|
||||
prefBranch->AddObserver(kPrefCookiePurgeAge, this, PR_TRUE);
|
||||
PrefChanged(prefBranch);
|
||||
}
|
||||
|
||||
|
@ -774,18 +782,19 @@ nsCookieService::NotifyRejected(nsIURI *aHostURI)
|
|||
mObserverService->NotifyObservers(aHostURI, "cookie-rejected", nsnull);
|
||||
}
|
||||
|
||||
// notify observers that the cookie list changed. there are four possible
|
||||
// notify observers that the cookie list changed. there are five possible
|
||||
// values for aData:
|
||||
// "deleted" means a cookie was deleted. aCookie is the deleted cookie.
|
||||
// "added" means a cookie was added. aCookie is the added cookie.
|
||||
// "changed" means a cookie was altered. aCookie is the new cookie.
|
||||
// "cleared" means the entire cookie list was cleared. aCookie is null.
|
||||
// "deleted" means a cookie was deleted. aSubject is the deleted cookie.
|
||||
// "added" means a cookie was added. aSubject is the added cookie.
|
||||
// "changed" means a cookie was altered. aSubject is the new cookie.
|
||||
// "cleared" means the entire cookie list was cleared. aSubject is null.
|
||||
// "batch-deleted" means multiple cookies were deleted. aSubject is the list of cookies.
|
||||
void
|
||||
nsCookieService::NotifyChanged(nsICookie2 *aCookie,
|
||||
nsCookieService::NotifyChanged(nsISupports *aSubject,
|
||||
const PRUnichar *aData)
|
||||
{
|
||||
if (mObserverService)
|
||||
mObserverService->NotifyObservers(aCookie, "cookie-changed", aData);
|
||||
mObserverService->NotifyObservers(aSubject, "cookie-changed", aData);
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -805,6 +814,9 @@ nsCookieService::PrefChanged(nsIPrefBranch *aPrefBranch)
|
|||
|
||||
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefMaxCookiesPerHost, &val)))
|
||||
mMaxCookiesPerHost = (PRUint16) LIMIT(val, 0, 0xFFFF, 0xFFFF);
|
||||
|
||||
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(kPrefCookiePurgeAge, &val)))
|
||||
mCookiePurgeAge = val * PR_USEC_PER_SEC; // convert seconds to microseconds
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
|
@ -895,7 +907,7 @@ nsCookieService::Add(const nsACString &aDomain,
|
|||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
AddInternal(cookie, currentTimeInUsec / PR_USEC_PER_SEC, nsnull, nsnull, PR_TRUE);
|
||||
AddInternal(cookie, currentTimeInUsec, nsnull, nsnull, PR_TRUE);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1135,7 +1147,7 @@ nsCookieService::ImportCookies(nsIFile *aCookieFile)
|
|||
if (originalCookieCount == 0)
|
||||
AddCookieToList(newCookie);
|
||||
else
|
||||
AddInternal(newCookie, currentTime, nsnull, nsnull, PR_TRUE);
|
||||
AddInternal(newCookie, currentTimeInUsec, nsnull, nsnull, PR_TRUE);
|
||||
}
|
||||
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("ImportCookies(): %ld cookies imported", mCookieCount));
|
||||
|
@ -1430,7 +1442,7 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
|
|||
|
||||
// add the cookie to the list. AddInternal() takes care of logging.
|
||||
// we get the current time again here, since it may have changed during prompting
|
||||
AddInternal(cookie, PR_Now() / PR_USEC_PER_SEC, aHostURI, savedCookieHeader.get(), aFromHttp);
|
||||
AddInternal(cookie, PR_Now(), aHostURI, savedCookieHeader.get(), aFromHttp);
|
||||
return newCookie;
|
||||
}
|
||||
|
||||
|
@ -1441,11 +1453,13 @@ nsCookieService::SetCookieInternal(nsIURI *aHostURI,
|
|||
// reached). also performs list maintenance by removing expired cookies.
|
||||
void
|
||||
nsCookieService::AddInternal(nsCookie *aCookie,
|
||||
PRInt64 aCurrentTime,
|
||||
PRInt64 aCurrentTimeInUsec,
|
||||
nsIURI *aHostURI,
|
||||
const char *aCookieHeader,
|
||||
PRBool aFromHttp)
|
||||
{
|
||||
PRInt64 currentTime = aCurrentTimeInUsec / PR_USEC_PER_SEC;
|
||||
|
||||
// if the new cookie is httponly, make sure we're not coming from script
|
||||
if (!aFromHttp && aCookie->IsHttpOnly()) {
|
||||
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie is httponly; coming from script");
|
||||
|
@ -1459,7 +1473,7 @@ nsCookieService::AddInternal(nsCookie *aCookie,
|
|||
|
||||
nsListIter matchIter;
|
||||
PRBool foundCookie = FindCookie(aCookie->Host(), aCookie->Name(), aCookie->Path(),
|
||||
matchIter, aCurrentTime);
|
||||
matchIter, currentTime);
|
||||
|
||||
nsRefPtr<nsCookie> oldCookie;
|
||||
if (foundCookie) {
|
||||
|
@ -1474,7 +1488,7 @@ nsCookieService::AddInternal(nsCookie *aCookie,
|
|||
RemoveCookieFromList(matchIter);
|
||||
|
||||
// check if the cookie has expired
|
||||
if (aCookie->Expiry() <= aCurrentTime) {
|
||||
if (aCookie->Expiry() <= currentTime) {
|
||||
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "previously stored cookie was deleted");
|
||||
NotifyChanged(oldCookie, NS_LITERAL_STRING("deleted").get());
|
||||
return;
|
||||
|
@ -1486,36 +1500,30 @@ nsCookieService::AddInternal(nsCookie *aCookie,
|
|||
|
||||
} else {
|
||||
// check if cookie has already expired
|
||||
if (aCookie->Expiry() <= aCurrentTime) {
|
||||
if (aCookie->Expiry() <= currentTime) {
|
||||
COOKIE_LOGFAILURE(SET_COOKIE, aHostURI, aCookieHeader, "cookie has already expired");
|
||||
return;
|
||||
}
|
||||
|
||||
// check if we have to delete an old cookie.
|
||||
nsEnumerationData data(aCurrentTime, LL_MAXINT);
|
||||
nsEnumerationData data(currentTime, LL_MAXINT);
|
||||
if (CountCookiesFromHostInternal(aCookie->RawHost(), data) >= mMaxCookiesPerHost) {
|
||||
// remove the oldest cookie from host
|
||||
oldCookie = data.iter.current;
|
||||
COOKIE_LOGEVICTED(oldCookie);
|
||||
RemoveCookieFromList(data.iter);
|
||||
|
||||
} else if (mCookieCount >= mMaxNumberOfCookies) {
|
||||
// try to make room, by removing expired cookies
|
||||
RemoveExpiredCookies(aCurrentTime);
|
||||
|
||||
// check if we still have to get rid of something
|
||||
if (mCookieCount >= mMaxNumberOfCookies) {
|
||||
// find the position of the oldest cookie, and remove it
|
||||
data.oldestTime = LL_MAXINT;
|
||||
FindOldestCookie(data);
|
||||
oldCookie = data.iter.current;
|
||||
RemoveCookieFromList(data.iter);
|
||||
}
|
||||
}
|
||||
|
||||
// if we deleted an old cookie, notify consumers
|
||||
if (oldCookie) {
|
||||
COOKIE_LOGEVICTED(oldCookie);
|
||||
NotifyChanged(oldCookie, NS_LITERAL_STRING("deleted").get());
|
||||
|
||||
} else if (mCookieCount >= 1.1 * mMaxNumberOfCookies &&
|
||||
aCurrentTimeInUsec - mCookieOldestTime >= 1.1 * mCookiePurgeAge) {
|
||||
// we're over both size and age limits by 10%; time to purge the table!
|
||||
// do this by:
|
||||
// 1) removing expired cookies;
|
||||
// 2) evicting the balance of old cookies, until we reach the size limit.
|
||||
// note that the mCookieOldestTime indicator can be pessimistic - if it's
|
||||
// older than the actual oldest cookie, we'll just purge more eagerly.
|
||||
PurgeCookies(aCurrentTimeInUsec);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2069,32 +2077,151 @@ nsCookieService::RemoveAllFromMemory()
|
|||
// which releases all their respective children.
|
||||
mHostTable->Clear();
|
||||
mCookieCount = 0;
|
||||
mCookieOldestTime = LL_MAXINT;
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
removeExpiredCallback(nsCookieEntry *aEntry,
|
||||
void *aArg)
|
||||
// stores temporary data for enumerating over the hash entries,
|
||||
// since enumeration is done using callback functions
|
||||
struct nsPurgeData
|
||||
{
|
||||
const PRInt64 ¤tTime = *static_cast<PRInt64*>(aArg);
|
||||
nsPurgeData(PRInt64 aCurrentTime,
|
||||
PRInt64 aPurgeTime,
|
||||
nsTArray<nsListIter> &aPurgeList,
|
||||
nsIMutableArray *aRemovedList)
|
||||
: currentTime(aCurrentTime)
|
||||
, purgeTime(aPurgeTime)
|
||||
, oldestTime(LL_MAXINT)
|
||||
, purgeList(aPurgeList)
|
||||
, removedList(aRemovedList) {}
|
||||
|
||||
// the current time, in seconds
|
||||
PRInt64 currentTime;
|
||||
|
||||
// lastAccessed time older than which cookies are eligible for purge
|
||||
PRInt64 purgeTime;
|
||||
|
||||
// lastAccessed time of the oldest cookie found during purge, to update our indicator
|
||||
PRInt64 oldestTime;
|
||||
|
||||
// list of cookies over the age limit, for purging
|
||||
nsTArray<nsListIter> &purgeList;
|
||||
|
||||
// list of all cookies we've removed, for notification
|
||||
nsIMutableArray *removedList;
|
||||
};
|
||||
|
||||
// comparator class for lastaccessed times of cookies.
|
||||
class CompareCookiesByAge {
|
||||
public:
|
||||
// returns true if (a == b); false otherwise.
|
||||
PRBool Equals(const nsListIter &a, const nsListIter &b) const
|
||||
{
|
||||
return a.current->LastAccessed() == b.current->LastAccessed();
|
||||
}
|
||||
|
||||
// returns true if (a < b); false otherwise.
|
||||
PRBool LessThan(const nsListIter &a, const nsListIter &b) const
|
||||
{
|
||||
return a.current->LastAccessed() < b.current->LastAccessed();
|
||||
}
|
||||
};
|
||||
|
||||
PLDHashOperator
|
||||
purgeCookiesCallback(nsCookieEntry *aEntry,
|
||||
void *aArg)
|
||||
{
|
||||
nsPurgeData &data = *static_cast<nsPurgeData*>(aArg);
|
||||
for (nsListIter iter(aEntry, nsnull, aEntry->Head()); iter.current; ) {
|
||||
if (iter.current->Expiry() <= currentTime)
|
||||
// check if the cookie has expired
|
||||
if (iter.current->Expiry() <= data.currentTime) {
|
||||
nsCookie *cookie = iter.current;
|
||||
data.removedList->AppendElement(cookie, PR_FALSE);
|
||||
COOKIE_LOGEVICTED(cookie);
|
||||
|
||||
// remove from list. this takes care of updating the iterator for us
|
||||
nsCookieService::gCookieService->RemoveCookieFromList(iter);
|
||||
else
|
||||
|
||||
} else {
|
||||
// check if the cookie is over the age limit
|
||||
if (iter.current->LastAccessed() <= data.purgeTime) {
|
||||
data.purgeList.AppendElement(iter);
|
||||
|
||||
} else if (iter.current->LastAccessed() < data.oldestTime) {
|
||||
// reset our indicator
|
||||
data.oldestTime = iter.current->LastAccessed();
|
||||
}
|
||||
|
||||
++iter;
|
||||
}
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
// removes any expired cookies from memory
|
||||
// purges expired and old cookies in a batch operation.
|
||||
void
|
||||
nsCookieService::RemoveExpiredCookies(PRInt64 aCurrentTime)
|
||||
nsCookieService::PurgeCookies(PRInt64 aCurrentTimeInUsec)
|
||||
{
|
||||
#ifdef PR_LOGGING
|
||||
PRUint32 initialCookieCount = mCookieCount;
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("PurgeCookies(): beginning purge with %ld cookies and %lld age",
|
||||
mCookieCount, aCurrentTimeInUsec - mCookieOldestTime));
|
||||
#endif
|
||||
mHostTable->EnumerateEntries(removeExpiredCallback, &aCurrentTime);
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("RemoveExpiredCookies(): %ld purged; %ld remain", initialCookieCount - mCookieCount, mCookieCount));
|
||||
|
||||
nsAutoTArray<nsListIter, kMaxNumberOfCookies> purgeList;
|
||||
|
||||
nsCOMPtr<nsIMutableArray> removedList = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
||||
if (!removedList)
|
||||
return;
|
||||
|
||||
nsPurgeData data(aCurrentTimeInUsec / PR_USEC_PER_SEC, aCurrentTimeInUsec - mCookiePurgeAge, purgeList, removedList);
|
||||
mHostTable->EnumerateEntries(purgeCookiesCallback, &data);
|
||||
|
||||
#ifdef PR_LOGGING
|
||||
PRUint32 postExpiryCookieCount = mCookieCount;
|
||||
#endif
|
||||
|
||||
// now we have a list of iterators for cookies over the age limit.
|
||||
// sort them by age, and then we'll see how many to remove...
|
||||
purgeList.Sort(CompareCookiesByAge());
|
||||
|
||||
// only remove old cookies until we reach the max cookie limit, no more.
|
||||
PRUint32 excess = mCookieCount - mMaxNumberOfCookies;
|
||||
//printf("count %u, maxnum %u, listlen %u, excess %d\n", mCookieCount, mMaxNumberOfCookies, list.Length(), excess);
|
||||
if (purgeList.Length() > excess) {
|
||||
// we're not purging everything in the list, so update our indicator
|
||||
data.oldestTime = purgeList[excess].current->LastAccessed();
|
||||
|
||||
purgeList.SetLength(excess);
|
||||
}
|
||||
|
||||
// traverse the list and remove cookies. the iterators we've stored
|
||||
// in the list aren't stable under list mutation, so we need to do a
|
||||
// fresh linked list traversal from the hash entryclass for each cookie.
|
||||
for (PRUint32 i = 0; i < purgeList.Length(); ++i) {
|
||||
for (nsListIter iter(purgeList[i].entry, nsnull, purgeList[i].entry->Head()); iter.current; ++iter) {
|
||||
if (iter.current == purgeList[i].current) {
|
||||
// remove from list. this takes care of updating the iterator for us
|
||||
nsCookie *cookie = iter.current;
|
||||
removedList->AppendElement(cookie, PR_FALSE);
|
||||
COOKIE_LOGEVICTED(cookie);
|
||||
|
||||
RemoveCookieFromList(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// take all the cookies in the removed list, and notify about them in one batch
|
||||
NotifyChanged(removedList, NS_LITERAL_STRING("batch-deleted").get());
|
||||
|
||||
// reset the oldest time indicator
|
||||
mCookieOldestTime = data.oldestTime;
|
||||
|
||||
COOKIE_LOGSTRING(PR_LOG_DEBUG, ("PurgeCookies(): %ld expired; %ld purged; %ld remain; %lld oldest age",
|
||||
initialCookieCount - postExpiryCookieCount,
|
||||
mCookieCount - postExpiryCookieCount,
|
||||
mCookieCount,
|
||||
aCurrentTimeInUsec - mCookieOldestTime));
|
||||
}
|
||||
|
||||
// find whether a given cookie has been previously set. this is provided by the
|
||||
|
@ -2282,6 +2409,10 @@ nsCookieService::AddCookieToList(nsCookie *aCookie, PRBool aWriteToDB)
|
|||
entry->Head() = aCookie;
|
||||
++mCookieCount;
|
||||
|
||||
// keep track of the oldest cookie, for when it comes time to purge
|
||||
if (aCookie->LastAccessed() < mCookieOldestTime)
|
||||
mCookieOldestTime = aCookie->LastAccessed();
|
||||
|
||||
// if it's a non-session cookie and hasn't just been read from the db, write it out.
|
||||
if (aWriteToDB && !aCookie->IsSession() && mStmtInsert) {
|
||||
// use our cached sqlite "insert" statement
|
||||
|
@ -2329,23 +2460,3 @@ nsCookieService::UpdateCookieInList(nsCookie *aCookie, PRInt64 aLastAccessed)
|
|||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
findOldestCallback(nsCookieEntry *aEntry,
|
||||
void *aArg)
|
||||
{
|
||||
nsEnumerationData *data = static_cast<nsEnumerationData*>(aArg);
|
||||
for (nsListIter iter(aEntry, nsnull, aEntry->Head()); iter.current; ++iter) {
|
||||
// check if we've found the oldest cookie so far
|
||||
if (data->oldestTime > iter.current->LastAccessed()) {
|
||||
data->oldestTime = iter.current->LastAccessed();
|
||||
data->iter = iter;
|
||||
}
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsCookieService::FindOldestCookie(nsEnumerationData &aData)
|
||||
{
|
||||
mHostTable->EnumerateEntries(findOldestCallback, &aData);
|
||||
}
|
||||
|
|
|
@ -171,7 +171,7 @@ class nsCookieService : public nsICookieService
|
|||
void GetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, PRBool aHttpBound, char **aCookie);
|
||||
nsresult SetCookieStringInternal(nsIURI *aHostURI, nsIPrompt *aPrompt, const char *aCookieHeader, const char *aServerTime, nsIChannel *aChannel, PRBool aFromHttp);
|
||||
PRBool SetCookieInternal(nsIURI *aHostURI, nsIChannel *aChannel, nsDependentCString &aCookieHeader, PRInt64 aServerTime, PRBool aFromHttp);
|
||||
void AddInternal(nsCookie *aCookie, PRInt64 aCurrentTime, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp);
|
||||
void AddInternal(nsCookie *aCookie, PRInt64 aCurrentTimeInUsec, nsIURI *aHostURI, const char *aCookieHeader, PRBool aFromHttp);
|
||||
void RemoveCookieFromList(nsListIter &aIter);
|
||||
PRBool AddCookieToList(nsCookie *aCookie, PRBool aWriteToDB = PR_TRUE);
|
||||
void UpdateCookieInList(nsCookie *aCookie, PRInt64 aLastAccessed);
|
||||
|
@ -183,12 +183,11 @@ class nsCookieService : public nsICookieService
|
|||
static PRBool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
|
||||
static PRBool GetExpiry(nsCookieAttributes &aCookie, PRInt64 aServerTime, PRInt64 aCurrentTime);
|
||||
void RemoveAllFromMemory();
|
||||
void RemoveExpiredCookies(PRInt64 aCurrentTime);
|
||||
void PurgeCookies(PRInt64 aCurrentTimeInUsec);
|
||||
PRBool FindCookie(const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter, PRInt64 aCurrentTime);
|
||||
void FindOldestCookie(nsEnumerationData &aData);
|
||||
PRUint32 CountCookiesFromHostInternal(const nsACString &aHost, nsEnumerationData &aData);
|
||||
void NotifyRejected(nsIURI *aHostURI);
|
||||
void NotifyChanged(nsICookie2 *aCookie, const PRUnichar *aData);
|
||||
void NotifyChanged(nsISupports *aSubject, const PRUnichar *aData);
|
||||
|
||||
protected:
|
||||
// cached members
|
||||
|
@ -204,19 +203,21 @@ class nsCookieService : public nsICookieService
|
|||
nsTHashtable<nsCookieEntry> *mHostTable;
|
||||
nsTHashtable<nsCookieEntry> mDefaultHostTable;
|
||||
nsTHashtable<nsCookieEntry> mPrivateHostTable;
|
||||
PRInt64 mCookieOldestTime;
|
||||
PRUint32 mCookieCount;
|
||||
|
||||
// cached prefs
|
||||
PRUint8 mCookiesPermissions; // BEHAVIOR_{ACCEPT, REJECTFOREIGN, REJECT}
|
||||
PRUint16 mMaxNumberOfCookies;
|
||||
PRUint16 mMaxCookiesPerHost;
|
||||
PRUint64 mCookiePurgeAge;
|
||||
|
||||
// private static member, used to cache a ptr to nsCookieService,
|
||||
// so we can make nsCookieService a singleton xpcom object.
|
||||
static nsCookieService *gCookieService;
|
||||
|
||||
// this callback needs access to member functions
|
||||
friend PLDHashOperator removeExpiredCallback(nsCookieEntry *aEntry, void *aArg);
|
||||
friend PLDHashOperator purgeCookiesCallback(nsCookieEntry *aEntry, void *aArg);
|
||||
};
|
||||
|
||||
#endif // nsCookieService_h__
|
||||
|
|
|
@ -761,41 +761,7 @@ main(PRInt32 argc, char *argv[])
|
|||
GetACookie(cookieService, "http://creation.ordering.tests/", nsnull, getter_Copies(cookie));
|
||||
rv[0] = CheckResult(cookie.get(), MUST_EQUAL, expected.get());
|
||||
|
||||
// test that cookies are evicted by order of lastAccessed time, if the limit on total cookies
|
||||
// (3000) is reached
|
||||
nsCAutoString host;
|
||||
for (PRInt32 i = 0; i < 3010; ++i) {
|
||||
host = NS_LITERAL_CSTRING("http://eviction.");
|
||||
host.AppendInt(i);
|
||||
host += NS_LITERAL_CSTRING(".tests/");
|
||||
SetACookie(cookieService, host.get(), nsnull, "test=eviction", nsnull);
|
||||
|
||||
if (i == 9) {
|
||||
// sleep a couple of seconds, to make sure the first 10 cookies are older than
|
||||
// subsequent ones (timer resolution varies on different platforms).
|
||||
PR_Sleep(2 * PR_TicksPerSecond());
|
||||
}
|
||||
}
|
||||
rv[1] = NS_SUCCEEDED(cookieMgr->GetEnumerator(getter_AddRefs(enumerator)));
|
||||
i = 0;
|
||||
rv[2] = PR_FALSE; // init to failure in case we break from the while loop
|
||||
while (NS_SUCCEEDED(enumerator->HasMoreElements(&more)) && more) {
|
||||
nsCOMPtr<nsISupports> cookie;
|
||||
if (NS_FAILED(enumerator->GetNext(getter_AddRefs(cookie)))) break;
|
||||
++i;
|
||||
|
||||
// keep tabs on the third cookie, so we can check it later
|
||||
nsCOMPtr<nsICookie2> cookie2(do_QueryInterface(cookie));
|
||||
if (!cookie2) break;
|
||||
nsCAutoString domain;
|
||||
cookie2->GetRawHost(domain);
|
||||
PRInt32 hostNumber;
|
||||
PRInt32 numInts = PR_sscanf(domain.get(), "eviction.%ld.tests", &hostNumber);
|
||||
if (numInts != 1 || hostNumber < 10) break;
|
||||
}
|
||||
rv[2] = i == 3000;
|
||||
|
||||
allTestsPassed = PrintResult(rv, 3) && allTestsPassed;
|
||||
allTestsPassed = PrintResult(rv, 1) && allTestsPassed;
|
||||
|
||||
|
||||
// XXX the following are placeholders: add these tests please!
|
||||
|
|
Загрузка…
Ссылка в новой задаче