You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2024/01/12 08:23:48 UTC

(httpcomponents-client) branch master updated (ef77109f3 -> d7108f60a)

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git


    from ef77109f3 HTTPCLIENT-2315: client builders fails to apply system properties to the default connection manager
     new 364fa60cd Promote RequestCacheControl and ResponseCacheControl to public API
     new b3da0ae13 Refactor ContextBuilder into a common abstract class and HttpClientContext specific builder
     new 7c0d083b1 Cache control and context API
     new d7108f60a Added #toString methods to Resource implementations

The 4 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../hc/client5/http/cache/CacheContextBuilder.java | 117 ++++++++++++
 .../http/{impl => }/cache/CacheControl.java        |   6 +-
 .../hc/client5/http/cache/HttpCacheContext.java    |  66 +++++++
 .../http/{impl => }/cache/RequestCacheControl.java |  44 ++---
 .../{impl => }/cache/ResponseCacheControl.java     |  23 +--
 .../client5/http/impl/cache/AsyncCachingExec.java  |  46 +++--
 .../impl/cache/CacheControlHeaderGenerator.java    | 106 +++++++++++
 .../http/impl/cache/CacheControlHeaderParser.java  |   8 +-
 .../http/impl/cache/CacheValidityPolicy.java       |   1 +
 .../http/impl/cache/CacheableRequestPolicy.java    |   1 +
 .../cache/CachedResponseSuitabilityChecker.java    |   2 +
 .../hc/client5/http/impl/cache/CachingExec.java    |  68 ++++---
 .../client5/http/impl/cache/CachingExecBase.java   |   5 -
 .../http/impl/cache/ConditionalRequestBuilder.java |   1 +
 .../hc/client5/http/impl/cache/FileResource.java   |   8 +
 .../hc/client5/http/impl/cache/HeapResource.java   |   8 +
 .../http/impl/cache/ResponseCachingPolicy.java     |   1 +
 .../cache/example/AsyncClientCacheControl.java     | 209 +++++++++++++++++++++
 .../http/cache/example/ClientCacheControl.java     | 150 +++++++++++++++
 .../http/impl/cache/CacheControlGeneratorTest.java |  86 +++++++++
 .../http/impl/cache/CacheControlParserTest.java    |   2 +
 .../http/impl/cache/TestCacheValidityPolicy.java   |   1 +
 .../impl/cache/TestCacheableRequestPolicy.java     |   1 +
 .../TestCachedResponseSuitabilityChecker.java      |   2 +
 .../http/impl/cache/TestCachingExecChain.java      |  61 +-----
 .../impl/cache/TestConditionalRequestBuilder.java  |   2 +
 .../http/impl/cache/TestResponseCachingPolicy.java |   1 +
 ...lder.java => AbstractClientContextBuilder.java} |  38 ++--
 .../org/apache/hc/client5/http/ContextBuilder.java |  66 +++----
 29 files changed, 916 insertions(+), 214 deletions(-)
 create mode 100644 httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/CacheContextBuilder.java
 rename httpclient5-cache/src/main/java/org/apache/hc/client5/http/{impl => }/cache/CacheControl.java (94%)
 rename httpclient5-cache/src/main/java/org/apache/hc/client5/http/{impl => }/cache/RequestCacheControl.java (82%)
 rename httpclient5-cache/src/main/java/org/apache/hc/client5/http/{impl => }/cache/ResponseCacheControl.java (96%)
 create mode 100644 httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControlHeaderGenerator.java
 create mode 100644 httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/example/AsyncClientCacheControl.java
 create mode 100644 httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/example/ClientCacheControl.java
 create mode 100644 httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/CacheControlGeneratorTest.java
 copy httpclient5/src/main/java/org/apache/hc/client5/http/{ContextBuilder.java => AbstractClientContextBuilder.java} (75%)


(httpcomponents-client) 03/04: Cache control and context API

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git

commit 7c0d083b124a5f3b8a5daa877ab42939be4d2519
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Thu Dec 28 16:39:07 2023 +0100

    Cache control and context API
---
 .../hc/client5/http/cache/CacheContextBuilder.java | 117 ++++++++++++
 .../hc/client5/http/cache/HttpCacheContext.java    |  66 +++++++
 .../hc/client5/http/cache/RequestCacheControl.java |  30 +--
 .../client5/http/cache/ResponseCacheControl.java   |   2 +
 .../client5/http/impl/cache/AsyncCachingExec.java  |  44 +++--
 .../impl/cache/CacheControlHeaderGenerator.java    | 106 +++++++++++
 .../http/impl/cache/CacheControlHeaderParser.java  |   2 +-
 .../hc/client5/http/impl/cache/CachingExec.java    |  66 ++++---
 .../client5/http/impl/cache/CachingExecBase.java   |   5 -
 .../cache/example/AsyncClientCacheControl.java     | 209 +++++++++++++++++++++
 .../http/cache/example/ClientCacheControl.java     | 150 +++++++++++++++
 .../http/impl/cache/CacheControlGeneratorTest.java |  86 +++++++++
 .../http/impl/cache/TestCachingExecChain.java      |  61 +-----
 13 files changed, 827 insertions(+), 117 deletions(-)

diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/CacheContextBuilder.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/CacheContextBuilder.java
new file mode 100644
index 000000000..85f1ab952
--- /dev/null
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/CacheContextBuilder.java
@@ -0,0 +1,117 @@
+/*
+ * ====================================================================
+ * 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
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.cache;
+
+import org.apache.hc.client5.http.AbstractClientContextBuilder;
+import org.apache.hc.client5.http.SchemePortResolver;
+import org.apache.hc.client5.http.auth.AuthCache;
+import org.apache.hc.client5.http.auth.AuthScheme;
+import org.apache.hc.client5.http.auth.AuthSchemeFactory;
+import org.apache.hc.client5.http.auth.CredentialsProvider;
+import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
+import org.apache.hc.client5.http.cookie.CookieSpecFactory;
+import org.apache.hc.client5.http.cookie.CookieStore;
+import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.config.Lookup;
+
+public class CacheContextBuilder extends AbstractClientContextBuilder<HttpCacheContext> {
+
+    public static CacheContextBuilder create(final SchemePortResolver schemePortResolver) {
+        return new CacheContextBuilder(schemePortResolver);
+    }
+
+    public static CacheContextBuilder create() {
+        return new CacheContextBuilder(DefaultSchemePortResolver.INSTANCE);
+    }
+
+    private RequestCacheControl cacheControl;
+
+    protected CacheContextBuilder(final SchemePortResolver schemePortResolver) {
+        super(schemePortResolver);
+    }
+
+    @Override
+    public CacheContextBuilder useCookieSpecRegistry(final Lookup<CookieSpecFactory> cookieSpecRegistry) {
+        super.useCookieSpecRegistry(cookieSpecRegistry);
+        return this;
+    }
+
+    @Override
+    public CacheContextBuilder useAuthSchemeRegistry(final Lookup<AuthSchemeFactory> authSchemeRegistry) {
+        super.useAuthSchemeRegistry(authSchemeRegistry);
+        return this;
+    }
+
+    @Override
+    public CacheContextBuilder useCookieStore(final CookieStore cookieStore) {
+        super.useCookieStore(cookieStore);
+        return this;
+    }
+
+    @Override
+    public CacheContextBuilder useCredentialsProvider(final CredentialsProvider credentialsProvider) {
+        super.useCredentialsProvider(credentialsProvider);
+        return this;
+    }
+
+    @Override
+    public CacheContextBuilder useAuthCache(final AuthCache authCache) {
+        super.useAuthCache(authCache);
+        return this;
+    }
+
+    @Override
+    public CacheContextBuilder preemptiveAuth(final HttpHost host, final AuthScheme authScheme) {
+        super.preemptiveAuth(host, authScheme);
+        return this;
+    }
+
+    @Override
+    public CacheContextBuilder preemptiveBasicAuth(final HttpHost host, final UsernamePasswordCredentials credentials) {
+        super.preemptiveBasicAuth(host, credentials);
+        return this;
+    }
+
+    public CacheContextBuilder setCacheControl(final RequestCacheControl cacheControl) {
+        this.cacheControl = cacheControl;
+        return this;
+    }
+
+    @Override
+    protected HttpCacheContext createContext() {
+        return HttpCacheContext.create();
+    }
+
+    @Override
+    public HttpCacheContext build() {
+        final HttpCacheContext context =  super.build();
+        context.setRequestCacheControl(cacheControl);
+        return context;
+    }
+
+}
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheContext.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheContext.java
index 435bc8618..ba6976977 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheContext.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/HttpCacheContext.java
@@ -45,6 +45,21 @@ public class HttpCacheContext extends HttpClientContext {
      */
     public static final String CACHE_RESPONSE_STATUS = "http.cache.response.status";
 
+    /**
+     * @since 5.4
+     */
+    public static final String CACHE_ENTRY = "http.cache.entry";
+
+    /**
+     * @since 5.4
+     */
+    public static final String CACHE_REQUEST_CONTROL = "http.cache.request.control";
+
+    /**
+     * @since 5.4
+     */
+    public static final String CACHE_RESPONSE_CONTROL = "http.cache.response.control";
+
     public static HttpCacheContext adapt(final HttpContext context) {
         if (context instanceof HttpCacheContext) {
             return (HttpCacheContext) context;
@@ -69,4 +84,55 @@ public class HttpCacheContext extends HttpClientContext {
         return getAttribute(CACHE_RESPONSE_STATUS, CacheResponseStatus.class);
     }
 
+    /**
+     * @since 5.4
+     */
+    public void setCacheResponseStatus(final CacheResponseStatus status) {
+        setAttribute(CACHE_RESPONSE_STATUS, status);
+    }
+
+    /**
+     * @since 5.4
+     */
+    public RequestCacheControl getRequestCacheControl() {
+        final RequestCacheControl cacheControl = getAttribute(CACHE_REQUEST_CONTROL, RequestCacheControl.class);
+        return cacheControl != null ? cacheControl : RequestCacheControl.DEFAULT;
+    }
+
+    /**
+     * @since 5.4
+     */
+    public void setRequestCacheControl(final RequestCacheControl requestCacheControl) {
+        setAttribute(CACHE_REQUEST_CONTROL, requestCacheControl);
+    }
+
+    /**
+     * @since 5.4
+     */
+    public ResponseCacheControl getResponseCacheControl() {
+        final ResponseCacheControl cacheControl = getAttribute(CACHE_RESPONSE_CONTROL, ResponseCacheControl.class);
+        return cacheControl != null ? cacheControl : ResponseCacheControl.DEFAULT;
+    }
+
+    /**
+     * @since 5.4
+     */
+    public void setResponseCacheControl(final ResponseCacheControl responseCacheControl) {
+        setAttribute(CACHE_RESPONSE_CONTROL, responseCacheControl);
+    }
+
+    /**
+     * @since 5.4
+     */
+    public HttpCacheEntry getCacheEntry() {
+        return getAttribute(CACHE_ENTRY, HttpCacheEntry.class);
+    }
+
+    /**
+     * @since 5.4
+     */
+    public void setCacheEntry(final HttpCacheEntry entry) {
+        setAttribute(CACHE_ENTRY, entry);
+    }
+
 }
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/RequestCacheControl.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/RequestCacheControl.java
index f6ea444ee..54c85b4e4 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/RequestCacheControl.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/RequestCacheControl.java
@@ -47,15 +47,8 @@ public final class RequestCacheControl implements CacheControl {
     private final boolean onlyIfCached;
     private final long staleIfError;
 
-    /**
-     * Flag for the 'no-transform' Cache-Control directive.
-     * If this field is true, then the 'no-transform' directive is present in the Cache-Control header.
-     * According to RFC 'no-transform' directive indicates that the cache MUST NOT transform the payload.
-     */
-    private final boolean noTransform;
-
     RequestCacheControl(final long maxAge, final long maxStale, final long minFresh, final boolean noCache,
-                               final boolean noStore, final boolean onlyIfCached, final long staleIfError, final boolean noTransform) {
+                               final boolean noStore, final boolean onlyIfCached, final long staleIfError) {
         this.maxAge = maxAge;
         this.maxStale = maxStale;
         this.minFresh = minFresh;
@@ -63,7 +56,6 @@ public final class RequestCacheControl implements CacheControl {
         this.noStore = noStore;
         this.onlyIfCached = onlyIfCached;
         this.staleIfError = staleIfError;
-        this.noTransform = noTransform;
     }
 
     /**
@@ -144,7 +136,7 @@ public final class RequestCacheControl implements CacheControl {
             buf.append("max-stale=").append(maxStale).append(",");
         }
         if (minFresh >= 0) {
-            buf.append("max-fresh=").append(minFresh).append(",");
+            buf.append("min-fresh=").append(minFresh).append(",");
         }
         if (noCache) {
             buf.append("no-cache").append(",");
@@ -158,9 +150,6 @@ public final class RequestCacheControl implements CacheControl {
         if (staleIfError >= 0) {
             buf.append("stale-if-error").append(staleIfError).append(",");
         }
-        if (noTransform) {
-            buf.append("no-transform").append(",");
-        }
         if (buf.charAt(buf.length() - 1) == ',') {
             buf.setLength(buf.length() - 1);
         }
@@ -172,6 +161,8 @@ public final class RequestCacheControl implements CacheControl {
         return new Builder();
     }
 
+    public static final RequestCacheControl DEFAULT = builder().build();
+
     public static class Builder {
 
         private long maxAge = -1;
@@ -181,7 +172,6 @@ public final class RequestCacheControl implements CacheControl {
         private boolean noStore;
         private boolean onlyIfCached;
         private long staleIfError = -1;
-        private boolean noTransform;
 
         Builder() {
         }
@@ -249,18 +239,8 @@ public final class RequestCacheControl implements CacheControl {
             return this;
         }
 
-        public boolean isNoTransform() {
-            return noTransform;
-        }
-
-        public Builder setNoTransform(final boolean noTransform) {
-            this.noTransform = noTransform;
-            return this;
-        }
-
-
         public RequestCacheControl build() {
-            return new RequestCacheControl(maxAge, maxStale, minFresh, noCache, noStore, onlyIfCached, staleIfError, noTransform);
+            return new RequestCacheControl(maxAge, maxStale, minFresh, noCache, noStore, onlyIfCached, staleIfError);
         }
 
     }
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/ResponseCacheControl.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/ResponseCacheControl.java
index c9b71a066..7e7f4b01f 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/ResponseCacheControl.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/ResponseCacheControl.java
@@ -341,6 +341,8 @@ public final class ResponseCacheControl implements CacheControl {
         return new Builder();
     }
 
+    public static final ResponseCacheControl DEFAULT = builder().build();
+
     public static class Builder {
 
         private long maxAge = -1;
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 428b8c9e2..8386558a0 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
@@ -239,7 +239,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
             final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {
 
         final String exchangeId = scope.exchangeId;
-        final HttpClientContext context = scope.clientContext;
+        final HttpCacheContext context = HttpCacheContext.adapt(scope.clientContext);
         final CancellableDependency operation = scope.cancellableDependency;
 
         if (LOG.isDebugEnabled()) {
@@ -247,6 +247,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
         }
 
         context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, CacheResponseStatus.CACHE_MISS);
+        context.setAttribute(HttpCacheContext.CACHE_ENTRY, null);
 
         if (clientRequestsOurOptions(request)) {
             context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, CacheResponseStatus.CACHE_MODULE_RESPONSE);
@@ -254,7 +255,15 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
             return;
         }
 
-        final RequestCacheControl requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
+        final RequestCacheControl requestCacheControl;
+        if (request.containsHeader(HttpHeaders.CACHE_CONTROL)) {
+            requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
+            context.setRequestCacheControl(requestCacheControl);
+        } else {
+            requestCacheControl = context.getRequestCacheControl();
+            CacheControlHeaderGenerator.INSTANCE.generate(requestCacheControl, request);
+        }
+
         if (LOG.isDebugEnabled()) {
             LOG.debug("{} request cache control: {}", exchangeId, requestCacheControl);
         }
@@ -273,6 +282,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
                         if (LOG.isDebugEnabled()) {
                             LOG.debug("{} response cache control: {}", exchangeId, responseCacheControl);
                         }
+                        context.setResponseCacheControl(responseCacheControl);
                         handleCacheHit(requestCacheControl, responseCacheControl, hit, target, request, entityProducer, scope, chain, asyncExecCallback);
                     }
                 }
@@ -540,6 +550,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
 
         void triggerNewCacheEntryResponse(final HttpResponse backendResponse, final Instant responseDate, final ByteArrayBuffer buffer) {
             final String exchangeId = scope.exchangeId;
+            final HttpClientContext context = scope.clientContext;
             final CancellableDependency operation = scope.cancellableDependency;
             operation.setDependency(responseCache.store(
                     target,
@@ -557,6 +568,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
                             }
                             try {
                                 final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
+                                context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
                                 triggerResponse(cacheResponse, scope, asyncExecCallback);
                             } catch (final ResourceIOException ex) {
                                 asyncExecCallback.failed(ex);
@@ -578,8 +590,10 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
         }
 
         void triggerCachedResponse(final HttpCacheEntry entry) {
+            final HttpClientContext context = scope.clientContext;
             try {
                 final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, entry);
+                context.setAttribute(HttpCacheContext.CACHE_ENTRY, entry);
                 triggerResponse(cacheResponse, scope, asyncExecCallback);
             } catch (final ResourceIOException ex) {
                 asyncExecCallback.failed(ex);
@@ -731,6 +745,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
             }
             try {
                 final SimpleHttpResponse cacheResponse = generateCachedResponse(request, hit.entry, now);
+                context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
                 triggerResponse(cacheResponse, scope, asyncExecCallback);
             } catch (final ResourceIOException ex) {
                 if (requestCacheControl.isOnlyIfCached()) {
@@ -802,7 +817,8 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
                                 asyncExecCallback,
                                 c -> revalidateCacheEntry(responseCacheControl, hit, target, request, entityProducer, fork, chain, c));
                         context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, CacheResponseStatus.CACHE_MODULE_RESPONSE);
-                        final SimpleHttpResponse cacheResponse = unvalidatedCacheHit(request, hit.entry);
+                        final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
+                        context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
                         triggerResponse(cacheResponse, scope, asyncExecCallback);
                     } catch (final IOException ex) {
                         asyncExecCallback.failed(ex);
@@ -861,6 +877,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
                             public void completed(final CacheHit updated) {
                                 try {
                                     final SimpleHttpResponse cacheResponse = generateCachedResponse(request, updated.entry, responseDate);
+                                    context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
                                     triggerResponse(cacheResponse, scope, asyncExecCallback);
                                 } catch (final ResourceIOException ex) {
                                     asyncExecCallback.failed(ex);
@@ -1080,7 +1097,8 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
                 if (response == null) {
                     try {
                         context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, CacheResponseStatus.CACHE_MODULE_RESPONSE);
-                        final SimpleHttpResponse cacheResponse = unvalidatedCacheHit(request, hit.entry);
+                        final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
+                        context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
                         triggerResponse(cacheResponse, scope, asyncExecCallback);
                     } catch (final IOException ex) {
                         asyncExecCallback.failed(ex);
@@ -1104,7 +1122,8 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
                             LOG.debug("{} serving stale response due to IOException and stale-if-error enabled", exchangeId);
                         }
                         try {
-                            final SimpleHttpResponse cacheResponse = unvalidatedCacheHit(request, hit.entry);
+                            final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
+                            context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
                             triggerResponse(cacheResponse, scope, asyncExecCallback);
                         } catch (final IOException ex) {
                             asyncExecCallback.failed(cause);
@@ -1219,16 +1238,12 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
 
                             @Override
                             public void completed(final CacheHit hit) {
-                                if (shouldSendNotModifiedResponse(request, hit.entry, Instant.now())) {
-                                    final SimpleHttpResponse cacheResponse = responseGenerator.generateNotModifiedResponse(hit.entry);
+                                try {
+                                    final SimpleHttpResponse cacheResponse = generateCachedResponse(request, hit.entry, responseDate);
+                                    context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
                                     triggerResponse(cacheResponse, scope, asyncExecCallback);
-                                } else {
-                                    try {
-                                        final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
-                                        triggerResponse(cacheResponse, scope, asyncExecCallback);
-                                    } catch (final ResourceIOException ex) {
-                                        asyncExecCallback.failed(ex);
-                                    }
+                                } catch (final ResourceIOException ex) {
+                                    asyncExecCallback.failed(ex);
                                 }
                             }
 
@@ -1249,6 +1264,7 @@ class AsyncCachingExec extends CachingExecBase implements AsyncExecChainHandler
             public AsyncDataConsumer handleResponse(
                     final HttpResponse backendResponse,
                     final EntityDetails entityDetails) throws HttpException, IOException {
+                final HttpClientContext context = scope.clientContext;
                 final Instant responseDate = getCurrentDate();
                 final AsyncExecCallback callback;
                 if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) {
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControlHeaderGenerator.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControlHeaderGenerator.java
new file mode 100644
index 000000000..360908be0
--- /dev/null
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControlHeaderGenerator.java
@@ -0,0 +1,106 @@
+/*
+ * ====================================================================
+ * 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
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.hc.client5.http.cache.RequestCacheControl;
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.Internal;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpMessage;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.BasicHeaderValueFormatter;
+import org.apache.hc.core5.http.message.BufferedHeader;
+import org.apache.hc.core5.util.Args;
+import org.apache.hc.core5.util.CharArrayBuffer;
+
+@Internal
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
+class CacheControlHeaderGenerator {
+
+    public static final CacheControlHeaderGenerator INSTANCE = new CacheControlHeaderGenerator();
+
+    public List<NameValuePair> convert(final RequestCacheControl cacheControl) {
+        Args.notNull(cacheControl, "Cache control");
+        final List<NameValuePair> params = new ArrayList<>(10);
+        if (cacheControl.getMaxAge() >= 0) {
+            params.add(new BasicHeader("max-age", cacheControl.getMaxAge()));
+        }
+        if (cacheControl.getMaxStale() >= 0) {
+            params.add(new BasicHeader("max-stale", cacheControl.getMaxStale()));
+        }
+        if (cacheControl.getMinFresh() >= 0) {
+            params.add(new BasicHeader("min-fresh", cacheControl.getMinFresh()));
+        }
+        if (cacheControl.isNoCache()) {
+            params.add(new BasicHeader("no-cache", null));
+        }
+        if (cacheControl.isNoStore()) {
+            params.add(new BasicHeader("no-store", null));
+        }
+        if (cacheControl.isOnlyIfCached()) {
+            params.add(new BasicHeader("only-if-cached", null));
+        }
+        if (cacheControl.getStaleIfError() >= 0) {
+            params.add(new BasicHeader("stale-if-error", cacheControl.getStaleIfError()));
+        }
+        return params;
+    }
+
+    public Header generate(final RequestCacheControl cacheControl) {
+        final List<NameValuePair> params = convert(cacheControl);
+        if (!params.isEmpty()) {
+            final CharArrayBuffer buf = new CharArrayBuffer(1024);
+            buf.append(HttpHeaders.CACHE_CONTROL);
+            buf.append(": ");
+            for (int i = 0; i < params.size(); i++) {
+                if (i > 0) {
+                    buf.append(", ");
+                }
+                BasicHeaderValueFormatter.INSTANCE.formatNameValuePair(buf, params.get(i), false);
+            }
+            return BufferedHeader.create(buf);
+        } else {
+            return null;
+        }
+    }
+
+    public void generate(final RequestCacheControl cacheControl, final HttpMessage message) {
+        final Header h = generate(cacheControl);
+        if (h != null) {
+            message.addHeader(h);
+        }
+    }
+
+}
+
+
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 79804907b..5d7b6c2d2 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
@@ -70,7 +70,7 @@ import org.slf4j.LoggerFactory;
  * </p>
  */
 @Internal
-@Contract(threading = ThreadingBehavior.SAFE)
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
 class CacheControlHeaderParser {
 
     /**
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 322cc1ef8..6ce584945 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
@@ -145,19 +145,28 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
             final ExecChain.Scope scope,
             final ExecChain chain) throws IOException, HttpException {
         final String exchangeId = scope.exchangeId;
-        final HttpClientContext context = scope.clientContext;
+        final HttpCacheContext context = HttpCacheContext.adapt(scope.clientContext);
 
         if (LOG.isDebugEnabled()) {
             LOG.debug("{} request via cache: {}", exchangeId, new RequestLine(request));
         }
 
         context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, CacheResponseStatus.CACHE_MISS);
+        context.setAttribute(HttpCacheContext.CACHE_ENTRY, null);
 
         if (clientRequestsOurOptions(request)) {
             context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, CacheResponseStatus.CACHE_MODULE_RESPONSE);
             return new BasicClassicHttpResponse(HttpStatus.SC_NOT_IMPLEMENTED);
         }
-        final RequestCacheControl requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
+
+        final RequestCacheControl requestCacheControl;
+        if (request.containsHeader(HttpHeaders.CACHE_CONTROL)) {
+            requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
+        } else {
+            requestCacheControl = context.getRequestCacheControl();
+            CacheControlHeaderGenerator.INSTANCE.generate(requestCacheControl, request);
+        }
+
         if (LOG.isDebugEnabled()) {
             LOG.debug("Request cache control: {}", requestCacheControl);
         }
@@ -176,6 +185,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
             return handleCacheMiss(requestCacheControl, root, target, request, scope, chain);
         } else {
             final ResponseCacheControl responseCacheControl = CacheControlHeaderParser.INSTANCE.parse(hit.entry);
+            context.setResponseCacheControl(responseCacheControl);
             if (LOG.isDebugEnabled()) {
                 LOG.debug("{} response cache control: {}", exchangeId, responseCacheControl);
             }
@@ -220,7 +230,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
         }
         final ClassicHttpResponse backendResponse = chain.proceed(request, scope);
         try {
-            return handleBackendResponse(exchangeId, target, request, requestDate, getCurrentDate(), backendResponse);
+            return handleBackendResponse(target, request, scope, requestDate, getCurrentDate(), backendResponse);
         } catch (final IOException | RuntimeException ex) {
             backendResponse.close();
             throw ex;
@@ -256,7 +266,9 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
                 LOG.debug("{} cache hit is fresh enough", exchangeId);
             }
             try {
-                return convert(generateCachedResponse(request, hit.entry, now));
+                final SimpleHttpResponse cacheResponse = generateCachedResponse(request, hit.entry, now);
+                context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
+                return convert(cacheResponse);
             } catch (final ResourceIOException ex) {
                 if (requestCacheControl.isOnlyIfCached()) {
                     if (LOG.isDebugEnabled()) {
@@ -315,7 +327,9 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
                             hit.getEntryKey(),
                             () -> revalidateCacheEntry(responseCacheControl, hit, target, request, fork, chain));
                     context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, CacheResponseStatus.CACHE_MODULE_RESPONSE);
-                    return convert(unvalidatedCacheHit(request, hit.entry));
+                    final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
+                    context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
+                    return convert(cacheResponse);
                 } else {
                     if (LOG.isDebugEnabled()) {
                         LOG.debug("{} revalidating stale cache entry (asynchronous revalidation disabled)", exchangeId);
@@ -368,9 +382,11 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
             }
             if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                 final CacheHit updated = responseCache.update(hit, target, request, backendResponse, requestDate, responseDate);
-                return convert(generateCachedResponse(request, updated.entry, responseDate));
+                final SimpleHttpResponse cacheResponse = generateCachedResponse(request, updated.entry, responseDate);
+                context.setAttribute(HttpCacheContext.CACHE_ENTRY, updated.entry);
+                return convert(cacheResponse);
             }
-            return handleBackendResponse(scope.exchangeId, target, conditionalRequest, requestDate, responseDate, backendResponse);
+            return handleBackendResponse(target, conditionalRequest, scope, requestDate, responseDate, backendResponse);
         } catch (final IOException | RuntimeException ex) {
             backendResponse.close();
             throw ex;
@@ -419,7 +435,9 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
                 if (LOG.isDebugEnabled()) {
                     LOG.debug("{} serving stale response due to IOException and stale-if-error enabled", exchangeId);
                 }
-                return convert(unvalidatedCacheHit(request, hit.entry));
+                final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
+                context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
+                return convert(cacheResponse);
             } else {
                 return convert(generateGatewayTimeout());
             }
@@ -432,19 +450,21 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
             }
             EntityUtils.consume(response.getEntity());
             context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, CacheResponseStatus.CACHE_MODULE_RESPONSE);
-            return convert(unvalidatedCacheHit(request, hit.entry));
+            final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
+            context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
+            return convert(cacheResponse);
         }
         return response;
     }
 
     ClassicHttpResponse handleBackendResponse(
-            final String exchangeId,
             final HttpHost target,
             final ClassicHttpRequest request,
+            final ExecChain.Scope scope,
             final Instant requestDate,
             final Instant responseDate,
             final ClassicHttpResponse backendResponse) throws IOException {
-
+        final String exchangeId = scope.exchangeId;
         responseCache.evictInvalidatedEntries(target, request, backendResponse);
         if (isResponseTooBig(backendResponse.getEntity())) {
             if (LOG.isDebugEnabled()) {
@@ -459,7 +479,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
             if (LOG.isDebugEnabled()) {
                 LOG.debug("{} caching backend response", exchangeId);
             }
-            return cacheAndReturnResponse(exchangeId, target, request, backendResponse, requestDate, responseDate);
+            return cacheAndReturnResponse(target, request, scope, backendResponse, requestDate, responseDate);
         } else {
             if (LOG.isDebugEnabled()) {
                 LOG.debug("{} backend response is not cacheable", exchangeId);
@@ -469,12 +489,14 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
     }
 
     ClassicHttpResponse cacheAndReturnResponse(
-            final String exchangeId,
             final HttpHost target,
             final HttpRequest request,
+            final ExecChain.Scope scope,
             final ClassicHttpResponse backendResponse,
             final Instant requestSent,
             final Instant responseReceived) throws IOException {
+        final String exchangeId = scope.exchangeId;
+        final HttpClientContext context = scope.clientContext;
         final int statusCode = backendResponse.getCode();
         // handle 304 Not Modified responses
         if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
@@ -488,7 +510,9 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
                         backendResponse,
                         requestSent,
                         responseReceived);
-                return convert(responseGenerator.generateResponse(request, updated.entry));
+                final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, updated.entry);
+                context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
+                return convert(cacheResponse);
             }
         }
 
@@ -536,7 +560,9 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
                 LOG.debug("{} backend response successfully cached (freshness check skipped)", exchangeId);
             }
         }
-        return convert(responseGenerator.generateResponse(request, hit.entry));
+        final SimpleHttpResponse cacheResponse = responseGenerator.generateResponse(request, hit.entry);
+        context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
+        return convert(cacheResponse);
     }
 
     private ClassicHttpResponse handleCacheMiss(
@@ -597,7 +623,7 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
             final Instant responseDate = getCurrentDate();
 
             if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) {
-                return handleBackendResponse(exchangeId, target, request, requestDate, responseDate, backendResponse);
+                return handleBackendResponse(target, request, scope, requestDate, responseDate, backendResponse);
             } else {
                 // 304 response are not expected to have an enclosed content body, but still
                 backendResponse.close();
@@ -630,11 +656,9 @@ class CachingExec extends CachingExecBase implements ExecChainHandler {
             cacheUpdates.getAndIncrement();
 
             final CacheHit hit = responseCache.storeFromNegotiated(match, target, request, backendResponse, requestDate, responseDate);
-            if (shouldSendNotModifiedResponse(request, hit.entry, responseDate)) {
-                return convert(responseGenerator.generateNotModifiedResponse(hit.entry));
-            } else {
-                return convert(responseGenerator.generateResponse(request, hit.entry));
-            }
+            final SimpleHttpResponse cacheResponse = generateCachedResponse(request, hit.entry, responseDate);
+            context.setAttribute(HttpCacheContext.CACHE_ENTRY, hit.entry);
+            return convert(cacheResponse);
         } catch (final IOException | RuntimeException ex) {
             backendResponse.close();
             throw ex;
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExecBase.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExecBase.java
index c2facf4ab..f886d5b3b 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExecBase.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachingExecBase.java
@@ -26,7 +26,6 @@
  */
 package org.apache.hc.client5.http.impl.cache;
 
-import java.io.IOException;
 import java.time.Instant;
 import java.util.concurrent.atomic.AtomicLong;
 
@@ -124,10 +123,6 @@ public class CachingExecBase {
         return SimpleHttpResponse.create(HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout");
     }
 
-    SimpleHttpResponse unvalidatedCacheHit(final HttpRequest request, final HttpCacheEntry entry) throws IOException {
-        return responseGenerator.generateResponse(request, entry);
-    }
-
     Instant getCurrentDate() {
         return Instant.now();
     }
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/example/AsyncClientCacheControl.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/example/AsyncClientCacheControl.java
new file mode 100644
index 000000000..4535cebcd
--- /dev/null
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/example/AsyncClientCacheControl.java
@@ -0,0 +1,209 @@
+/*
+ * ====================================================================
+ * 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
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.cache.example;
+
+import java.util.concurrent.Future;
+
+import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
+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.CacheContextBuilder;
+import org.apache.hc.client5.http.cache.HttpCacheContext;
+import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.RequestCacheControl;
+import org.apache.hc.client5.http.impl.async.CloseableHttpAsyncClient;
+import org.apache.hc.client5.http.impl.cache.CacheConfig;
+import org.apache.hc.client5.http.impl.cache.CachingHttpAsyncClients;
+import org.apache.hc.client5.http.impl.cache.HeapResourceFactory;
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.message.StatusLine;
+import org.apache.hc.core5.io.CloseMode;
+
+/**
+ * This is an example demonstrating how to control execution of cache
+ * operation and determine its outcome using the async HTTP cache API.
+ */
+public class AsyncClientCacheControl {
+
+    public static void main(final String[] args) throws Exception {
+
+        final HttpHost target = new HttpHost("https", "www.apache.org");
+
+        try (final CloseableHttpAsyncClient httpclient = CachingHttpAsyncClients.custom()
+                .setCacheConfig(CacheConfig.custom()
+                        .setMaxObjectSize(200000)
+                        .setHeuristicCachingEnabled(true)
+                        .build())
+                .setResourceFactory(HeapResourceFactory.INSTANCE)
+                .build()) {
+
+            httpclient.start();
+
+            final SimpleHttpRequest httpget1 = SimpleRequestBuilder.get()
+                    .setHttpHost(target)
+                    .setPath("/")
+                    .build();
+
+            // Use default cache control
+            final HttpCacheContext context = CacheContextBuilder.create()
+                    .setCacheControl(RequestCacheControl.DEFAULT)
+                    .build();
+
+            System.out.println("Executing request " + httpget1.getMethod() + " " + httpget1.getUri());
+            final Future<SimpleHttpResponse> future = httpclient.execute(
+                    SimpleRequestProducer.create(httpget1),
+                    SimpleResponseConsumer.create(),
+                    context,
+                    new FutureCallback<SimpleHttpResponse>() {
+
+                        @Override
+                        public void completed(final SimpleHttpResponse response) {
+                            System.out.println(httpget1 + "->" + new StatusLine(response));
+                            System.out.println("Cache status: " + context.getCacheResponseStatus());
+                            System.out.println("Request cache control: " + context.getRequestCacheControl());
+                            System.out.println("Response cache control: " + context.getResponseCacheControl());
+                            final HttpCacheEntry cacheEntry = context.getCacheEntry();
+                            if (cacheEntry != null) {
+                                System.out.println("Cache entry resource: " + cacheEntry.getResource());
+                                System.out.println("Date: " + cacheEntry.getInstant());
+                                System.out.println("Expires: " + cacheEntry.getExpires());
+                                System.out.println("Last modified: " + cacheEntry.getLastModified());
+                            }
+                        }
+
+                        @Override
+                        public void failed(final Exception ex) {
+                            System.out.println(httpget1 + "->" + ex);
+                        }
+
+                        @Override
+                        public void cancelled() {
+                            System.out.println(httpget1 + " cancelled");
+                        }
+
+                    });
+            future.get();
+
+            final SimpleHttpRequest httpget2 = SimpleRequestBuilder.get()
+                    .setHttpHost(target)
+                    .setPath("/")
+                    .build();
+
+            // Ensure a custom freshness for the cache entry
+            context.setRequestCacheControl(RequestCacheControl.builder()
+                    .setMinFresh(100)
+                    .build());
+
+            System.out.println("Executing request " + httpget2.getMethod() + " " + httpget2.getUri());
+            final Future<SimpleHttpResponse> future2 = httpclient.execute(
+                    SimpleRequestProducer.create(httpget2),
+                    SimpleResponseConsumer.create(),
+                    context,
+                    new FutureCallback<SimpleHttpResponse>() {
+
+                        @Override
+                        public void completed(final SimpleHttpResponse response) {
+                            System.out.println(httpget2 + "->" + new StatusLine(response));
+                            System.out.println("Cache status: " + context.getCacheResponseStatus());
+                            System.out.println("Request cache control: " + context.getRequestCacheControl());
+                            System.out.println("Response cache control: " + context.getResponseCacheControl());
+                            final HttpCacheEntry cacheEntry = context.getCacheEntry();
+                            if (cacheEntry != null) {
+                                System.out.println("Cache entry resource: " + cacheEntry.getResource());
+                                System.out.println("Date: " + cacheEntry.getInstant());
+                                System.out.println("Expires: " + cacheEntry.getExpires());
+                                System.out.println("Last modified: " + cacheEntry.getLastModified());
+                            }
+                        }
+
+                        @Override
+                        public void failed(final Exception ex) {
+                            System.out.println(httpget2 + "->" + ex);
+                        }
+
+                        @Override
+                        public void cancelled() {
+                            System.out.println(httpget2 + " cancelled");
+                        }
+
+                    });
+            future2.get();
+
+            Thread.sleep(2000);
+
+            final SimpleHttpRequest httpget3 = SimpleRequestBuilder.get()
+                    .setHttpHost(target)
+                    .setPath("/")
+                    .build();
+
+            // Try to force cache entry re-validation
+            context.setRequestCacheControl(RequestCacheControl.builder()
+                    .setMaxAge(0)
+                    .build());
+
+            System.out.println("Executing request " + httpget3.getMethod() + " " + httpget3.getUri());
+            final Future<SimpleHttpResponse> future3 = httpclient.execute(
+                    SimpleRequestProducer.create(httpget3),
+                    SimpleResponseConsumer.create(),
+                    context,
+                    new FutureCallback<SimpleHttpResponse>() {
+
+                        @Override
+                        public void completed(final SimpleHttpResponse response) {
+                            System.out.println(httpget3 + "->" + new StatusLine(response));
+                            System.out.println("Cache status: " + context.getCacheResponseStatus());
+                            System.out.println("Request cache control: " + context.getRequestCacheControl());
+                            System.out.println("Response cache control: " + context.getResponseCacheControl());
+                            final HttpCacheEntry cacheEntry = context.getCacheEntry();
+                            if (cacheEntry != null) {
+                                System.out.println("Cache entry resource: " + cacheEntry.getResource());
+                                System.out.println("Date: " + cacheEntry.getInstant());
+                                System.out.println("Expires: " + cacheEntry.getExpires());
+                                System.out.println("Last modified: " + cacheEntry.getLastModified());
+                            }
+                        }
+
+                        @Override
+                        public void failed(final Exception ex) {
+                            System.out.println(httpget3 + "->" + ex);
+                        }
+
+                        @Override
+                        public void cancelled() {
+                            System.out.println(httpget3 + " cancelled");
+                        }
+
+                    });
+            future3.get();
+
+            httpclient.close(CloseMode.GRACEFUL);
+        }
+    }
+}
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/example/ClientCacheControl.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/example/ClientCacheControl.java
new file mode 100644
index 000000000..b5ea29cd3
--- /dev/null
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/cache/example/ClientCacheControl.java
@@ -0,0 +1,150 @@
+/*
+ * ====================================================================
+ * 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
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.cache.example;
+
+import org.apache.hc.client5.http.cache.CacheContextBuilder;
+import org.apache.hc.client5.http.cache.HttpCacheContext;
+import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.RequestCacheControl;
+import org.apache.hc.client5.http.impl.cache.CacheConfig;
+import org.apache.hc.client5.http.impl.cache.CachingHttpClients;
+import org.apache.hc.client5.http.impl.cache.HeapResourceFactory;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.io.entity.EntityUtils;
+import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
+import org.apache.hc.core5.http.message.StatusLine;
+
+/**
+ * This is an example demonstrating how to control execution of cache
+ * operation and determine its outcome using the classic HTTP cache API.
+ */
+public class ClientCacheControl {
+
+    public static void main(final String[] args) throws Exception {
+
+        final HttpHost target = new HttpHost("https", "www.apache.org");
+
+        try (final CloseableHttpClient httpclient = CachingHttpClients.custom()
+                .setCacheConfig(CacheConfig.custom()
+                        .setMaxObjectSize(200000)
+                        .setHeuristicCachingEnabled(true)
+                        .build())
+                .setResourceFactory(HeapResourceFactory.INSTANCE)
+                .build()) {
+
+            final ClassicHttpRequest httpget1 = ClassicRequestBuilder.get()
+                    .setHttpHost(target)
+                    .setPath("/")
+                    .build();
+
+            // Use default cache control
+            final HttpCacheContext context = CacheContextBuilder.create()
+                    .setCacheControl(RequestCacheControl.DEFAULT)
+                    .build();
+
+            System.out.println("Executing request " + httpget1.getMethod() + " " + httpget1.getUri());
+            httpclient.execute(httpget1, context, response -> {
+                System.out.println("----------------------------------------");
+                System.out.println(httpget1 + "->" + new StatusLine(response));
+                EntityUtils.consume(response.getEntity());
+                System.out.println("Cache status: " + context.getCacheResponseStatus());
+                System.out.println("Request cache control: " + context.getRequestCacheControl());
+                System.out.println("Response cache control: " + context.getResponseCacheControl());
+                final HttpCacheEntry cacheEntry = context.getCacheEntry();
+                if (cacheEntry != null) {
+                    System.out.println("Cache entry resource: " + cacheEntry.getResource());
+                    System.out.println("Date: " + cacheEntry.getInstant());
+                    System.out.println("Expires: " + cacheEntry.getExpires());
+                    System.out.println("Last modified: " + cacheEntry.getLastModified());
+                }
+                return null;
+            });
+
+            final ClassicHttpRequest httpget2 = ClassicRequestBuilder.get()
+                    .setHttpHost(target)
+                    .setPath("/")
+                    .build();
+
+            // Ensure a custom freshness for the cache entry
+            context.setRequestCacheControl(RequestCacheControl.builder()
+                    .setMinFresh(100)
+                    .build());
+
+            System.out.println("Executing request " + httpget2.getMethod() + " " + httpget2.getUri());
+            httpclient.execute(httpget2, context, response -> {
+                System.out.println("----------------------------------------");
+                System.out.println(httpget2 + "->" + new StatusLine(response));
+                EntityUtils.consume(response.getEntity());
+                System.out.println("Cache status: " + context.getCacheResponseStatus());
+                System.out.println("Request cache control: " + context.getRequestCacheControl());
+                System.out.println("Response cache control: " + context.getResponseCacheControl());
+                final HttpCacheEntry cacheEntry = context.getCacheEntry();
+                if (cacheEntry != null) {
+                    System.out.println("Cache entry resource: " + cacheEntry.getResource());
+                    System.out.println("Date: " + cacheEntry.getInstant());
+                    System.out.println("Expires: " + cacheEntry.getExpires());
+                    System.out.println("Last modified: " + cacheEntry.getLastModified());
+                }
+                return null;
+            });
+
+            Thread.sleep(2000);
+
+            final ClassicHttpRequest httpget3 = ClassicRequestBuilder.get()
+                    .setHttpHost(target)
+                    .setPath("/")
+                    .build();
+
+            // Try to force cache entry re-validation
+            context.setRequestCacheControl(RequestCacheControl.builder()
+                    .setMaxAge(0)
+                    .build());
+
+            System.out.println("Executing request " + httpget3.getMethod() + " " + httpget3.getUri());
+            httpclient.execute(httpget3, context, response -> {
+                System.out.println("----------------------------------------");
+                System.out.println(httpget3 + "->" + new StatusLine(response));
+                EntityUtils.consume(response.getEntity());
+                System.out.println("Cache status: " + context.getCacheResponseStatus());
+                System.out.println("Request cache control: " + context.getRequestCacheControl());
+                System.out.println("Response cache control: " + context.getResponseCacheControl());
+                final HttpCacheEntry cacheEntry = context.getCacheEntry();
+                if (cacheEntry != null) {
+                    System.out.println("Cache entry resource: " + cacheEntry.getResource());
+                    System.out.println("Date: " + cacheEntry.getInstant());
+                    System.out.println("Expires: " + cacheEntry.getExpires());
+                    System.out.println("Last modified: " + cacheEntry.getLastModified());
+                }
+                return null;
+            });
+        }
+
+
+    }
+}
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/CacheControlGeneratorTest.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/CacheControlGeneratorTest.java
new file mode 100644
index 000000000..0efa6dad3
--- /dev/null
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/CacheControlGeneratorTest.java
@@ -0,0 +1,86 @@
+/*
+ * ====================================================================
+ * 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
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.client5.http.impl.cache;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import org.apache.hc.client5.http.HeaderMatcher;
+import org.apache.hc.client5.http.cache.RequestCacheControl;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class CacheControlGeneratorTest {
+
+    private final CacheControlHeaderGenerator generator = CacheControlHeaderGenerator.INSTANCE;
+
+    @Test
+    public void testGenerateRequestCacheControlHeader() {
+        assertThat(generator.generate(
+                        RequestCacheControl.builder()
+                                .setMaxAge(12)
+                                .setMaxStale(23)
+                                .setMinFresh(34)
+                                .setNoCache(true)
+                                .setNoStore(true)
+                                .setOnlyIfCached(true)
+                                .setStaleIfError(56)
+                                .build()),
+                HeaderMatcher.same("Cache-Control", "max-age=12, max-stale=23, " +
+                        "min-fresh=34, no-cache, no-store, only-if-cached, stale-if-error=56"));
+        assertThat(generator.generate(
+                        RequestCacheControl.builder()
+                                .setMaxAge(12)
+                                .setNoCache(true)
+                                .setMinFresh(34)
+                                .setMaxStale(23)
+                                .setNoStore(true)
+                                .setStaleIfError(56)
+                                .setOnlyIfCached(true)
+                                .build()),
+                HeaderMatcher.same("Cache-Control", "max-age=12, max-stale=23, " +
+                        "min-fresh=34, no-cache, no-store, only-if-cached, stale-if-error=56"));
+        assertThat(generator.generate(
+                        RequestCacheControl.builder()
+                                .setMaxAge(0)
+                                .build()),
+                HeaderMatcher.same("Cache-Control", "max-age=0"));
+        assertThat(generator.generate(
+                        RequestCacheControl.builder()
+                                .setMaxAge(-1)
+                                .setMinFresh(10)
+                                .build()),
+                HeaderMatcher.same("Cache-Control", "min-fresh=10"));
+    }
+
+    @Test
+    public void testGenerateRequestCacheControlHeaderNoDirectives() {
+        final RequestCacheControl cacheControl = RequestCacheControl.builder()
+                .build();
+        Assertions.assertNull(generator.generate(cacheControl));
+    }
+
+}
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 b1616c331..4d96eedc5 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
@@ -27,7 +27,6 @@
 package org.apache.hc.client5.http.impl.cache;
 
 
-import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.mockito.Mockito.mock;
 
 import java.io.IOException;
@@ -50,7 +49,6 @@ import org.apache.hc.client5.http.classic.ExecChain;
 import org.apache.hc.client5.http.classic.ExecRuntime;
 import org.apache.hc.client5.http.classic.methods.HttpGet;
 import org.apache.hc.client5.http.classic.methods.HttpOptions;
-import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.utils.DateUtils;
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
@@ -91,6 +89,7 @@ public class TestCachingExecChain {
     HttpCache cache;
     CachingExec impl;
     CacheConfig customConfig;
+    ExecChain.Scope scope;
 
     @BeforeEach
     public void setUp() {
@@ -101,6 +100,7 @@ public class TestCachingExecChain {
         context = HttpCacheContext.create();
         entry = HttpTestUtils.makeCacheEntry();
         customConfig = CacheConfig.DEFAULT;
+        scope = new ExecChain.Scope("test", route, request, mockExecRuntime, context);
 
         cache = Mockito.spy(new BasicHttpCache());
 
@@ -900,7 +900,7 @@ public class TestCachingExecChain {
         originResponse.setHeader("Date", DateUtils.formatStandardDate(responseGenerated));
         originResponse.setHeader("ETag", "\"etag\"");
 
-        impl.cacheAndReturnResponse("exchange-id", host, request, originResponse, requestSent, responseReceived);
+        impl.cacheAndReturnResponse(host, request, scope, originResponse, requestSent, responseReceived);
 
         Mockito.verify(cache, Mockito.never()).store(
                 Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
@@ -936,7 +936,7 @@ public class TestCachingExecChain {
                 Mockito.eq(requestSent),
                 Mockito.eq(responseReceived))).thenReturn(new CacheHit("key", httpCacheEntry));
 
-        impl.cacheAndReturnResponse("exchange-id", host, request, originResponse, requestSent, responseReceived);
+        impl.cacheAndReturnResponse(host, request, scope, originResponse, requestSent, responseReceived);
 
         Mockito.verify(mockCache).store(
                 Mockito.any(),
@@ -956,45 +956,6 @@ public class TestCachingExecChain {
         Assertions.assertEquals(HttpStatus.SC_GATEWAY_TIMEOUT, resp.getCode());
     }
 
-    @Test
-    public void testSetsRequestInContextOnCacheHit() throws Exception {
-        final ClassicHttpResponse response = HttpTestUtils.make200Response();
-        response.setHeader("Cache-Control", "max-age=3600");
-        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response);
-
-        final HttpClientContext ctx = HttpClientContext.create();
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), mockExecChain);
-        if (!HttpTestUtils.equivalent(request, ctx.getRequest())) {
-            assertSame(request, ctx.getRequest());
-        }
-    }
-
-    @Test
-    public void testSetsResponseInContextOnCacheHit() throws Exception {
-        final ClassicHttpResponse response = HttpTestUtils.make200Response();
-        response.setHeader("Cache-Control", "max-age=3600");
-        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response);
-
-        final HttpClientContext ctx = HttpClientContext.create();
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
-        final ClassicHttpResponse result = impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), null);
-        if (!HttpTestUtils.equivalent(result, ctx.getResponse())) {
-            assertSame(result, ctx.getResponse());
-        }
-    }
-
-    @Test
-    public void testSetsRequestSentInContextOnCacheHit() throws Exception {
-        final ClassicHttpResponse response = HttpTestUtils.make200Response();
-        response.setHeader("Cache-Control", "max-age=3600");
-        Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response);
-
-        final HttpClientContext ctx = HttpClientContext.create();
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, ctx), mockExecChain);
-    }
-
     @Test
     public void testCanCacheAResponseWithoutABody() throws Exception {
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_NO_CONTENT, "No Content");
@@ -1002,8 +963,8 @@ public class TestCachingExecChain {
         response.setHeader("Cache-Control", "max-age=300");
         Mockito.when(mockExecChain.proceed(RequestEquivalent.eq(request), Mockito.any())).thenReturn(response);
 
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+        impl.execute(request, scope, mockExecChain);
+        impl.execute(request, scope, mockExecChain);
 
         Mockito.verify(mockExecChain).proceed(Mockito.any(), Mockito.any());
     }
@@ -1148,16 +1109,16 @@ public class TestCachingExecChain {
         response.setHeader("Cache-Control", "max-age=3600");
         Mockito.when(mockExecChain.proceed(Mockito.any(), Mockito.any())).thenReturn(response);
 
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+        impl.execute(request, scope, mockExecChain);
 
         Mockito.verify(mockExecChain, Mockito.times(1)).proceed(Mockito.any(), Mockito.any());
 
         request.setAuthority(new URIAuthority("bar.example.com"));
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+        impl.execute(request, scope, mockExecChain);
 
         Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
 
-        impl.execute(request, new ExecChain.Scope("test", route, request, mockExecRuntime, context), mockExecChain);
+        impl.execute(request, scope, mockExecChain);
 
         Mockito.verify(mockExecChain, Mockito.times(2)).proceed(Mockito.any(), Mockito.any());
     }
@@ -1255,8 +1216,6 @@ public class TestCachingExecChain {
         backendResponse.setHeader("Cache-Control", "public, max-age=3600");
         backendResponse.setHeader("ETag", "\"etag\"");
 
-        final ExecChain.Scope scope = new ExecChain.Scope("test", route, request, mockExecRuntime, context);
-
         final Header[] headers = new Header[5];
         for (int i = 0; i < headers.length; i++) {
             headers[i] = new BasicHeader("header" + i, "value" + i);
@@ -1274,7 +1233,7 @@ public class TestCachingExecChain {
                 .thenReturn(new CacheHit("key", cacheEntry));
 
         // Call cacheAndReturnResponse with 304 Not Modified response
-        final ClassicHttpResponse cachedResponse = impl.cacheAndReturnResponse("exchange-id", host, request, backendResponse, requestSent, responseReceived);
+        final ClassicHttpResponse cachedResponse = impl.cacheAndReturnResponse(host, request, scope, backendResponse, requestSent, responseReceived);
 
         // Verify cache entry is updated
         Mockito.verify(mockCache).update(


(httpcomponents-client) 04/04: Added #toString methods to Resource implementations

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git

commit d7108f60ae8f6d229fcd425a7cfc2010b64a6bf4
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Thu Jan 4 09:28:53 2024 +0100

    Added #toString methods to Resource implementations
---
 .../java/org/apache/hc/client5/http/impl/cache/FileResource.java  | 8 ++++++++
 .../java/org/apache/hc/client5/http/impl/cache/HeapResource.java  | 8 ++++++++
 2 files changed, 16 insertions(+)

diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/FileResource.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/FileResource.java
index 69ed1a412..4636bdaf3 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/FileResource.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/FileResource.java
@@ -109,4 +109,12 @@ public class FileResource extends Resource {
         }
     }
 
+    @Override
+    public String toString() {
+        return "[" +
+                "file=" + getFile() + "," +
+                "len=" + length() +
+                ']';
+    }
+
 }
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HeapResource.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HeapResource.java
index 8a23e3916..a632f5266 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HeapResource.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/HeapResource.java
@@ -74,4 +74,12 @@ public class HeapResource extends Resource {
         this.arrayRef.set(null);
     }
 
+    @Override
+    public String toString() {
+        return "[" +
+                "heap," +
+                "len=" + length() +
+                ']';
+    }
+
 }


(httpcomponents-client) 01/04: Promote RequestCacheControl and ResponseCacheControl to public API

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git

commit 364fa60cd3028613ba80a2e28bbf07f2096c6602
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Dec 27 11:40:03 2023 +0100

    Promote RequestCacheControl and ResponseCacheControl to public API
---
 .../client5/http/{impl => }/cache/CacheControl.java |  6 ++----
 .../http/{impl => }/cache/RequestCacheControl.java  | 14 ++++++--------
 .../http/{impl => }/cache/ResponseCacheControl.java | 21 +++++----------------
 .../client5/http/impl/cache/AsyncCachingExec.java   |  2 ++
 .../http/impl/cache/CacheControlHeaderParser.java   |  6 ++++--
 .../http/impl/cache/CacheValidityPolicy.java        |  1 +
 .../http/impl/cache/CacheableRequestPolicy.java     |  1 +
 .../cache/CachedResponseSuitabilityChecker.java     |  2 ++
 .../hc/client5/http/impl/cache/CachingExec.java     |  2 ++
 .../http/impl/cache/ConditionalRequestBuilder.java  |  1 +
 .../http/impl/cache/ResponseCachingPolicy.java      |  1 +
 .../http/impl/cache/CacheControlParserTest.java     |  2 ++
 .../http/impl/cache/TestCacheValidityPolicy.java    |  1 +
 .../http/impl/cache/TestCacheableRequestPolicy.java |  1 +
 .../cache/TestCachedResponseSuitabilityChecker.java |  2 ++
 .../impl/cache/TestConditionalRequestBuilder.java   |  2 ++
 .../http/impl/cache/TestResponseCachingPolicy.java  |  1 +
 17 files changed, 36 insertions(+), 30 deletions(-)

diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControl.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/CacheControl.java
similarity index 94%
rename from httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControl.java
rename to httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/CacheControl.java
index a836a352e..21e326d67 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheControl.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/CacheControl.java
@@ -25,10 +25,9 @@
  *
  */
 
-package org.apache.hc.client5.http.impl.cache;
+package org.apache.hc.client5.http.cache;
 
 import org.apache.hc.core5.annotation.Contract;
-import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 
 /**
@@ -37,9 +36,8 @@ import org.apache.hc.core5.annotation.ThreadingBehavior;
  *
  * @since 5.4
  */
-@Internal
 @Contract(threading = ThreadingBehavior.IMMUTABLE)
-interface CacheControl {
+public interface CacheControl {
 
     /**
      * Returns the max-age value from the Cache-Control header.
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/RequestCacheControl.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/RequestCacheControl.java
similarity index 94%
rename from httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/RequestCacheControl.java
rename to httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/RequestCacheControl.java
index 441b58bf7..f6ea444ee 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/RequestCacheControl.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/RequestCacheControl.java
@@ -25,21 +25,19 @@
  *
  */
 
-package org.apache.hc.client5.http.impl.cache;
+package org.apache.hc.client5.http.cache;
 
 import org.apache.hc.core5.annotation.Contract;
-import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 
 /**
- * Represents the values of the Cache-Control header in an HTTP request, which indicate whether and for how long
- * the client is willing to cache the response.
+ * Represents the value of the Cache-Control header in an HTTP request containing cache
+ * control directives.
  *
  * @since 5.4
  */
-@Internal
 @Contract(threading = ThreadingBehavior.IMMUTABLE)
-final class RequestCacheControl implements CacheControl {
+public final class RequestCacheControl implements CacheControl {
 
     private final long maxAge;
     private final long maxStale;
@@ -170,11 +168,11 @@ final class RequestCacheControl implements CacheControl {
         return buf.toString();
     }
 
-    static Builder builder() {
+    public static Builder builder() {
         return new Builder();
     }
 
-    static class Builder {
+    public static class Builder {
 
         private long maxAge = -1;
         private long maxStale = -1;
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/cache/ResponseCacheControl.java
similarity index 96%
rename from httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ResponseCacheControl.java
rename to httpclient5-cache/src/main/java/org/apache/hc/client5/http/cache/ResponseCacheControl.java
index 16f563f39..c9b71a066 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/cache/ResponseCacheControl.java
@@ -25,7 +25,7 @@
  *
  */
 
-package org.apache.hc.client5.http.impl.cache;
+package org.apache.hc.client5.http.cache;
 
 import java.util.Arrays;
 import java.util.Collections;
@@ -33,11 +33,10 @@ import java.util.HashSet;
 import java.util.Set;
 
 import org.apache.hc.core5.annotation.Contract;
-import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
 
 /**
- * Represents the values of the Cache-Control header in an HTTP response, which indicate whether and for how long
+ * Represents the value of the Cache-Control header in an HTTP response, which indicate whether and for how long
  * the response can be cached by the client and intermediary proxies.
  * <p>
  * The class provides methods to retrieve the maximum age of the response and the maximum age that applies to shared
@@ -50,9 +49,8 @@ import org.apache.hc.core5.annotation.ThreadingBehavior;
  *
  * @since 5.4
  */
-@Internal
 @Contract(threading = ThreadingBehavior.IMMUTABLE)
-final class ResponseCacheControl implements CacheControl {
+public final class ResponseCacheControl implements CacheControl {
 
     /**
      * The max-age directive value.
@@ -339,11 +337,11 @@ final class ResponseCacheControl implements CacheControl {
             return buf.toString();
     }
 
-    static Builder builder() {
+    public static Builder builder() {
         return new Builder();
     }
 
-    static class Builder {
+    public static class Builder {
 
         private long maxAge = -1;
         private long sharedMaxAge = -1;
@@ -477,15 +475,6 @@ final class ResponseCacheControl implements CacheControl {
             return this;
         }
 
-        public boolean isNoTransform() {
-            return noStore;
-        }
-
-        public Builder setNoTransform(final boolean noTransform) {
-            this.noTransform = noTransform;
-            return this;
-        }
-
         public boolean isImmutable() {
             return immutable;
         }
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 2d25b204f..428b8c9e2 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
@@ -49,7 +49,9 @@ import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
 import org.apache.hc.client5.http.cache.CacheResponseStatus;
 import org.apache.hc.client5.http.cache.HttpCacheContext;
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.RequestCacheControl;
 import org.apache.hc.client5.http.cache.ResourceIOException;
+import org.apache.hc.client5.http.cache.ResponseCacheControl;
 import org.apache.hc.client5.http.impl.ExecSupport;
 import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.schedule.SchedulingStrategy;
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 f6c0d773b..79804907b 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
@@ -34,6 +34,8 @@ import java.util.function.BiConsumer;
 
 import org.apache.hc.client5.http.cache.HeaderConstants;
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.RequestCacheControl;
+import org.apache.hc.client5.http.cache.ResponseCacheControl;
 import org.apache.hc.core5.annotation.Contract;
 import org.apache.hc.core5.annotation.Internal;
 import org.apache.hc.core5.annotation.ThreadingBehavior;
@@ -160,7 +162,7 @@ class CacheControlHeaderParser {
      */
     public final ResponseCacheControl parseResponse(final Iterator<Header> headerIterator) {
         Args.notNull(headerIterator, "headerIterator");
-        final ResponseCacheControl.Builder builder = new ResponseCacheControl.Builder();
+        final ResponseCacheControl.Builder builder = ResponseCacheControl.builder();
         parse(headerIterator, (name, value) -> {
             if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_S_MAX_AGE)) {
                 builder.setSharedMaxAge(parseSeconds(name, value));
@@ -224,7 +226,7 @@ class CacheControlHeaderParser {
      */
     public final RequestCacheControl parseRequest(final Iterator<Header> headerIterator) {
         Args.notNull(headerIterator, "headerIterator");
-        final RequestCacheControl.Builder builder = new RequestCacheControl.Builder();
+        final RequestCacheControl.Builder builder = RequestCacheControl.builder();
         parse(headerIterator, (name, value) -> {
             if (name.equalsIgnoreCase(HeaderConstants.CACHE_CONTROL_MAX_AGE)) {
                 builder.setMaxAge(parseSeconds(name, value));
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheValidityPolicy.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheValidityPolicy.java
index f3c99f1a1..a9fa16a40 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheValidityPolicy.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheValidityPolicy.java
@@ -31,6 +31,7 @@ import java.time.Instant;
 import java.util.concurrent.atomic.AtomicReference;
 
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.ResponseCacheControl;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpHeaders;
 import org.apache.hc.core5.http.message.MessageSupport;
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheableRequestPolicy.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheableRequestPolicy.java
index 7ea900f49..f1fbdb6a7 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheableRequestPolicy.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CacheableRequestPolicy.java
@@ -26,6 +26,7 @@
  */
 package org.apache.hc.client5.http.impl.cache;
 
+import org.apache.hc.client5.http.cache.RequestCacheControl;
 import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.Method;
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedResponseSuitabilityChecker.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedResponseSuitabilityChecker.java
index de7d41b4d..2eccbcf69 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedResponseSuitabilityChecker.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/CachedResponseSuitabilityChecker.java
@@ -38,6 +38,8 @@ import java.util.Objects;
 import java.util.Set;
 
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
+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.Header;
 import org.apache.hc.core5.http.HeaderElement;
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 f451a589d..322cc1ef8 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
@@ -42,7 +42,9 @@ import org.apache.hc.client5.http.cache.CacheResponseStatus;
 import org.apache.hc.client5.http.cache.HttpCacheContext;
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
 import org.apache.hc.client5.http.cache.HttpCacheStorage;
+import org.apache.hc.client5.http.cache.RequestCacheControl;
 import org.apache.hc.client5.http.cache.ResourceIOException;
+import org.apache.hc.client5.http.cache.ResponseCacheControl;
 import org.apache.hc.client5.http.classic.ExecChain;
 import org.apache.hc.client5.http.classic.ExecChainHandler;
 import org.apache.hc.client5.http.impl.ExecSupport;
diff --git a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ConditionalRequestBuilder.java b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ConditionalRequestBuilder.java
index e476c7643..b262673fa 100644
--- a/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ConditionalRequestBuilder.java
+++ b/httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/ConditionalRequestBuilder.java
@@ -30,6 +30,7 @@ import java.util.List;
 
 import org.apache.hc.client5.http.cache.HeaderConstants;
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.ResponseCacheControl;
 import org.apache.hc.core5.function.Factory;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpHeaders;
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 859776bbe..ccbfc4fcb 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.Duration;
 import java.time.Instant;
 import java.util.Iterator;
 
+import org.apache.hc.client5.http.cache.ResponseCacheControl;
 import org.apache.hc.client5.http.utils.DateUtils;
 import org.apache.hc.core5.http.HttpHeaders;
 import org.apache.hc.core5.http.HttpRequest;
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/CacheControlParserTest.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/CacheControlParserTest.java
index 31a7bc109..640402ecb 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/CacheControlParserTest.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/CacheControlParserTest.java
@@ -34,6 +34,8 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.util.Arrays;
 import java.util.Collections;
 
+import org.apache.hc.client5.http.cache.RequestCacheControl;
+import org.apache.hc.client5.http.cache.ResponseCacheControl;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.message.BasicHeader;
 import org.junit.jupiter.api.Test;
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCacheValidityPolicy.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCacheValidityPolicy.java
index 06d4aa7de..93a27f00c 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCacheValidityPolicy.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCacheValidityPolicy.java
@@ -33,6 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.time.Instant;
 
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
+import org.apache.hc.client5.http.cache.ResponseCacheControl;
 import org.apache.hc.client5.http.utils.DateUtils;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.message.BasicHeader;
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCacheableRequestPolicy.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCacheableRequestPolicy.java
index b4b04190e..3c5d88074 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCacheableRequestPolicy.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCacheableRequestPolicy.java
@@ -26,6 +26,7 @@
  */
 package org.apache.hc.client5.http.impl.cache;
 
+import org.apache.hc.client5.http.cache.RequestCacheControl;
 import org.apache.hc.core5.http.message.BasicHttpRequest;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachedResponseSuitabilityChecker.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachedResponseSuitabilityChecker.java
index 0b2ce981e..0219f3311 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachedResponseSuitabilityChecker.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestCachedResponseSuitabilityChecker.java
@@ -29,6 +29,8 @@ package org.apache.hc.client5.http.impl.cache;
 import java.time.Instant;
 
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
+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.Header;
 import org.apache.hc.core5.http.HttpHost;
diff --git a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestConditionalRequestBuilder.java b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestConditionalRequestBuilder.java
index 56360bf96..15bc20645 100644
--- a/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestConditionalRequestBuilder.java
+++ b/httpclient5-cache/src/test/java/org/apache/hc/client5/http/impl/cache/TestConditionalRequestBuilder.java
@@ -31,6 +31,8 @@ import java.util.Arrays;
 import java.util.List;
 
 import org.apache.hc.client5.http.cache.HttpCacheEntry;
+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.Header;
 import org.apache.hc.core5.http.HttpHeaders;
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 eb5b9a3dc..c2c5cb53d 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
@@ -35,6 +35,7 @@ import java.time.temporal.ChronoUnit;
 import java.util.Random;
 
 import org.apache.hc.client5.http.auth.StandardAuthScheme;
+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;
 import org.apache.hc.core5.http.HttpHeaders;


(httpcomponents-client) 02/04: Refactor ContextBuilder into a common abstract class and HttpClientContext specific builder

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-client.git

commit b3da0ae1386d5d3158b348bf1c12666345551445
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Dec 27 15:46:48 2023 +0100

    Refactor ContextBuilder into a common abstract class and HttpClientContext specific builder
---
 ...lder.java => AbstractClientContextBuilder.java} | 38 ++++++-------
 .../org/apache/hc/client5/http/ContextBuilder.java | 66 +++++++---------------
 2 files changed, 37 insertions(+), 67 deletions(-)

diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ContextBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/AbstractClientContextBuilder.java
similarity index 75%
copy from httpclient5/src/main/java/org/apache/hc/client5/http/ContextBuilder.java
copy to httpclient5/src/main/java/org/apache/hc/client5/http/AbstractClientContextBuilder.java
index 8264b3f36..1358d9cbc 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/ContextBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/AbstractClientContextBuilder.java
@@ -43,15 +43,14 @@ import org.apache.hc.client5.http.protocol.HttpClientContext;
 import org.apache.hc.client5.http.routing.RoutingSupport;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.config.Lookup;
-import org.apache.hc.core5.http.protocol.BasicHttpContext;
 import org.apache.hc.core5.util.Args;
 
 /**
- * {@link HttpClientContext} builder.
+ * Abstract {@link HttpClientContext} builder.
  *
- * @since 5.2
+ * @since 5.4
  */
-public class ContextBuilder {
+public abstract class AbstractClientContextBuilder<T extends HttpClientContext> {
 
     private final SchemePortResolver schemePortResolver;
 
@@ -62,44 +61,36 @@ public class ContextBuilder {
     private AuthCache authCache;
     private Map<HttpHost, AuthScheme> authSchemeMap;
 
-    ContextBuilder(final SchemePortResolver schemePortResolver) {
+    protected AbstractClientContextBuilder(final SchemePortResolver schemePortResolver) {
         this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE;
     }
 
-    public static ContextBuilder create(final SchemePortResolver schemePortResolver) {
-        return new ContextBuilder(schemePortResolver);
-    }
-
-    public static ContextBuilder create() {
-        return new ContextBuilder(DefaultSchemePortResolver.INSTANCE);
-    }
-
-    public ContextBuilder useCookieSpecRegistry(final Lookup<CookieSpecFactory> cookieSpecRegistry) {
+    public AbstractClientContextBuilder<T> useCookieSpecRegistry(final Lookup<CookieSpecFactory> cookieSpecRegistry) {
         this.cookieSpecRegistry = cookieSpecRegistry;
         return this;
     }
 
-    public ContextBuilder useAuthSchemeRegistry(final Lookup<AuthSchemeFactory> authSchemeRegistry) {
+    public AbstractClientContextBuilder<T> useAuthSchemeRegistry(final Lookup<AuthSchemeFactory> authSchemeRegistry) {
         this.authSchemeRegistry = authSchemeRegistry;
         return this;
     }
 
-    public ContextBuilder useCookieStore(final CookieStore cookieStore) {
+    public AbstractClientContextBuilder<T> useCookieStore(final CookieStore cookieStore) {
         this.cookieStore = cookieStore;
         return this;
     }
 
-    public ContextBuilder useCredentialsProvider(final CredentialsProvider credentialsProvider) {
+    public AbstractClientContextBuilder<T> useCredentialsProvider(final CredentialsProvider credentialsProvider) {
         this.credentialsProvider = credentialsProvider;
         return this;
     }
 
-    public ContextBuilder useAuthCache(final AuthCache authCache) {
+    public AbstractClientContextBuilder<T> useAuthCache(final AuthCache authCache) {
         this.authCache = authCache;
         return this;
     }
 
-    public ContextBuilder preemptiveAuth(final HttpHost host, final AuthScheme authScheme) {
+    public AbstractClientContextBuilder<T> preemptiveAuth(final HttpHost host, final AuthScheme authScheme) {
         Args.notNull(host, "HTTP host");
         if (authSchemeMap == null) {
             authSchemeMap = new HashMap<>();
@@ -108,15 +99,18 @@ public class ContextBuilder {
         return this;
     }
 
-    public ContextBuilder preemptiveBasicAuth(final HttpHost host, final UsernamePasswordCredentials credentials) {
+    public AbstractClientContextBuilder<T> preemptiveBasicAuth(final HttpHost host, final UsernamePasswordCredentials credentials) {
         Args.notNull(host, "HTTP host");
         final BasicScheme authScheme = new BasicScheme();
         authScheme.initPreemptive(credentials);
         preemptiveAuth(host, authScheme);
         return this;
     }
-    public HttpClientContext build() {
-        final HttpClientContext context = new HttpClientContext(new BasicHttpContext());
+
+    protected abstract T createContext();
+
+    public T build() {
+        final T context = createContext();
         context.setCookieSpecRegistry(cookieSpecRegistry);
         context.setAuthSchemeRegistry(authSchemeRegistry);
         context.setCookieStore(cookieStore);
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/ContextBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/ContextBuilder.java
index 8264b3f36..73f6d17a6 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/ContextBuilder.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/ContextBuilder.java
@@ -27,9 +27,6 @@
 
 package org.apache.hc.client5.http;
 
-import java.util.HashMap;
-import java.util.Map;
-
 import org.apache.hc.client5.http.auth.AuthCache;
 import org.apache.hc.client5.http.auth.AuthScheme;
 import org.apache.hc.client5.http.auth.AuthSchemeFactory;
@@ -38,32 +35,20 @@ import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
 import org.apache.hc.client5.http.cookie.CookieSpecFactory;
 import org.apache.hc.client5.http.cookie.CookieStore;
 import org.apache.hc.client5.http.impl.DefaultSchemePortResolver;
-import org.apache.hc.client5.http.impl.auth.BasicScheme;
 import org.apache.hc.client5.http.protocol.HttpClientContext;
-import org.apache.hc.client5.http.routing.RoutingSupport;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.config.Lookup;
 import org.apache.hc.core5.http.protocol.BasicHttpContext;
-import org.apache.hc.core5.util.Args;
 
 /**
  * {@link HttpClientContext} builder.
  *
  * @since 5.2
  */
-public class ContextBuilder {
-
-    private final SchemePortResolver schemePortResolver;
+public class ContextBuilder extends AbstractClientContextBuilder<HttpClientContext> {
 
-    private Lookup<CookieSpecFactory> cookieSpecRegistry;
-    private Lookup<AuthSchemeFactory> authSchemeRegistry;
-    private CookieStore cookieStore;
-    private CredentialsProvider credentialsProvider;
-    private AuthCache authCache;
-    private Map<HttpHost, AuthScheme> authSchemeMap;
-
-    ContextBuilder(final SchemePortResolver schemePortResolver) {
-        this.schemePortResolver = schemePortResolver != null ? schemePortResolver : DefaultSchemePortResolver.INSTANCE;
+    protected ContextBuilder(final SchemePortResolver schemePortResolver) {
+        super(schemePortResolver);
     }
 
     public static ContextBuilder create(final SchemePortResolver schemePortResolver) {
@@ -74,60 +59,51 @@ public class ContextBuilder {
         return new ContextBuilder(DefaultSchemePortResolver.INSTANCE);
     }
 
+    @Override
     public ContextBuilder useCookieSpecRegistry(final Lookup<CookieSpecFactory> cookieSpecRegistry) {
-        this.cookieSpecRegistry = cookieSpecRegistry;
+        super.useCookieSpecRegistry(cookieSpecRegistry);
         return this;
     }
 
+    @Override
     public ContextBuilder useAuthSchemeRegistry(final Lookup<AuthSchemeFactory> authSchemeRegistry) {
-        this.authSchemeRegistry = authSchemeRegistry;
+        super.useAuthSchemeRegistry(authSchemeRegistry);
         return this;
     }
 
+    @Override
     public ContextBuilder useCookieStore(final CookieStore cookieStore) {
-        this.cookieStore = cookieStore;
+        super.useCookieStore(cookieStore);
         return this;
     }
 
+    @Override
     public ContextBuilder useCredentialsProvider(final CredentialsProvider credentialsProvider) {
-        this.credentialsProvider = credentialsProvider;
+        super.useCredentialsProvider(credentialsProvider);
         return this;
     }
 
+    @Override
     public ContextBuilder useAuthCache(final AuthCache authCache) {
-        this.authCache = authCache;
+        super.useAuthCache(authCache);
         return this;
     }
 
+    @Override
     public ContextBuilder preemptiveAuth(final HttpHost host, final AuthScheme authScheme) {
-        Args.notNull(host, "HTTP host");
-        if (authSchemeMap == null) {
-            authSchemeMap = new HashMap<>();
-        }
-        authSchemeMap.put(RoutingSupport.normalize(host, schemePortResolver), authScheme);
+        super.preemptiveAuth(host, authScheme);
         return this;
     }
 
+    @Override
     public ContextBuilder preemptiveBasicAuth(final HttpHost host, final UsernamePasswordCredentials credentials) {
-        Args.notNull(host, "HTTP host");
-        final BasicScheme authScheme = new BasicScheme();
-        authScheme.initPreemptive(credentials);
-        preemptiveAuth(host, authScheme);
+        super.preemptiveBasicAuth(host, credentials);
         return this;
     }
-    public HttpClientContext build() {
-        final HttpClientContext context = new HttpClientContext(new BasicHttpContext());
-        context.setCookieSpecRegistry(cookieSpecRegistry);
-        context.setAuthSchemeRegistry(authSchemeRegistry);
-        context.setCookieStore(cookieStore);
-        context.setCredentialsProvider(credentialsProvider);
-        context.setAuthCache(authCache);
-        if (authSchemeMap != null) {
-            for (final Map.Entry<HttpHost, AuthScheme> entry : authSchemeMap.entrySet()) {
-                context.resetAuthExchange(entry.getKey(), entry.getValue());
-            }
-        }
-        return context;
+
+    @Override
+    protected HttpClientContext createContext() {
+        return new HttpClientContext(new BasicHttpContext());
     }
 
 }