diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java index d99e77b37f5..69bbb4850d3 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/control/CacheManager.java @@ -22,16 +22,26 @@ import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.commons.collections.map.LRUMap; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.http.Header; +import org.apache.http.HeaderElement; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.utils.DateUtils; +import org.apache.http.message.BasicHeader; import org.apache.jmeter.config.ConfigTestElement; import org.apache.jmeter.engine.event.LoopIterationEvent; import org.apache.jmeter.protocol.http.sampler.HTTPSampleResult; @@ -96,28 +106,54 @@ public CacheManager() { * Perhaps add original response later? */ // package-protected to allow access by unit-test cases - static class CacheEntry{ + static class CacheEntry { private final String lastModified; private final String etag; private final Date expires; - public CacheEntry(String lastModified, Date expires, String etag){ - this.lastModified = lastModified; - this.etag = etag; - this.expires = expires; - } + private final String varyHeader; + + /** + * + * @param lastModified + * @param expires + * @param etag + * @deprecated use {@link CacheEntry(String lastModified, Date expires, String etag, String varyHeader)} instead + */ + @Deprecated + public CacheEntry(String lastModified, Date expires, String etag) { + this.lastModified = lastModified; + this.etag = etag; + this.expires = expires; + this.varyHeader = null; + } + + public CacheEntry(String lastModified, Date expires, String etag, String varyHeader) { + this.lastModified = lastModified; + this.etag = etag; + this.expires = expires; + this.varyHeader = varyHeader; + } + public String getLastModified() { return lastModified; } + public String getEtag() { return etag; } + @Override - public String toString(){ - return lastModified+" "+etag; + public String toString() { + return lastModified + " " + etag + " " + varyHeader; } + public Date getExpires() { return expires; } + + public String getVaryHeader() { + return varyHeader; + } } /** @@ -127,19 +163,34 @@ public Date getExpires() { * @param res result */ public void saveDetails(URLConnection conn, HTTPSampleResult res){ - if (isCacheable(res) && !hasVaryHeader(conn)){ + final String varyHeader = conn.getHeaderField(HTTPConstants.VARY); + if (isCacheable(res, varyHeader)){ String lastModified = conn.getHeaderField(HTTPConstants.LAST_MODIFIED); String expires = conn.getHeaderField(HTTPConstants.EXPIRES); String etag = conn.getHeaderField(HTTPConstants.ETAG); String url = conn.getURL().toString(); String cacheControl = conn.getHeaderField(HTTPConstants.CACHE_CONTROL); String date = conn.getHeaderField(HTTPConstants.DATE); - setCache(lastModified, cacheControl, expires, etag, url, date); + setCache(lastModified, cacheControl, expires, etag, url, date, getVaryHeader(varyHeader, asHeaders(res.getRequestHeaders()))); } } - private boolean hasVaryHeader(URLConnection conn) { - return conn.getHeaderField(HTTPConstants.VARY) != null; + private Pair getVaryHeader(String headerName, Header[] reqHeaders) { + if (headerName == null) { + return null; + } + final Set names = new HashSet<>(Arrays.asList(headerName.split(",\\s*"))); + final Map> values = new HashMap<>(); + for (final String name: names) { + values.put(name, new ArrayList()); + } + for (Header header: reqHeaders) { + if (names.contains(header.getName())) { + log.debug("Found vary value {} for {} in response", header, headerName); + values.get(header.getName()).add(header.getValue()); + } + } + return new ImmutablePair<>(headerName, values.toString()); } /** @@ -152,22 +203,19 @@ private boolean hasVaryHeader(URLConnection conn) { * result to decide if result is cacheable */ public void saveDetails(HttpResponse method, HTTPSampleResult res) { - if (isCacheable(res) && !hasVaryHeader(method)){ + final String varyHeader = getHeader(method, HTTPConstants.VARY); + if (isCacheable(res, varyHeader)){ String lastModified = getHeader(method ,HTTPConstants.LAST_MODIFIED); String expires = getHeader(method ,HTTPConstants.EXPIRES); String etag = getHeader(method ,HTTPConstants.ETAG); String cacheControl = getHeader(method, HTTPConstants.CACHE_CONTROL); String date = getHeader(method, HTTPConstants.DATE); - setCache(lastModified, cacheControl, expires, etag, res.getUrlAsString(), date); // TODO correct URL? + setCache(lastModified, cacheControl, expires, etag, res.getUrlAsString(), date, getVaryHeader(varyHeader, asHeaders(res.getRequestHeaders()))); // TODO correct URL? } } - private boolean hasVaryHeader(HttpResponse method) { - return getHeader(method, HTTPConstants.VARY) != null; - } - // helper method to save the cache entry - private void setCache(String lastModified, String cacheControl, String expires, String etag, String url, String date) { + private void setCache(String lastModified, String cacheControl, String expires, String etag, String url, String date, Pair varyHeader) { if (log.isDebugEnabled()){ log.debug("setCache(" + lastModified + "," @@ -180,65 +228,99 @@ private void setCache(String lastModified, String cacheControl, String expires, } Date expiresDate = null; // i.e. not using Expires if (useExpires) {// Check that we are processing Expires/CacheControl - final String MAX_AGE = "max-age="; - + final String maxAge = "max-age="; + if(cacheControl != null && cacheControl.contains("no-store")) { // We must not store an CacheEntry, otherwise a // conditional request may be made return; } if (expires != null) { - try { - expiresDate = org.apache.http.client.utils.DateUtils.parseDate(expires); - } catch (IllegalArgumentException e) { - if (log.isDebugEnabled()){ - log.debug("Unable to parse Expires: '"+expires+"' "+e); - } - expiresDate = CacheManager.EXPIRED_DATE; // invalid dates must be treated as expired - } + expiresDate = extractExpiresDateFromExpires(expires); } // if no-cache is present, ensure that expiresDate remains null, which forces revalidation if(cacheControl != null && !cacheControl.contains("no-cache")) { - // the max-age directive overrides the Expires header, - if(cacheControl.contains(MAX_AGE)) { - long maxAgeInSecs = Long.parseLong( - cacheControl.substring(cacheControl.indexOf(MAX_AGE)+MAX_AGE.length()) - .split("[, ]")[0] // Bug 51932 - allow for optional trailing attributes - ); - expiresDate=new Date(System.currentTimeMillis()+maxAgeInSecs*1000); - - } else if(expires==null) { // No max-age && No expires - if(!StringUtils.isEmpty(lastModified) && !StringUtils.isEmpty(date)) { - try { - Date responseDate = DateUtils.parseDate( date ); - Date lastModifiedAsDate = DateUtils.parseDate( lastModified ); - // see https://developer.mozilla.org/en/HTTP_Caching_FAQ - // see http://www.ietf.org/rfc/rfc2616.txt#13.2.4 - expiresDate=new Date(System.currentTimeMillis() - +Math.round((responseDate.getTime()-lastModifiedAsDate.getTime())*0.1)); - } catch(IllegalArgumentException e) { - // date or lastModified may be null or in bad format - if(log.isWarnEnabled()) { - log.warn("Failed computing expiration date with following info:" - +lastModified + "," - + cacheControl + "," - + expires + "," - + etag + "," - + url + "," - + date); - } - // TODO Can't see anything in SPEC - expiresDate = new Date(System.currentTimeMillis()+ONE_YEAR_MS); - } - } else { - // TODO Can't see anything in SPEC - expiresDate = new Date(System.currentTimeMillis()+ONE_YEAR_MS); - } - } + expiresDate = extractExpiresDateFromCacheControl(lastModified, + cacheControl, expires, etag, url, date, maxAge, expiresDate); // else expiresDate computed in (expires!=null) condition is used } } - getCache().put(url, new CacheEntry(lastModified, expiresDate, etag)); + if (varyHeader != null) { + if (log.isDebugEnabled()) { + log.debug("Set entry into cache for url {} and vary {} ({})", url, + varyHeader, + varyUrl(url, varyHeader.getLeft(), varyHeader.getRight())); + } + getCache().put(url, new CacheEntry(lastModified, expiresDate, etag, varyHeader.getLeft())); + getCache().put(varyUrl(url, varyHeader.getLeft(), varyHeader.getRight()), new CacheEntry(lastModified, expiresDate, etag, null)); + } else { + getCache().put(url, new CacheEntry(lastModified, expiresDate, etag, null)); + } + } + + private Date extractExpiresDateFromExpires(String expires) { + Date expiresDate; + try { + expiresDate = org.apache.http.client.utils.DateUtils + .parseDate(expires); + } catch (IllegalArgumentException e) { + if (log.isDebugEnabled()) { + log.debug("Unable to parse Expires: '" + expires + "' " + e); + } + expiresDate = CacheManager.EXPIRED_DATE; // invalid dates must be + // treated as expired + } + return expiresDate; + } + + private Date extractExpiresDateFromCacheControl(String lastModified, + String cacheControl, String expires, String etag, String url, + String date, final String maxAge, Date defaultExpiresDate) { + // the max-age directive overrides the Expires header, + if (cacheControl.contains(maxAge)) { + long maxAgeInSecs = Long.parseLong(cacheControl + .substring(cacheControl.indexOf(maxAge) + maxAge.length()) + .split("[, ]")[0] // Bug 51932 - allow for optional trailing + // attributes + ); + return new Date(System.currentTimeMillis() + maxAgeInSecs * 1000); + + } else if (expires == null) { // No max-age && No expires + return calcExpiresDate(lastModified, cacheControl, expires, etag, + url, date); + } + return defaultExpiresDate; + } + + private Date calcExpiresDate(String lastModified, String cacheControl, + String expires, String etag, String url, String date) { + if(!StringUtils.isEmpty(lastModified) && !StringUtils.isEmpty(date)) { + try { + Date responseDate = DateUtils.parseDate(date); + Date lastModifiedAsDate = DateUtils.parseDate(lastModified); + // see https://developer.mozilla.org/en/HTTP_Caching_FAQ + // see http://www.ietf.org/rfc/rfc2616.txt#13.2.4 + return new Date(System.currentTimeMillis() + Math.round( + (responseDate.getTime() - lastModifiedAsDate.getTime()) + * 0.1)); + } catch(IllegalArgumentException e) { + // date or lastModified may be null or in bad format + if(log.isWarnEnabled()) { + log.warn("Failed computing expiration date with following info:" + +lastModified + "," + + cacheControl + "," + + expires + "," + + etag + "," + + url + "," + + date); + } + // TODO Can't see anything in SPEC + return new Date(System.currentTimeMillis() + ONE_YEAR_MS); + } + } else { + // TODO Can't see anything in SPEC + return new Date(System.currentTimeMillis() + ONE_YEAR_MS); + } } // Apache HttpClient @@ -251,7 +333,10 @@ private String getHeader(HttpResponse method, String name) { * Is the sample result OK to cache? * i.e is it in the 2xx range or equal to 304, and is it a cacheable method? */ - private boolean isCacheable(HTTPSampleResult res){ + private boolean isCacheable(HTTPSampleResult res, String varyHeader){ + if ("*".equals(varyHeader)) { + return false; + } final String responseCode = res.getResponseCode(); return isCacheableMethod(res) && (("200".compareTo(responseCode) <= 0 // $NON-NLS-1$ @@ -280,9 +365,9 @@ private boolean isCacheableMethod(HTTPSampleResult res) { * @param request where to set the headers */ public void setHeaders(URL url, HttpRequestBase request) { - CacheEntry entry = getCache().get(url.toString()); + CacheEntry entry = getEntry(url.toString(), request.getAllHeaders()); if (log.isDebugEnabled()){ - log.debug(request.getMethod()+"(OAH) "+url.toString()+" "+entry); + log.debug("{}(OAH) {} {}", request.getMethod(), url.toString(), entry); } if (entry != null){ final String lastModified = entry.getLastModified(); @@ -306,9 +391,9 @@ public void setHeaders(URL url, HttpRequestBase request) { * @param conn where to set the headers */ public void setHeaders(HttpURLConnection conn, URL url) { - CacheEntry entry = getCache().get(url.toString()); + CacheEntry entry = getEntry(url.toString(), asHeaders(conn.getHeaderFields())); if (log.isDebugEnabled()){ - log.debug(conn.getRequestMethod()+"(Java) "+url.toString()+" "+entry); + log.debug("{}(Java) {} {}", conn.getRequestMethod(), url.toString(), entry); } if (entry != null){ final String lastModified = entry.getLastModified(); @@ -323,35 +408,129 @@ public void setHeaders(HttpURLConnection conn, URL url) { } /** - * Check the cache, if the entry has an expires header and the entry has not expired, return true
- * @param url {@link URL} to look up in cache - * @return true if entry has an expires header and the entry has not expired, else false + * Check the cache, if the entry has an expires header and the entry has not + * expired, return true
+ * + * @param url + * {@link URL} to look up in cache + * @return true if entry has an expires header and the entry + * has not expired, else false + * @deprecated use a version of {@link CacheManager#inCache(URL, Header[])} + * or + * {@link CacheManager#inCache(URL, org.apache.jmeter.protocol.http.control.Header[])} */ + @Deprecated public boolean inCache(URL url) { - CacheEntry entry = getCache().get(url.toString()); - if (log.isDebugEnabled()){ - log.debug("inCache "+url.toString()+" "+entry); + return entryStillValid(getEntry(url.toString(), null)); + } + + public boolean inCache(URL url, Header[] allHeaders) { + return entryStillValid(getEntry(url.toString(), allHeaders)); + } + + public boolean inCache(URL url, org.apache.jmeter.protocol.http.control.Header[] allHeaders) { + return entryStillValid(getEntry(url.toString(), asHeaders(allHeaders))); + } + + private Header[] asHeaders( + org.apache.jmeter.protocol.http.control.Header[] allHeaders) { + final List
result = new ArrayList<>(allHeaders.length); + for (org.apache.jmeter.protocol.http.control.Header header: allHeaders) { + result.add(new HeaderAdapter(header)); } - if (entry != null){ + return result.toArray(new Header[result.size()]); + } + + private Header[] asHeaders(String allHeaders) { + List
result = new ArrayList<>(); + for (String line: allHeaders.split("\\n")) { + String[] splitted = line.split(": ", 2); + if (splitted.length == 2) { + result.add(new BasicHeader(splitted[0], splitted[1])); + } + } + return result.toArray(new Header[result.size()]); + } + + private Header[] asHeaders(Map> headers) { + List
result = new ArrayList<>(headers.size()); + for (Map.Entry> header: headers.entrySet()) { + new BasicHeader(header.getKey(), String.join(", ", header.getValue())); + } + return result.toArray(new Header[result.size()]); + } + + private static class HeaderAdapter implements Header { + + private final org.apache.jmeter.protocol.http.control.Header delegate; + + public HeaderAdapter(org.apache.jmeter.protocol.http.control.Header delegate) { + this.delegate = delegate; + } + + @Override + public HeaderElement[] getElements() { + throw new UnsupportedOperationException(); + } + + @Override + public String getName() { + return delegate.getName(); + } + + @Override + public String getValue() { + return delegate.getValue(); + } + + } + + private boolean entryStillValid(CacheEntry entry) { + log.debug("Check if entry {} is still valid", entry); + if (entry != null && entry.getVaryHeader() == null) { final Date expiresDate = entry.getExpires(); if (expiresDate != null) { if (expiresDate.after(new Date())) { - if (log.isDebugEnabled()){ - log.debug("Expires= " + expiresDate + " (Valid)"); - } + log.debug("Expires= {} (Valid)", expiresDate); return true; } else { - if (log.isDebugEnabled()){ - log.debug("Expires= " + expiresDate + " (Expired)"); - } + log.debug("Expires= {} (Expired)", expiresDate); } } } return false; } + private CacheEntry getEntry(String url, Header[] headers) { + CacheEntry entry = getCache().get(url); + log.debug("Cache: {}", getCache()); + log.debug("inCache {} {} {}", url, entry, headers); + if (entry == null) { + log.debug("No entry found for url {}", url); + return null; + } + if (entry.getVaryHeader() == null) { + log.debug("Entry {} with no vary found for url {}", entry, url); + return entry; + } + if (headers == null) { + log.debug("Entry {} found, but it should depend on vary {} for url {}", entry, entry.getVaryHeader(), url); + return null; + } + Pair varyPair = getVaryHeader(entry.getVaryHeader(), headers); + if (varyPair != null) { + log.debug("Looking again for {} because of {} with vary: {} ({})", url, entry, entry.getVaryHeader(), varyPair); + return getEntry(varyUrl(url, entry.getVaryHeader(), varyPair.getRight()), null); + } + return null; + } + + private String varyUrl(String url, String headerName, String headerValue) { + return "vary-" + headerName + "-" + headerValue + "-" + url; + } + private Map getCache() { - return localCache != null?localCache:threadCache.get(); + return localCache != null ? localCache : threadCache.get(); } public boolean getClearEachIteration() { diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java index defbf653de4..573262182f9 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPHC4Impl.java @@ -440,7 +440,7 @@ protected HTTPSampleResult sample(URL url, String method, res.sampleStart(); final CacheManager cacheManager = getCacheManager(); - if (cacheManager != null && HTTPConstants.GET.equalsIgnoreCase(method) && cacheManager.inCache(url)) { + if (cacheManager != null && HTTPConstants.GET.equalsIgnoreCase(method) && cacheManager.inCache(url, httpRequest.getAllHeaders())) { return updateSampleResultForResourceInCache(res); } CloseableHttpResponse httpResponse = null; diff --git a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPJavaImpl.java b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPJavaImpl.java index 4c8eba48f44..0b49036da26 100644 --- a/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPJavaImpl.java +++ b/src/protocol/http/org/apache/jmeter/protocol/http/sampler/HTTPJavaImpl.java @@ -26,6 +26,7 @@ import java.net.Proxy; import java.net.URL; import java.net.URLConnection; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -484,7 +485,7 @@ protected HTTPSampleResult sample(URL url, String method, boolean areFollowingRe // Check cache for an entry with an Expires header in the future final CacheManager cacheManager = getCacheManager(); if (cacheManager != null && HTTPConstants.GET.equalsIgnoreCase(method)) { - if (cacheManager.inCache(url)) { + if (cacheManager.inCache(url, getHeaders(getHeaderManager()))) { return updateSampleResultForResourceInCache(res); } } @@ -626,6 +627,19 @@ protected HTTPSampleResult sample(URL url, String method, boolean areFollowingRe } } + private Header[] getHeaders(HeaderManager headerManager) { + final List
allHeaders = new ArrayList<>(); + if (headerManager != null) { + final CollectionProperty headers = headerManager.getHeaders(); + if (headers != null) { + for (final JMeterProperty jMeterProperty : headers) { + allHeaders.add((Header) jMeterProperty.getObjectValue()); + } + } + } + return allHeaders.toArray(new Header[allHeaders.size()]); + } + protected void disconnect(HttpURLConnection conn) { if (conn != null) { String connection = conn.getHeaderField(HTTPConstants.HEADER_CONNECTION); diff --git a/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerBase.java b/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerBase.java index fa88dfacfac..98da883142c 100644 --- a/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerBase.java +++ b/test/src/org/apache/jmeter/protocol/http/control/TestCacheManagerBase.java @@ -18,6 +18,12 @@ package org.apache.jmeter.protocol.http.control; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + import java.lang.reflect.Field; import java.net.URL; import java.text.SimpleDateFormat; @@ -33,8 +39,6 @@ import org.junit.Before; import org.junit.Test; -import static org.junit.Assert.*; - public abstract class TestCacheManagerBase extends JMeterTestCase { protected static final String LOCAL_HOST = "http://localhost/"; protected static final String EXPECTED_ETAG = "0xCAFEBABEDEADBEEF"; @@ -157,25 +161,75 @@ public void testCacheControl() throws Exception { @Test public void testCacheVarySomething() throws Exception { - testCacheVary("Something"); + String varyHeader = "Something"; + testCacheVary(varyHeader, new Header[] { new Header(varyHeader, "value") }, + new Header[] { + new Header(varyHeader, "something completely different") }); } @Test public void testCacheVaryAcceptEncoding() throws Exception { - testCacheVary("Accept-Encoding"); + String varyHeader = "Accept-Encoding"; + testCacheVary(varyHeader, + new Header[] { new Header(varyHeader, "value") }, new Header[] { + new Header(varyHeader, "something completely different") }); + } + + @Test + public void testCacheMultiValueVaryHeaders() throws Exception { + String varyHeader = "Accept-Encoding"; + testCacheVary(varyHeader, + new Header[] { new Header(varyHeader, "value"), + new Header(varyHeader, "another value") }, + new Header[] { new Header(varyHeader, + "something completely different") }); } - private void testCacheVary(String vary) throws Exception { + @Test + public void testCacheMultipleVaryHeaders() throws Exception { + String varyHeaderOne = "Accept-Encoding"; + String varyHeaderTwo = "Something"; + testCacheVary(varyHeaderOne + "," + varyHeaderTwo, + new Header[] { new Header(varyHeaderOne, "first value"), + new Header(varyHeaderTwo, "another value") }, + new Header[] { new Header(varyHeaderOne, + "first") }); + } + + @Test + public void testCacheMultipleMultiVaryHeaders() throws Exception { + String varyHeaderOne = "Accept-Encoding"; + String varyHeaderTwo = "Something"; + testCacheVary(varyHeaderOne + "," + varyHeaderTwo, + new Header[] { new Header(varyHeaderOne, "first value"), + new Header(varyHeaderOne, "second value"), + new Header(varyHeaderTwo, "another value") }, + new Header[] { new Header(varyHeaderOne, "first value"), + new Header(varyHeaderOne, "another value") }); + } + + private String asString(Header[] headers) { + StringBuilder result = new StringBuilder(); + for (Header header: headers) { + result.append(header.getName()).append(": ").append(header.getValue()).append("\n"); + } + return result.toString(); + } + + private void testCacheVary(String vary, Header[] origHeaders, Header[] differentHeaders) throws Exception { this.cacheManager.setUseExpires(true); this.cacheManager.testIterationStart(null); assertNull("Should not find entry", getThreadCacheEntry(LOCAL_HOST)); - assertFalse("Should not find valid entry", this.cacheManager.inCache(url)); + assertFalse("Should not find valid entry", this.cacheManager.inCache(url, origHeaders)); setExpires(makeDate(new Date(System.currentTimeMillis()))); setCacheControl("public, max-age=5"); + sampleResultOK.setRequestHeaders(asString(origHeaders)); this.vary = vary; cacheResult(sampleResultOK); - assertNull("Should not find entry", getThreadCacheEntry(LOCAL_HOST)); - assertFalse("Should not find valid entry", this.cacheManager.inCache(url)); + assertNotNull("Should find entry with vary header", getThreadCacheEntry(LOCAL_HOST).getVaryHeader()); + assertFalse("Should not find valid entry without headers", this.cacheManager.inCache(url)); + assertTrue("Should find valid entry with headers", this.cacheManager.inCache(url, origHeaders)); + assertFalse("Should not find valid entry with different header", this.cacheManager.inCache(url, differentHeaders)); this.vary = null; } diff --git a/xdocs/usermanual/component_reference.xml b/xdocs/usermanual/component_reference.xml index 1bb816435b2..f0acb22144a 100644 --- a/xdocs/usermanual/component_reference.xml +++ b/xdocs/usermanual/component_reference.xml @@ -3805,7 +3805,6 @@ If the requested document has not changed since it was cached, then the response Likewise if the Expires date is in the future. This may cause problems for Assertions. -Responses with a Vary header will not be cached. Descriptive name for this element that is shown in the tree.