hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject svn commit: r990240 - in /httpcomponents/httpclient/trunk/httpclient-cache/src: main/java/org/apache/http/client/cache/ main/java/org/apache/http/impl/client/cache/ test/java/org/apache/http/impl/client/cache/
Date Fri, 27 Aug 2010 19:35:18 GMT
Author: olegk
Date: Fri Aug 27 19:35:18 2010
New Revision: 990240

URL: http://svn.apache.org/viewvc?rev=990240&view=rev
Log:
HTTPCLIENT-982: HTTP cache response status
Contributed by Jonathan Moore <jonathan_moore at comcast.com>

Added:
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
  (with props)
Modified:
    httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
    httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java?rev=990240&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
(added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
Fri Aug 27 19:35:18 2010
@@ -0,0 +1,54 @@
+/*
+ * ====================================================================
+ * 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.http.client.cache;
+
+/**
+ * This enumeration represents the various ways a response can be generated
+ * by the {@link CachingHttpClient}; if a request is executed with an
+ * {@link org.apache.http.protocol.HttpContext}
+ * then a parameter with one of these values will be registered in the
+ * context.
+ */
+public enum CacheResponseStatus {
+
+    /** The response was generated directly by the caching module. */
+    CACHE_MODULE_RESPONSE,
+
+    /** A response was generated from the cache with no requests sent
+     * upstream.
+     */
+    CACHE_HIT,
+
+    /** The response came from an upstream server. */
+    CACHE_MISS,
+
+    /** The response was generated from the cache after validating the
+     * entry with the origin server.
+     */
+    VALIDATED;
+
+}

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/CacheResponseStatus.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java?rev=990240&r1=990239&r2=990240&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
(original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/impl/client/cache/CachingHttpClient.java
Fri Aug 27 19:35:18 2010
@@ -45,6 +45,7 @@ import org.apache.http.annotation.Thread
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.cache.CacheResponseStatus;
 import org.apache.http.client.cache.HeaderConstants;
 import org.apache.http.client.cache.HttpCache;
 import org.apache.http.client.cache.HttpCacheEntry;
@@ -61,6 +62,8 @@ import org.apache.http.protocol.HttpCont
 @ThreadSafe // So long as the responseCache implementation is threadsafe
 public class CachingHttpClient implements HttpClient {
 
+    public static final String CACHE_RESPONSE_STATUS = "http.cache.response.status";
+
     private final static boolean SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS = false;
 
     private final AtomicLong cacheHits = new AtomicLong();
@@ -352,13 +355,18 @@ public class CachingHttpClient implement
     public HttpResponse execute(HttpHost target, HttpRequest request, HttpContext context)
             throws IOException {
 
+        // default response context
+        setResponseStatus(context, CacheResponseStatus.CACHE_MISS);
+
         if (clientRequestsOurOptions(request)) {
+            setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
             return new OptionsHttp11Response();
         }
 
         List<RequestProtocolError> fatalError = requestCompliance.requestIsFatallyNonCompliant(request);
 
         for (RequestProtocolError error : fatalError) {
+            setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
             return requestCompliance.getErrorForRequest(error);
         }
 
@@ -393,6 +401,7 @@ public class CachingHttpClient implement
         cacheHits.getAndIncrement();
 
         if (suitabilityChecker.canCachedResponseBeUsed(target, request, entry)) {
+            setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
             return responseGenerator.generateResponse(entry);
         }
 
@@ -404,8 +413,10 @@ public class CachingHttpClient implement
             } catch (IOException ioex) {
                 if (validityPolicy.mustRevalidate(entry)
                     || (isSharedCache() && validityPolicy.proxyRevalidate(entry)))
{
+                    setResponseStatus(context, CacheResponseStatus.CACHE_MODULE_RESPONSE);
                     return new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_GATEWAY_TIMEOUT,
"Gateway Timeout");
                 } else {
+                    setResponseStatus(context, CacheResponseStatus.CACHE_HIT);
                     HttpResponse response = responseGenerator.generateResponse(entry);
                     response.addHeader(HeaderConstants.WARNING, "111 Revalidation Failed
- " + ioex.getMessage());
                     log.debug("111 revalidation failed due to exception: " + ioex);
@@ -418,6 +429,12 @@ public class CachingHttpClient implement
         return callBackend(target, request, context);
     }
 
+    private void setResponseStatus(final HttpContext context, final CacheResponseStatus value)
{
+        if (context != null) {
+            context.setAttribute(CACHE_RESPONSE_STATUS, value);
+        }
+    }
+
     public boolean supportsRangeAndContentRangeHeaders() {
         return SUPPORTS_RANGE_AND_CONTENT_RANGE_HEADERS;
     }
@@ -472,6 +489,7 @@ public class CachingHttpClient implement
         int statusCode = backendResponse.getStatusLine().getStatusCode();
         if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) {
             cacheUpdates.getAndIncrement();
+            setResponseStatus(context, CacheResponseStatus.VALIDATED);
             return responseCache.updateCacheEntry(target, request, cacheEntry,
                     backendResponse, requestDate, responseDate);
         }

Modified: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java?rev=990240&r1=990239&r2=990240&view=diff
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java
(original)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/impl/client/cache/TestCachingHttpClient.java
Fri Aug 27 19:35:18 2010
@@ -42,11 +42,15 @@ import org.apache.http.StatusLine;
 import org.apache.http.client.ClientProtocolException;
 import org.apache.http.client.HttpClient;
 import org.apache.http.client.ResponseHandler;
+import org.apache.http.client.cache.CacheResponseStatus;
 import org.apache.http.client.cache.HttpCache;
 import org.apache.http.client.cache.HttpCacheEntry;
+import org.apache.http.client.methods.HttpGet;
 import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.impl.cookie.DateUtils;
 import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.message.BasicHttpResponse;
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.HttpParams;
 import org.apache.http.protocol.BasicHttpContext;
@@ -727,6 +731,178 @@ public class TestCachingHttpClient {
     }
 
     @Test
+    public void testSetsModuleGeneratedResponseContextForCacheOptionsResponse()
+        throws Exception {
+        impl = new CachingHttpClient(mockBackend);
+        HttpRequest req = new BasicHttpRequest("OPTIONS","*",HttpVersion.HTTP_1_1);
+        req.setHeader("Max-Forwards","0");
+
+        impl.execute(host, req, context);
+        Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE,
+                context.getAttribute("http.cache.response.context"));
+    }
+
+    @Test
+    public void testSetsModuleGeneratedResponseContextForFatallyNoncompliantRequest()
+        throws Exception {
+        impl = new CachingHttpClient(mockBackend);
+        HttpRequest req = new HttpGet("http://foo.example.com/");
+        req.setHeader("Range","bytes=0-50");
+        req.setHeader("If-Range","W/\"weak-etag\"");
+
+        impl.execute(host, req, context);
+        Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE,
+                context.getAttribute("http.cache.response.context"));
+    }
+
+    @Test
+    public void testSetsCacheMissContextIfRequestNotServableFromCache()
+        throws Exception {
+        impl = new CachingHttpClient(mockBackend);
+        HttpRequest req = new HttpGet("http://foo.example.com/");
+        req.setHeader("Cache-Control","no-cache");
+        HttpResponse resp = new BasicHttpResponse(HttpVersion.HTTP_1_1, HttpStatus.SC_NO_CONTENT,
"No Content");
+
+        EasyMock.expect(mockBackend.execute(EasyMock.isA(HttpHost.class),
+                EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpContext.class)))
+            .andReturn(resp);
+
+        replayMocks();
+        impl.execute(host, req, context);
+        verifyMocks();
+        Assert.assertEquals(CacheResponseStatus.CACHE_MISS,
+                context.getAttribute("http.cache.response.context"));
+    }
+
+    @Test
+    public void testSetsCacheHitContextIfRequestServedFromCache()
+        throws Exception {
+        impl = new CachingHttpClient(mockBackend);
+        HttpRequest req1 = new HttpGet("http://foo.example.com/");
+        HttpRequest req2 = new HttpGet("http://foo.example.com/");
+        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");
+
+        EasyMock.expect(mockBackend.execute(EasyMock.isA(HttpHost.class),
+                EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpContext.class)))
+            .andReturn(resp1);
+
+        replayMocks();
+        impl.execute(host, req1, new BasicHttpContext());
+        impl.execute(host, req2, context);
+        verifyMocks();
+        Assert.assertEquals(CacheResponseStatus.CACHE_HIT,
+                context.getAttribute("http.cache.response.context"));
+    }
+
+    @Test
+    public void testSetsValidatedContextIfRequestWasSuccessfullyValidated()
+        throws Exception {
+        Date now = new Date();
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+
+        impl = new CachingHttpClient(mockBackend);
+        HttpRequest req1 = new HttpGet("http://foo.example.com/");
+        HttpRequest req2 = new HttpGet("http://foo.example.com/");
+
+        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");
+
+        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");
+
+        EasyMock.expect(mockBackend.execute(EasyMock.isA(HttpHost.class),
+                EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpContext.class)))
+            .andReturn(resp1);
+        EasyMock.expect(mockBackend.execute(EasyMock.isA(HttpHost.class),
+                EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpContext.class)))
+            .andReturn(resp2);
+
+        replayMocks();
+        impl.execute(host, req1, new BasicHttpContext());
+        impl.execute(host, req2, context);
+        verifyMocks();
+        Assert.assertEquals(CacheResponseStatus.VALIDATED,
+                context.getAttribute("http.cache.response.context"));
+    }
+
+    @Test
+    public void testSetsModuleResponseContextIfValidationRequiredButFailed()
+        throws Exception {
+        Date now = new Date();
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+
+        impl = new CachingHttpClient(mockBackend);
+        HttpRequest req1 = new HttpGet("http://foo.example.com/");
+        HttpRequest req2 = new HttpGet("http://foo.example.com/");
+
+        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");
+
+        EasyMock.expect(mockBackend.execute(EasyMock.isA(HttpHost.class),
+                EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpContext.class)))
+            .andReturn(resp1);
+        EasyMock.expect(mockBackend.execute(EasyMock.isA(HttpHost.class),
+                EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpContext.class)))
+            .andThrow(new IOException());
+
+        replayMocks();
+        impl.execute(host, req1, new BasicHttpContext());
+        impl.execute(host, req2, context);
+        verifyMocks();
+        Assert.assertEquals(CacheResponseStatus.CACHE_MODULE_RESPONSE,
+                context.getAttribute("http.cache.response.context"));
+    }
+
+    @Test
+    public void testSetsModuleResponseContextIfValidationFailsButNotRequired()
+        throws Exception {
+        Date now = new Date();
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+
+        impl = new CachingHttpClient(mockBackend);
+        HttpRequest req1 = new HttpGet("http://foo.example.com/");
+        HttpRequest req2 = new HttpGet("http://foo.example.com/");
+
+        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");
+
+        EasyMock.expect(mockBackend.execute(EasyMock.isA(HttpHost.class),
+                EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpContext.class)))
+            .andReturn(resp1);
+        EasyMock.expect(mockBackend.execute(EasyMock.isA(HttpHost.class),
+                EasyMock.isA(HttpRequest.class), EasyMock.isA(HttpContext.class)))
+            .andThrow(new IOException());
+
+        replayMocks();
+        impl.execute(host, req1, new BasicHttpContext());
+        impl.execute(host, req2, context);
+        verifyMocks();
+        Assert.assertEquals(CacheResponseStatus.CACHE_HIT,
+                context.getAttribute("http.cache.response.context"));
+    }
+
+    @Test
     public void testIsSharedCache() {
         Assert.assertTrue(impl.isSharedCache());
     }



Mime
View raw message