@SuppressWarnings("nls") public static void logCacheResponseStatus(String prefix, CacheResponseStatus cacheResponseStatus) { switch (cacheResponseStatus) { case CACHE_HIT: LOGGER.debug(prefix + ":A response was generated from the cache with no requests sent upstream"); break; case CACHE_MODULE_RESPONSE: LOGGER.debug(prefix + ":The response was generated directly by the caching module"); break; case CACHE_MISS: LOGGER.debug(prefix + ":The response came from an upstream server"); break; case VALIDATED: LOGGER.debug(prefix + ":The response was generated from the cache after validating the entry with the origin server"); break; default: break; } }
@Test public void testSetsCacheMissContextIfRequestNotServableFromCache() throws Exception { impl = createCachingExecChain(mockBackend, new BasicHttpCache(), CacheConfig.DEFAULT); final HttpRequestWrapper req = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); req.setHeader("Cache-Control", "no-cache"); final HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_NO_CONTENT, "No Content"); backendExpectsAnyRequestAndReturn(resp); replayMocks(); impl.execute(route, req, context, null); verifyMocks(); Assert.assertEquals(CacheResponseStatus.CACHE_MISS, context.getCacheResponseStatus()); }
@Test public void testSetsCacheHitContextIfRequestServedFromCache() throws Exception { impl = createCachingExecChain(mockBackend, new BasicHttpCache(), CacheConfig.DEFAULT); final HttpRequestWrapper req1 = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); final HttpRequestWrapper req2 = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); final HttpResponse resp1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatDate(new Date())); resp1.setHeader("Cache-Control", "public, max-age=3600"); backendExpectsAnyRequestAndReturn(resp1); replayMocks(); impl.execute(route, req1, context, null); impl.execute(route, req2, context, null); verifyMocks(); Assert.assertEquals(CacheResponseStatus.CACHE_HIT, context.getCacheResponseStatus()); }
private HttpResponse getFatallyNoncompliantResponse(final HttpRequestWrapper request, final HttpContext context) { HttpResponse fatalErrorResponse = null; final List<RequestProtocolError> fatalError = requestCompliance.requestIsFatallyNonCompliant(request); for (final RequestProtocolError error : fatalError) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); fatalErrorResponse = requestCompliance.getErrorForRequest(error); } return fatalErrorResponse; }
private HttpResponse generateCachedResponse(final HttpRequestWrapper request, final HttpContext context, final HttpCacheEntry entry, final Date now) { final HttpResponse cachedResponse; if (request.containsHeader(HeaderConstants.IF_NONE_MATCH) || request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) { cachedResponse = responseGenerator.generateNotModifiedResponse(entry); } else { cachedResponse = responseGenerator.generateResponse(request, entry); } setResponseStatus(context, CacheResponseStatus.CACHE_HIT); if (validityPolicy.getStalenessSecs(entry, now) > 0L) { cachedResponse.addHeader(HeaderConstants.WARNING,"110 localhost \"Response is stale\""); } return cachedResponse; }
private HttpResponse unvalidatedCacheHit( final HttpRequestWrapper request, final HttpContext context, final HttpCacheEntry entry) { final HttpResponse cachedResponse = responseGenerator.generateResponse(request, entry); setResponseStatus(context, CacheResponseStatus.CACHE_HIT); cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\""); return cachedResponse; }
private HttpResponse getFatallyNoncompliantResponse( final HttpRequestWrapper request, final HttpContext context) { HttpResponse fatalErrorResponse = null; final List<RequestProtocolError> fatalError = requestCompliance.requestIsFatallyNonCompliant(request); for (final RequestProtocolError error : fatalError) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); fatalErrorResponse = requestCompliance.getErrorForRequest(error); } return fatalErrorResponse; }
private CloseableHttpResponse generateCachedResponse(final HttpRequestWrapper request, final HttpContext context, final HttpCacheEntry entry, final Date now) { final CloseableHttpResponse cachedResponse; if (request.containsHeader(HeaderConstants.IF_NONE_MATCH) || request.containsHeader(HeaderConstants.IF_MODIFIED_SINCE)) { cachedResponse = responseGenerator.generateNotModifiedResponse(entry); } else { cachedResponse = responseGenerator.generateResponse(request, entry); } setResponseStatus(context, CacheResponseStatus.CACHE_HIT); if (validityPolicy.getStalenessSecs(entry, now) > 0L) { cachedResponse.addHeader(HeaderConstants.WARNING,"110 localhost \"Response is stale\""); } return cachedResponse; }
private CloseableHttpResponse generateGatewayTimeout( final HttpContext context) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); return Proxies.enhanceResponse(new BasicHttpResponse( HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout")); }
private CloseableHttpResponse unvalidatedCacheHit( final HttpRequestWrapper request, final HttpContext context, final HttpCacheEntry entry) { final CloseableHttpResponse cachedResponse = responseGenerator.generateResponse(request, entry); setResponseStatus(context, CacheResponseStatus.CACHE_HIT); cachedResponse.addHeader(HeaderConstants.WARNING, "111 localhost \"Revalidation failed\""); return cachedResponse; }
@Test public void testSetsModuleGeneratedResponseContextForCacheOptionsResponse() throws Exception { impl = createCachingExecChain(mockBackend, new BasicHttpCache(), CacheConfig.DEFAULT); final HttpRequestWrapper req = HttpRequestWrapper.wrap(new BasicHttpRequest("OPTIONS", "*", HttpVersion.HTTP_1_1)); req.setHeader("Max-Forwards", "0"); impl.execute(route, req, context, null); Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE, context.getCacheResponseStatus()); }
@Test public void testSetsModuleGeneratedResponseContextForFatallyNoncompliantRequest() throws Exception { impl = createCachingExecChain(mockBackend, new BasicHttpCache(), CacheConfig.DEFAULT); final HttpRequestWrapper req = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); req.setHeader("Range", "bytes=0-50"); req.setHeader("If-Range", "W/\"weak-etag\""); impl.execute(route, req, context, null); Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE, context.getCacheResponseStatus()); }
@Test public void testSetsValidatedContextIfRequestWasSuccessfullyValidated() throws Exception { final Date now = new Date(); final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); impl = createCachingExecChain(mockBackend, new BasicHttpCache(), CacheConfig.DEFAULT); final HttpRequestWrapper req1 = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); final HttpRequestWrapper req2 = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); final HttpResponse resp1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); final HttpResponse resp2 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); resp2.setEntity(HttpTestUtils.makeBody(128)); resp2.setHeader("Content-Length", "128"); resp2.setHeader("ETag", "\"etag\""); resp2.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); resp2.setHeader("Cache-Control", "public, max-age=5"); backendExpectsAnyRequestAndReturn(resp1); backendExpectsAnyRequestAndReturn(resp2); replayMocks(); impl.execute(route, req1, context, null); impl.execute(route, req2, context, null); verifyMocks(); Assert.assertEquals(CacheResponseStatus.VALIDATED, context.getCacheResponseStatus()); }
@Test public void testSetsModuleResponseContextIfValidationRequiredButFailed() throws Exception { final Date now = new Date(); final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); impl = createCachingExecChain(mockBackend, new BasicHttpCache(), CacheConfig.DEFAULT); final HttpRequestWrapper req1 = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); final HttpRequestWrapper req2 = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); final HttpResponse resp1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5, must-revalidate"); backendExpectsAnyRequestAndReturn(resp1); backendExpectsAnyRequestAndThrows(new IOException()); replayMocks(); impl.execute(route, req1, context, null); impl.execute(route, req2, context, null); verifyMocks(); Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE, context.getCacheResponseStatus()); }
@Test public void testSetsModuleResponseContextIfValidationFailsButNotRequired() throws Exception { final Date now = new Date(); final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); impl = createCachingExecChain(mockBackend, new BasicHttpCache(), CacheConfig.DEFAULT); final HttpRequestWrapper req1 = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); final HttpRequestWrapper req2 = HttpRequestWrapper.wrap(new HttpGet( "http://foo.example.com/")); final HttpResponse resp1 = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); resp1.setEntity(HttpTestUtils.makeBody(128)); resp1.setHeader("Content-Length", "128"); resp1.setHeader("ETag", "\"etag\""); resp1.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); resp1.setHeader("Cache-Control", "public, max-age=5"); backendExpectsAnyRequestAndReturn(resp1); backendExpectsAnyRequestAndThrows(new IOException()); replayMocks(); impl.execute(route, req1, context, null); impl.execute(route, req2, context, null); verifyMocks(); Assert.assertEquals(CacheResponseStatus.CACHE_HIT, context.getCacheResponseStatus()); }
private HttpResponse execute(HttpRequestBase method) throws IOException { HttpCacheContext context = HttpCacheContext.create(); HttpResponse response = httpClient.execute(method, context); try { CacheResponseStatus responseStatus = context.getCacheResponseStatus(); switch (responseStatus) { case CACHE_HIT: SpotifyApi.LOGGER.log( Level.CONFIG, "A response was generated from the cache with no requests sent upstream"); break; case CACHE_MODULE_RESPONSE: SpotifyApi.LOGGER.log( Level.CONFIG, "The response was generated directly by the caching module"); break; case CACHE_MISS: SpotifyApi.LOGGER.log( Level.CONFIG, "The response came from an upstream server"); break; case VALIDATED: SpotifyApi.LOGGER.log( Level.CONFIG, "The response was generated from the cache after validating the entry with the origin server"); break; } } catch (Exception e) { SpotifyApi.LOGGER.log(Level.SEVERE, e.getMessage()); } return response; }
public HttpResponse execute(final HttpHost target, final HttpRequest originalRequest, final HttpContext context) throws IOException { HttpRequestWrapper request; if (originalRequest instanceof HttpRequestWrapper) { request = ((HttpRequestWrapper) originalRequest); } else { request = HttpRequestWrapper.wrap(originalRequest); } final String via = generateViaHeader(originalRequest); // default response context setResponseStatus(context, CacheResponseStatus.CACHE_MISS); if (clientRequestsOurOptions(request)) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); return new OptionsHttp11Response(); } final HttpResponse fatalErrorResponse = getFatallyNoncompliantResponse( request, context); if (fatalErrorResponse != null) { return fatalErrorResponse; } requestCompliance.makeRequestCompliant(request); request.addHeader("Via",via); flushEntriesInvalidatedByRequest(target, request); if (!cacheableRequestPolicy.isServableFromCache(request)) { log.debug("Request is not servable from cache"); return callBackend(target, request, context); } final HttpCacheEntry entry = satisfyFromCache(target, request); if (entry == null) { log.debug("Cache miss"); return handleCacheMiss(target, request, context); } return handleCacheHit(target, request, context, entry); }
private void recordCacheUpdate(final HttpContext context) { cacheUpdates.getAndIncrement(); setResponseStatus(context, CacheResponseStatus.VALIDATED); }
private HttpResponse generateGatewayTimeout(final HttpContext context) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); return new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT, "Gateway Timeout"); }
private void setResponseStatus(final HttpContext context, final CacheResponseStatus value) { if (context != null) { context.setAttribute(CACHE_RESPONSE_STATUS, value); } }
@Override public CloseableHttpResponse execute( final HttpRoute route, final HttpRequestWrapper request, final HttpClientContext context, final HttpExecutionAware execAware) throws IOException, HttpException { final HttpHost target = context.getTargetHost(); final String via = generateViaHeader(request.getOriginal()); // default response context setResponseStatus(context, CacheResponseStatus.CACHE_MISS); if (clientRequestsOurOptions(request)) { setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE); return Proxies.enhanceResponse(new OptionsHttp11Response()); } final HttpResponse fatalErrorResponse = getFatallyNoncompliantResponse(request, context); if (fatalErrorResponse != null) { return Proxies.enhanceResponse(fatalErrorResponse); } requestCompliance.makeRequestCompliant(request); request.addHeader("Via",via); flushEntriesInvalidatedByRequest(context.getTargetHost(), request); if (!cacheableRequestPolicy.isServableFromCache(request)) { log.debug("Request is not servable from cache"); return callBackend(route, request, context, execAware); } final HttpCacheEntry entry = satisfyFromCache(target, request); if (entry == null) { log.debug("Cache miss"); return handleCacheMiss(route, request, context, execAware); } else { return handleCacheHit(route, request, context, execAware, entry); } }
private void setResponseStatus(final HttpContext context, final CacheResponseStatus value) { if (context != null) { context.setAttribute(HttpCacheContext.CACHE_RESPONSE_STATUS, value); } }
public static CacheResponseStatus getCacheResponseStatus() { return cacheResponseStatus; }