private void assertEquivalent(final HttpCacheEntry entry, final HttpCacheEntry resultEntry) throws IOException { /* Ugh. Implementing HttpCacheEntry#equals is problematic * due to the Resource response body (may cause unexpected * I/O to users). Checking that two entries * serialize to the same bytes seems simpler, on the whole, * (while still making for a somewhat yucky test). At * least we encapsulate it off here in its own method so * the test that uses it remains clear. */ final DefaultHttpCacheEntrySerializer ser = new DefaultHttpCacheEntrySerializer(); final ByteArrayOutputStream bos1 = new ByteArrayOutputStream(); ser.writeTo(entry, bos1); final byte[] bytes1 = bos1.toByteArray(); final ByteArrayOutputStream bos2 = new ByteArrayOutputStream(); ser.writeTo(resultEntry, bos2); final byte[] bytes2 = bos2.toByteArray(); assertEquals(bytes1.length, bytes2.length); for(int i = 0; i < bytes1.length; i++) { assertEquals(bytes1[i], bytes2[i]); } }
private boolean revalidationResponseIsTooOld(final HttpResponse backendResponse, final HttpCacheEntry cacheEntry) { final Header entryDateHeader = cacheEntry.getFirstHeader(HTTP.DATE_HEADER); final Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER); if (entryDateHeader != null && responseDateHeader != null) { try { final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue()); final Date respDate = DateUtils.parseDate(responseDateHeader.getValue()); if (respDate.before(entryDate)) { return true; } } catch (final DateParseException e) { // either backend response or cached entry did not have a valid // Date header, so we can't tell if they are out of order // according to the origin clock; thus we can skip the // unconditional retry recommended in 13.2.6 of RFC 2616. } } return false; }
/** * Used internally by {@link AsynchronousValidator} to schedule a * revalidation. * @param request * @param context * @param cacheEntry * @param identifier * @param consecutiveFailedAttempts */ AsynchronousValidationRequest( final AsynchronousValidator parent, final CachingExec cachingExec, final HttpRoute route, final HttpRequestWrapper request, final HttpClientContext context, final HttpExecutionAware execAware, final HttpCacheEntry cacheEntry, final String identifier, final int consecutiveFailedAttempts) { this.parent = parent; this.cachingExec = cachingExec; this.route = route; this.request = request; this.context = context; this.execAware = execAware; this.cacheEntry = cacheEntry; this.identifier = identifier; this.consecutiveFailedAttempts = consecutiveFailedAttempts; }
@Test public void testResponseIsNotFreshIfFreshnessLifetimeEqualsCurrentAge() { final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(); impl = new CacheValidityPolicy() { @Override public long getCurrentAgeSecs(final HttpCacheEntry e, final Date d) { assertEquals(now, d); assertSame(entry, e); return 6; } @Override public long getFreshnessLifetimeSecs(final HttpCacheEntry e) { assertSame(entry, e); return 6; } }; assertFalse(impl.isResponseFresh(entry, now)); }
/** * Schedules an asynchronous revalidation * * @param target * @param request * @param context * @param entry */ public synchronized void revalidateCacheEntry(final HttpHost target, final HttpRequestWrapper request, final HttpContext context, final HttpCacheEntry entry) { // getVariantURI will fall back on getURI if no variants exist final String uri = cacheKeyGenerator.getVariantURI(target, request, entry); if (!queued.contains(uri)) { final AsynchronousValidationRequest revalidationRequest = new AsynchronousValidationRequest(this, cachingClient, target, request, context, entry, uri); try { executor.execute(revalidationRequest); queued.add(uri); } catch (final RejectedExecutionException ree) { log.debug("Revalidation for [" + uri + "] not scheduled: " + ree); } } }
private CloseableHttpResponse revalidateCacheEntry( final HttpRoute route, final HttpRequestWrapper request, final HttpClientContext context, final HttpExecutionAware execAware, final HttpCacheEntry entry, final Date now) throws HttpException { try { if (asynchRevalidator != null && !staleResponseNotAllowed(request, entry, now) && validityPolicy.mayReturnStaleWhileRevalidating(entry, now)) { log.trace("Serving stale with asynchronous revalidation"); final CloseableHttpResponse resp = generateCachedResponse(request, context, entry, now); asynchRevalidator.revalidateCacheEntry(this, route, request, context, execAware, entry); return resp; } return revalidateCacheEntry(route, request, context, execAware, entry); } catch (final IOException ioex) { return handleRevalidationFailure(request, context, entry, now); } }
private boolean explicitFreshnessRequest( final HttpRequestWrapper request, final HttpCacheEntry entry, final Date now) { for(final Header h : request.getHeaders(HeaderConstants.CACHE_CONTROL)) { for(final HeaderElement elt : h.getElements()) { if (HeaderConstants.CACHE_CONTROL_MAX_STALE.equals(elt.getName())) { try { final int maxstale = Integer.parseInt(elt.getValue()); final long age = validityPolicy.getCurrentAgeSecs(entry, now); final long lifetime = validityPolicy.getFreshnessLifetimeSecs(entry); if (age - lifetime > maxstale) { return true; } } catch (final NumberFormatException nfe) { return true; } } else if (HeaderConstants.CACHE_CONTROL_MIN_FRESH.equals(elt.getName()) || HeaderConstants.CACHE_CONTROL_MAX_AGE.equals(elt.getName())) { return true; } } } return false; }
private boolean revalidationResponseIsTooOld(final HttpResponse backendResponse, final HttpCacheEntry cacheEntry) { final Header entryDateHeader = cacheEntry.getFirstHeader(HTTP.DATE_HEADER); final Header responseDateHeader = backendResponse.getFirstHeader(HTTP.DATE_HEADER); if (entryDateHeader != null && responseDateHeader != null) { final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue()); final Date respDate = DateUtils.parseDate(responseDateHeader.getValue()); if (entryDate == null || respDate == null) { // either backend response or cached entry did not have a valid // Date header, so we can't tell if they are out of order // according to the origin clock; thus we can skip the // unconditional retry recommended in 13.2.6 of RFC 2616. return false; } if (respDate.before(entryDate)) { return true; } } return false; }
@Test public void testHeadersAreMergedCorrectly() throws IOException { final Header[] headers = { new BasicHeader("Date", DateUtils.formatDate(responseDate)), new BasicHeader("ETag", "\"etag\"")}; entry = HttpTestUtils.makeCacheEntry(headers); response.setHeaders(new Header[]{}); final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry, new Date(), new Date(), response); final Header[] updatedHeaders = updatedEntry.getAllHeaders(); assertEquals(2, updatedHeaders.length); headersContain(updatedHeaders, "Date", DateUtils.formatDate(responseDate)); headersContain(updatedHeaders, "ETag", "\"etag\""); }
@Test public void flushesEntryIfFresherAndSpecifiedByNonCanonicalContentLocation() throws Exception { response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatDate(now)); final String cacheKey = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", "http://foo.example.com/bar"); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)), new BasicHeader("ETag", "\"old-etag\"") }); when(mockStorage.getEntry(cacheKey)).thenReturn(entry); impl.flushInvalidatedCacheEntries(host, request, response); verify(mockStorage).getEntry(cacheKey); verify(mockStorage).removeEntry(cacheKey); }
public boolean mayReturnStaleWhileRevalidating(final HttpCacheEntry entry, final Date now) { for (final Header h : entry.getHeaders(HeaderConstants.CACHE_CONTROL)) { for(final HeaderElement elt : h.getElements()) { if (HeaderConstants.STALE_WHILE_REVALIDATE.equalsIgnoreCase(elt.getName())) { try { final int allowedStalenessLifetime = Integer.parseInt(elt.getValue()); if (getStalenessSecs(entry, now) <= allowedStalenessLifetime) { return true; } } catch (final NumberFormatException nfe) { // skip malformed directive } } } } return false; }
@Test public void testTreatsNullFromMemcachedAsCacheMiss() throws UnsupportedEncodingException, IOException { final String url = "foo"; final String key = "key"; when(mockKeyHashingScheme.hash(url)).thenReturn(key); when(mockMemcachedClient.get(key)).thenReturn(null); final HttpCacheEntry resultingEntry = impl.getEntry(url); verify(mockKeyHashingScheme).hash(url); verify(mockMemcachedClient).get(key); assertNull(resultingEntry); }
@Test public void testSuccessfulCachePut() throws IOException { final String url = "foo"; final String key = "key"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); final byte[] serialized = HttpTestUtils.getRandomBytes(128); when(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, value)) .thenReturn(mockMemcachedCacheEntry); when(mockMemcachedCacheEntry.toByteArray()) .thenReturn(serialized); when(mockKeyHashingScheme.hash(url)) .thenReturn(key); when(mockMemcachedClient.set(key, 0, serialized)) .thenReturn(null); impl.putEntry(url, value); verify(mockMemcachedCacheEntryFactory).getMemcachedCacheEntry(url, value); verify(mockMemcachedCacheEntry).toByteArray(); verify(mockKeyHashingScheme).hash(url); verify(mockMemcachedClient).set(key, 0, serialized); }
@Test public void testGetCacheEntryReturnsVariantIfPresentInCache() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest request = new HttpGet("http://foo.example.com/bar"); request.setHeader("Accept-Encoding","gzip"); final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar"); origRequest.setHeader("Accept-Encoding","gzip"); final HttpResponse origResponse = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_OK, "OK"); origResponse.setEntity(HttpTestUtils.makeBody(128)); origResponse.setHeader("Date", DateUtils.formatDate(new Date())); origResponse.setHeader("Cache-Control", "max-age=3600, public"); origResponse.setHeader("ETag", "\"etag\""); origResponse.setHeader("Vary", "Accept-Encoding"); origResponse.setHeader("Content-Encoding","gzip"); impl.cacheAndReturnResponse(host, origRequest, origResponse, new Date(), new Date()); final HttpCacheEntry result = impl.getCacheEntry(host, request); assertNotNull(result); }
@Test public void testCacheGet() throws IOException { final String key = "foo"; final HttpCacheEntry cachedValue = HttpTestUtils.makeCacheEntry(); final Element element = new Element(key, new byte[]{}); when(mockCache.get(key)) .thenReturn(element); when(mockSerializer.readFrom(isA(InputStream.class))) .thenReturn(cachedValue); final HttpCacheEntry resultingEntry = impl.getEntry(key); verify(mockCache).get(key); verify(mockSerializer).readFrom(isA(InputStream.class)); assertSame(cachedValue, resultingEntry); }
@Test public void doesNotFlushEntrySpecifiedByContentLocationIfOlder() throws Exception { response.setHeader("ETag","\"new-etag\""); response.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); final String theURI = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", theURI); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatDate(now)), new BasicHeader("ETag", "\"old-etag\"") }); when(mockStorage.getEntry(theURI)).thenReturn(entry); impl.flushInvalidatedCacheEntries(host, request, response); verify(mockStorage).getEntry(theURI); verifyNoMoreInteractions(mockStorage); }
@Test public void testFlushContentLocationEntryIfUnSafeRequest() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpPost("/foo"); final HttpResponse resp = HttpTestUtils.make200Response(); resp.setHeader("Content-Location", "/bar"); resp.setHeader(HeaderConstants.ETAG, "\"etag\""); final String key = (new CacheKeyGenerator()).getURI(host, new HttpGet("/bar")); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatDate(new Date())), new BasicHeader("ETag", "\"old-etag\"") }); backing.map.put(key, entry); impl.flushInvalidatedCacheEntriesFor(host, req, resp); assertNull(backing.map.get(key)); }
@Test public void testCachePutThrowsIOExceptionIfCannotSerializeEntry() { final String url = "foo"; final String key = "key"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); when(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, value)) .thenReturn(mockMemcachedCacheEntry); when(mockMemcachedCacheEntry.toByteArray()) .thenThrow(new MemcachedSerializationException(new Exception())); try { impl.putEntry(url, value); fail("should have thrown exception"); } catch (final IOException expected) { } verify(mockMemcachedCacheEntryFactory).getMemcachedCacheEntry(url, value); verify(mockMemcachedCacheEntry).toByteArray(); }
HttpCacheEntry doGetUpdatedParentEntry( final String requestId, final HttpCacheEntry existing, final HttpCacheEntry entry, final String variantKey, final String variantCacheKey) throws IOException { HttpCacheEntry src = existing; if (src == null) { src = entry; } Resource resource = null; if (src.getResource() != null) { resource = resourceFactory.copy(requestId, src.getResource()); } final Map<String,String> variantMap = new HashMap<String,String>(src.getVariantMap()); variantMap.put(variantKey, variantCacheKey); return new HttpCacheEntry( src.getRequestDate(), src.getResponseDate(), src.getStatusLine(), src.getAllHeaders(), resource, variantMap, src.getRequestMethod()); }
@Test public void testCachePutFailsSilentlyWhenWeCannotHashAKey() throws IOException { final String url = "foo"; final HttpCacheEntry value = HttpTestUtils.makeCacheEntry(); final byte[] serialized = HttpTestUtils.getRandomBytes(128); when(mockMemcachedCacheEntryFactory.getMemcachedCacheEntry(url, value)) .thenReturn(mockMemcachedCacheEntry); when(mockMemcachedCacheEntry.toByteArray()) .thenReturn(serialized); when(mockKeyHashingScheme.hash(url)) .thenThrow(new MemcachedKeyHashingException(new Exception())); impl.putEntry(url, value); verify(mockMemcachedCacheEntryFactory).getMemcachedCacheEntry(url, value); verify(mockMemcachedCacheEntry).toByteArray(); verify(mockKeyHashingScheme).hash(url); }
@Test public void testSuccessfulCacheGet() throws UnsupportedEncodingException, IOException { final String url = "foo"; final String key = "key"; final byte[] serialized = HttpTestUtils.getRandomBytes(128); final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(); when(mockKeyHashingScheme.hash(url)).thenReturn(key); when(mockMemcachedClient.get(key)).thenReturn(serialized); when(mockMemcachedCacheEntryFactory.getUnsetCacheEntry()) .thenReturn(mockMemcachedCacheEntry); when(mockMemcachedCacheEntry.getStorageKey()).thenReturn(url); when(mockMemcachedCacheEntry.getHttpCacheEntry()).thenReturn(cacheEntry); final HttpCacheEntry resultingEntry = impl.getEntry(url); verify(mockKeyHashingScheme).hash(url); verify(mockMemcachedClient).get(key); verify(mockMemcachedCacheEntryFactory).getUnsetCacheEntry(); verify(mockMemcachedCacheEntry).set(serialized); verify(mockMemcachedCacheEntry).getStorageKey(); verify(mockMemcachedCacheEntry).getHttpCacheEntry(); assertSame(cacheEntry, resultingEntry); }
private HttpCacheEntry makeCacheEntryWithVariantMap() { final Header[] headers = new Header[5]; for (int i = 0; i < headers.length; i++) { headers[i] = new BasicHeader("header" + i, "value" + i); } final String body = "Lorem ipsum dolor sit amet"; final ProtocolVersion pvObj = new ProtocolVersion("HTTP", 1, 1); final StatusLine slObj = new BasicStatusLine(pvObj, 200, "ok"); final Map<String,String> variantMap = new HashMap<String,String>(); variantMap.put("test variant 1","true"); variantMap.put("test variant 2","true"); final HttpCacheEntry cacheEntry = new HttpCacheEntry(new Date(), new Date(), slObj, headers, new HeapResource(Base64.decodeBase64(body .getBytes(UTF8))), variantMap, HeaderConstants.GET_METHOD); return cacheEntry; }
@Test public void flushesEntrySpecifiedByContentLocationIfResponseHasNoDate() throws Exception { response.setHeader("ETag", "\"new-etag\""); response.removeHeaders("Date"); final String theURI = "http://foo.example.com:80/bar"; response.setHeader("Content-Location", theURI); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", "\"old-etag\""), new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)), }); when(mockStorage.getEntry(theURI)).thenReturn(entry); impl.flushInvalidatedCacheEntries(host, request, response); verify(mockStorage).getEntry(theURI); verify(mockStorage).removeEntry(theURI); verifyNoMoreInteractions(mockStorage); }
@Test public void testDoNotFlushContentLocationEntryIfSafeRequest() throws Exception { final HttpHost host = new HttpHost("foo.example.com"); final HttpRequest req = new HttpGet("/foo"); final HttpResponse resp = HttpTestUtils.make200Response(); resp.setHeader("Content-Location", "/bar"); final String key = (new CacheKeyGenerator()).getURI(host, new HttpGet("/bar")); final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("Date", DateUtils.formatDate(new Date())), new BasicHeader("ETag", "\"old-etag\"") }); backing.map.put(key, entry); impl.flushInvalidatedCacheEntriesFor(host, req, resp); assertEquals(entry, backing.map.get(key)); }
/** * Check entry against If-None-Match * @param request The current httpRequest being made * @param entry the cache entry * @return boolean does the etag validator match */ private boolean etagValidatorMatches(final HttpRequest request, final HttpCacheEntry entry) { final Header etagHeader = entry.getFirstHeader(HeaderConstants.ETAG); final String etag = (etagHeader != null) ? etagHeader.getValue() : null; final Header[] ifNoneMatch = request.getHeaders(HeaderConstants.IF_NONE_MATCH); if (ifNoneMatch != null) { for (final Header h : ifNoneMatch) { for (final HeaderElement elt : h.getElements()) { final String reqEtag = elt.toString(); if (("*".equals(reqEtag) && etag != null) || reqEtag.equals(etag)) { return true; } } } } return false; }
/** * Schedules an asynchronous revalidation */ public synchronized void revalidateCacheEntry( final CachingExec cachingExec, final HttpRoute route, final HttpRequestWrapper request, final HttpClientContext context, final HttpExecutionAware execAware, final HttpCacheEntry entry) { // getVariantURI will fall back on getURI if no variants exist final String uri = cacheKeyGenerator.getVariantURI(context.getTargetHost(), request, entry); if (!queued.contains(uri)) { final int consecutiveFailedAttempts = failureCache.getErrorCount(uri); final AsynchronousValidationRequest revalidationRequest = new AsynchronousValidationRequest( this, cachingExec, route, request, context, execAware, entry, uri, consecutiveFailedAttempts); try { schedulingStrategy.schedule(revalidationRequest); queued.add(uri); } catch (final RejectedExecutionException ree) { log.debug("Revalidation for [" + uri + "] not scheduled: " + ree); } } }
protected Header[] mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) { if (entryAndResponseHaveDateHeader(entry, response) && entryDateHeaderNewerThenResponse(entry, response)) { // Don't merge headers, keep the entry's headers as they are newer. return entry.getAllHeaders(); } final List<Header> cacheEntryHeaderList = new ArrayList<Header>(Arrays.asList(entry .getAllHeaders())); removeCacheHeadersThatMatchResponse(cacheEntryHeaderList, response); removeCacheEntry1xxWarnings(cacheEntryHeaderList, entry); cacheEntryHeaderList.addAll(Arrays.asList(response.getAllHeaders())); return cacheEntryHeaderList.toArray(new Header[cacheEntryHeaderList.size()]); }
/** * Update the entry with the new information from the response. Should only be used for * 304 responses. * * @param requestId * @param entry The cache Entry to be updated * @param requestDate When the request was performed * @param responseDate When the response was gotten * @param response The HttpResponse from the backend server call * @return HttpCacheEntry an updated version of the cache entry * @throws java.io.IOException if something bad happens while trying to read the body from the original entry */ public HttpCacheEntry updateCacheEntry( final String requestId, final HttpCacheEntry entry, final Date requestDate, final Date responseDate, final HttpResponse response) throws IOException { Args.check(response.getStatusLine().getStatusCode() == HttpStatus.SC_NOT_MODIFIED, "Response must have 304 status code"); final Header[] mergedHeaders = mergeHeaders(entry, response); Resource resource = null; if (entry.getResource() != null) { resource = resourceFactory.copy(requestId, entry.getResource()); } return new HttpCacheEntry( requestDate, responseDate, entry.getStatusLine(), mergedHeaders, resource, entry.getRequestMethod()); }
private void removeCacheEntry1xxWarnings(final List<Header> cacheEntryHeaderList, final HttpCacheEntry entry) { final ListIterator<Header> cacheEntryHeaderListIter = cacheEntryHeaderList.listIterator(); while (cacheEntryHeaderListIter.hasNext()) { final String cacheEntryHeaderName = cacheEntryHeaderListIter.next().getName(); if (HeaderConstants.WARNING.equals(cacheEntryHeaderName)) { for (final Header cacheEntryWarning : entry.getHeaders(HeaderConstants.WARNING)) { if (cacheEntryWarning.getValue().startsWith("1")) { cacheEntryHeaderListIter.remove(); } } } } }
private void saveCacheEntry(String url, HttpCacheEntry entry) { ObjectOutputStream stream = null; try { final File cache = getCacheFile(url); stream = new ObjectOutputStream(new FileOutputStream(cache)); stream.writeObject(entry); stream.close(); } catch (final Exception e) { LOGGER.error("Faled to save cache entry " + entry, e); } }
@Test public void testCacheEntryWithProxyRevalidateDoesEndToEndRevalidation() throws Exception { final HttpRequest basicRequest = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1); final HttpRequestWrapper requestWrapper = HttpRequestWrapper.wrap(basicRequest); final Date now = new Date(); final Date elevenSecondsAgo = new Date(now.getTime() - 11 * 1000L); final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); final Date nineSecondsAgo = new Date(now.getTime() - 9 * 1000L); final Header[] cacheEntryHeaders = new Header[] { new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)), new BasicHeader("ETag", "\"etag\""), new BasicHeader("Cache-Control","max-age=5, proxy-revalidate") }; final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders); final HttpRequest result = impl.buildConditionalRequest(requestWrapper, cacheEntry); boolean foundMaxAge0 = false; for(final Header h : result.getHeaders("Cache-Control")) { for(final HeaderElement elt : h.getElements()) { if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) { foundMaxAge0 = true; } } } Assert.assertTrue(foundMaxAge0); }
@Test public void testMayNotReturnStaleIfErrorInResponseAndAfterResponseWindow(){ final Header[] headers = new Header[] { new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)), new BasicHeader("Cache-Control", "max-age=5, stale-if-error=1") }; final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(now, now, headers); final HttpRequest req = new BasicHttpRequest("GET","/",HttpVersion.HTTP_1_1); assertFalse(impl.mayReturnStaleIfError(req, entry, now)); }
private HttpCacheEntry satisfyFromCache(final HttpHost target, final HttpRequestWrapper request) { HttpCacheEntry entry = null; try { entry = responseCache.getCacheEntry(target, request); } catch (final IOException ioe) { log.warn("Unable to retrieve entries from cache", ioe); } return entry; }
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 handleRevalidationFailure(final HttpRequestWrapper request, final HttpContext context, final HttpCacheEntry entry, final Date now) { if (staleResponseNotAllowed(request, entry, now)) { return generateGatewayTimeout(context); } else { return unvalidatedCacheHit(request, context, entry); } }
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 boolean responseDateOlderThanEntryDate(final HttpResponse response, final HttpCacheEntry entry) { final Header entryDateHeader = entry.getFirstHeader(HTTP.DATE_HEADER); final Header responseDateHeader = response.getFirstHeader(HTTP.DATE_HEADER); if (entryDateHeader == null || responseDateHeader == null) { /* be conservative; should probably flush */ return false; } final Date entryDate = DateUtils.parseDate(entryDateHeader.getValue()); final Date responseDate = DateUtils.parseDate(responseDateHeader.getValue()); if (entryDate == null || responseDate == null) { return false; } return responseDate.before(entryDate); }
@Override public synchronized void updateEntry(final String key, final HttpCacheUpdateCallback callback) throws IOException, HttpCacheUpdateException { int numRetries = 0; do{ final Element oldElement = cache.get(key); HttpCacheEntry existingEntry = null; if(oldElement != null){ final byte[] data = (byte[])oldElement.getValue(); existingEntry = serializer.readFrom(new ByteArrayInputStream(data)); } final HttpCacheEntry updatedEntry = callback.update(existingEntry); if (existingEntry == null) { putEntry(key, updatedEntry); return; } else { // Attempt to do a CAS replace, if we fail then retry // While this operation should work fine within this instance, multiple instances // could trample each others' data final ByteArrayOutputStream bos = new ByteArrayOutputStream(); serializer.writeTo(updatedEntry, bos); final Element newElement = new Element(key, bos.toByteArray()); if (cache.replace(oldElement, newElement)) { return; }else{ numRetries++; } } }while(numRetries <= maxUpdateRetries); throw new HttpCacheUpdateException("Failed to update"); }
@Override public synchronized HttpCacheEntry getEntry(final String key) throws IOException { final Element e = cache.get(key); if(e == null){ return null; } final byte[] data = (byte[])e.getValue(); return serializer.readFrom(new ByteArrayInputStream(data)); }
@Test public void entry1xxWarningsAreRemovedOnUpdate() throws Exception { final Header[] headers = { new BasicHeader("Warning", "110 fred \"Response is stale\""), new BasicHeader("ETag", "\"old\""), new BasicHeader("Date", DateUtils.formatDate(eightSecondsAgo)) }; entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, headers); response.setHeader("ETag", "\"new\""); response.setHeader("Date", DateUtils.formatDate(twoSecondsAgo)); final HttpCacheEntry updated = impl.updateCacheEntry(null, entry, twoSecondsAgo, oneSecondAgo, response); assertEquals(0, updated.getHeaders("Warning").length); }