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
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