public void doExecute(
            final HttpHost target,
            final HttpRequest request,
            final AsyncEntityProducer entityProducer,
            final AsyncExecChain.Scope scope,
            final AsyncExecChain chain,
            final AsyncExecCallback asyncExecCallback) throws HttpException, IOException {

        final String exchangeId = scope.exchangeId;
        final HttpCacheContext context = HttpCacheContext.cast(scope.clientContext);
        final CancellableDependency operation = scope.cancellableDependency;

        if (LOG.isDebugEnabled()) {
            LOG.debug("{} request via cache: {} {}", exchangeId, request.getMethod(), request.getRequestUri());
        }

        context.setCacheResponseStatus(CacheResponseStatus.CACHE_MISS);
        context.setCacheEntry(null);

        if (clientRequestsOurOptions(request)) {
            context.setCacheResponseStatus(CacheResponseStatus.CACHE_MODULE_RESPONSE);
            triggerResponse(SimpleHttpResponse.create(HttpStatus.SC_NOT_IMPLEMENTED), scope, asyncExecCallback);
            return;
        }

        final RequestCacheControl requestCacheControl;
        if (request.containsHeader(HttpHeaders.CACHE_CONTROL)) {
            requestCacheControl = CacheControlHeaderParser.INSTANCE.parse(request);
            context.setRequestCacheControl(requestCacheControl);
        } else {
            requestCacheControl = context.getRequestCacheControlOrDefault();
            CacheControlHeaderGenerator.INSTANCE.generate(requestCacheControl, request);
        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("{} request cache control: {}", exchangeId, requestCacheControl);
        }

        if (cacheableRequestPolicy.canBeServedFromCache(requestCacheControl, request)) {
            operation.setDependency(responseCache.match(target, request, new FutureCallback<CacheMatch>() {

                @Override
                public void completed(final CacheMatch result) {
                    final CacheHit hit = result != null ? result.hit : null;
                    final CacheHit root = result != null ? result.root : null;
                    if (hit == null) {
                        handleCacheMiss(requestCacheControl, root, target, request, entityProducer, scope, chain, asyncExecCallback);
                    } else {
                        final ResponseCacheControl responseCacheControl = CacheControlHeaderParser.INSTANCE.parse(hit.entry);
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("{} response cache control: {}", exchangeId, responseCacheControl);
                        }
                        context.setResponseCacheControl(responseCacheControl);
                        handleCacheHit(requestCacheControl, responseCacheControl, hit, target, request, entityProducer, scope, chain, asyncExecCallback);
                    }
                }

                @Override
                public void failed(final Exception cause) {
                    asyncExecCallback.failed(cause);
                }

                @Override
                public void cancelled() {
                    asyncExecCallback.failed(new InterruptedIOException());
                }

            }));

        } else {
            if (LOG.isDebugEnabled()) {
                LOG.debug("{} request cannot be served from cache", exchangeId);
            }
            callBackend(target, request, entityProducer, scope, chain, asyncExecCallback);
        }
    }
Request executor in the request execution chain that is responsible for
transparent client-side caching.
<p>
The current implementation is conditionally
compliant with HTTP/1.1 (meaning all the MUST and MUST NOTs are obeyed),
although quite a lot, though not all, of the SHOULDs and SHOULD NOTs
are obeyed too.

@since 5.0
httpclient5-cache/src/main/java/org/apache/hc/client5/http/impl/cache/AsyncCachingExec.java