You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ab...@apache.org on 2023/06/29 16:02:47 UTC
[httpcomponents-client] branch 5.3.x updated: HTTPCLIENT-2277: Implement must-understand directive according to RFC 9111 (#461)
This is an automated email from the ASF dual-hosted git repository.
abernal pushed a commit to branch 5.3.x
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git
The following commit(s) were added to refs/heads/5.3.x by this push:
new a77a92639 HTTPCLIENT-2277: Implement must-understand directive according to RFC 9111 (#461)
a77a92639 is described below
commit a77a92639435d5ae2d21c97f4ff6c5cb69951083
Author: Arturo Bernal <ab...@apache.org>
AuthorDate: Thu Jun 29 18:02:37 2023 +0200
HTTPCLIENT-2277: Implement must-understand directive according to RFC 9111 (#461)
This commit introduces changes to handle the 'must-understand' directive in accordance with the updated HTTP cache-related RFC 9111. The logic ensures that the cache only stores responses with status codes it understands when the 'must-understand' directive is present.
This implementation adheres to the following RFC guidance:
- A cache that understands and conforms to the requirements of a response's status code may cache it when the 'must-understand' directive is present.
- If the 'no-store' directive is present along with the 'must-understand' directive, the cache can ignore the 'no-store' directive if it understands the status code's caching requirements.
---
.../hc/client5/http/cache/HeaderConstants.java | 1 +
.../http/impl/cache/CacheControlHeaderParser.java | 2 ++
.../http/impl/cache/ResponseCacheControl.java | 31 ++++++++++++++++++++--
.../http/impl/cache/ResponseCachingPolicy.java | 29 +++++++++++++++++++-
4 files changed, 60 insertions(+), 3 deletions(-)
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HeaderConstants.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HeaderConstants.java
index 1dd1e9b08..60c0ba5d1 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HeaderConstants.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HeaderConstants.java
@@ -164,6 +164,7 @@ public class HeaderConstants {
public static final String CACHE_CONTROL_STALE_IF_ERROR = "stale-if-error";
public static final String CACHE_CONTROL_STALE_WHILE_REVALIDATE = "stale-while-revalidate";
public static final String CACHE_CONTROL_ONLY_IF_CACHED = "only-if-cached";
+ public static final String CACHE_CONTROL_MUST_UNDERSTAND = "must-understand";
/**
* @deprecated Use {@link #CACHE_CONTROL_STALE_IF_ERROR}
*/
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControlHeaderParser.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControlHeaderParser.java
index fb685fbf7..f912c3146 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControlHeaderParser.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControlHeaderParser.java
@@ -196,6 +196,8 @@ class CacheControlHeaderParser {
builder.setStaleWhileRevalidate(parseSeconds(name, value));
} else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_STALE_IF_ERROR)) {
builder.setStaleIfError(parseSeconds(name, value));
+ } else if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_MUST_UNDERSTAND)) {
+ builder.setMustUnderstand(true);
}
});
return builder.build();
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCacheControl.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCacheControl.java
index 54b10da60..866757c00 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCacheControl.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCacheControl.java
@@ -84,6 +84,11 @@ final class ResponseCacheControl implements CacheControl {
* Indicates whether the Cache-Control header includes the "public" directive.
*/
private final boolean cachePublic;
+ /**
+ * Indicates whether the Cache-Control header includes the "must-understand" directive.
+ */
+ private final boolean mustUnderstand;
+
/**
* The number of seconds that a stale response is considered fresh for the purpose
* of serving a response while a revalidation request is made to the origin server.
@@ -115,11 +120,12 @@ final class ResponseCacheControl implements CacheControl {
* @param staleWhileRevalidate The stale-while-revalidate value from the Cache-Control header.
* @param staleIfError The stale-if-error value from the Cache-Control header.
* @param noCacheFields The set of field names specified in the "no-cache" directive of the Cache-Control header.
+ * @param mustUnderstand The must-understand value from the Cache-Control header.
*/
ResponseCacheControl(final long maxAge, final long sharedMaxAge, final boolean mustRevalidate, final boolean noCache,
final boolean noStore, final boolean cachePrivate, final boolean proxyRevalidate,
final boolean cachePublic, final long staleWhileRevalidate, final long staleIfError,
- final Set<String> noCacheFields) {
+ final Set<String> noCacheFields, final boolean mustUnderstand) {
this.maxAge = maxAge;
this.sharedMaxAge = sharedMaxAge;
this.noCache = noCache;
@@ -141,6 +147,7 @@ final class ResponseCacheControl implements CacheControl {
!cachePublic &&
staleWhileRevalidate == -1
&& staleIfError == -1;
+ this.mustUnderstand = mustUnderstand;
}
/**
@@ -191,6 +198,15 @@ final class ResponseCacheControl implements CacheControl {
return cachePrivate;
}
+ /**
+ * Returns the must-understand directive from the Cache-Control header.
+ *
+ * @return The must-understand directive.
+ */
+ public boolean isMustUnderstand() {
+ return mustUnderstand;
+ }
+
/**
* Returns whether the must-revalidate directive is present in the Cache-Control header.
*
@@ -265,6 +281,7 @@ final class ResponseCacheControl implements CacheControl {
", staleWhileRevalidate=" + staleWhileRevalidate +
", staleIfError=" + staleIfError +
", noCacheFields=" + noCacheFields +
+ ", mustUnderstand=" + mustUnderstand +
'}';
}
@@ -285,6 +302,7 @@ final class ResponseCacheControl implements CacheControl {
private long staleWhileRevalidate = -1;
private long staleIfError = -1;
private Set<String> noCacheFields;
+ private boolean mustUnderstand;
Builder() {
}
@@ -388,9 +406,18 @@ final class ResponseCacheControl implements CacheControl {
return this;
}
+ public boolean isMustUnderstand() {
+ return mustUnderstand;
+ }
+
+ public Builder setMustUnderstand(final boolean mustUnderstand) {
+ this.mustUnderstand = mustUnderstand;
+ return this;
+ }
+
public ResponseCacheControl build() {
return new ResponseCacheControl(maxAge, sharedMaxAge, mustRevalidate, noCache, noStore, cachePrivate, proxyRevalidate,
- cachePublic, staleWhileRevalidate, staleIfError, noCacheFields);
+ cachePublic, staleWhileRevalidate, staleIfError, noCacheFields, mustUnderstand);
}
}
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 a055bbd3a..fab185b4a 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
@@ -307,11 +307,19 @@ class ResponseCachingPolicy {
}
return false;
}
- if (cacheControl.isNoStore()) {
+
+ if (cacheControl.isMustUnderstand() && cacheControl.isNoStore() && !understoodStatusCode(response.getCode())) {
+ // must-understand cache directive overrides no-store
+ LOG.debug("Response contains a status code that the cache does not understand, so it's not cacheable");
+ return false;
+ }
+
+ if (!cacheControl.isMustUnderstand() && cacheControl.isNoStore()) {
LOG.debug("Response is explicitly non-cacheable per cache control directive");
return false;
}
+
if (request.getRequestUri().contains("?")) {
if (neverCache1_0ResponsesWithQueryString && from1_0Origin(response)) {
LOG.debug("Response is not cacheable as it had a query string");
@@ -494,4 +502,23 @@ class ResponseCachingPolicy {
return false;
}
+ /**
+ * This method checks if a given HTTP status code is understood according to RFC 7231.
+ * Understood status codes include:
+ * - All 2xx (Successful) status codes (200-299)
+ * - All 3xx (Redirection) status codes (300-399)
+ * - All 4xx (Client Error) status codes up to 417 and 421
+ * - All 5xx (Server Error) status codes up to 505
+ *
+ * @param status The HTTP status code to be checked.
+ * @return true if the HTTP status code is understood, false otherwise.
+ */
+ private boolean understoodStatusCode(final int status) {
+ return (status >= 200 && status <= 206) ||
+ (status >= 300 && status <= 399) ||
+ (status >= 400 && status <= 417) ||
+ (status == 421) ||
+ (status >= 500 && status <= 505);
+ }
+
}