diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java index 01141b32c6..200cfe4313 100644 --- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java +++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java @@ -444,6 +444,7 @@ void callChain( } void callBackend( + final RequestCacheControl requestCacheControl, final HttpHost target, final SimpleHttpRequest request, final AsyncExecChain.Scope scope, @@ -463,7 +464,7 @@ public AsyncDataConsumer handleResponse( final HttpResponse backendResponse, final EntityDetails entityDetails) throws HttpException, IOException { final Instant responseDate = getCurrentDate(); - final AsyncExecCallback callback = new BackendResponseHandler(target, request, requestDate, responseDate, scope, asyncExecCallback); + final AsyncExecCallback callback = new BackendResponseHandler(requestCacheControl, target, request, requestDate, responseDate, scope, asyncExecCallback); callbackRef.set(callback); return callback.handleResponse(backendResponse, entityDetails); } @@ -592,6 +593,7 @@ public void releaseResources() { class BackendResponseHandler implements AsyncExecCallback { + private final RequestCacheControl requestCacheControl; private final HttpHost target; private final SimpleHttpRequest request; private final Instant requestDate; @@ -601,12 +603,14 @@ class BackendResponseHandler implements AsyncExecCallback { private final AtomicReference cachingConsumerRef; BackendResponseHandler( + final RequestCacheControl requestCacheControl, final HttpHost target, final SimpleHttpRequest request, final Instant requestDate, final Instant responseDate, final AsyncExecChain.Scope scope, final AsyncExecCallback asyncExecCallback) { + this.requestCacheControl = requestCacheControl; this.target = target; this.request = request; this.requestDate = requestDate; @@ -649,7 +653,8 @@ public void cancelled() { final HttpCacheContext context = HttpCacheContext.cast(scope.clientContext); final ResponseCacheControl responseCacheControl = CacheControlHeaderParser.INSTANCE.parse(backendResponse); context.setResponseCacheControl(responseCacheControl); - final boolean cacheable = responseCachingPolicy.isResponseCacheable(responseCacheControl, request, backendResponse); + final boolean cacheable = responseCachingPolicy.isResponseCacheable( + requestCacheControl, responseCacheControl, request, backendResponse); if (cacheable) { storeRequestIfModifiedSinceFor304Response(request, backendResponse); if (LOG.isDebugEnabled()) { @@ -894,17 +899,17 @@ private void handleCacheHit( if (LOG.isDebugEnabled()) { LOG.debug("{} cache entry does not match the request; calling backend", exchangeId); } - callBackend(target, request, scope, chain, asyncExecCallback); + callBackend(requestCacheControl, target, request, scope, chain, asyncExecCallback); } else if (hit.entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request)) { if (LOG.isDebugEnabled()) { LOG.debug("{} non-modified cache entry does not match the non-conditional request; calling backend", exchangeId); } - callBackend(target, request, scope, chain, asyncExecCallback); + callBackend(requestCacheControl, target, request, scope, chain, asyncExecCallback); } else if (cacheSuitability == CacheSuitability.REVALIDATION_REQUIRED) { if (LOG.isDebugEnabled()) { LOG.debug("{} revalidation required; revalidating cache entry", exchangeId); } - revalidateCacheEntryWithoutFallback(responseCacheControl, hit, target, request, scope, chain, asyncExecCallback); + revalidateCacheEntryWithoutFallback(requestCacheControl, responseCacheControl, hit, target, request, scope, chain, asyncExecCallback); } else if (cacheSuitability == CacheSuitability.STALE_WHILE_REVALIDATED) { if (cacheRevalidator != null) { if (LOG.isDebugEnabled()) { @@ -928,7 +933,7 @@ private void handleCacheHit( cacheRevalidator.revalidateCacheEntry( hit.getEntryKey(), asyncExecCallback, - c -> revalidateCacheEntry(responseCacheControl, hit, target, request, fork, chain, c)); + c -> revalidateCacheEntry(requestCacheControl, responseCacheControl, hit, target, request, fork, chain, c)); context.setCacheResponseStatus(CacheResponseStatus.CACHE_MODULE_RESPONSE); final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry); context.setCacheEntry(hit.entry); @@ -951,12 +956,13 @@ private void handleCacheHit( if (LOG.isDebugEnabled()) { LOG.debug("{} cache entry not usable; calling backend", exchangeId); } - callBackend(target, request, scope, chain, asyncExecCallback); + callBackend(requestCacheControl, target, request, scope, chain, asyncExecCallback); } } } void revalidateCacheEntry( + final RequestCacheControl requestCacheControl, final ResponseCacheControl responseCacheControl, final CacheHit hit, final HttpHost target, @@ -1018,7 +1024,7 @@ AsyncExecCallback evaluateResponse(final HttpResponse backendResponse, final Ins if (statusCode == HttpStatus.SC_NOT_MODIFIED) { return new AsyncExecCallbackWrapper(() -> triggerUpdatedCacheEntryResponse(backendResponse, responseDate), asyncExecCallback::failed); } - return new BackendResponseHandler(target, conditional, requestDate, responseDate, scope, asyncExecCallback); + return new BackendResponseHandler(requestCacheControl, target, conditional, requestDate, responseDate, scope, asyncExecCallback); } @Override @@ -1117,6 +1123,7 @@ public void failed(final Exception cause) { } void revalidateCacheEntryWithoutFallback( + final RequestCacheControl requestCacheControl, final ResponseCacheControl responseCacheControl, final CacheHit hit, final HttpHost target, @@ -1126,7 +1133,7 @@ void revalidateCacheEntryWithoutFallback( final AsyncExecCallback asyncExecCallback) { final String exchangeId = scope.exchangeId; final HttpCacheContext context = HttpCacheContext.cast(scope.clientContext); - revalidateCacheEntry(responseCacheControl, hit, target, request, scope, chain, new AsyncExecCallback() { + revalidateCacheEntry(requestCacheControl, responseCacheControl, hit, target, request, scope, chain, new AsyncExecCallback() { private final AtomicBoolean committed = new AtomicBoolean(); @@ -1175,7 +1182,7 @@ void revalidateCacheEntryWithFallback( final AsyncExecCallback asyncExecCallback) { final String exchangeId = scope.exchangeId; final HttpCacheContext context = HttpCacheContext.cast(scope.clientContext); - revalidateCacheEntry(responseCacheControl, hit, target, request, scope, chain, new AsyncExecCallback() { + revalidateCacheEntry(requestCacheControl, responseCacheControl, hit, target, request, scope, chain, new AsyncExecCallback() { private final AtomicReference committed = new AtomicReference<>(); @@ -1280,9 +1287,9 @@ private void handleCacheMiss( @Override public void completed(final Collection variants) { if (variants != null && !variants.isEmpty()) { - negotiateResponseFromVariants(target, request, scope, chain, asyncExecCallback, variants); + negotiateResponseFromVariants(requestCacheControl, target, request, scope, chain, asyncExecCallback, variants); } else { - callBackend(target, request, scope, chain, asyncExecCallback); + callBackend(requestCacheControl, target, request, scope, chain, asyncExecCallback); } } @@ -1298,11 +1305,12 @@ public void cancelled() { })); } else { - callBackend(target, request, scope, chain, asyncExecCallback); + callBackend(requestCacheControl, target, request, scope, chain, asyncExecCallback); } } void negotiateResponseFromVariants( + final RequestCacheControl requestCacheControl, final HttpHost target, final SimpleHttpRequest request, final AsyncExecChain.Scope scope, @@ -1373,25 +1381,25 @@ public AsyncDataConsumer handleResponse( final Instant responseDate = getCurrentDate(); final AsyncExecCallback callback; if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) { - callback = new BackendResponseHandler(target, request, requestDate, responseDate, scope, asyncExecCallback); + callback = new BackendResponseHandler(requestCacheControl, target, request, requestDate, responseDate, scope, asyncExecCallback); } else { final ETag resultEtag = ETag.get(backendResponse); if (resultEtag == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} 304 response did not contain ETag", exchangeId); } - callback = new AsyncExecCallbackWrapper(() -> callBackend(target, request, scope, chain, asyncExecCallback), asyncExecCallback::failed); + callback = new AsyncExecCallbackWrapper(() -> callBackend(requestCacheControl, target, request, scope, chain, asyncExecCallback), asyncExecCallback::failed); } else { final CacheHit match = variantMap.get(resultEtag); if (match == null) { if (LOG.isDebugEnabled()) { LOG.debug("{} 304 response did not contain ETag matching one sent in If-None-Match", exchangeId); } - callback = new AsyncExecCallbackWrapper(() -> callBackend(target, request, scope, chain, asyncExecCallback), asyncExecCallback::failed); + callback = new AsyncExecCallbackWrapper(() -> callBackend(requestCacheControl, target, request, scope, chain, asyncExecCallback), asyncExecCallback::failed); } else { if (HttpCacheEntry.isNewer(match.entry, backendResponse)) { final SimpleHttpRequest unconditional = conditionalRequestBuilder.makeUnconditional(request); - callback = new AsyncExecCallbackWrapper(() -> callBackend(target, unconditional, scope, chain, asyncExecCallback), asyncExecCallback::failed); + callback = new AsyncExecCallbackWrapper(() -> callBackend(requestCacheControl, target, unconditional, scope, chain, asyncExecCallback), asyncExecCallback::failed); } else { callback = new AsyncExecCallbackWrapper(() -> updateVariantCacheEntry(backendResponse, responseDate, match), asyncExecCallback::failed); } diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java index 81f1abc936..24c1dadf9b 100644 --- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java +++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExec.java @@ -180,7 +180,7 @@ ClassicHttpResponse doExecute( if (LOG.isDebugEnabled()) { LOG.debug("{} request cannot be served from cache", exchangeId); } - return callBackend(target, cacheRequest, scope, chain); + return callBackend(requestCacheControl, target, cacheRequest, scope, chain); } final CacheMatch result = responseCache.match(target, cacheRequest); @@ -230,6 +230,7 @@ SimpleHttpRequest prepareRequest(final ClassicHttpRequest request) { } ClassicHttpResponse callBackend( + final RequestCacheControl requestCacheControl, final HttpHost target, final SimpleHttpRequest request, final ExecChain.Scope scope, @@ -243,7 +244,7 @@ ClassicHttpResponse callBackend( } final ClassicHttpResponse backendResponse = callChain(request, scope, chain); try { - return handleBackendResponse(target, request, scope, requestDate, getCurrentDate(), backendResponse); + return handleBackendResponse(requestCacheControl, target, request, scope, requestDate, getCurrentDate(), backendResponse); } catch (final IOException | RuntimeException ex) { backendResponse.close(); throw ex; @@ -325,17 +326,17 @@ private ClassicHttpResponse handleCacheHit( if (LOG.isDebugEnabled()) { LOG.debug("{} cache entry does not match the request; calling backend", exchangeId); } - return callBackend(target, request, scope, chain); + return callBackend(requestCacheControl, target, request, scope, chain); } else if (hit.entry.getStatus() == HttpStatus.SC_NOT_MODIFIED && !suitabilityChecker.isConditional(request)) { if (LOG.isDebugEnabled()) { LOG.debug("{} non-modified cache entry does not match the non-conditional request; calling backend", exchangeId); } - return callBackend(target, request, scope, chain); + return callBackend(requestCacheControl, target, request, scope, chain); } else if (cacheSuitability == CacheSuitability.REVALIDATION_REQUIRED) { if (LOG.isDebugEnabled()) { LOG.debug("{} revalidation required; revalidating cache entry", exchangeId); } - return revalidateCacheEntryWithoutFallback(responseCacheControl, hit, target, request, scope, chain); + return revalidateCacheEntryWithoutFallback(requestCacheControl, responseCacheControl, hit, target, request, scope, chain); } else if (cacheSuitability == CacheSuitability.STALE_WHILE_REVALIDATED) { if (cacheRevalidator != null) { if (LOG.isDebugEnabled()) { @@ -354,7 +355,7 @@ private ClassicHttpResponse handleCacheHit( } cacheRevalidator.revalidateCacheEntry( hit.getEntryKey(), - () -> revalidateCacheEntry(responseCacheControl, hit, target, request, fork, chain)); + () -> revalidateCacheEntry(requestCacheControl, responseCacheControl, hit, target, request, fork, chain)); context.setCacheResponseStatus(CacheResponseStatus.CACHE_MODULE_RESPONSE); final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry); context.setCacheEntry(hit.entry); @@ -373,11 +374,12 @@ private ClassicHttpResponse handleCacheHit( if (LOG.isDebugEnabled()) { LOG.debug("{} cache entry not usable; calling backend", exchangeId); } - return callBackend(target, request, scope, chain); + return callBackend(requestCacheControl, target, request, scope, chain); } } ClassicHttpResponse revalidateCacheEntry( + final RequestCacheControl requestCacheControl, final ResponseCacheControl responseCacheControl, final CacheHit hit, final HttpHost target, @@ -412,7 +414,7 @@ ClassicHttpResponse revalidateCacheEntry( context.setCacheEntry(updated.entry); return convert(cacheResponse); } - return handleBackendResponse(target, conditional, scope, requestDate, responseDate, backendResponse); + return handleBackendResponse(requestCacheControl, target, conditional, scope, requestDate, responseDate, backendResponse); } catch (final IOException | RuntimeException ex) { backendResponse.close(); throw ex; @@ -420,6 +422,7 @@ ClassicHttpResponse revalidateCacheEntry( } ClassicHttpResponse revalidateCacheEntryWithoutFallback( + final RequestCacheControl requestCacheControl, final ResponseCacheControl responseCacheControl, final CacheHit hit, final HttpHost target, @@ -429,7 +432,7 @@ ClassicHttpResponse revalidateCacheEntryWithoutFallback( final String exchangeId = scope.exchangeId; final HttpCacheContext context = HttpCacheContext.cast(scope.clientContext); try { - return revalidateCacheEntry(responseCacheControl, hit, target, request, scope, chain); + return revalidateCacheEntry(requestCacheControl, responseCacheControl, hit, target, request, scope, chain); } catch (final IOException ex) { if (LOG.isDebugEnabled()) { LOG.debug("{} I/O error while revalidating cache entry", exchangeId, ex); @@ -451,7 +454,7 @@ ClassicHttpResponse revalidateCacheEntryWithFallback( final HttpCacheContext context = HttpCacheContext.cast(scope.clientContext); final ClassicHttpResponse response; try { - response = revalidateCacheEntry(responseCacheControl, hit, target, request, scope, chain); + response = revalidateCacheEntry(requestCacheControl, responseCacheControl, hit, target, request, scope, chain); } catch (final IOException ex) { if (LOG.isDebugEnabled()) { LOG.debug("{} I/O error while revalidating cache entry", exchangeId, ex); @@ -483,6 +486,7 @@ ClassicHttpResponse revalidateCacheEntryWithFallback( } ClassicHttpResponse handleBackendResponse( + final RequestCacheControl requestCacheControl, final HttpHost target, final SimpleHttpRequest request, final ExecChain.Scope scope, @@ -500,7 +504,8 @@ ClassicHttpResponse handleBackendResponse( final HttpCacheContext context = HttpCacheContext.cast(scope.clientContext); final ResponseCacheControl responseCacheControl = CacheControlHeaderParser.INSTANCE.parse(backendResponse); context.setResponseCacheControl(responseCacheControl); - final boolean cacheable = responseCachingPolicy.isResponseCacheable(responseCacheControl, request, backendResponse); + final boolean cacheable = responseCachingPolicy.isResponseCacheable( + requestCacheControl, responseCacheControl, request, backendResponse); if (cacheable) { storeRequestIfModifiedSinceFor304Response(request, backendResponse); if (LOG.isDebugEnabled()) { @@ -616,14 +621,15 @@ private ClassicHttpResponse handleCacheMiss( if (partialMatch != null && partialMatch.entry.hasVariants() && request.getBody() == null) { final List variants = responseCache.getVariants(partialMatch); if (variants != null && !variants.isEmpty()) { - return negotiateResponseFromVariants(target, request, scope, chain, variants); + return negotiateResponseFromVariants(requestCacheControl, target, request, scope, chain, variants); } } - return callBackend(target, request, scope, chain); + return callBackend(requestCacheControl, target, request, scope, chain); } ClassicHttpResponse negotiateResponseFromVariants( + final RequestCacheControl requestCacheControl, final HttpHost target, final SimpleHttpRequest request, final ExecChain.Scope scope, @@ -649,7 +655,7 @@ ClassicHttpResponse negotiateResponseFromVariants( final Instant responseDate = getCurrentDate(); if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) { - return handleBackendResponse(target, request, scope, requestDate, responseDate, backendResponse); + return handleBackendResponse(requestCacheControl, target, request, scope, requestDate, responseDate, backendResponse); } // 304 response are not expected to have an enclosed content body, but still backendResponse.close(); @@ -659,7 +665,7 @@ ClassicHttpResponse negotiateResponseFromVariants( if (LOG.isDebugEnabled()) { LOG.debug("{} 304 response did not contain ETag", exchangeId); } - return callBackend(target, request, scope, chain); + return callBackend(requestCacheControl, target, request, scope, chain); } final CacheHit match = variantMap.get(resultEtag); @@ -667,12 +673,12 @@ ClassicHttpResponse negotiateResponseFromVariants( if (LOG.isDebugEnabled()) { LOG.debug("{} 304 response did not contain ETag matching one sent in If-None-Match", exchangeId); } - return callBackend(target, request, scope, chain); + return callBackend(requestCacheControl, target, request, scope, chain); } if (HttpCacheEntry.isNewer(match.entry, backendResponse)) { final SimpleHttpRequest unconditional = conditionalRequestBuilder.makeUnconditional(request); - return callBackend(target, unconditional, scope, chain); + return callBackend(requestCacheControl, target, unconditional, scope, chain); } final HttpCacheContext context = HttpCacheContext.cast(scope.clientContext); diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCachingPolicy.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCachingPolicy.java index f5d25c35c6..81850a8fd3 100644 --- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCachingPolicy.java +++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCachingPolicy.java @@ -30,6 +30,7 @@ import java.time.Instant; import java.util.Iterator; +import org.apache.hc.client5.http.cache.RequestCacheControl; import org.apache.hc.client5.http.cache.ResponseCacheControl; import org.apache.hc.client5.http.utils.DateUtils; import org.apache.hc.core5.http.HttpHeaders; @@ -87,9 +88,16 @@ public ResponseCachingPolicy( * Determine if the {@link HttpResponse} gotten from the origin is a * cacheable response. * + * @param requestCacheControl the cache control directives of the request * @return {@code true} if response is cacheable */ - public boolean isResponseCacheable(final ResponseCacheControl cacheControl, final HttpRequest request, final HttpResponse response) { + public boolean isResponseCacheable(final RequestCacheControl requestCacheControl, final ResponseCacheControl cacheControl, final HttpRequest request, final HttpResponse response) { + if (requestCacheControl.isNoStore()) { + if (LOG.isDebugEnabled()) { + LOG.debug("Response is not cacheable as the request enforced no-store"); + } + return false; + } final ProtocolVersion version = request.getVersion() != null ? request.getVersion() : HttpVersion.DEFAULT; if (version.compareToVersion(HttpVersion.HTTP_1_1) > 0) { if (LOG.isDebugEnabled()) { diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestAsyncCachingExecRequestNoStore.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestAsyncCachingExecRequestNoStore.java new file mode 100644 index 0000000000..b3477181cd --- /dev/null +++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestAsyncCachingExecRequestNoStore.java @@ -0,0 +1,120 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.hc.client5.http.impl.cache; + +import java.io.IOException; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpServer; + +import org.apache.hc.client5.http.async.methods.SimpleHttpResponse; +import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder; +import org.apache.hc.client5.http.async.methods.SimpleRequestProducer; +import org.apache.hc.client5.http.async.methods.SimpleResponseConsumer; +import org.apache.hc.client5.http.cache.CacheResponseStatus; +import org.apache.hc.client5.http.cache.HttpCacheContext; +import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient; +import org.apache.hc.client5.http.utils.DateUtils; +import org.apache.hc.core5.http.HttpHeaders; +import org.apache.hc.core5.http.HttpHost; +import org.apache.hc.core5.io.CloseMode; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +class TestAsyncCachingExecRequestNoStore { + + @Test + void testResponseToRequestWithNoStoreIsNotCached() throws Exception { + final AtomicInteger originHits = new AtomicInteger(0); + final HttpServer server = HttpServer.create(new InetSocketAddress(0), 0); + server.createContext("/", exchange -> handleOrigin(exchange, originHits)); + final ExecutorService executorService = Executors.newCachedThreadPool(); + server.setExecutor(executorService); + server.start(); + + final CloseableHttpAsyncClient client = CachingHttpAsyncClients.custom() + .setCacheConfig(CacheConfig.custom().setHeuristicCachingEnabled(false).build()) + .setResourceFactory(HeapResourceFactory.INSTANCE) + .build(); + client.start(); + try { + final HttpHost target = new HttpHost("http", "localhost", server.getAddress().getPort()); + + // First request forbids storage via Cache-Control: no-store. + final SimpleHttpResponse first = client.execute( + SimpleRequestProducer.create(SimpleRequestBuilder.get() + .setHttpHost(target) + .setPath("/") + .addHeader(HttpHeaders.CACHE_CONTROL, "no-store") + .build()), + SimpleResponseConsumer.create(), + HttpCacheContext.create(), + null).get(1, TimeUnit.MINUTES); + Assertions.assertEquals(200, first.getCode()); + + // The response was cacheable on its own, so a follow-up request would be a cache hit + // if it had been stored; since no-store forbade storage, it must miss the cache. + final HttpCacheContext context = HttpCacheContext.create(); + final SimpleHttpResponse second = client.execute( + SimpleRequestProducer.create(SimpleRequestBuilder.get() + .setHttpHost(target) + .setPath("/") + .build()), + SimpleResponseConsumer.create(), + context, + null).get(1, TimeUnit.MINUTES); + Assertions.assertEquals(200, second.getCode()); + + // The no-store response was not stored, so the origin is hit twice. + Assertions.assertEquals(2, originHits.get()); + Assertions.assertEquals(CacheResponseStatus.CACHE_MISS, context.getCacheResponseStatus()); + } finally { + client.close(CloseMode.IMMEDIATE); + server.stop(0); + executorService.shutdownNow(); + } + } + + private static void handleOrigin(final HttpExchange exchange, final AtomicInteger originHits) throws IOException { + originHits.incrementAndGet(); + final byte[] body = "OK".getBytes(StandardCharsets.US_ASCII); + exchange.getResponseHeaders().add("Cache-Control", "public, max-age=60"); + exchange.getResponseHeaders().add("Date", DateUtils.formatStandardDate(Instant.now())); + exchange.sendResponseHeaders(200, body.length); + try (final OutputStream out = exchange.getResponseBody()) { + out.write(body); + } + } +} diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExecChain.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExecChain.java index 4af20bb94d..61cc885700 100644 --- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExecChain.java +++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachingExecChain.java @@ -135,6 +135,22 @@ void testCacheableResponsesGoIntoCache() throws Exception { Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); } + @Test + void testResponseToRequestWithNoStoreIsNotCached() throws Exception { + final ClassicHttpRequest req = HttpTestUtils.makeDefaultRequest(); + req.setHeader("Cache-Control", "no-store"); + final ClassicHttpResponse resp = HttpTestUtils.make200Response(); + resp.setHeader("Cache-Control", "max-age=3600"); + + Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(resp); + + execute(req); + + // A no-store request must not have its response stored. + Mockito.verify(cache, Mockito.never()).store(Mockito.any(), Mockito.any(), + Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any()); + } + @Test void testOlderCacheableResponsesDoNotGoIntoCache() throws Exception { final Instant now = Instant.now(); diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestResponseCachingPolicy.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestResponseCachingPolicy.java index fe263e9065..8fdb3b684e 100644 --- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestResponseCachingPolicy.java +++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestResponseCachingPolicy.java @@ -34,6 +34,7 @@ import java.util.Random; import org.apache.hc.client5.http.auth.StandardAuthScheme; +import org.apache.hc.client5.http.cache.RequestCacheControl; import org.apache.hc.client5.http.cache.ResponseCacheControl; import org.apache.hc.client5.http.classic.methods.HttpOptions; import org.apache.hc.client5.http.utils.DateUtils; @@ -61,6 +62,7 @@ class TestResponseCachingPolicy { private Instant tenSecondsFromNow; private Instant sixSecondsAgo; private ResponseCacheControl responseCacheControl; + private RequestCacheControl requestCacheControl; @BeforeEach void setUp() { @@ -74,36 +76,45 @@ void setUp() { response.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); response.setHeader("Content-Length", "0"); responseCacheControl = ResponseCacheControl.builder().build(); + requestCacheControl = RequestCacheControl.builder().build(); } @Test void testGetCacheable() { policy = new ResponseCachingPolicy(true, false, false); request = new BasicHttpRequest(Method.GET, "/"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testHeadCacheable() { policy = new ResponseCachingPolicy(true, false, false); request = new BasicHttpRequest(Method.HEAD, "/"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testArbitraryMethodNotCacheable() { request = new BasicHttpRequest("PUT", "/"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); request = new BasicHttpRequest("huh", "/"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); + } + + @Test + void testResponseToRequestWithNoStoreIsNotCacheable() { + request = new BasicHttpRequest(Method.GET, "/"); + requestCacheControl = RequestCacheControl.builder().setNoStore(true).build(); + responseCacheControl = ResponseCacheControl.builder().setMaxAge(3600).build(); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesToRequestsWithAuthorizationHeadersAreNotCacheableBySharedCache() { request = new BasicHttpRequest("GET","/"); request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -111,7 +122,7 @@ void testResponsesToRequestsWithAuthorizationHeadersAreCacheableByNonSharedCache policy = new ResponseCachingPolicy(false, false, false); request = new BasicHttpRequest("GET","/"); request.setHeader("Authorization", StandardAuthScheme.BASIC + " dXNlcjpwYXNzd2Q="); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -122,7 +133,7 @@ void testAuthorizedResponsesWithSMaxAgeAreCacheable() { .setSharedMaxAge(3600) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -132,7 +143,7 @@ void testAuthorizedResponsesWithCacheControlPublicAreCacheable() { responseCacheControl = ResponseCacheControl.builder() .setCachePublic(true) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -142,51 +153,51 @@ void testAuthorizedResponsesWithCacheControlMaxAgeAreNotCacheable() { responseCacheControl = ResponseCacheControl.builder() .setMaxAge(3600) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void test203ResponseCodeIsCacheable() { response.setCode(HttpStatus.SC_NON_AUTHORITATIVE_INFORMATION); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void test206ResponseCodeIsNotCacheable() { response.setCode(HttpStatus.SC_PARTIAL_CONTENT); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void test300ResponseCodeIsCacheable() { response.setCode(HttpStatus.SC_MULTIPLE_CHOICES); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void test301ResponseCodeIsCacheable() { response.setCode(HttpStatus.SC_MOVED_PERMANENTLY); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void test410ResponseCodeIsCacheable() { response.setCode(HttpStatus.SC_GONE); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testPlain302ResponseCodeIsNotCacheable() { response.setCode(HttpStatus.SC_MOVED_TEMPORARILY); response.removeHeaders("Expires"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testPlain303ResponseCodeIsNotCacheableUnderDefaultBehavior() { response.setCode(HttpStatus.SC_SEE_OTHER); response.removeHeaders("Expires"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -194,7 +205,7 @@ void testPlain303ResponseCodeIsNotCacheableEvenIf303CachingEnabled() { policy = new ResponseCachingPolicy(true, false, true); response.setCode(HttpStatus.SC_SEE_OTHER); response.removeHeaders("Expires"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @@ -202,7 +213,7 @@ void testPlain303ResponseCodeIsNotCacheableEvenIf303CachingEnabled() { void testPlain307ResponseCodeIsNotCacheable() { response.setCode(HttpStatus.SC_TEMPORARY_REDIRECT); response.removeHeaders("Expires"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -210,7 +221,7 @@ void testNon206WithExplicitExpiresIsCacheable() { final int status = getRandomStatus(); response.setCode(status); response.setHeader("Expires", DateUtils.formatStandardDate(Instant.now().plus(1, ChronoUnit.HOURS))); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -220,7 +231,7 @@ void testNon206WithMaxAgeIsCacheable() { responseCacheControl = ResponseCacheControl.builder() .setMaxAge(0) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -228,7 +239,7 @@ void testMissingCacheControlHeader() { final int status = getRandomStatus(); response.setCode(status); response.removeHeaders(HttpHeaders.CACHE_CONTROL); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -238,7 +249,7 @@ void testNon206WithSMaxAgeIsCacheable() { responseCacheControl = ResponseCacheControl.builder() .setSharedMaxAge(1) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -248,7 +259,7 @@ void testNon206WithMustRevalidateIsCacheable() { responseCacheControl = ResponseCacheControl.builder() .setMustRevalidate(true) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -258,7 +269,7 @@ void testNon206WithProxyRevalidateIsCacheable() { responseCacheControl = ResponseCacheControl.builder() .setProxyRevalidate(true) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -268,7 +279,7 @@ void testNon206WithPublicCacheControlIsCacheable() { responseCacheControl = ResponseCacheControl.builder() .setCachePublic(true) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -278,7 +289,7 @@ void testNon206WithPrivateCacheControlIsNotCacheableBySharedCache() { responseCacheControl = ResponseCacheControl.builder() .setCachePrivate(true) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -288,7 +299,7 @@ void test200ResponseWithPrivateCacheControlIsCacheableByNonSharedCache() { responseCacheControl = ResponseCacheControl.builder() .setCachePrivate(true) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -297,7 +308,7 @@ void testControlNoCacheCacheable() { .setNoCache(true) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -306,7 +317,7 @@ void testControlNoStoreNotCacheable() { .setNoStore(true) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -316,7 +327,7 @@ void testControlNoStoreEmbeddedInListCacheable() { .setNoStore(true) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -326,7 +337,7 @@ void testControlNoCacheEmbeddedInListCacheable() { .setNoCache(true) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -337,7 +348,7 @@ void testControlNoCacheEmbeddedInListAfterFirstHeaderCacheable() { .setNoCache(true) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -348,7 +359,7 @@ void testControlNoStoreEmbeddedInListAfterFirstHeaderCacheable() { .setNoStore(true) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -357,7 +368,7 @@ void testControlAnyCacheControlCacheable() { .setMaxAge(10) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); response = new BasicHttpResponse(HttpStatus.SC_OK, ""); response.setHeader("Date", DateUtils.formatStandardDate(Instant.now())); @@ -365,7 +376,7 @@ void testControlAnyCacheControlCacheable() { responseCacheControl = ResponseCacheControl.builder() .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -373,17 +384,17 @@ void testControlWithNonCacheableStatusCode() { // 402 Payment Required is not a heuristically cacheable status code HttpResponse response402 = new BasicHttpResponse(HttpStatus.SC_PAYMENT_REQUIRED, ""); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response402)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response402)); response402 = new BasicHttpResponse(HttpStatus.SC_GATEWAY_TIMEOUT, ""); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response402)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response402)); } @Test void testVaryStarIsNotCacheable() { response.setHeader("Vary", "*"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -395,13 +406,13 @@ void testVaryStarIsNotCacheableUsingSharedPublicCache() { responseCacheControl = ResponseCacheControl.builder() .setCachePublic(true) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testRequestWithVaryHeaderCacheable() { response.addHeader("Vary", "Accept-Encoding"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -415,14 +426,14 @@ void testIsArbitraryMethodCacheableUsingSharedPublicCache() { .setCachePublic(true) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesWithMultipleAgeHeadersAreCacheable() { response.addHeader("Age", "3"); response.addHeader("Age", "5"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -435,14 +446,14 @@ void testResponsesWithMultipleAgeHeadersAreNotCacheableUsingSharedPublicCache() responseCacheControl = ResponseCacheControl.builder() .setCachePublic(true) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesWithMultipleDateHeadersAreNotCacheable() { response.addHeader("Date", DateUtils.formatStandardDate(now)); response.addHeader("Date", DateUtils.formatStandardDate(sixSecondsAgo)); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -455,13 +466,13 @@ void testResponsesWithMultipleDateHeadersAreNotCacheableUsingSharedPublicCache() responseCacheControl = ResponseCacheControl.builder() .setCachePublic(true) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesWithMalformedDateHeadersAreNotCacheable() { response.addHeader("Date", "garbage"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -473,14 +484,14 @@ void testResponsesWithMalformedDateHeadersAreNotCacheableUsingSharedPublicCache( responseCacheControl = ResponseCacheControl.builder() .setCachePublic(true) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesWithMultipleExpiresHeadersAreNotCacheable() { response.addHeader("Expires", DateUtils.formatStandardDate(now)); response.addHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -493,39 +504,39 @@ void testResponsesWithMultipleExpiresHeadersAreNotCacheableUsingSharedPublicCach responseCacheControl = ResponseCacheControl.builder() .setCachePublic(true) .build(); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesThatAreSmallEnoughAreCacheable() { response.setHeader("Content-Length", "0"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesToGETWithQueryParamsButNoExplicitCachingAreNotCacheable() { request = new BasicHttpRequest("GET", "/foo?s=bar"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesToHEADWithQueryParamsButNoExplicitCachingAreNotCacheable() { request = new BasicHttpRequest("HEAD", "/foo?s=bar"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesToGETWithQueryParamsButNoExplicitCachingAreNotCacheableEvenWhen1_0QueryCachingDisabled() { policy = new ResponseCachingPolicy(true, true, false); request = new BasicHttpRequest("GET", "/foo?s=bar"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void testResponsesToHEADWithQueryParamsButNoExplicitCachingAreNotCacheableEvenWhen1_0QueryCachingDisabled() { policy = new ResponseCachingPolicy(true, true, false); request = new BasicHttpRequest("HEAD", "/foo?s=bar"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -533,7 +544,7 @@ void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheable() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -542,7 +553,7 @@ void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheable() { request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -551,7 +562,7 @@ void testResponsesToGETWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0Q request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -560,7 +571,7 @@ void testResponsesToHEADWithQueryParamsAndExplicitCachingAreCacheableEvenWhen1_0 request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -568,7 +579,7 @@ void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheable() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -576,7 +587,7 @@ void headsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheable() { request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -585,7 +596,7 @@ void getsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithSetting request = new BasicHttpRequest("GET", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -594,7 +605,7 @@ void headsWithQueryParametersDirectlyFrom1_0OriginsAreNotCacheableEvenWithSettin request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response = new BasicHttpResponse(HttpStatus.SC_OK, "OK"); response.setVersion(HttpVersion.HTTP_1_0); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -604,7 +615,7 @@ void getsWithQueryParametersDirectlyFrom1_0OriginsAreCacheableWithExpires() { response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -615,7 +626,7 @@ void headsWithQueryParametersDirectlyFrom1_0OriginsAreCacheableWithExpires() { response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -626,7 +637,7 @@ void getsWithQueryParametersDirectlyFrom1_0OriginsCanBeNotCacheableEvenWithExpir response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -637,21 +648,21 @@ void headsWithQueryParametersDirectlyFrom1_0OriginsCanBeNotCacheableEvenWithExpi response.setVersion(HttpVersion.HTTP_1_0); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void getsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() { request = new BasicHttpRequest("GET", "/foo?s=bar"); response.setHeader(HttpHeaders.VIA, "1.0 someproxy"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void headsWithQueryParametersFrom1_0OriginsViaProxiesAreNotCacheable() { request = new BasicHttpRequest("HEAD", "/foo?s=bar"); response.setHeader(HttpHeaders.VIA, "1.0 someproxy"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -660,7 +671,7 @@ void getsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() { response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "1.0 someproxy"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -670,7 +681,7 @@ void headsWithQueryParametersFrom1_0OriginsViaProxiesAreCacheableWithExpires() { response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "1.0 someproxy"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -680,7 +691,7 @@ void getsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithExp response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "1.0 someproxy"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -690,7 +701,7 @@ void headsWithQueryParametersFrom1_0OriginsViaProxiesCanNotBeCacheableEvenWithEx response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "1.0 someproxy"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -699,7 +710,7 @@ void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExpi response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -709,7 +720,7 @@ void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesAreCacheableWithExp response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -719,7 +730,7 @@ void getsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEve response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -729,7 +740,7 @@ void headsWithQueryParametersFrom1_0OriginsViaExplicitProxiesCanNotBeCacheableEv response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "HTTP/1.0 someproxy"); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -740,7 +751,7 @@ void getsWithQueryParametersFrom1_1OriginsVia1_0ProxiesAreCacheableWithExpires() response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "1.1 someproxy"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -753,21 +764,21 @@ void headsWithQueryParametersFrom1_1OriginsVia1_0ProxiesAreCacheableWithExpires( response.setHeader("Expires", DateUtils.formatStandardDate(tenSecondsFromNow)); response.setHeader(HttpHeaders.VIA, "1.1 someproxy"); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void notCacheableIfExpiresEqualsDateAndNoCacheControl() { response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(now)); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test void notCacheableIfExpiresPrecedesDateAndNoCacheControl() { response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Expires", DateUtils.formatStandardDate(sixSecondsAgo)); - Assertions.assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -777,7 +788,7 @@ void test302WithExplicitCachingHeaders() { responseCacheControl = ResponseCacheControl.builder() .setMaxAge(300) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -790,7 +801,7 @@ void test303WithExplicitCachingHeadersWhenPermittedByConfig() { responseCacheControl = ResponseCacheControl.builder() .setMaxAge(300) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -800,7 +811,7 @@ void test307WithExplicitCachingHeaders() { responseCacheControl = ResponseCacheControl.builder() .setMaxAge(300) .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -810,7 +821,7 @@ void otherStatusCodesAreCacheableWithExplicitCachingHeaders() { responseCacheControl = ResponseCacheControl.builder() .setMaxAge(300) .build(); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -823,13 +834,13 @@ void testCacheableWithExpiresAndMaxAge() { request = new BasicHttpRequest("GET", "/foo"); // Cacheable with Expires header alone - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); // Cacheable with explicit max-age responseCacheControl = ResponseCacheControl.builder() .setMaxAge(60) .build(); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } private int getRandomStatus() { @@ -846,7 +857,7 @@ void testIsResponseCacheable() { policy = new ResponseCachingPolicy(true, false, true); response.setCode(HttpStatus.SC_OK); response.setHeader("Date", DateUtils.formatStandardDate(now)); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -861,7 +872,7 @@ void testIsResponseCacheableNoCache() { responseCacheControl = ResponseCacheControl.builder() .setNoCache(true) .build(); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -876,7 +887,7 @@ void testIsResponseCacheableNoStore() { responseCacheControl = ResponseCacheControl.builder() .setNoStore(true) .build(); - assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -886,7 +897,7 @@ void testImmutableAndFreshResponseIsCacheable() { .setMaxAge(3600) // set this to a value that ensures the response is still fresh .build(); - Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + Assertions.assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -898,7 +909,7 @@ void testPublicWithAuthorizationIsCacheable() { .setCachePublic(true) .build(); - final boolean isCacheable = policy.isResponseCacheable(responseCacheControl, request, response); + final boolean isCacheable = policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response); Assertions.assertTrue(isCacheable, "Response with public directive and Authorization header should be cacheable in shared cache."); } @@ -912,7 +923,7 @@ void testSMaxageWithAuthorizationIsCacheable() { .setSharedMaxAge(60) .build(); - final boolean isCacheable = policy.isResponseCacheable(responseCacheControl, request, response); + final boolean isCacheable = policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response); Assertions.assertTrue(isCacheable, "Response with s-maxage and Authorization header should be cacheable in shared cache."); } @@ -925,7 +936,7 @@ void testNoDirectivesWithAuthorizationNotCacheable() { responseCacheControl = ResponseCacheControl.builder() .build(); - final boolean isCacheable = policy.isResponseCacheable(responseCacheControl, request, response); + final boolean isCacheable = policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response); Assertions.assertFalse(isCacheable, "Response without must-revalidate, public, or s-maxage should not be cacheable with Authorization header."); } @@ -939,7 +950,7 @@ void testMustRevalidateWithAuthorizationIsCacheable() { .setMustRevalidate(true) .build(); - final boolean isCacheable = policy.isResponseCacheable(responseCacheControl, request, response); + final boolean isCacheable = policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response); Assertions.assertTrue(isCacheable, "Response with must-revalidate and Authorization header should be cacheable in shared cache."); } @@ -954,7 +965,7 @@ void testMustUnderstandWithNoStoreAndUnderstoodStatusIsCacheable() { .setNoStore(true) .setCachePublic(true) .build(); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -967,7 +978,7 @@ void testMustUnderstandWithNoStoreAndUnknownStatusIsNotCacheable() { .setNoStore(true) .setCachePublic(true) .build(); - assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -978,7 +989,7 @@ void testNoStoreWithoutMustUnderstandIsNotCacheable() { responseCacheControl = ResponseCacheControl.builder() .setNoStore(true) .build(); - assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -992,7 +1003,7 @@ void testMustUnderstandWithNoStoreAndPrivateNotCacheableBySharedCache() { .setNoStore(true) .setCachePrivate(true) .build(); - assertFalse(policy.isResponseCacheable(responseCacheControl, request, response)); + assertFalse(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -1006,7 +1017,7 @@ void testMustUnderstandWithNoStoreAndPrivateCacheableByNonSharedCache() { .setNoStore(true) .setCachePrivate(true) .build(); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -1019,7 +1030,7 @@ void testPrivateCacheIgnoresSharedMaxAgeForFreshness() { .setSharedMaxAge(0) .setMaxAge(3600) .build(); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -1027,7 +1038,7 @@ void testHeuristicallyCacheable204() { response = new BasicHttpResponse(HttpStatus.SC_NO_CONTENT, ""); response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Last-Modified", DateUtils.formatStandardDate(sixSecondsAgo)); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -1036,7 +1047,7 @@ void testHeuristicallyCacheable404() { response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Content-Length", "0"); response.setHeader("Last-Modified", DateUtils.formatStandardDate(sixSecondsAgo)); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -1045,7 +1056,7 @@ void testHeuristicallyCacheable405() { response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Content-Length", "0"); response.setHeader("Last-Modified", DateUtils.formatStandardDate(sixSecondsAgo)); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } @Test @@ -1054,7 +1065,7 @@ void testHeuristicallyCacheable501() { response.setHeader("Date", DateUtils.formatStandardDate(now)); response.setHeader("Content-Length", "0"); response.setHeader("Last-Modified", DateUtils.formatStandardDate(sixSecondsAgo)); - assertTrue(policy.isResponseCacheable(responseCacheControl, request, response)); + assertTrue(policy.isResponseCacheable(requestCacheControl, responseCacheControl, request, response)); } } \ No newline at end of file