View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  package org.apache.http.impl.client.cache;
28  
29  import static org.junit.Assert.assertEquals;
30  import static org.junit.Assert.assertNotSame;
31  import static org.junit.Assert.fail;
32  
33  import java.io.IOException;
34  import java.util.Date;
35  
36  import org.apache.http.Header;
37  import org.apache.http.HttpResponse;
38  import org.apache.http.HttpStatus;
39  import org.apache.http.HttpVersion;
40  import org.apache.http.client.cache.HttpCacheEntry;
41  import org.apache.http.client.utils.DateUtils;
42  import org.apache.http.message.BasicHeader;
43  import org.apache.http.message.BasicHttpResponse;
44  import org.junit.Before;
45  import org.junit.Test;
46  
47  public class TestCacheEntryUpdater {
48  
49      private Date requestDate;
50      private Date responseDate;
51  
52      private CacheEntryUpdater impl;
53      private HttpCacheEntry entry;
54      private Date now;
55      private Date oneSecondAgo;
56      private Date twoSecondsAgo;
57      private Date eightSecondsAgo;
58      private Date tenSecondsAgo;
59      private HttpResponse response;
60  
61      @Before
62      public void setUp() throws Exception {
63          requestDate = new Date(System.currentTimeMillis() - 1000);
64          responseDate = new Date();
65  
66          now = new Date();
67          oneSecondAgo = new Date(now.getTime() - 1000L);
68          twoSecondsAgo = new Date(now.getTime() - 2000L);
69          eightSecondsAgo = new Date(now.getTime() - 8000L);
70          tenSecondsAgo = new Date(now.getTime() - 10000L);
71  
72          response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
73                  HttpStatus.SC_NOT_MODIFIED, "Not Modified");
74  
75          impl = new CacheEntryUpdater();
76      }
77  
78      @Test
79      public void testUpdateCacheEntryReturnsDifferentEntryInstance()
80              throws IOException {
81          entry = HttpTestUtils.makeCacheEntry();
82          final HttpCacheEntry newEntry = impl.updateCacheEntry(null, entry,
83                  requestDate, responseDate, response);
84          assertNotSame(newEntry, entry);
85      }
86  
87      @Test
88      public void testHeadersAreMergedCorrectly() throws IOException {
89          final Header[] headers = {
90                  new BasicHeader("Date", DateUtils.formatDate(responseDate)),
91                  new BasicHeader("ETag", "\"etag\"")};
92          entry = HttpTestUtils.makeCacheEntry(headers);
93          response.setHeaders(new Header[]{});
94  
95          final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry,
96                  new Date(), new Date(), response);
97  
98  
99          final Header[] updatedHeaders = updatedEntry.getAllHeaders();
100         assertEquals(2, updatedHeaders.length);
101         headersContain(updatedHeaders, "Date", DateUtils.formatDate(responseDate));
102         headersContain(updatedHeaders, "ETag", "\"etag\"");
103     }
104 
105     @Test
106     public void testNewerHeadersReplaceExistingHeaders() throws IOException {
107         final Header[] headers = {
108                 new BasicHeader("Date", DateUtils.formatDate(requestDate)),
109                 new BasicHeader("Cache-Control", "private"),
110                 new BasicHeader("ETag", "\"etag\""),
111                 new BasicHeader("Last-Modified", DateUtils.formatDate(requestDate)),
112                 new BasicHeader("Cache-Control", "max-age=0"),};
113         entry = HttpTestUtils.makeCacheEntry(headers);
114 
115         response.setHeaders(new Header[] {
116                 new BasicHeader("Last-Modified", DateUtils.formatDate(responseDate)),
117                 new BasicHeader("Cache-Control", "public")});
118 
119         final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry,
120                 new Date(), new Date(), response);
121 
122         final Header[] updatedHeaders = updatedEntry.getAllHeaders();
123 
124         assertEquals(4, updatedHeaders.length);
125         headersContain(updatedHeaders, "Date", DateUtils.formatDate(requestDate));
126         headersContain(updatedHeaders, "ETag", "\"etag\"");
127         headersContain(updatedHeaders, "Last-Modified", DateUtils.formatDate(responseDate));
128         headersContain(updatedHeaders, "Cache-Control", "public");
129     }
130 
131     @Test
132     public void testNewHeadersAreAddedByMerge() throws IOException {
133 
134         final Header[] headers = {
135                 new BasicHeader("Date", DateUtils.formatDate(requestDate)),
136                 new BasicHeader("ETag", "\"etag\"")};
137 
138         entry = HttpTestUtils.makeCacheEntry(headers);
139         response.setHeaders(new Header[]{
140                 new BasicHeader("Last-Modified", DateUtils.formatDate(responseDate)),
141                 new BasicHeader("Cache-Control", "public"),});
142 
143         final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry,
144                 new Date(), new Date(), response);
145 
146         final Header[] updatedHeaders = updatedEntry.getAllHeaders();
147         assertEquals(4, updatedHeaders.length);
148 
149         headersContain(updatedHeaders, "Date", DateUtils.formatDate(requestDate));
150         headersContain(updatedHeaders, "ETag", "\"etag\"");
151         headersContain(updatedHeaders, "Last-Modified", DateUtils.formatDate(responseDate));
152         headersContain(updatedHeaders, "Cache-Control", "public");
153     }
154 
155     @Test
156     public void oldHeadersRetainedIfResponseOlderThanEntry()
157             throws Exception {
158         final Header[] headers = {
159                 new BasicHeader("Date", DateUtils.formatDate(oneSecondAgo)),
160                 new BasicHeader("ETag", "\"new-etag\"")
161         };
162         entry = HttpTestUtils.makeCacheEntry(twoSecondsAgo, now, headers);
163         response.setHeader("Date", DateUtils.formatDate(tenSecondsAgo));
164         response.setHeader("ETag", "\"old-etag\"");
165         final HttpCacheEntry result = impl.updateCacheEntry("A", entry, new Date(),
166                 new Date(), response);
167         assertEquals(2, result.getAllHeaders().length);
168         headersContain(result.getAllHeaders(), "Date", DateUtils.formatDate(oneSecondAgo));
169         headersContain(result.getAllHeaders(), "ETag", "\"new-etag\"");
170     }
171 
172     @Test
173     public void testUpdatedEntryHasLatestRequestAndResponseDates()
174             throws IOException {
175         entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo);
176         final HttpCacheEntry updated = impl.updateCacheEntry(null, entry,
177                 twoSecondsAgo, oneSecondAgo, response);
178 
179         assertEquals(twoSecondsAgo, updated.getRequestDate());
180         assertEquals(oneSecondAgo, updated.getResponseDate());
181     }
182 
183     @Test
184     public void entry1xxWarningsAreRemovedOnUpdate() throws Exception {
185         final Header[] headers = {
186                 new BasicHeader("Warning", "110 fred \"Response is stale\""),
187                 new BasicHeader("ETag", "\"old\""),
188                 new BasicHeader("Date", DateUtils.formatDate(eightSecondsAgo))
189         };
190         entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, headers);
191         response.setHeader("ETag", "\"new\"");
192         response.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
193         final HttpCacheEntry updated = impl.updateCacheEntry(null, entry,
194                 twoSecondsAgo, oneSecondAgo, response);
195 
196         assertEquals(0, updated.getHeaders("Warning").length);
197     }
198 
199     @Test
200     public void entryWithMalformedDateIsStillUpdated() throws Exception {
201         final Header[] headers = {
202                 new BasicHeader("ETag", "\"old\""),
203                 new BasicHeader("Date", "bad-date")
204         };
205         entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, headers);
206         response.setHeader("ETag", "\"new\"");
207         response.setHeader("Date", DateUtils.formatDate(twoSecondsAgo));
208         final HttpCacheEntry updated = impl.updateCacheEntry(null, entry,
209                 twoSecondsAgo, oneSecondAgo, response);
210 
211         assertEquals("\"new\"", updated.getFirstHeader("ETag").getValue());
212     }
213 
214     @Test
215     public void entryIsStillUpdatedByResponseWithMalformedDate() throws Exception {
216         final Header[] headers = {
217                 new BasicHeader("ETag", "\"old\""),
218                 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo))
219         };
220         entry = HttpTestUtils.makeCacheEntry(tenSecondsAgo, eightSecondsAgo, headers);
221         response.setHeader("ETag", "\"new\"");
222         response.setHeader("Date", "bad-date");
223         final HttpCacheEntry updated = impl.updateCacheEntry(null, entry,
224                 twoSecondsAgo, oneSecondAgo, response);
225 
226         assertEquals("\"new\"", updated.getFirstHeader("ETag").getValue());
227     }
228 
229     @Test
230     public void cannotUpdateFromANon304OriginResponse() throws Exception {
231         entry = HttpTestUtils.makeCacheEntry();
232         response = new BasicHttpResponse(HttpVersion.HTTP_1_1,
233                 HttpStatus.SC_OK, "OK");
234         try {
235             impl.updateCacheEntry("A", entry, new Date(), new Date(),
236                     response);
237             fail("should have thrown exception");
238         } catch (final IllegalArgumentException expected) {
239         }
240     }
241 
242     @Test
243     public void testContentEncodingHeaderIsNotUpdatedByMerge() throws IOException {
244         final Header[] headers = {
245                 new BasicHeader("Date", DateUtils.formatDate(requestDate)),
246                 new BasicHeader("ETag", "\"etag\""),
247                 new BasicHeader("Content-Encoding", "identity")};
248 
249         entry = HttpTestUtils.makeCacheEntry(headers);
250         response.setHeaders(new Header[]{
251                 new BasicHeader("Last-Modified", DateUtils.formatDate(responseDate)),
252                 new BasicHeader("Cache-Control", "public"),
253                 new BasicHeader("Content-Encoding", "gzip")});
254 
255         final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry,
256                 new Date(), new Date(), response);
257 
258         final Header[] updatedHeaders = updatedEntry.getAllHeaders();
259         headersContain(updatedHeaders, "Content-Encoding", "identity");
260         headersNotContain(updatedHeaders, "Content-Encoding", "gzip");
261     }
262 
263     @Test
264     public void testContentLengthIsNotAddedWhenTransferEncodingIsPresent() throws IOException {
265         final Header[] headers = {
266                 new BasicHeader("Date", DateUtils.formatDate(requestDate)),
267                 new BasicHeader("ETag", "\"etag\""),
268                 new BasicHeader("Transfer-Encoding", "chunked")};
269 
270         entry = HttpTestUtils.makeCacheEntry(headers);
271         response.setHeaders(new Header[]{
272                 new BasicHeader("Last-Modified", DateUtils.formatDate(responseDate)),
273                 new BasicHeader("Cache-Control", "public"),
274                 new BasicHeader("Content-Length", "0")});
275 
276         final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry,
277                 new Date(), new Date(), response);
278 
279         final Header[] updatedHeaders = updatedEntry.getAllHeaders();
280         headersContain(updatedHeaders, "Transfer-Encoding", "chunked");
281         headersNotContain(updatedHeaders, "Content-Length", "0");
282     }
283 
284     private void headersContain(final Header[] headers, final String name, final String value) {
285         for (final Header header : headers) {
286             if (header.getName().equals(name)) {
287                 if (header.getValue().equals(value)) {
288                     return;
289                 }
290             }
291         }
292         fail("Header [" + name + ": " + value + "] not found in headers.");
293     }
294 
295     private void headersNotContain(final Header[] headers, final String name, final String value) {
296         for (final Header header : headers) {
297             if (header.getName().equals(name)) {
298                 if (header.getValue().equals(value)) {
299                     fail("Header [" + name + ": " + value + "] found in headers where it should not be");
300                 }
301             }
302         }
303     }
304 }