Bug 1267474 - cache-control: immutable 2/3 r=mayhemer
💩💩 backed out by be7b95b5e548 💩 💩
authorPatrick McManus <mcmanus@ducksong.com>
Wed, 04 May 2016 22:37:53 -0400
changeset 296978 928d0afa32b7
parent 296977 81d69d7cf909
child 296979 2ab63d0cea9f
push id76542
push usermcmanus@ducksong.com
push date2016-05-11 13:48 +0000
treeherdermozilla-inbound@2ab63d0cea9f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer
bugs1267474
milestone49.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1267474 - cache-control: immutable 2/3 r=mayhemer
netwerk/base/nsIRequest.idl
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpResponseHead.cpp
netwerk/protocol/http/nsHttpResponseHead.h
--- a/netwerk/base/nsIRequest.idl
+++ b/netwerk/base/nsIRequest.idl
@@ -175,17 +175,17 @@ interface nsIRequest : nsISupports
     const unsigned long LOAD_FROM_CACHE   = 1 << 10;
 
     /**
      * The following flags control the frequency of cached content validation
      * when neither LOAD_BYPASS_CACHE or LOAD_FROM_CACHE are set.  By default,
      * cached content is automatically validated if necessary before reuse.
      * 
      * VALIDATE_ALWAYS forces validation of any cached content independent of
-     * its expiration time.
+     * its expiration time (unless it is https with Cache-Control: immutable)
      * 
      * VALIDATE_NEVER disables validation of cached content, unless it arrived
      * with the "Cache: no-store" header, or arrived via HTTPS with the
      * "Cache: no-cache" header.
      *
      * VALIDATE_ONCE_PER_SESSION disables validation of expired content, 
      * provided it has already been validated (at least once) since the start 
      * of this session.
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -3402,16 +3402,23 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
     NS_ENSURE_SUCCESS(rv,rv);
 
     bool doValidation = false;
     bool canAddImsHeader = true;
 
     bool isForcedValid = false;
     entry->GetIsForcedValid(&isForcedValid);
 
+    nsXPIDLCString framedBuf;
+    rv = entry->GetMetaDataElement("strongly-framed", getter_Copies(framedBuf));
+    // describe this in terms of explicitly weakly framed so as to be backwards
+    // compatible with old cache contents which dont have strongly-framed makers
+    bool weaklyFramed = NS_SUCCEEDED(rv) && framedBuf.EqualsLiteral("0");
+    bool isImmutable = !weaklyFramed && isHttps && mCachedResponseHead->Immutable();
+
     // Cached entry is not the entity we request (see bug #633743)
     if (ResponseWouldVary(entry)) {
         LOG(("Validating based on Vary headers returning TRUE\n"));
         canAddImsHeader = false;
         doValidation = true;
     }
     // Check isForcedValid to see if it is possible to skip validation.
     // Don't skip validation if we have serious reason to believe that this
@@ -3427,17 +3434,17 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
     }
     // If the LOAD_FROM_CACHE flag is set, any cached data can simply be used
     else if (mLoadFlags & nsIRequest::LOAD_FROM_CACHE || mAllowStaleCacheContent) {
         LOG(("NOT validating based on LOAD_FROM_CACHE load flag\n"));
         doValidation = false;
     }
     // If the VALIDATE_ALWAYS flag is set, any cached data won't be used until
     // it's revalidated with the server.
-    else if (mLoadFlags & nsIRequest::VALIDATE_ALWAYS) {
+    else if ((mLoadFlags & nsIRequest::VALIDATE_ALWAYS) && !isImmutable) {
         LOG(("Validating based on VALIDATE_ALWAYS load flag\n"));
         doValidation = true;
     }
     // Even if the VALIDATE_NEVER flag is set, there are still some cases in
     // which we must validate the cached response with the server.
     else if (mLoadFlags & nsIRequest::VALIDATE_NEVER) {
         LOG(("VALIDATE_NEVER set\n"));
         // if no-store validate cached response (see bug 112564)
@@ -3554,39 +3561,33 @@ nsHttpChannel::OnCacheEntryCheck(nsICach
 
     if (doValidation && mInterceptCache == INTERCEPTED) {
         doValidation = false;
     }
 
     mCachedContentIsValid = !doValidation;
 
     if (doValidation) {
-        nsXPIDLCString buf;
-        rv = entry->GetMetaDataElement("strongly-framed", getter_Copies(buf));
-        // describe this in terms of explicitly weakly framed so as to be backwards
-        // compatible with old cache contents which dont have strongly-framed makers
-        bool weaklyFramed = NS_SUCCEEDED(rv) && buf.EqualsLiteral("0");
-
         //
         // now, we are definitely going to issue a HTTP request to the server.
         // make it conditional if possible.
         //
         // do not attempt to validate no-store content, since servers will not
         // expect it to be cached.  (we only keep it in our cache for the
         // purposes of back/forward, etc.)
         //
         // the request method MUST be either GET or HEAD (see bug 175641) and
         // the cached response code must be < 400
         //
-        // the cached content must not be weakly framed
+        // the cached content must not be weakly framed or marked immutable
         //
         // do not override conditional headers when consumer has defined its own
         if (!mCachedResponseHead->NoStore() &&
             (mRequestHead.IsGet() || mRequestHead.IsHead()) &&
-            !mCustomConditionalRequest && !weaklyFramed &&
+            !mCustomConditionalRequest && !weaklyFramed && !isImmutable &&
             (mCachedResponseHead->Status() < 400)) {
 
             if (mConcurentCacheAccess) {
                 // In case of concurrent read and also validation request we
                 // must wait for the current writer to close the output stream
                 // first.  Otherwise, when the writer's job would have been interrupted
                 // before all the data were downloaded, we'd have to do a range request
                 // which would be a second request in line during this channel's
--- a/netwerk/protocol/http/nsHttpResponseHead.cpp
+++ b/netwerk/protocol/http/nsHttpResponseHead.cpp
@@ -638,16 +638,17 @@ nsHttpResponseHead::Reset()
     ClearHeaders();
 
     mVersion = NS_HTTP_VERSION_1_1;
     mStatus = 200;
     mContentLength = -1;
     mCacheControlPrivate = false;
     mCacheControlNoStore = false;
     mCacheControlNoCache = false;
+    mCacheControlImmutable = false;
     mPragmaNoCache = false;
     mStatusText.Truncate();
     mContentType.Truncate();
     mContentCharset.Truncate();
 }
 
 nsresult
 nsHttpResponseHead::ParseDateHeader(nsHttpAtom header, uint32_t *result) const
@@ -807,31 +808,37 @@ nsHttpResponseHead::ParseVersion(const c
 void
 nsHttpResponseHead::ParseCacheControl(const char *val)
 {
     if (!(val && *val)) {
         // clear flags
         mCacheControlPrivate = false;
         mCacheControlNoCache = false;
         mCacheControlNoStore = false;
+        mCacheControlImmutable = false;
         return;
     }
 
     // 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;
+    }
 }
 
 void
 nsHttpResponseHead::ParsePragma(const char *val)
 {
     LOG(("nsHttpResponseHead::ParsePragma [val=%s]\n", val));
 
     if (!(val && *val)) {
--- a/netwerk/protocol/http/nsHttpResponseHead.h
+++ b/netwerk/protocol/http/nsHttpResponseHead.h
@@ -27,31 +27,33 @@ class nsHttpResponseHead
 {
 public:
     nsHttpResponseHead() : mVersion(NS_HTTP_VERSION_1_1)
                          , mStatus(200)
                          , mContentLength(-1)
                          , mCacheControlPrivate(false)
                          , mCacheControlNoStore(false)
                          , mCacheControlNoCache(false)
+                         , mCacheControlImmutable(false)
                          , mPragmaNoCache(false) {}
 
     const nsHttpHeaderArray & Headers()   const { return mHeaders; }
     nsHttpHeaderArray    &Headers()             { return mHeaders; }
     nsHttpVersion         Version()       const { return mVersion; }
 // X11's Xlib.h #defines 'Status' to 'int' on some systems!
 #undef Status
     uint16_t              Status()        const { return mStatus; }
     const nsAFlatCString &StatusText()    const { return mStatusText; }
     int64_t               ContentLength() const { return mContentLength; }
     const nsAFlatCString &ContentType()   const { return mContentType; }
     const nsAFlatCString &ContentCharset() const { return mContentCharset; }
     bool                  Private() const { return mCacheControlPrivate; }
     bool                  NoStore() const { return mCacheControlNoStore; }
     bool                  NoCache() const { return (mCacheControlNoCache || mPragmaNoCache); }
+    bool                  Immutable() const { return mCacheControlImmutable; }
     /**
      * Full length of the entity. For byte-range requests, this may be larger
      * than ContentLength(), which will only represent the requested part of the
      * entity.
      */
     int64_t               TotalEntitySize() const;
 
     const char *PeekHeader(nsHttpAtom h) const      { return mHeaders.PeekHeader(h); }
@@ -128,16 +130,17 @@ public:
                mStatus == aOther.mStatus &&
                mStatusText == aOther.mStatusText &&
                mContentLength == aOther.mContentLength &&
                mContentType == aOther.mContentType &&
                mContentCharset == aOther.mContentCharset &&
                mCacheControlPrivate == aOther.mCacheControlPrivate &&
                mCacheControlNoCache == aOther.mCacheControlNoCache &&
                mCacheControlNoStore == aOther.mCacheControlNoStore &&
+               mCacheControlImmutable == aOther.mCacheControlImmutable &&
                mPragmaNoCache == aOther.mPragmaNoCache;
     }
 
 private:
     void     AssignDefaultStatusText();
     void     ParseVersion(const char *);
     void     ParseCacheControl(const char *);
     void     ParsePragma(const char *);
@@ -149,16 +152,17 @@ private:
     uint16_t          mStatus;
     nsCString         mStatusText;
     int64_t           mContentLength;
     nsCString         mContentType;
     nsCString         mContentCharset;
     bool              mCacheControlPrivate;
     bool              mCacheControlNoStore;
     bool              mCacheControlNoCache;
+    bool              mCacheControlImmutable;
     bool              mPragmaNoCache;
 
     friend struct IPC::ParamTraits<nsHttpResponseHead>;
 };
 
 } // namespace net
 } // namespace mozilla