public InternalHttpClient( final ClientExecChain execChain, final HttpClientConnectionManager connManager, final HttpRoutePlanner routePlanner, final Lookup<CookieSpecProvider> cookieSpecRegistry, final Lookup<AuthSchemeProvider> authSchemeRegistry, final CookieStore cookieStore, final CredentialsProvider credentialsProvider, final RequestConfig defaultConfig, final List<Closeable> closeables) { super(); Args.notNull(execChain, "HTTP client exec chain"); Args.notNull(connManager, "HTTP connection manager"); Args.notNull(routePlanner, "HTTP route planner"); this.execChain = execChain; this.connManager = connManager; this.routePlanner = routePlanner; this.cookieSpecRegistry = cookieSpecRegistry; this.authSchemeRegistry = authSchemeRegistry; this.cookieStore = cookieStore; this.credentialsProvider = credentialsProvider; this.defaultConfig = defaultConfig; this.closeables = closeables; }
@DataProvider(value = { "true", "false" }, splitBy = "\\|") @Test public void decorateProtocolExec_uses_subspan_option_value_at_time_of_creation_not_time_of_execution( boolean subspanOptionOn ) throws IOException, HttpException { // given builder = WingtipsHttpClientBuilder.create(subspanOptionOn); Span parentSpan = Tracer.getInstance().startRequestWithRootSpan("someParentSpan"); SpanCapturingClientExecChain origCec = spy(new SpanCapturingClientExecChain()); // when ClientExecChain result = builder.decorateProtocolExec(origCec); // Set builder's subspan option to the opposite of what it was when the ClientExecChain was decorated. builder.setSurroundCallsWithSubspan(!subspanOptionOn); // then // Even though the *builder's* subspan option has been flipped, the ClientExecChain should still execute with // the subspan option value from when the ClientExecChain was originally decorated. verifyDecoratedClientExecChainPerformsTracingLogic( result, origCec, parentSpan, subspanOptionOn ); }
public CachingExec( final ClientExecChain backend, final HttpCache cache, final CacheConfig config, final AsynchronousValidator asynchRevalidator) { super(); Args.notNull(backend, "HTTP backend"); Args.notNull(cache, "HttpCache"); this.cacheConfig = config != null ? config : CacheConfig.DEFAULT; this.backend = backend; this.responseCache = cache; this.validityPolicy = new CacheValidityPolicy(); this.responseGenerator = new CachedHttpResponseGenerator(this.validityPolicy); this.cacheableRequestPolicy = new CacheableRequestPolicy(); this.suitabilityChecker = new CachedResponseSuitabilityChecker(this.validityPolicy, this.cacheConfig); this.conditionalRequestBuilder = new ConditionalRequestBuilder(); this.responseCompliance = new ResponseProtocolCompliance(); this.requestCompliance = new RequestProtocolCompliance(this.cacheConfig.isWeakETagOnPutDeleteAllowed()); this.responseCachingPolicy = new ResponseCachingPolicy( this.cacheConfig.getMaxObjectSize(), this.cacheConfig.isSharedCache(), this.cacheConfig.isNeverCacheHTTP10ResponsesWithQuery(), this.cacheConfig.is303CachingEnabled()); this.asynchRevalidator = asynchRevalidator; }
CachingExec( final ClientExecChain backend, final HttpCache responseCache, final CacheValidityPolicy validityPolicy, final ResponseCachingPolicy responseCachingPolicy, final CachedHttpResponseGenerator responseGenerator, final CacheableRequestPolicy cacheableRequestPolicy, final CachedResponseSuitabilityChecker suitabilityChecker, final ConditionalRequestBuilder conditionalRequestBuilder, final ResponseProtocolCompliance responseCompliance, final RequestProtocolCompliance requestCompliance, final CacheConfig config, final AsynchronousValidator asynchRevalidator) { this.cacheConfig = config != null ? config : CacheConfig.DEFAULT; this.backend = backend; this.responseCache = responseCache; this.validityPolicy = validityPolicy; this.responseCachingPolicy = responseCachingPolicy; this.responseGenerator = responseGenerator; this.cacheableRequestPolicy = cacheableRequestPolicy; this.suitabilityChecker = suitabilityChecker; this.conditionalRequestBuilder = conditionalRequestBuilder; this.responseCompliance = responseCompliance; this.requestCompliance = requestCompliance; this.asynchRevalidator = asynchRevalidator; }
@Override public CachingExec createCachingExecChain(final ClientExecChain mockBackend, final HttpCache mockCache, final CacheValidityPolicy mockValidityPolicy, final ResponseCachingPolicy mockResponsePolicy, final CachedHttpResponseGenerator mockResponseGenerator, final CacheableRequestPolicy mockRequestPolicy, final CachedResponseSuitabilityChecker mockSuitabilityChecker, final ConditionalRequestBuilder mockConditionalRequestBuilder, final ResponseProtocolCompliance mockResponseProtocolCompliance, final RequestProtocolCompliance mockRequestProtocolCompliance, final CacheConfig config, final AsynchronousValidator asyncValidator) { return impl = new CachingExec( mockBackend, mockCache, mockValidityPolicy, mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder, mockResponseProtocolCompliance, mockRequestProtocolCompliance, config, asyncValidator); }
@Before public void setUp() { host = new HttpHost("foo.example.com", 80); route = new HttpRoute(host); body = HttpTestUtils.makeBody(entityLength); request = HttpRequestWrapper.wrap(new BasicHttpRequest("GET", "/foo", HttpVersion.HTTP_1_1)); context = HttpCacheContext.create(); context.setTargetHost(host); originResponse = Proxies.enhanceResponse(HttpTestUtils.make200Response()); config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .build(); cache = new BasicHttpCache(config); mockBackend = EasyMock.createNiceMock(ClientExecChain.class); mockCache = EasyMock.createNiceMock(HttpCache.class); impl = createCachingExecChain(mockBackend, cache, config); }
protected void emptyMockCacheExpectsNoPuts() throws Exception { mockBackend = EasyMock.createNiceMock(ClientExecChain.class); mockCache = EasyMock.createNiceMock(HttpCache.class); impl = new CachingExec(mockBackend, mockCache, config); EasyMock.expect(mockCache.getCacheEntry(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class))) .andReturn(null).anyTimes(); EasyMock.expect(mockCache.getVariantCacheEntriesWithEtags(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class))) .andReturn(new HashMap<String,Variant>()).anyTimes(); mockCache.flushCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)); EasyMock.expectLastCall().anyTimes(); mockCache.flushCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)); EasyMock.expectLastCall().anyTimes(); mockCache.flushInvalidatedCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class)); EasyMock.expectLastCall().anyTimes(); mockCache.flushInvalidatedCacheEntriesFor(EasyMock.isA(HttpHost.class), EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpResponse.class)); EasyMock.expectLastCall().anyTimes(); }
/** * Produces an instance of {@link ClientExecChain} to be used as a main exec. * <p> * Default implementation produces an instance of {@link MainClientExec} * </p> * <p> * For internal use. * </p> * * @since 4.4 */ protected ClientExecChain createMainExec( final HttpRequestExecutor requestExec, final HttpClientConnectionManager connManager, final ConnectionReuseStrategy reuseStrategy, final ConnectionKeepAliveStrategy keepAliveStrategy, final HttpProcessor proxyHttpProcessor, final AuthenticationStrategy targetAuthStrategy, final AuthenticationStrategy proxyAuthStrategy, final UserTokenHandler userTokenHandler) { return new MainClientExec( requestExec, connManager, reuseStrategy, keepAliveStrategy, proxyHttpProcessor, targetAuthStrategy, proxyAuthStrategy, userTokenHandler); }
@SuppressWarnings("unchecked") @Before public void setup() throws Exception { execChain = Mockito.mock(ClientExecChain.class); connManager = Mockito.mock(HttpClientConnectionManager.class); routePlanner = Mockito.mock(HttpRoutePlanner.class); cookieSpecRegistry = Mockito.mock(Lookup.class); authSchemeRegistry = Mockito.mock(Lookup.class); cookieStore = Mockito.mock(CookieStore.class); credentialsProvider = Mockito.mock(CredentialsProvider.class); defaultConfig = RequestConfig.custom().build(); closeable1 = Mockito.mock(Closeable.class); closeable2 = Mockito.mock(Closeable.class); client = new InternalHttpClient(execChain, connManager, routePlanner, cookieSpecRegistry, authSchemeRegistry, cookieStore, credentialsProvider, defaultConfig, Arrays.asList(closeable1, closeable2)); }
public TracingClientExec( ClientExecChain clientExecChain, RedirectStrategy redirectStrategy, boolean redirectHandlingDisabled, Tracer tracer, List<ApacheClientSpanDecorator> spanDecorators) { this.requestExecutor = clientExecChain; this.redirectStrategy = redirectStrategy; this.redirectHandlingDisabled = redirectHandlingDisabled; this.tracer = tracer; this.spanDecorators = new ArrayList<>(spanDecorators); }
@Override protected ClientExecChain decorateProtocolExec(final ClientExecChain protocolExec) { final boolean myHttpClientSurroundCallsWithSubspan = surroundCallsWithSubspan; return new ClientExecChain() { @Override @SuppressWarnings("TryFinallyCanBeTryWithResources") public CloseableHttpResponse execute(HttpRoute route, HttpRequestWrapper request, HttpClientContext clientContext, HttpExecutionAware execAware) throws IOException, HttpException { Tracer tracer = Tracer.getInstance(); Span spanAroundCall = null; if (myHttpClientSurroundCallsWithSubspan) { // Will start a new trace if necessary, or a subspan if a trace is already in progress. spanAroundCall = tracer.startSpanInCurrentContext(getSubspanSpanName(request), SpanPurpose.CLIENT); } try { propagateTracingHeaders(request, tracer.getCurrentSpan()); return protocolExec.execute(route, request, clientContext, execAware); } finally { if (spanAroundCall != null) { // Span.close() contains the logic we want - if the spanAroundCall was an overall span (new // trace) then tracer.completeRequestSpan() will be called, otherwise it's a subspan and // tracer.completeSubSpan() will be called. spanAroundCall.close(); } } } }; }
@DataProvider(value = { "true | true | true", "false | true | true", "true | false | true", "false | false | true", "true | true | false", "false | true | false", "true | false | false", "false | false | false" }, splitBy = "\\|") @Test public void decorateProtocolExec_works_as_expected( boolean subspanOptionOn, boolean parentSpanExists, boolean throwExceptionInInnerChain ) throws IOException, HttpException { // given builder = WingtipsHttpClientBuilder.create(subspanOptionOn); RuntimeException exceptionToThrowInInnerChain = (throwExceptionInInnerChain) ? new RuntimeException("kaboom") : null; SpanCapturingClientExecChain origCec = spy(new SpanCapturingClientExecChain(exceptionToThrowInInnerChain)); Span parentSpan = null; if (parentSpanExists) { parentSpan = Tracer.getInstance().startRequestWithRootSpan("someParentSpan"); } // when ClientExecChain result = builder.decorateProtocolExec(origCec); // then verifyDecoratedClientExecChainPerformsTracingLogic( result, origCec, parentSpan, subspanOptionOn ); }
public CachingExec( final ClientExecChain backend, final ResourceFactory resourceFactory, final HttpCacheStorage storage, final CacheConfig config) { this(backend, new BasicHttpCache(resourceFactory, storage, config), config); }
@SuppressWarnings("unchecked") @Before public void setUp() { mockRequestPolicy = createNiceMock(CacheableRequestPolicy.class); mockValidityPolicy = createNiceMock(CacheValidityPolicy.class); mockBackend = createNiceMock(ClientExecChain.class); mockCache = createNiceMock(HttpCache.class); mockSuitabilityChecker = createNiceMock(CachedResponseSuitabilityChecker.class); mockResponsePolicy = createNiceMock(ResponseCachingPolicy.class); mockHandler = createNiceMock(ResponseHandler.class); mockUriRequest = createNiceMock(HttpUriRequest.class); mockCacheEntry = createNiceMock(HttpCacheEntry.class); mockResponseGenerator = createNiceMock(CachedHttpResponseGenerator.class); mockCachedResponse = createNiceMock(CloseableHttpResponse.class); mockConditionalRequestBuilder = createNiceMock(ConditionalRequestBuilder.class); mockConditionalRequest = createNiceMock(HttpRequest.class); mockStatusLine = createNiceMock(StatusLine.class); mockResponseProtocolCompliance = createNiceMock(ResponseProtocolCompliance.class); mockRequestProtocolCompliance = createNiceMock(RequestProtocolCompliance.class); mockStorage = createNiceMock(HttpCacheStorage.class); config = CacheConfig.DEFAULT; asyncValidator = new AsynchronousValidator(config); host = new HttpHost("foo.example.com", 80); route = new HttpRoute(host); request = HttpRequestWrapper.wrap(new BasicHttpRequest("GET", "/stuff", HttpVersion.HTTP_1_1)); context = HttpCacheContext.create(); context.setTargetHost(host); entry = HttpTestUtils.makeCacheEntry(); impl = createCachingExecChain(mockBackend, mockCache, mockValidityPolicy, mockResponsePolicy, mockResponseGenerator, mockRequestPolicy, mockSuitabilityChecker, mockConditionalRequestBuilder, mockResponseProtocolCompliance, mockRequestProtocolCompliance, config, asyncValidator); }
public abstract ClientExecChain createCachingExecChain(ClientExecChain backend, HttpCache responseCache, CacheValidityPolicy validityPolicy, ResponseCachingPolicy responseCachingPolicy, CachedHttpResponseGenerator responseGenerator, CacheableRequestPolicy cacheableRequestPolicy, CachedResponseSuitabilityChecker suitabilityChecker, ConditionalRequestBuilder conditionalRequestBuilder, ResponseProtocolCompliance responseCompliance, RequestProtocolCompliance requestCompliance, CacheConfig config, AsynchronousValidator asynchRevalidator);
@Override @Before public void setUp() { super.setUp(); config = CacheConfig.custom().setMaxObjectSize(MAX_BYTES).build(); if (CACHE_MANAGER.cacheExists(TEST_EHCACHE_NAME)){ CACHE_MANAGER.removeCache(TEST_EHCACHE_NAME); } CACHE_MANAGER.addCache(TEST_EHCACHE_NAME); final HttpCacheStorage storage = new EhcacheHttpCacheStorage(CACHE_MANAGER.getCache(TEST_EHCACHE_NAME)); mockBackend = EasyMock.createNiceMock(ClientExecChain.class); impl = new CachingExec(mockBackend, new HeapResourceFactory(), storage, config); }
private AsynchronousValidationRequest createAsynchronousValidationRequest(final int errorCount) { final ClientExecChain clientExecChain = mock(ClientExecChain.class); final CachingExec cachingHttpClient = new CachingExec(clientExecChain); final AsynchronousValidator mockValidator = new AsynchronousValidator(impl); final HttpRoute httpRoute = new HttpRoute(new HttpHost("foo.example.com", 80)); final HttpRequestWrapper httpRequestWrapper = HttpRequestWrapper.wrap(new BasicHttpRequest("GET", "/")); final HttpClientContext httpClientContext = new HttpClientContext(); return new AsynchronousValidationRequest(mockValidator, cachingHttpClient, httpRoute, httpRequestWrapper, httpClientContext, null, null, "identifier", errorCount); }
@Before public void setUp() { host = new HttpHost("foo.example.com", 80); route = new HttpRoute(host); body = makeBody(entityLength); request = new BasicHttpRequest("GET", "/foo", HTTP_1_1); context = HttpCacheContext.create(); context.setTargetHost(host); originResponse = Proxies.enhanceResponse(make200Response()); final CacheConfig config = CacheConfig.custom() .setMaxCacheEntries(MAX_ENTRIES) .setMaxObjectSize(MAX_BYTES) .build(); final HttpCache cache = new BasicHttpCache(config); mockBackend = EasyMock.createNiceMock(ClientExecChain.class); mockEntity = EasyMock.createNiceMock(HttpEntity.class); mockCache = EasyMock.createNiceMock(HttpCache.class); impl = createCachingExecChain(mockBackend, cache, config); }
@Override protected ClientExecChain decorateProtocolExec(final ClientExecChain requestExecutor) { return new TracingClientExec(requestExecutor, redirectStrategy, redirectHandlingDisabled, tracer, spanDecorators); }
public ProxyFeedBackClientExecChain(ClientExecChain delegate) { this.delegate = delegate; }
@Override protected ClientExecChain decorateProtocolExec(ClientExecChain protocolExec) { return new ProxyFeedBackClientExecChain(protocolExec); }
/** * For internal use. */ protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) { return mainExec; }
/** * For internal use. */ protected ClientExecChain decorateProtocolExec(final ClientExecChain protocolExec) { return protocolExec; }
private void verifyDecoratedClientExecChainPerformsTracingLogic( ClientExecChain decoratedCec, SpanCapturingClientExecChain origCecSpy, Span parentSpan, boolean expectSubspan ) throws IOException, HttpException { // given HttpRoute httpRoute = new HttpRoute(new HttpHost("localhost")); HttpRequestWrapper requestWrapperSpy = spy(HttpRequestWrapper.wrap(requestMock)); HttpClientContext httpClientContextMock = mock(HttpClientContext.class); HttpExecutionAware httpExecutionAwareMock = mock(HttpExecutionAware.class); assertThat(origCecSpy.capturedSpan).isNull(); assertThat(spanRecorder.completedSpans).isEmpty(); // when CloseableHttpResponse result = null; Throwable exFromChain = null; try { result = decoratedCec.execute( httpRoute, requestWrapperSpy, httpClientContextMock, httpExecutionAwareMock ); } catch(Throwable ex) { exFromChain = ex; } // then verify(origCecSpy).execute(httpRoute, requestWrapperSpy, httpClientContextMock, httpExecutionAwareMock); if (origCecSpy.exceptionToThrow == null) { assertThat(result).isSameAs(origCecSpy.response); } else { assertThat(exFromChain).isSameAs(origCecSpy.exceptionToThrow); } // The only time the capturedSpan should be null is if expectSubspan is false and parentSpan is null, and then // no tracing propagation headers should have been set. // Otherwise, the tracing propagation headers should match capturedSpan. if (origCecSpy.capturedSpan == null) { assertThat(expectSubspan).isFalse(); assertThat(parentSpan).isNull(); verify(requestWrapperSpy, never()).setHeader(anyString(), anyString()); } else { verify(requestWrapperSpy).setHeader(TRACE_ID, origCecSpy.capturedSpan.getTraceId()); verify(requestWrapperSpy).setHeader(SPAN_ID, origCecSpy.capturedSpan.getSpanId()); verify(requestWrapperSpy).setHeader( TRACE_SAMPLED, convertSampleableBooleanToExpectedB3Value(origCecSpy.capturedSpan.isSampleable()) ); if (origCecSpy.capturedSpan.getParentSpanId() == null) { verify(requestWrapperSpy, never()).setHeader(eq(PARENT_SPAN_ID), anyString()); } else { verify(requestWrapperSpy).setHeader(PARENT_SPAN_ID, origCecSpy.capturedSpan.getParentSpanId()); } } // If we have a subspan, then it should have been completed. Otherwise, no spans should have been completed. if (expectSubspan) { assertThat(spanRecorder.completedSpans).containsExactly(origCecSpy.capturedSpan); } else { assertThat(spanRecorder.completedSpans).isEmpty(); } }
public CachingExec( final ClientExecChain backend, final HttpCache cache, final CacheConfig config) { this(backend, cache, config, null); }
public CachingExec(final ClientExecChain backend) { this(backend, new BasicHttpCache(), CacheConfig.DEFAULT); }
@Override protected ClientExecChain decorateMainExec(final ClientExecChain mainExec) { final CacheConfig config = this.cacheConfig != null ? this.cacheConfig : CacheConfig.DEFAULT; // We copy the instance fields to avoid changing them, and rename to avoid accidental use of the wrong version ResourceFactory resourceFactoryCopy = this.resourceFactory; if (resourceFactoryCopy == null) { if (this.cacheDir == null) { resourceFactoryCopy = new HeapResourceFactory(); } else { resourceFactoryCopy = new FileResourceFactory(cacheDir); } } HttpCacheStorage storageCopy = this.storage; if (storageCopy == null) { if (this.cacheDir == null) { storageCopy = new BasicHttpCacheStorage(config); } else { final ManagedHttpCacheStorage managedStorage = new ManagedHttpCacheStorage(config); if (this.deleteCache) { addCloseable(new Closeable() { @Override public void close() throws IOException { managedStorage.shutdown(); } }); } else { addCloseable(managedStorage); } storageCopy = managedStorage; } } final AsynchronousValidator revalidator = createAsynchronousRevalidator(config); final CacheKeyGenerator uriExtractor = new CacheKeyGenerator(); HttpCacheInvalidator cacheInvalidator = this.httpCacheInvalidator; if (cacheInvalidator == null) { cacheInvalidator = new CacheInvalidator(uriExtractor, storageCopy); } return new CachingExec(mainExec, new BasicHttpCache( resourceFactoryCopy, storageCopy, config, uriExtractor, cacheInvalidator), config, revalidator); }
public abstract ClientExecChain createCachingExecChain(ClientExecChain backend, HttpCache cache, CacheConfig config);
@Test public void testIssue1147() throws Exception { final CacheConfig cacheConfig = CacheConfig.custom() .setSharedCache(true) .setMaxObjectSize(262144) //256kb .build(); final ResourceFactory resourceFactory = new FileResourceFactory(cacheDir); final HttpCacheStorage httpCacheStorage = new ManagedHttpCacheStorage(cacheConfig); final ClientExecChain backend = mock(ClientExecChain.class); final HttpRequestWrapper get = HttpRequestWrapper.wrap(new HttpGet("http://somehost/")); final HttpClientContext context = HttpClientContext.create(); final HttpHost target = new HttpHost("somehost", 80); final HttpRoute route = new HttpRoute(target); context.setTargetHost(target); final Date now = new Date(); final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); final HttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_1, 200, "OK"); response.setEntity(HttpTestUtils.makeBody(128)); response.setHeader("Content-Length", "128"); response.setHeader("ETag", "\"etag\""); response.setHeader("Cache-Control", "public, max-age=3600"); response.setHeader("Last-Modified", DateUtils.formatDate(tenSecondsAgo)); when(backend.execute( eq(route), isA(HttpRequestWrapper.class), isA(HttpClientContext.class), (HttpExecutionAware) Matchers.isNull())).thenReturn(Proxies.enhanceResponse(response)); final BasicHttpCache cache = new BasicHttpCache(resourceFactory, httpCacheStorage, cacheConfig); final ClientExecChain t = createCachingExecChain(backend, cache, cacheConfig); final HttpResponse response1 = t.execute(route, get, context, null); Assert.assertEquals(200, response1.getStatusLine().getStatusCode()); IOUtils.consume(response1.getEntity()); verify(backend).execute( eq(route), isA(HttpRequestWrapper.class), isA(HttpClientContext.class), (HttpExecutionAware) Matchers.isNull()); removeCache(); reset(backend); when(backend.execute( eq(route), isA(HttpRequestWrapper.class), isA(HttpClientContext.class), (HttpExecutionAware) Matchers.isNull())).thenReturn(Proxies.enhanceResponse(response)); final HttpResponse response2 = t.execute(route, get, context, null); Assert.assertEquals(200, response2.getStatusLine().getStatusCode()); IOUtils.consume(response2.getEntity()); verify(backend).execute( eq(route), isA(HttpRequestWrapper.class), isA(HttpClientContext.class), (HttpExecutionAware) Matchers.isNull()); }
protected ClientExecChain createCachingExecChain(final ClientExecChain backend, final BasicHttpCache cache, final CacheConfig config) { return new CachingExec(backend, cache, config); }
@Override public CachingExec createCachingExecChain(final ClientExecChain backend, final HttpCache cache, final CacheConfig config) { return impl = new CachingExec(backend, cache, config); }
protected ClientExecChain createCachingExecChain( final ClientExecChain backend, final HttpCache cache, final CacheConfig config) { return new CachingExec(backend, cache, config); }
protected ClientExecChain createCachingExecChain(final ClientExecChain backend, final HttpCache cache, final CacheConfig config) { return new CachingExec(backend, cache, config); }
protected boolean supportsRangeAndContentRangeHeaders(final ClientExecChain impl) { return impl instanceof CachingExec && ((CachingExec) impl).supportsRangeAndContentRangeHeaders(); }