Skip to content

Commit 8b1ee82

Browse files
Fix HTTPCLIENT-2354 by updating ResponseCachingPolicy to allow caching of responses with "must-revalidate, max-age=0" in shared caches with Authorization headers. The change aligns with RFC 9111 Section 5.2.2.2, ensuring responses with "must-revalidate," "s-maxage," or "public" directives are cacheable. This addresses cases where responses with Authorization headers were unnecessarily excluded from caching. (#609)
1 parent becb177 commit 8b1ee82

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCachingPolicy.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public boolean isResponseCacheable(final ResponseCacheControl cacheControl, fina
145145
if (sharedCache) {
146146
if (request.containsHeader(HttpHeaders.AUTHORIZATION) &&
147147
cacheControl.getSharedMaxAge() == -1 &&
148-
!cacheControl.isPublic()) {
148+
!(cacheControl.isPublic() || cacheControl.isMustRevalidate())) {
149149
LOG.debug("Request contains private credentials");
150150
return false;
151151
}

httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestResponseCachingPolicy.java

+56
Original file line numberDiff line numberDiff line change
@@ -944,4 +944,60 @@ void testImmutableAndFreshResponseIsCacheable() {
944944

945945
Assertions.assertTrue(policy.isResponseCacheable(responseCacheControl, request, response));
946946
}
947+
948+
@Test
949+
void testPublicWithAuthorizationIsCacheable() {
950+
request = new BasicHttpRequest("GET", "/resource");
951+
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic dXNlcjpwYXNzd2Q=");
952+
response.setHeader("Cache-Control", "public");
953+
responseCacheControl = ResponseCacheControl.builder()
954+
.setCachePublic(true)
955+
.build();
956+
957+
final boolean isCacheable = policy.isResponseCacheable(responseCacheControl, request, response);
958+
Assertions.assertTrue(isCacheable,
959+
"Response with public directive and Authorization header should be cacheable in shared cache.");
960+
}
961+
962+
@Test
963+
void testSMaxageWithAuthorizationIsCacheable() {
964+
request = new BasicHttpRequest("GET", "/resource");
965+
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic dXNlcjpwYXNzd2Q=");
966+
response.setHeader("Cache-Control", "s-maxage=60");
967+
responseCacheControl = ResponseCacheControl.builder()
968+
.setSharedMaxAge(60)
969+
.build();
970+
971+
final boolean isCacheable = policy.isResponseCacheable(responseCacheControl, request, response);
972+
Assertions.assertTrue(isCacheable,
973+
"Response with s-maxage and Authorization header should be cacheable in shared cache.");
974+
}
975+
976+
@Test
977+
void testNoDirectivesWithAuthorizationNotCacheable() {
978+
request = new BasicHttpRequest("GET", "/resource");
979+
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic dXNlcjpwYXNzd2Q=");
980+
response.setHeader("Cache-Control", "");
981+
responseCacheControl = ResponseCacheControl.builder()
982+
.build();
983+
984+
final boolean isCacheable = policy.isResponseCacheable(responseCacheControl, request, response);
985+
Assertions.assertFalse(isCacheable,
986+
"Response without must-revalidate, public, or s-maxage should not be cacheable with Authorization header.");
987+
}
988+
989+
@Test
990+
void testMustRevalidateWithAuthorizationIsCacheable() {
991+
request = new BasicHttpRequest("GET", "/resource");
992+
request.setHeader(HttpHeaders.AUTHORIZATION, "Basic dXNlcjpwYXNzd2Q=");
993+
response.setHeader("Cache-Control", "must-revalidate");
994+
responseCacheControl = ResponseCacheControl.builder()
995+
.setMustRevalidate(true)
996+
.build();
997+
998+
final boolean isCacheable = policy.isResponseCacheable(responseCacheControl, request, response);
999+
Assertions.assertTrue(isCacheable,
1000+
"Response with must-revalidate and Authorization header should be cacheable in shared cache.");
1001+
}
1002+
9471003
}

0 commit comments

Comments
 (0)