diff --git a/netwerk/protocol/http/CacheControlParser.cpp b/netwerk/protocol/http/CacheControlParser.cpp index 766027ffe9a2..00d5b42d4ff2 100644 --- a/netwerk/protocol/http/CacheControlParser.cpp +++ b/netwerk/protocol/http/CacheControlParser.cpp @@ -17,8 +17,13 @@ CacheControlParser::CacheControlParser(nsACString const& aHeader) mMaxStale(0), mMinFreshSet(false), mMinFresh(0), + mStaleWhileRevalidateSet(false), + mStaleWhileRevalidate(0), mNoCache(false), - mNoStore(false) { + mNoStore(false), + mPublic(false), + mPrivate(false), + mImmutable(false) { SkipWhites(); if (!CheckEOF()) { Directive(); @@ -37,6 +42,14 @@ void CacheControlParser::Directive() { mMaxStaleSet = SecondsValue(&mMaxStale, PR_UINT32_MAX); } else if (CheckWord("min-fresh")) { mMinFreshSet = SecondsValue(&mMinFresh); + } else if (CheckWord("stale-while-revalidate")) { + mStaleWhileRevalidateSet = SecondsValue(&mStaleWhileRevalidate); + } else if (CheckWord("public")) { + mPublic = true; + } else if (CheckWord("private")) { + mPrivate = true; + } else if (CheckWord("immutable")) { + mImmutable = true; } else { IgnoreDirective(); } @@ -103,9 +116,20 @@ bool CacheControlParser::MinFresh(uint32_t* seconds) { return mMinFreshSet; } +bool CacheControlParser::StaleWhileRevalidate(uint32_t* seconds) { + *seconds = mStaleWhileRevalidate; + return mStaleWhileRevalidateSet; +} + bool CacheControlParser::NoCache() { return mNoCache; } bool CacheControlParser::NoStore() { return mNoStore; } +bool CacheControlParser::Public() { return mPublic; } + +bool CacheControlParser::Private() { return mPrivate; } + +bool CacheControlParser::Immutable() { return mImmutable; } + } // namespace net } // namespace mozilla diff --git a/netwerk/protocol/http/CacheControlParser.h b/netwerk/protocol/http/CacheControlParser.h index 6e00784cfd5b..6a6588be0cbb 100644 --- a/netwerk/protocol/http/CacheControlParser.h +++ b/netwerk/protocol/http/CacheControlParser.h @@ -19,8 +19,12 @@ class CacheControlParser final : Tokenizer { [[nodiscard]] bool MaxAge(uint32_t* seconds); [[nodiscard]] bool MaxStale(uint32_t* seconds); [[nodiscard]] bool MinFresh(uint32_t* seconds); + [[nodiscard]] bool StaleWhileRevalidate(uint32_t* seconds); bool NoCache(); bool NoStore(); + bool Public(); + bool Private(); + bool Immutable(); private: void Directive(); @@ -33,8 +37,13 @@ class CacheControlParser final : Tokenizer { uint32_t mMaxStale; bool mMinFreshSet; uint32_t mMinFresh; + bool mStaleWhileRevalidateSet; + uint32_t mStaleWhileRevalidate; bool mNoCache; bool mNoStore; + bool mPublic; + bool mPrivate; + bool mImmutable; }; } // namespace net diff --git a/netwerk/protocol/http/nsHttpResponseHead.cpp b/netwerk/protocol/http/nsHttpResponseHead.cpp index 3561ebf581e0..38dd1936185a 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.cpp +++ b/netwerk/protocol/http/nsHttpResponseHead.cpp @@ -14,6 +14,7 @@ #include "prtime.h" #include "plstr.h" #include "nsURLHelper.h" +#include "CacheControlParser.h" #include namespace mozilla { @@ -41,6 +42,11 @@ nsHttpResponseHead::nsHttpResponseHead(const nsHttpResponseHead& aOther) mCacheControlNoStore = other.mCacheControlNoStore; mCacheControlNoCache = other.mCacheControlNoCache; mCacheControlImmutable = other.mCacheControlImmutable; + mCacheControlStaleWhileRevalidateSet = + other.mCacheControlStaleWhileRevalidateSet; + mCacheControlStaleWhileRevalidate = other.mCacheControlStaleWhileRevalidate; + mCacheControlMaxAgeSet = other.mCacheControlMaxAgeSet; + mCacheControlMaxAge = other.mCacheControlMaxAge; mPragmaNoCache = other.mPragmaNoCache; } @@ -62,6 +68,11 @@ nsHttpResponseHead& nsHttpResponseHead::operator=( mCacheControlNoStore = other.mCacheControlNoStore; mCacheControlNoCache = other.mCacheControlNoCache; mCacheControlImmutable = other.mCacheControlImmutable; + mCacheControlStaleWhileRevalidateSet = + other.mCacheControlStaleWhileRevalidateSet; + mCacheControlStaleWhileRevalidate = other.mCacheControlStaleWhileRevalidate; + mCacheControlMaxAgeSet = other.mCacheControlMaxAgeSet; + mCacheControlMaxAge = other.mCacheControlMaxAge; mPragmaNoCache = other.mPragmaNoCache; return *this; @@ -782,22 +793,16 @@ bool nsHttpResponseHead::MustValidateIfExpired() { bool nsHttpResponseHead::StaleWhileRevalidate(uint32_t now, uint32_t expiration) { - nsresult rv; + RecursiveMutexAutoLock monitor(mRecursiveMutex); - if (expiration <= 0) { - return false; - } - - uint32_t revalidateWindow; - rv = GetStaleWhileRevalidateValue(&revalidateWindow); - if (NS_FAILED(rv)) { + if (expiration <= 0 || !mCacheControlStaleWhileRevalidateSet) { return false; } // 'expiration' is the expiration time (an absolute unit), the swr window // extends the expiration time. CheckedInt stallValidUntil = expiration; - stallValidUntil += revalidateWindow; + stallValidUntil += mCacheControlStaleWhileRevalidate; if (!stallValidUntil.isValid()) { // overflow means an indefinite stale window return true; @@ -901,6 +906,10 @@ void nsHttpResponseHead::Reset() { mCacheControlNoStore = false; mCacheControlNoCache = false; mCacheControlImmutable = false; + mCacheControlStaleWhileRevalidateSet = false; + mCacheControlStaleWhileRevalidate = 0; + mCacheControlMaxAgeSet = false; + mCacheControlMaxAge = 0; mPragmaNoCache = false; mStatusText.Truncate(); mContentType.Truncate(); @@ -941,59 +950,11 @@ nsresult nsHttpResponseHead::GetMaxAgeValue(uint32_t* result) { } nsresult nsHttpResponseHead::GetMaxAgeValue_locked(uint32_t* result) const { - const char* val = mHeaders.PeekHeader(nsHttp::Cache_Control); - if (!val) return NS_ERROR_NOT_AVAILABLE; - - const char* p = nsHttp::FindToken(val, "max-age", HTTP_HEADER_VALUE_SEPS "="); - if (!p) return NS_ERROR_NOT_AVAILABLE; - p += 7; - while (*p == ' ' || *p == '\t') ++p; - if (*p != '=') return NS_ERROR_NOT_AVAILABLE; - ++p; - while (*p == ' ' || *p == '\t') ++p; - - int maxAgeValue = atoi(p); - if (maxAgeValue < 0) maxAgeValue = 0; - *result = static_cast(maxAgeValue); - return NS_OK; -} - -// Get the stale-while-revalidate directive value, if present -nsresult nsHttpResponseHead::GetStaleWhileRevalidateValue(uint32_t* result) { - RecursiveMutexAutoLock monitor(mRecursiveMutex); - return GetStaleWhileRevalidateValue_locked(result); -} - -nsresult nsHttpResponseHead::GetStaleWhileRevalidateValue_locked( - uint32_t* result) const { - const char* val = mHeaders.PeekHeader(nsHttp::Cache_Control); - if (!val) { + if (!mCacheControlMaxAgeSet) { return NS_ERROR_NOT_AVAILABLE; } - // Rewrite (best all FindToken users) to mozilla::Tokenizer, bug 1542293. - const char* p = nsHttp::FindToken(val, "stale-while-revalidate", - HTTP_HEADER_VALUE_SEPS "="); - if (!p) { - return NS_ERROR_NOT_AVAILABLE; - } - p += 22; - while (*p == ' ' || *p == '\t') { - ++p; - } - if (*p != '=') { - return NS_ERROR_NOT_AVAILABLE; - } - ++p; - while (*p == ' ' || *p == '\t') { - ++p; - } - - int revalidateWindow = atoi(p); - if (revalidateWindow < 0) { - revalidateWindow = 0; - } - *result = static_cast(revalidateWindow); + *result = mCacheControlMaxAge; return NS_OK; } @@ -1048,6 +1009,12 @@ bool nsHttpResponseHead::operator==(const nsHttpResponseHead& aOther) const { mCacheControlNoCache == aOther.mCacheControlNoCache && mCacheControlNoStore == aOther.mCacheControlNoStore && mCacheControlImmutable == aOther.mCacheControlImmutable && + mCacheControlStaleWhileRevalidateSet == + aOther.mCacheControlStaleWhileRevalidateSet && + mCacheControlStaleWhileRevalidate == + aOther.mCacheControlStaleWhileRevalidate && + mCacheControlMaxAgeSet == aOther.mCacheControlMaxAgeSet && + mCacheControlMaxAge == aOther.mCacheControlMaxAge && mPragmaNoCache == aOther.mPragmaNoCache; } @@ -1153,31 +1120,25 @@ void nsHttpResponseHead::ParseCacheControl(const char* val) { mCacheControlNoCache = false; mCacheControlNoStore = false; mCacheControlImmutable = false; + mCacheControlStaleWhileRevalidateSet = false; + mCacheControlStaleWhileRevalidate = 0; + mCacheControlMaxAgeSet = false; + mCacheControlMaxAge = 0; return; } - // search header value for occurrence of "public" - if (nsHttp::FindToken(val, "public", HTTP_HEADER_VALUE_SEPS)) { - mCacheControlPublic = true; - } + nsDependentCString cacheControlRequestHeader(val); + CacheControlParser cacheControlRequest(cacheControlRequestHeader); - // search header value for occurrence of "private" - if (nsHttp::FindToken(val, "private", HTTP_HEADER_VALUE_SEPS)) - mCacheControlPrivate = true; - - // search header value for occurrence(s) of "no-cache" but ignore - // occurrence(s) of "no-cache=blah" - if (nsHttp::FindToken(val, "no-cache", HTTP_HEADER_VALUE_SEPS)) - mCacheControlNoCache = true; - - // search header value for occurrence of "no-store" - if (nsHttp::FindToken(val, "no-store", HTTP_HEADER_VALUE_SEPS)) - mCacheControlNoStore = true; - - // search header value for occurrence of "immutable" - if (nsHttp::FindToken(val, "immutable", HTTP_HEADER_VALUE_SEPS)) { - mCacheControlImmutable = true; - } + mCacheControlPublic = cacheControlRequest.Public(); + mCacheControlPrivate = cacheControlRequest.Private(); + mCacheControlNoCache = cacheControlRequest.NoCache(); + mCacheControlNoStore = cacheControlRequest.NoStore(); + mCacheControlImmutable = cacheControlRequest.Immutable(); + mCacheControlStaleWhileRevalidateSet = + cacheControlRequest.StaleWhileRevalidate( + &mCacheControlStaleWhileRevalidate); + mCacheControlMaxAgeSet = cacheControlRequest.MaxAge(&mCacheControlMaxAge); } void nsHttpResponseHead::ParsePragma(const char* val) { diff --git a/netwerk/protocol/http/nsHttpResponseHead.h b/netwerk/protocol/http/nsHttpResponseHead.h index ec839a591301..d38ee8d8328a 100644 --- a/netwerk/protocol/http/nsHttpResponseHead.h +++ b/netwerk/protocol/http/nsHttpResponseHead.h @@ -47,6 +47,10 @@ class nsHttpResponseHead { mCacheControlNoStore(false), mCacheControlNoCache(false), mCacheControlImmutable(false), + mCacheControlStaleWhileRevalidateSet(false), + mCacheControlStaleWhileRevalidate(0), + mCacheControlMaxAgeSet(false), + mCacheControlMaxAge(0), mPragmaNoCache(false), mRecursiveMutex("nsHttpResponseHead.mRecursiveMutex"), mInVisitHeaders(false) {} @@ -202,6 +206,10 @@ class nsHttpResponseHead { bool mCacheControlNoStore; bool mCacheControlNoCache; bool mCacheControlImmutable; + bool mCacheControlStaleWhileRevalidateSet; + uint32_t mCacheControlStaleWhileRevalidate; + bool mCacheControlMaxAgeSet; + uint32_t mCacheControlMaxAge; bool mPragmaNoCache; // We are using RecursiveMutex instead of a Mutex because VisitHeader diff --git a/netwerk/test/unit/test_race_cache_with_network.js b/netwerk/test/unit/test_race_cache_with_network.js index cdfdd95c0544..9944988ddadb 100644 --- a/netwerk/test/unit/test_race_cache_with_network.js +++ b/netwerk/test/unit/test_race_cache_with_network.js @@ -43,7 +43,7 @@ function test_handler(metadata, response) { function cached_handler(metadata, response) { response.setHeader("Content-Type", "text/plain"); - response.setHeader("Cache-Control", "Cache-Control: max-age=3600"); + response.setHeader("Cache-Control", "max-age=3600"); response.setHeader("ETag", "test-etag1"); response.setStatusLine(metadata.httpVersion, 200, "OK");