You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2010/04/30 23:00:10 UTC

svn commit: r939814 [3/6] - in /httpcomponents/httpclient/trunk: ./ httpclient-cache/ httpclient-cache/src/ httpclient-cache/src/main/ httpclient-cache/src/main/java/ httpclient-cache/src/main/java/org/ httpclient-cache/src/main/java/org/apache/ httpcl...

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/impl/URIExtractor.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/impl/URIExtractor.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/impl/URIExtractor.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/main/java/org/apache/http/client/cache/impl/URIExtractor.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,101 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+
+/**
+ * @since 4.1
+ */
+public class URIExtractor {
+
+    public String getURI(HttpHost host, HttpRequest req) {
+        return String.format("%s%s", host.toString(), req.getRequestLine().getUri());
+    }
+
+    protected String getFullHeaderValue(Header[] headers) {
+        if (headers == null)
+            return "";
+
+        StringBuilder buf = new StringBuilder("");
+        boolean first = true;
+        for (Header hdr : headers) {
+            if (!first) {
+                buf.append(", ");
+            }
+            buf.append(hdr.getValue().trim());
+            first = false;
+
+        }
+        return buf.toString();
+    }
+
+    public String getVariantURI(HttpHost host, HttpRequest req, CacheEntry entry) {
+        Header[] varyHdrs = entry.getHeaders(HeaderConstants.VARY);
+        if (varyHdrs == null || varyHdrs.length == 0) {
+            return getURI(host, req);
+        }
+
+        List<String> variantHeaderNames = new ArrayList<String>();
+        for (Header varyHdr : varyHdrs) {
+            for (HeaderElement elt : varyHdr.getElements()) {
+                variantHeaderNames.add(elt.getName());
+            }
+        }
+        Collections.sort(variantHeaderNames);
+
+        try {
+            StringBuilder buf = new StringBuilder("{");
+            boolean first = true;
+            for (String headerName : variantHeaderNames) {
+                if (!first) {
+                    buf.append("&");
+                }
+                buf.append(URLEncoder.encode(headerName, "UTF-8"));
+                buf.append("=");
+                buf.append(URLEncoder.encode(getFullHeaderValue(req.getHeaders(headerName)),
+                        "UTF-8"));
+                first = false;
+            }
+            buf.append("}");
+            buf.append(getURI(host, req));
+            return buf.toString();
+        } catch (UnsupportedEncodingException uee) {
+            throw new RuntimeException("couldn't encode to UTF-8", uee);
+        }
+    }
+    
+}
\ No newline at end of file

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/Counter.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/Counter.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/Counter.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/Counter.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,41 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+public class Counter {
+
+    private int count;
+
+    public void incr() {
+        count++;
+    }
+
+    public int getCount() {
+        return count;
+    }
+
+}
\ No newline at end of file

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/DoNotTestProtocolRequirements.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/DoNotTestProtocolRequirements.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/DoNotTestProtocolRequirements.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/DoNotTestProtocolRequirements.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,187 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.util.Date;
+import java.util.Random;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.NameValuePair;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.ClientProtocolException;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.cache.HttpCache;
+import org.apache.http.client.cache.impl.BasicHttpCache;
+import org.apache.http.client.cache.impl.CachingHttpClient;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.message.BasicHttpRequest;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.protocol.HttpContext;
+import org.easymock.classextension.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class DoNotTestProtocolRequirements {
+
+    private static ProtocolVersion HTTP_1_1 = new ProtocolVersion("HTTP", 1, 1);
+
+    private static int MAX_BYTES = 1024;
+    private static int MAX_ENTRIES = 100;
+
+    private HttpHost host;
+    private HttpEntity mockEntity;
+    private HttpClient mockBackend;
+    private HttpCache<CacheEntry> mockCache;
+    private HttpRequest request;
+    private HttpResponse originResponse;
+
+    private CachingHttpClient impl;
+
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        host = new HttpHost("foo.example.com");
+
+        request = new BasicHttpRequest("GET", "/foo", HTTP_1_1);
+
+        originResponse = make200Response();
+
+        HttpCache<CacheEntry> cache = new BasicHttpCache(MAX_ENTRIES);
+        mockBackend = EasyMock.createMock(HttpClient.class);
+        mockEntity = EasyMock.createMock(HttpEntity.class);
+        mockCache = EasyMock.createMock(HttpCache.class);
+        impl = new CachingHttpClient(mockBackend, cache, MAX_BYTES);
+    }
+
+    private HttpResponse make200Response() {
+        HttpResponse out = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_OK, "OK");
+        out.setHeader("Date", DateUtils.formatDate(new Date()));
+        out.setHeader("Server", "MockOrigin/1.0");
+        out.setEntity(makeBody(128));
+        return out;
+    }
+
+    private void replayMocks() {
+        EasyMock.replay(mockBackend);
+        EasyMock.replay(mockCache);
+        EasyMock.replay(mockEntity);
+    }
+
+    private HttpEntity makeBody(int nbytes) {
+        byte[] bytes = new byte[nbytes];
+        (new Random()).nextBytes(bytes);
+        return new ByteArrayEntity(bytes);
+    }
+
+    /*
+     * "10.2.7 206 Partial Content ... The response MUST include the following
+     * header fields:
+     *
+     * - Either a Content-Range header field (section 14.16) indicating the
+     * range included with this response, or a multipart/byteranges Content-Type
+     * including Content-Range fields for each part. If a Content-Length header
+     * field is present in the response, its value MUST match the actual number
+     * of OCTETs transmitted in the message-body.
+     *
+     * - Date
+     *
+     * - ETag and/or Content-Location, if the header would have been sent in a
+     * 200 response to the same request
+     *
+     * - Expires, Cache-Control, and/or Vary, if the field-value might differ
+     * from that sent in any previous response for the same variant
+     *
+     * http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.7
+     */
+    @Test
+    @Ignore
+    public void test206ResponseReturnedToClientMustHaveContentRangeOrByteRangesContentType()
+            throws Exception {
+        request.addHeader("Range", "bytes 0-499/1234");
+        originResponse = new BasicHttpResponse(HTTP_1_1, HttpStatus.SC_PARTIAL_CONTENT,
+                "Partial Content");
+        originResponse.setHeader("Date", DateUtils.formatDate(new Date()));
+        originResponse.setHeader("Server", "MockOrigin/1.0");
+        originResponse.setEntity(makeBody(500));
+
+        org.easymock.EasyMock.expect(
+                mockBackend.execute(org.easymock.EasyMock.isA(HttpHost.class),
+                        org.easymock.EasyMock.isA(HttpRequest.class),
+                        (HttpContext) org.easymock.EasyMock.isNull())).andReturn(originResponse);
+
+        replayMocks();
+
+        try {
+            HttpResponse result = impl.execute(host, request);
+            Header crHdr = result.getFirstHeader("Content-Range");
+            Header ctHdr = result.getFirstHeader("Content-Type");
+            if (result.getStatusLine().getStatusCode() == HttpStatus.SC_PARTIAL_CONTENT) {
+                if (crHdr == null) {
+                    Assert.assertNotNull(ctHdr);
+                    boolean foundMultipartByteRanges = false;
+                    for (HeaderElement elt : ctHdr.getElements()) {
+                        if ("multipart/byteranges".equalsIgnoreCase(elt.getName())) {
+                            NameValuePair param = elt.getParameterByName("boundary");
+                            Assert.assertNotNull(param);
+                            String boundary = param.getValue();
+                            Assert.assertNotNull(boundary);
+                            // perhaps eventually should parse out the
+                            // request body to check proper formatting
+                            // but that might be excessive; HttpClient
+                            // developers have indicated that
+                            // HttpClient's job does not involve
+                            // parsing a response body
+                        }
+                    }
+                    Assert.assertTrue(foundMultipartByteRanges);
+                }
+            }
+        } catch (ClientProtocolException acceptableBehavior) {
+        }
+    }
+
+    @Test
+    @Ignore
+    public void test206ResponseReturnedToClientWithAContentLengthMustMatchActualOctetsTransmitted() {
+        // We are explicitly saying that CachingHttpClient does not care about
+        // this:
+        // We do not attempt to cache 206, nor do we ever construct a 206. We
+        // simply pass along a 206,
+        // which could be malformed. But protocol compliance of a downstream
+        // server is not our responsibility
+    }
+
+}

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/HttpTestUtils.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/HttpTestUtils.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/HttpTestUtils.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/HttpTestUtils.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,194 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.io.InputStream;
+
+import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpMessage;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.RequestLine;
+import org.apache.http.StatusLine;
+
+public class HttpTestUtils {
+
+    /*
+     * "The following HTTP/1.1 headers are hop-by-hop headers..."
+     *
+     * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
+     */
+    public static String[] HOP_BY_HOP_HEADERS = { "Connection", "Keep-Alive", "Proxy-Authenticate",
+            "Proxy-Authorization", "TE", "Trailers", "Transfer-Encoding", "Upgrade" };
+
+    /*
+     * "Multiple message-header fields with the same field-name MAY be present
+     * in a message if and only if the entire field-value for that header field
+     * is defined as a comma-separated list [i.e., #(values)]."
+     *
+     * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
+     */
+    public static String[] MULTI_HEADERS = { "Accept", "Accept-Charset", "Accept-Encoding",
+            "Accept-Language", "Allow", "Cache-Control", "Connection", "Content-Encoding",
+            "Content-Language", "Expect", "Pragma", "Proxy-Authenticate", "TE", "Trailer",
+            "Transfer-Encoding", "Upgrade", "Via", "Warning", "WWW-Authenticate" };
+    public static String[] SINGLE_HEADERS = { "Accept-Ranges", "Age", "Authorization",
+            "Content-Length", "Content-Location", "Content-MD5", "Content-Range", "Content-Type",
+            "Date", "ETag", "Expires", "From", "Host", "If-Match", "If-Modified-Since",
+            "If-None-Match", "If-Range", "If-Unmodified-Since", "Last-Modified", "Location",
+            "Max-Forwards", "Proxy-Authorization", "Range", "Referer", "Retry-After", "Server",
+            "User-Agent", "Vary" };
+
+    /*
+     * Determines whether the given header name is considered a hop-by-hop
+     * header.
+     *
+     * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
+     */
+    public static boolean isHopByHopHeader(String name) {
+        for (String s : HOP_BY_HOP_HEADERS) {
+            if (s.equalsIgnoreCase(name))
+                return true;
+        }
+        return false;
+    }
+
+    /*
+     * Determines whether a given header name may only appear once in a message.
+     */
+    public static boolean isSingleHeader(String name) {
+        for (String s : SINGLE_HEADERS) {
+            if (s.equalsIgnoreCase(name))
+                return true;
+        }
+        return false;
+    }
+
+    /*
+     * Assert.asserts that two request or response bodies are byte-equivalent.
+     */
+    public static boolean equivalent(HttpEntity e1, HttpEntity e2) throws Exception {
+        InputStream i1 = e1.getContent();
+        InputStream i2 = e2.getContent();
+        if (i1 == null && i2 == null)
+            return true;
+        int b1 = -1;
+        while ((b1 = i1.read()) != -1) {
+            if (b1 != i2.read())
+                return false;
+        }
+        return (-1 == i2.read());
+    }
+
+    /*
+     * Assert.asserts that the components of two status lines match in a way
+     * that differs only by hop-by-hop information. "2.1 Proxy Behavior ...We
+     * remind the reader that HTTP version numbers are hop-by-hop components of
+     * HTTP meesages, and are not end-to-end."
+     *
+     * @see http://www.ietf.org/rfc/rfc2145.txt
+     */
+    public static boolean semanticallyTransparent(StatusLine l1, StatusLine l2) {
+        return (l1.getReasonPhrase().equals(l2.getReasonPhrase()) && l1.getStatusCode() == l2
+                .getStatusCode());
+    }
+
+    /* Assert.asserts that the components of two status lines match. */
+    public static boolean equivalent(StatusLine l1, StatusLine l2) {
+        return (l1.getProtocolVersion().equals(l2.getProtocolVersion()) && semanticallyTransparent(
+                l1, l2));
+    }
+
+    /* Assert.asserts that the components of two request lines match. */
+    public static boolean equivalent(RequestLine l1, RequestLine l2) {
+        return (l1.getMethod().equals(l2.getMethod())
+                && l1.getProtocolVersion().equals(l2.getProtocolVersion()) && l1.getUri().equals(
+                l2.getUri()));
+    }
+
+    /*
+     * Retrieves the full header value by combining multiple headers and
+     * separating with commas, canonicalizing whitespace along the way.
+     *
+     * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
+     */
+    public static String getCanonicalHeaderValue(HttpMessage r, String name) {
+        if (isSingleHeader(name)) {
+            Header h = r.getFirstHeader(name);
+            return (h != null) ? h.getValue() : null;
+        }
+        StringBuilder buf = new StringBuilder();
+        boolean first = true;
+        for (Header h : r.getHeaders(name)) {
+            if (!first) {
+                buf.append(", ");
+            }
+            buf.append(h.getValue().trim());
+            first = false;
+        }
+        return buf.toString();
+    }
+
+    /*
+     * Assert.asserts that all the headers appearing in r1 also appear in r2
+     * with the same canonical header values.
+     */
+    public static boolean isEndToEndHeaderSubset(HttpMessage r1, HttpMessage r2) {
+        for (Header h : r1.getAllHeaders()) {
+            if (!isHopByHopHeader(h.getName())) {
+                String r1val = getCanonicalHeaderValue(r1, h.getName());
+                String r2val = getCanonicalHeaderValue(r2, h.getName());
+                if (!r1val.equals(r2val))
+                    return false;
+            }
+        }
+        return true;
+    }
+
+    /*
+     * Assert.asserts that message <code>r2</code> represents exactly the same
+     * message as <code>r1</code>, except for hop-by-hop headers. "When a cache
+     * is semantically transparent, the client receives exactly the same
+     * response (except for hop-by-hop headers) that it would have received had
+     * its request been handled directly by the origin server."
+     *
+     * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec1.html#sec1.3
+     */
+    public static boolean semanticallyTransparent(HttpResponse r1, HttpResponse r2)
+            throws Exception {
+        return (equivalent(r1.getEntity(), r2.getEntity())
+                && semanticallyTransparent(r1.getStatusLine(), r2.getStatusLine()) && isEndToEndHeaderSubset(
+                r1, r2));
+    }
+
+    /* Assert.asserts that two requests are morally equivalent. */
+    public static boolean equivalent(HttpRequest r1, HttpRequest r2) {
+        return (equivalent(r1.getRequestLine(), r2.getRequestLine()) && isEndToEndHeaderSubset(r1,
+                r2));
+    }
+}
\ No newline at end of file

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/RequestEquivalent.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/RequestEquivalent.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/RequestEquivalent.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/RequestEquivalent.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,53 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import org.apache.http.HttpRequest;
+import org.easymock.IArgumentMatcher;
+
+public class RequestEquivalent implements IArgumentMatcher {
+
+    private HttpRequest expected;
+
+    public RequestEquivalent(HttpRequest expected) {
+        this.expected = expected;
+    }
+
+    public boolean matches(Object actual) {
+        if (!(actual instanceof HttpRequest))
+            return false;
+        HttpRequest other = (HttpRequest) actual;
+        return HttpTestUtils.equivalent(expected, other);
+    }
+
+    public void appendTo(StringBuffer buf) {
+        buf.append("eqRequest(");
+        buf.append(expected);
+        buf.append(")");
+    }
+
+}
\ No newline at end of file

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/Serializer.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/Serializer.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/Serializer.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/Serializer.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,51 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+public class Serializer<T> {
+
+    public byte[] serialize(T object) throws Exception {
+        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+        ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
+        objectStream.writeObject(object);
+        return byteStream.toByteArray();
+    }
+
+    @SuppressWarnings("unchecked")
+    public T deserialize(byte[] serialized) throws Exception {
+        ByteArrayInputStream byteStream = new ByteArrayInputStream(serialized);
+        ObjectInputStream objectStream = new ObjectInputStream(byteStream);
+        T object = (T) objectStream.readObject();
+        return object;
+    }
+
+}

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntry.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntry.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntry.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntry.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,494 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.util.Date;
+import java.util.Set;
+
+import org.apache.http.Header;
+import org.apache.http.client.cache.impl.CacheEntry;
+import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.message.BasicHeader;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCacheEntry {
+
+    @Test
+    public void testGetHeadersReturnsCorrectHeaders() {
+        Header[] headers = new Header[] { new BasicHeader("foo", "fooValue"),
+                new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") };
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals(2, entry.getHeaders("bar").length);
+    }
+
+    @Test
+    public void testGetFirstHeaderReturnsCorrectHeader() {
+        Header[] headers = new Header[] { new BasicHeader("foo", "fooValue"),
+                new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") };
+
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals("barValue1", entry.getFirstHeader("bar").getValue());
+    }
+
+    @Test
+    public void testGetHeadersReturnsEmptyArrayIfNoneMatch() {
+        Header[] headers = new Header[] { new BasicHeader("foo", "fooValue"),
+                new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") };
+
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+
+        Assert.assertEquals(0, entry.getHeaders("baz").length);
+    }
+
+    @Test
+    public void testGetFirstHeaderReturnsNullIfNoneMatch() {
+        Header[] headers = new Header[] { new BasicHeader("foo", "fooValue"),
+                new BasicHeader("bar", "barValue1"), new BasicHeader("bar", "barValue2") };
+
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+
+        Assert.assertEquals(null, entry.getFirstHeader("quux"));
+    }
+
+    @Test
+    public void testApparentAgeIsMaxIntIfDateHeaderNotPresent() {
+        Header[] headers = new Header[0];
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals(2147483648L, entry.getApparentAgeSecs());
+    }
+
+    @Test
+    public void testApparentAgeIsResponseReceivedTimeLessDateHeader() {
+        Date now = new Date();
+        Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+
+        Header[] headers = new Header[] { new BasicHeader("Date", DateUtils
+                .formatDate(tenSecondsAgo)) };
+
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        entry.setResponseDate(sixSecondsAgo);
+
+        Assert.assertEquals(4, entry.getApparentAgeSecs());
+    }
+
+    @Test
+    public void testNegativeApparentAgeIsBroughtUpToZero() {
+        Date now = new Date();
+        Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+
+        Header[] headers = new Header[] { new BasicHeader("Date", DateUtils
+                .formatDate(sixSecondsAgo)) };
+
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        entry.setResponseDate(tenSecondsAgo);
+
+        Assert.assertEquals(0, entry.getApparentAgeSecs());
+    }
+
+    @Test
+    public void testCorrectedReceivedAgeIsAgeHeaderIfLarger() {
+        Header[] headers = new Header[] { new BasicHeader("Age", "10"), };
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected long getApparentAgeSecs() {
+                return 6;
+            }
+        };
+        entry.setResponseHeaders(headers);
+
+        Assert.assertEquals(10, entry.getCorrectedReceivedAgeSecs());
+    }
+
+    @Test
+    public void testCorrectedReceivedAgeIsApparentAgeIfLarger() {
+        Header[] headers = new Header[] { new BasicHeader("Age", "6"), };
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected long getApparentAgeSecs() {
+                return 10;
+            }
+        };
+        entry.setResponseHeaders(headers);
+
+        Assert.assertEquals(10, entry.getCorrectedReceivedAgeSecs());
+    }
+
+    @Test
+    public void testResponseDelayIsDifferenceBetweenResponseAndRequestTimes() {
+        Date now = new Date();
+        Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+
+        CacheEntry entry = new CacheEntry();
+        entry.setRequestDate(tenSecondsAgo);
+        entry.setResponseDate(sixSecondsAgo);
+
+        Assert.assertEquals(4, entry.getResponseDelaySecs());
+    }
+
+    @Test
+    public void testCorrectedInitialAgeIsCorrectedReceivedAgePlusResponseDelay() {
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected long getCorrectedReceivedAgeSecs() {
+                return 7;
+            }
+
+            @Override
+            protected long getResponseDelaySecs() {
+                return 13;
+            }
+        };
+        Assert.assertEquals(20, entry.getCorrectedInitialAgeSecs());
+    }
+
+    @Test
+    public void testResidentTimeSecondsIsTimeSinceResponseTime() {
+        final Date now = new Date();
+        Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
+
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected Date getCurrentDate() {
+                return now;
+            }
+        };
+        entry.setResponseDate(sixSecondsAgo);
+
+        Assert.assertEquals(6, entry.getResidentTimeSecs());
+    }
+
+    @Test
+    public void testCurrentAgeIsCorrectedInitialAgePlusResidentTime() {
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            protected long getCorrectedInitialAgeSecs() {
+                return 11;
+            }
+
+            @Override
+            protected long getResidentTimeSecs() {
+                return 17;
+            }
+        };
+        Assert.assertEquals(28, entry.getCurrentAgeSecs());
+    }
+
+    @Test
+    public void testFreshnessLifetimeIsSMaxAgeIfPresent() {
+        Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10") };
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
+    }
+
+    @Test
+    public void testFreshnessLifetimeIsMaxAgeIfPresent() {
+        Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10") };
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
+    }
+
+    @Test
+    public void testFreshnessLifetimeIsMostRestrictiveOfMaxAgeAndSMaxAge() {
+        Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"),
+                new BasicHeader("Cache-Control", "s-maxage=20") };
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
+
+        headers = new Header[] { new BasicHeader("Cache-Control", "max-age=20"),
+                new BasicHeader("Cache-Control", "s-maxage=10") };
+        entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
+    }
+
+    @Test
+    public void testFreshnessLifetimeIsMaxAgeEvenIfExpiresIsPresent() {
+        Date now = new Date();
+        Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+        Header[] headers = new Header[] { new BasicHeader("Cache-Control", "max-age=10"),
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Expires", DateUtils.formatDate(sixSecondsAgo)) };
+
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
+    }
+
+    @Test
+    public void testFreshnessLifetimeIsSMaxAgeEvenIfExpiresIsPresent() {
+        Date now = new Date();
+        Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+        Header[] headers = new Header[] { new BasicHeader("Cache-Control", "s-maxage=10"),
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Expires", DateUtils.formatDate(sixSecondsAgo)) };
+
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals(10, entry.getFreshnessLifetimeSecs());
+    }
+
+    @Test
+    public void testFreshnessLifetimeIsFromExpiresHeaderIfNoMaxAge() {
+        Date now = new Date();
+        Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+        Header[] headers = new Header[] {
+                new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
+                new BasicHeader("Expires", DateUtils.formatDate(sixSecondsAgo)) };
+
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertEquals(4, entry.getFreshnessLifetimeSecs());
+    }
+
+    @Test
+    public void testResponseIsFreshIfFreshnessLifetimeExceedsCurrentAge() {
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public long getCurrentAgeSecs() {
+                return 6;
+            }
+
+            @Override
+            public long getFreshnessLifetimeSecs() {
+                return 10;
+            }
+        };
+
+        Assert.assertTrue(entry.isResponseFresh());
+    }
+
+    @Test
+    public void testResponseIsNotFreshIfFreshnessLifetimeEqualsCurrentAge() {
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public long getCurrentAgeSecs() {
+                return 6;
+            }
+
+            @Override
+            public long getFreshnessLifetimeSecs() {
+                return 6;
+            }
+        };
+
+        Assert.assertFalse(entry.isResponseFresh());
+    }
+
+    @Test
+    public void testResponseIsNotFreshIfCurrentAgeExceedsFreshnessLifetime() {
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public long getCurrentAgeSecs() {
+                return 10;
+            }
+
+            @Override
+            public long getFreshnessLifetimeSecs() {
+                return 6;
+            }
+        };
+
+        Assert.assertFalse(entry.isResponseFresh());
+    }
+
+    @Test
+    public void testCacheEntryIsRevalidatableIfHeadersIncludeETag() {
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(new Header[] {
+                new BasicHeader("Expires", DateUtils.formatDate(new Date())),
+                new BasicHeader("ETag", "somevalue") });
+
+        Assert.assertTrue(entry.isRevalidatable());
+    }
+
+    @Test
+    public void testCacheEntryIsRevalidatableIfHeadersIncludeLastModifiedDate() {
+        CacheEntry entry = new CacheEntry();
+
+        entry.setResponseHeaders(new Header[] {
+                new BasicHeader("Expires", DateUtils.formatDate(new Date())),
+                new BasicHeader("Last-Modified", DateUtils.formatDate(new Date())) });
+
+        Assert.assertTrue(entry.isRevalidatable());
+    }
+
+    @Test
+    public void testCacheEntryIsNotRevalidatableIfNoAppropriateHeaders() {
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(new Header[] {
+                new BasicHeader("Expires", DateUtils.formatDate(new Date())),
+                new BasicHeader("Cache-Control", "public") });
+    }
+
+    @Test
+    public void testCacheEntryWithNoVaryHeaderDoesNotHaveVariants() {
+        Header[] headers = new Header[0];
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertFalse(entry.hasVariants());
+    }
+
+    @Test
+    public void testCacheEntryWithOneVaryHeaderHasVariants() {
+        Header[] headers = { new BasicHeader("Vary", "User-Agent") };
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertTrue(entry.hasVariants());
+    }
+
+    @Test
+    public void testCacheEntryWithMultipleVaryHeadersHasVariants() {
+        Header[] headers = { new BasicHeader("Vary", "User-Agent"),
+                new BasicHeader("Vary", "Accept-Encoding") };
+        CacheEntry entry = new CacheEntry();
+        entry.setResponseHeaders(headers);
+        Assert.assertTrue(entry.hasVariants());
+    }
+
+    @Test
+    public void testCacheEntryCanStoreMultipleVariantUris() {
+
+        CacheEntry entry = new CacheEntry();
+
+        entry.addVariantURI("foo");
+        entry.addVariantURI("bar");
+
+        Set<String> variants = entry.getVariantURIs();
+
+        Assert.assertTrue(variants.contains("foo"));
+        Assert.assertTrue(variants.contains("bar"));
+    }
+
+    @Test
+    public void testMalformedDateHeaderIsIgnored() {
+
+        Header[] h = new Header[] { new BasicHeader("Date", "asdf") };
+        CacheEntry e = new CacheEntry();
+        e.setResponseHeaders(h);
+
+        Date d = e.getDateValue();
+
+        Assert.assertNull(d);
+
+    }
+
+    @Test
+    public void testMalformedContentLengthReturnsNegativeOne() {
+
+        Header[] h = new Header[] { new BasicHeader("Content-Length", "asdf") };
+        CacheEntry e = new CacheEntry();
+        e.setResponseHeaders(h);
+
+        long length = e.getContentLengthValue();
+
+        Assert.assertEquals(-1, length);
+
+    }
+
+    @Test
+    public void testNegativeAgeHeaderValueReturnsMaxAge() {
+
+        Header[] h = new Header[] { new BasicHeader("Age", "-100") };
+        CacheEntry e = new CacheEntry();
+        e.setResponseHeaders(h);
+
+        long length = e.getAgeValue();
+
+        Assert.assertEquals(CacheEntry.MAX_AGE, length);
+
+    }
+
+    @Test
+    public void testMalformedAgeHeaderValueReturnsMaxAge() {
+
+        Header[] h = new Header[] { new BasicHeader("Age", "asdf") };
+        CacheEntry e = new CacheEntry();
+        e.setResponseHeaders(h);
+
+        long length = e.getAgeValue();
+
+        Assert.assertEquals(CacheEntry.MAX_AGE, length);
+
+    }
+
+    @Test
+    public void testMalformedCacheControlMaxAgeHeaderReturnsZero() {
+
+        Header[] h = new Header[] { new BasicHeader("Cache-Control", "max-age=asdf") };
+        CacheEntry e = new CacheEntry();
+        e.setResponseHeaders(h);
+
+        long maxage = e.getMaxAge();
+
+        Assert.assertEquals(0, maxage);
+
+    }
+
+    @Test
+    public void testMalformedExpirationDateReturnsNull() {
+        Header[] h = new Header[] { new BasicHeader("Expires", "asdf") };
+        CacheEntry e = new CacheEntry();
+        e.setResponseHeaders(h);
+
+        Date expirationDate = e.getExpirationDate();
+
+        Assert.assertNull(expirationDate);
+    }
+}

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntryGenerator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntryGenerator.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntryGenerator.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntryGenerator.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,64 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.io.IOException;
+import java.util.Date;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.cache.impl.CacheEntry;
+import org.apache.http.client.cache.impl.CacheEntryGenerator;
+import org.apache.http.entity.ByteArrayEntity;
+import org.apache.http.message.BasicHttpResponse;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCacheEntryGenerator {
+
+    @Test
+    public void testEntryMatchesInputs() throws IOException {
+
+        CacheEntryGenerator gen = new CacheEntryGenerator();
+
+        HttpResponse response = new BasicHttpResponse(new ProtocolVersion("HTTP", 1, 1),
+                HttpStatus.SC_OK, "Success");
+        HttpEntity entity = new ByteArrayEntity(new byte[] {});
+        response.setEntity(entity);
+
+        response.setHeader("fooHeader", "fooHeaderValue");
+
+        CacheEntry entry = gen.generateEntry(new Date(), new Date(), response, new byte[] {});
+
+        Assert.assertEquals("HTTP", entry.getProtocolVersion().getProtocol());
+        Assert.assertEquals(1, entry.getProtocolVersion().getMajor());
+        Assert.assertEquals(1, entry.getProtocolVersion().getMinor());
+        Assert.assertEquals("fooHeaderValue", entry.getFirstHeader("fooHeader").getValue());
+    }
+}

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntryUpdater.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntryUpdater.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntryUpdater.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheEntryUpdater.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,187 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.util.Date;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.HttpStatus;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.cache.impl.CacheEntry;
+import org.apache.http.client.cache.impl.CacheEntryUpdater;
+import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.message.BasicHttpResponse;
+import org.apache.http.message.BasicStatusLine;
+import org.easymock.classextension.EasyMock;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestCacheEntryUpdater {
+
+    private HttpResponse mockResponse;
+    private CacheEntry mockCacheEntry;
+    private Date requestDate;
+    private Date responseDate;
+
+    private boolean implMocked = false;
+    private CacheEntryUpdater impl;
+
+    @Before
+    public void setUp() throws Exception {
+        mockResponse = EasyMock.createMock(HttpResponse.class);
+        mockCacheEntry = EasyMock.createMock(CacheEntry.class);
+
+        requestDate = new Date(System.currentTimeMillis() - 1000);
+        responseDate = new Date();
+
+        impl = new CacheEntryUpdater();
+    }
+
+    private void replayMocks() {
+        EasyMock.replay(mockResponse);
+        EasyMock.replay(mockCacheEntry);
+        if (implMocked) {
+            EasyMock.replay(impl);
+        }
+    }
+
+    private void verifyMocks() {
+        EasyMock.verify(mockResponse);
+        EasyMock.verify(mockCacheEntry);
+        if (implMocked) {
+            EasyMock.verify(impl);
+        }
+    }
+
+    @Test
+    public void testUpdateCacheEntry() {
+        mockImplMethods("mergeHeaders");
+        mockCacheEntry.setRequestDate(requestDate);
+        mockCacheEntry.setResponseDate(responseDate);
+        impl.mergeHeaders(mockCacheEntry, mockResponse);
+
+        replayMocks();
+
+        impl.updateCacheEntry(mockCacheEntry, requestDate, responseDate, mockResponse);
+
+        verifyMocks();
+    }
+
+    @Test
+    public void testExistingHeadersNotInResponseDontChange() {
+
+        CacheEntry cacheEntry = new CacheEntry();
+        cacheEntry.setResponseHeaders(new Header[] {
+                new BasicHeader("Date", DateUtils.formatDate(responseDate)),
+                new BasicHeader("ETag", "eTag") });
+
+        HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion(
+                "http", 1, 1), HttpStatus.SC_NOT_MODIFIED, ""));
+        response.setHeaders(new Header[] {});
+
+        impl.mergeHeaders(cacheEntry, response);
+
+        Assert.assertEquals(2, cacheEntry.getAllHeaders().length);
+
+        headersContain(cacheEntry.getAllHeaders(), "Date", DateUtils.formatDate(responseDate));
+        headersContain(cacheEntry.getAllHeaders(), "ETag", "eTag");
+
+    }
+
+    @Test
+    public void testNewerHeadersReplaceExistingHeaders() {
+        CacheEntry cacheEntry = new CacheEntry();
+        cacheEntry.setResponseHeaders(new Header[] {
+                new BasicHeader("Date", DateUtils.formatDate(requestDate)),
+                new BasicHeader("Cache-Control", "private"), new BasicHeader("ETag", "eTag"),
+                new BasicHeader("Last-Modified", DateUtils.formatDate(requestDate)),
+                new BasicHeader("Cache-Control", "max-age=0"), });
+
+        HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion(
+                "http", 1, 1), HttpStatus.SC_NOT_MODIFIED, ""));
+        response.setHeaders(new Header[] {
+                new BasicHeader("Last-Modified", DateUtils.formatDate(responseDate)),
+                new BasicHeader("Cache-Control", "public"), });
+
+        impl.mergeHeaders(cacheEntry, response);
+
+        Assert.assertEquals(4, cacheEntry.getAllHeaders().length);
+
+        headersContain(cacheEntry.getAllHeaders(), "Date", DateUtils.formatDate(requestDate));
+        headersContain(cacheEntry.getAllHeaders(), "ETag", "eTag");
+        headersContain(cacheEntry.getAllHeaders(), "Last-Modified", DateUtils
+                .formatDate(responseDate));
+        headersContain(cacheEntry.getAllHeaders(), "Cache-Control", "public");
+    }
+
+    @Test
+    public void testNewHeadersAreAddedByMerge() {
+
+        CacheEntry cacheEntry = new CacheEntry();
+        cacheEntry.setResponseHeaders(new Header[] {
+                new BasicHeader("Date", DateUtils.formatDate(requestDate)),
+                new BasicHeader("ETag", "eTag"), });
+
+        HttpResponse response = new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion(
+                "http", 1, 1), HttpStatus.SC_NOT_MODIFIED, ""));
+        response.setHeaders(new Header[] {
+                new BasicHeader("Last-Modified", DateUtils.formatDate(responseDate)),
+                new BasicHeader("Cache-Control", "public"), });
+
+        impl.mergeHeaders(cacheEntry, response);
+
+        Assert.assertEquals(4, cacheEntry.getAllHeaders().length);
+
+        headersContain(cacheEntry.getAllHeaders(), "Date", DateUtils.formatDate(requestDate));
+        headersContain(cacheEntry.getAllHeaders(), "ETag", "eTag");
+        headersContain(cacheEntry.getAllHeaders(), "Last-Modified", DateUtils
+                .formatDate(responseDate));
+        headersContain(cacheEntry.getAllHeaders(), "Cache-Control", "public");
+
+    }
+
+    private void headersContain(Header[] headers, String name, String value) {
+        for (Header header : headers) {
+            if (header.getName().equals(name)) {
+                if (header.getValue().equals(value)) {
+                    return;
+                }
+            }
+        }
+        Assert.fail("Header [" + name + ": " + value + "] not found in headers.");
+    }
+
+    private void mockImplMethods(String... methods) {
+        implMocked = true;
+        impl = EasyMock.createMockBuilder(CacheEntryUpdater.class).addMockedMethods(methods)
+                .createMock();
+    }
+
+}

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheInvalidator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheInvalidator.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheInvalidator.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheInvalidator.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,276 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.http.Header;
+import org.apache.http.HeaderElement;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpRequest;
+import org.apache.http.RequestLine;
+import org.apache.http.client.cache.HttpCacheOperationException;
+import org.apache.http.client.cache.HttpCache;
+import org.apache.http.client.cache.impl.CacheEntry;
+import org.apache.http.client.cache.impl.CacheInvalidator;
+import org.apache.http.client.cache.impl.URIExtractor;
+import org.easymock.classextension.EasyMock;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestCacheInvalidator {
+
+    private CacheInvalidator impl;
+    private HttpCache<CacheEntry> mockCache;
+    private Header mockHeader;
+    private Header[] mockHeaderArray = new Header[1];
+    private HttpHost host;
+    private HttpRequest mockRequest;
+    private RequestLine mockRequestLine;
+    private URIExtractor mockExtractor;
+    private CacheEntry mockEntry;
+
+    private boolean mockedImpl;
+    private HeaderElement mockElement;
+    private HeaderElement[] mockElementArray = new HeaderElement[1];
+
+    @SuppressWarnings("unchecked")
+    @Before
+    public void setUp() {
+        host = new HttpHost("foo.example.com");
+        mockCache = EasyMock.createMock(HttpCache.class);
+        mockExtractor = EasyMock.createMock(URIExtractor.class);
+        mockHeader = EasyMock.createMock(Header.class);
+        mockElement = EasyMock.createMock(HeaderElement.class);
+        mockRequest = EasyMock.createMock(HttpRequest.class);
+        mockRequestLine = EasyMock.createMock(RequestLine.class);
+        mockEntry = EasyMock.createMock(CacheEntry.class);
+        mockHeaderArray[0] = mockHeader;
+        mockElementArray[0] = mockElement;
+
+        impl = new CacheInvalidator(mockExtractor, mockCache);
+    }
+
+    private void mockImplMethods(String... methods) {
+        mockedImpl = true;
+        impl = EasyMock.createMockBuilder(CacheInvalidator.class).withConstructor(mockExtractor,
+                mockCache).addMockedMethods(methods).createMock();
+    }
+
+    private void replayMocks() {
+        EasyMock.replay(mockCache);
+        EasyMock.replay(mockExtractor);
+        EasyMock.replay(mockHeader);
+        EasyMock.replay(mockRequest);
+        EasyMock.replay(mockRequestLine);
+        EasyMock.replay(mockEntry);
+        EasyMock.replay(mockElement);
+
+        if (mockedImpl)
+            EasyMock.replay(impl);
+    }
+
+    private void verifyMocks() {
+        EasyMock.verify(mockCache);
+        EasyMock.verify(mockExtractor);
+        EasyMock.verify(mockHeader);
+        EasyMock.verify(mockRequest);
+        EasyMock.verify(mockRequestLine);
+        EasyMock.verify(mockEntry);
+        EasyMock.verify(mockElement);
+
+        if (mockedImpl)
+            EasyMock.verify(impl);
+    }
+
+    // Tests
+    @Test
+    public void testInvalidatesRequestsThatArentGETorHEAD() throws Exception {
+        final String theUri = "theUri";
+        Set<String> variantURIs = new HashSet<String>();
+        cacheEntryHasVariantURIs(variantURIs);
+
+        cacheReturnsEntryForUri(theUri);
+        requestLineIsRead();
+        requestMethodIs("POST");
+        extractorReturns(theUri);
+        entryIsRemoved(theUri);
+        replayMocks();
+
+        impl.flushInvalidatedCacheEntries(host, mockRequest);
+
+        verifyMocks();
+    }
+
+    @Test
+    public void testDoesNotInvalidateGETRequest() throws HttpCacheOperationException {
+
+        requestLineIsRead();
+        requestMethodIs("GET");
+        requestContainsCacheControlHeader(null);
+        requestContainsPragmaHeader(null);
+        replayMocks();
+
+        impl.flushInvalidatedCacheEntries(host, mockRequest);
+
+        verifyMocks();
+    }
+
+    @Test
+    public void testDoesNotInvalidateHEADRequest() throws HttpCacheOperationException {
+
+        requestLineIsRead();
+        requestMethodIs("HEAD");
+        requestContainsCacheControlHeader(null);
+        requestContainsPragmaHeader(null);
+        replayMocks();
+
+        impl.flushInvalidatedCacheEntries(host, mockRequest);
+
+        verifyMocks();
+    }
+
+    @Test
+    public void testInvalidatesRequestsWithClientCacheControlHeaders() throws Exception {
+        final String theUri = "theUri";
+        extractorReturns(theUri);
+        cacheReturnsEntryForUri(theUri);
+        Set<String> variantURIs = new HashSet<String>();
+        cacheEntryHasVariantURIs(variantURIs);
+
+        requestLineIsRead();
+        requestMethodIs("GET");
+        requestContainsCacheControlHeader(mockHeaderArray);
+
+        org.easymock.EasyMock.expect(mockHeader.getElements()).andReturn(mockElementArray);
+        org.easymock.EasyMock.expect(mockElement.getName()).andReturn("no-cache").anyTimes();
+
+        entryIsRemoved(theUri);
+        replayMocks();
+
+        impl.flushInvalidatedCacheEntries(host, mockRequest);
+
+        verifyMocks();
+    }
+
+    @Test
+    public void testInvalidatesRequestsWithClientPragmaHeaders() throws Exception {
+        final String theUri = "theUri";
+        extractorReturns(theUri);
+        cacheReturnsEntryForUri(theUri);
+        Set<String> variantURIs = new HashSet<String>();
+        cacheEntryHasVariantURIs(variantURIs);
+
+        requestLineIsRead();
+        requestMethodIs("GET");
+        requestContainsCacheControlHeader(null);
+        requestContainsPragmaHeader(mockHeader);
+        entryIsRemoved(theUri);
+        replayMocks();
+
+        impl.flushInvalidatedCacheEntries(host, mockRequest);
+
+        verifyMocks();
+    }
+
+    @Test
+    public void testVariantURIsAreFlushedAlso() throws HttpCacheOperationException {
+        final String theUri = "theUri";
+        final String variantUri = "theVariantURI";
+
+        Set<String> listOfURIs = new HashSet<String>();
+        listOfURIs.add(variantUri);
+
+        extractorReturns(theUri);
+        cacheReturnsEntryForUri(theUri);
+        cacheEntryHasVariantURIs(listOfURIs);
+
+        entryIsRemoved(variantUri);
+        entryIsRemoved(theUri);
+
+        mockImplMethods("requestShouldNotBeCached");
+        org.easymock.EasyMock.expect(impl.requestShouldNotBeCached(mockRequest)).andReturn(true);
+
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, mockRequest);
+        verifyMocks();
+    }
+
+    @Test
+    public void testCacheFlushException() throws Exception {
+        String theURI = "theURI";
+
+        mockImplMethods("requestShouldNotBeCached");
+        org.easymock.EasyMock.expect(impl.requestShouldNotBeCached(mockRequest)).andReturn(true);
+
+        extractorReturns(theURI);
+        cacheReturnsExceptionForUri(theURI);
+
+        replayMocks();
+        impl.flushInvalidatedCacheEntries(host, mockRequest);
+        verifyMocks();
+    }
+
+    // Expectations
+    private void requestContainsPragmaHeader(Header header) {
+        org.easymock.EasyMock.expect(mockRequest.getFirstHeader("Pragma")).andReturn(header);
+    }
+
+    private void requestMethodIs(String s) {
+        org.easymock.EasyMock.expect(mockRequestLine.getMethod()).andReturn(s);
+    }
+
+    private void cacheEntryHasVariantURIs(Set<String> variantURIs) {
+        org.easymock.EasyMock.expect(mockEntry.getVariantURIs()).andReturn(variantURIs);
+    }
+
+    private void cacheReturnsEntryForUri(String theUri) throws HttpCacheOperationException {
+        org.easymock.EasyMock.expect(mockCache.getEntry(theUri)).andReturn(mockEntry);
+    }
+
+    private void cacheReturnsExceptionForUri(String theUri) throws HttpCacheOperationException {
+        org.easymock.EasyMock.expect(mockCache.getEntry(theUri)).andThrow(
+                new HttpCacheOperationException("TOTAL FAIL"));
+    }
+
+    private void extractorReturns(String theUri) {
+        org.easymock.EasyMock.expect(mockExtractor.getURI(host, mockRequest)).andReturn(theUri);
+    }
+
+    private void entryIsRemoved(String theUri) throws HttpCacheOperationException {
+        mockCache.removeEntry(theUri);
+    }
+
+    private void requestLineIsRead() {
+        org.easymock.EasyMock.expect(mockRequest.getRequestLine()).andReturn(mockRequestLine);
+    }
+
+    private void requestContainsCacheControlHeader(Header[] header) {
+        org.easymock.EasyMock.expect(mockRequest.getHeaders("Cache-Control")).andReturn(header);
+    }
+}
\ No newline at end of file

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheableRequestPolicy.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheableRequestPolicy.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheableRequestPolicy.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCacheableRequestPolicy.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,99 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import org.apache.http.client.cache.impl.CacheableRequestPolicy;
+import org.apache.http.message.BasicHttpRequest;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+public class TestCacheableRequestPolicy {
+
+    private CacheableRequestPolicy policy;
+
+    @Before
+    public void setUp() throws Exception {
+        policy = new CacheableRequestPolicy();
+    }
+
+    @Test
+    public void testIsGetServableFromCache() {
+        BasicHttpRequest request = new BasicHttpRequest("GET", "someUri");
+
+        Assert.assertTrue(policy.isServableFromCache(request));
+
+    }
+
+    @Test
+    public void testIsGetWithCacheControlServableFromCache() {
+        BasicHttpRequest request = new BasicHttpRequest("GET", "someUri");
+        request.addHeader("Cache-Control", "no-cache");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+
+        request = new BasicHttpRequest("GET", "someUri");
+        request.addHeader("Cache-Control", "no-store");
+        request.addHeader("Cache-Control", "max-age=20");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+
+        request = new BasicHttpRequest("GET", "someUri");
+        request.addHeader("Cache-Control", "public");
+        request.addHeader("Cache-Control", "no-store, max-age=20");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+    }
+
+    @Test
+    public void testIsGetWithPragmaServableFromCache() {
+        BasicHttpRequest request = new BasicHttpRequest("GET", "someUri");
+        request.addHeader("Pragma", "no-cache");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+
+        request = new BasicHttpRequest("GET", "someUri");
+        request.addHeader("Pragma", "value1");
+        request.addHeader("Pragma", "value2");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+    }
+
+    @Test
+    public void testIsArbitraryMethodServableFromCache() {
+
+        BasicHttpRequest request = new BasicHttpRequest("HEAD", "someUri");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+
+        request = new BasicHttpRequest("get", "someUri");
+
+        Assert.assertFalse(policy.isServableFromCache(request));
+
+    }
+
+}

Added: httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCachedHttpResponseGenerator.java
URL: http://svn.apache.org/viewvc/httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCachedHttpResponseGenerator.java?rev=939814&view=auto
==============================================================================
--- httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCachedHttpResponseGenerator.java (added)
+++ httpcomponents/httpclient/trunk/httpclient-cache/src/test/java/org/apache/http/client/cache/impl/TestCachedHttpResponseGenerator.java Fri Apr 30 21:00:08 2010
@@ -0,0 +1,192 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import java.util.Date;
+
+import org.apache.http.Header;
+import org.apache.http.HttpResponse;
+import org.apache.http.ProtocolVersion;
+import org.apache.http.client.cache.impl.CacheEntry;
+import org.apache.http.client.cache.impl.CachedHttpResponseGenerator;
+import org.apache.http.impl.cookie.DateUtils;
+import org.apache.http.message.BasicHeader;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestCachedHttpResponseGenerator {
+
+    @Test
+    public void testResponseHasContentLength() {
+
+        CacheEntry entry = new CacheEntry();
+        Header[] hdrs = new Header[] {};
+        byte[] buf = new byte[] { 1, 2, 3, 4, 5 };
+        entry.setResponseHeaders(hdrs);
+        entry.setProtocolVersion(new ProtocolVersion("HTTP", 1, 1));
+        entry.setBody(buf);
+        entry.setResponseDate(new Date());
+        entry.setRequestDate(new Date());
+
+        CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
+        HttpResponse response = gen.generateResponse(entry);
+
+        Header length = response.getFirstHeader("Content-Length");
+        Assert.assertNotNull("Content-Length Header is missing", length);
+
+        Assert.assertEquals("Content-Length does not match buffer length", buf.length, Integer
+                .parseInt(length.getValue()));
+    }
+
+    @Test
+    public void testContentLengthIsNotAddedWhenTransferEncodingIsPresent() {
+        CacheEntry entry = new CacheEntry();
+        Header[] hdrs = new Header[] { new BasicHeader("Transfer-Encoding", "chunked") };
+        byte[] buf = new byte[] { 1, 2, 3, 4, 5 };
+        entry.setResponseHeaders(hdrs);
+        entry.setProtocolVersion(new ProtocolVersion("HTTP", 1, 1));
+        entry.setBody(buf);
+        entry.setResponseDate(new Date());
+        entry.setRequestDate(new Date());
+
+        CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
+        HttpResponse response = gen.generateResponse(entry);
+
+        Header length = response.getFirstHeader("Content-Length");
+
+        Assert.assertNull(length);
+    }
+
+    @Test
+    public void testResponseMatchesCacheEntry() {
+        CacheEntry entry = new CacheEntry();
+        buildEntry(entry);
+
+        CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
+        HttpResponse response = gen.generateResponse(entry);
+
+        Assert.assertTrue(response.containsHeader("Content-Length"));
+
+        Assert.assertSame("HTTP", response.getProtocolVersion().getProtocol());
+        Assert.assertSame(1, response.getProtocolVersion().getMajor());
+        Assert.assertSame(1, response.getProtocolVersion().getMinor());
+    }
+
+    @Test
+    public void testResponseStatusCodeMatchesCacheEntry() {
+        CacheEntry entry = new CacheEntry();
+        buildEntry(entry);
+
+        CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
+        HttpResponse response = gen.generateResponse(entry);
+
+        Assert.assertEquals(entry.getStatusCode(), response.getStatusLine().getStatusCode());
+    }
+
+    @Test
+    public void testAgeHeaderIsPopulatedWithCurrentAgeOfCacheEntryIfNonZero() {
+        final long currAge = 10L;
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public long getCurrentAgeSecs() {
+                return currAge;
+            }
+        };
+
+        buildEntry(entry);
+
+        CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
+        HttpResponse response = gen.generateResponse(entry);
+
+        Header ageHdr = response.getFirstHeader("Age");
+        Assert.assertNotNull(ageHdr);
+        Assert.assertEquals(currAge, Long.parseLong(ageHdr.getValue()));
+    }
+
+    @Test
+    public void testAgeHeaderIsNotPopulatedIfCurrentAgeOfCacheEntryIsZero() {
+        final long currAge = 0L;
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public long getCurrentAgeSecs() {
+                return currAge;
+            }
+        };
+        buildEntry(entry);
+
+        CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
+        HttpResponse response = gen.generateResponse(entry);
+
+        Header ageHdr = response.getFirstHeader("Age");
+        Assert.assertNull(ageHdr);
+    }
+
+    @Test
+    public void testAgeHeaderIsPopulatedWithMaxAgeIfCurrentAgeTooBig() {
+        final long currAge = CacheEntry.MAX_AGE + 1L;
+        CacheEntry entry = new CacheEntry() {
+            private static final long serialVersionUID = 1L;
+
+            @Override
+            public long getCurrentAgeSecs() {
+                return currAge;
+            }
+        };
+        buildEntry(entry);
+
+        CachedHttpResponseGenerator gen = new CachedHttpResponseGenerator();
+        HttpResponse response = gen.generateResponse(entry);
+
+        Header ageHdr = response.getFirstHeader("Age");
+        Assert.assertNotNull(ageHdr);
+        Assert.assertEquals(CacheEntry.MAX_AGE, Long.parseLong(ageHdr.getValue()));
+    }
+
+    private CacheEntry buildEntry(CacheEntry entry) {
+        Date now = new Date();
+        Date sixSecondsAgo = new Date(now.getTime() - 6 * 1000L);
+        Date eightSecondsAgo = new Date(now.getTime() - 8 * 1000L);
+        Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
+        Date tenSecondsFromNow = new Date(now.getTime() + 10 * 1000L);
+        Header[] hdrs = { new BasicHeader("Date", DateUtils.formatDate(eightSecondsAgo)),
+                new BasicHeader("Expires", DateUtils.formatDate(tenSecondsFromNow)),
+                new BasicHeader("Content-Length", "150") };
+        entry.setRequestDate(tenSecondsAgo);
+        entry.setResponseDate(sixSecondsAgo);
+        entry.setBody(new byte[] {});
+        entry.setResponseHeaders(hdrs);
+
+        entry.setProtocolVersion(new ProtocolVersion("HTTP", 1, 1));
+
+        return entry;
+
+    }
+}