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.hc.client5.http.impl.cache;
28  
29  
30  import static org.junit.jupiter.api.Assertions.assertEquals;
31  import static org.junit.jupiter.api.Assertions.assertFalse;
32  import static org.junit.jupiter.api.Assertions.assertNotNull;
33  import static org.junit.jupiter.api.Assertions.assertNull;
34  import static org.junit.jupiter.api.Assertions.assertSame;
35  
36  import java.time.Instant;
37  import java.util.Map;
38  
39  import org.apache.hc.client5.http.cache.HeaderConstants;
40  import org.apache.hc.client5.http.cache.HttpCacheEntry;
41  import org.apache.hc.client5.http.classic.methods.HttpDelete;
42  import org.apache.hc.client5.http.classic.methods.HttpGet;
43  import org.apache.hc.client5.http.classic.methods.HttpHead;
44  import org.apache.hc.client5.http.classic.methods.HttpOptions;
45  import org.apache.hc.client5.http.classic.methods.HttpPost;
46  import org.apache.hc.client5.http.classic.methods.HttpTrace;
47  import org.apache.hc.client5.http.utils.DateUtils;
48  import org.apache.hc.core5.http.Header;
49  import org.apache.hc.core5.http.HttpHost;
50  import org.apache.hc.core5.http.HttpRequest;
51  import org.apache.hc.core5.http.HttpResponse;
52  import org.apache.hc.core5.http.HttpStatus;
53  import org.apache.hc.core5.http.message.BasicHeader;
54  import org.apache.hc.core5.http.message.BasicHttpResponse;
55  import org.apache.hc.core5.util.ByteArrayBuffer;
56  import org.junit.jupiter.api.BeforeEach;
57  import org.junit.jupiter.api.Test;
58  
59  public class TestBasicHttpCache {
60  
61      private BasicHttpCache impl;
62      private SimpleHttpCacheStorage backing;
63  
64      @BeforeEach
65      public void setUp() throws Exception {
66          backing = new SimpleHttpCacheStorage();
67          impl = new BasicHttpCache(new HeapResourceFactory(), backing);
68      }
69  
70      @Test
71      public void testDoNotFlushCacheEntriesOnGet() throws Exception {
72          final HttpHost host = new HttpHost("foo.example.com");
73          final HttpRequest req = new HttpGet("/bar");
74          final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
75          final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
76  
77          backing.map.put(key, entry);
78  
79          impl.flushCacheEntriesFor(host, req);
80  
81          assertEquals(entry, backing.map.get(key));
82      }
83  
84      @Test
85      public void testDoNotFlushCacheEntriesOnHead() throws Exception {
86          final HttpHost host = new HttpHost("foo.example.com");
87          final HttpRequest req = new HttpHead("/bar");
88          final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
89          final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
90  
91          backing.map.put(key, entry);
92  
93          impl.flushCacheEntriesFor(host, req);
94  
95          assertEquals(entry, backing.map.get(key));
96      }
97  
98      @Test
99      public void testDoNotFlushCacheEntriesOnOptions() throws Exception {
100         final HttpHost host = new HttpHost("foo.example.com");
101         final HttpRequest req = new HttpOptions("/bar");
102         final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
103         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
104 
105         backing.map.put(key, entry);
106 
107         impl.flushCacheEntriesFor(host, req);
108 
109         assertEquals(entry, backing.map.get(key));
110     }
111 
112     @Test
113     public void testDoNotFlushCacheEntriesOnTrace() throws Exception {
114         final HttpHost host = new HttpHost("foo.example.com");
115         final HttpRequest req = new HttpTrace("/bar");
116         final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
117         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
118 
119         backing.map.put(key, entry);
120 
121         impl.flushCacheEntriesFor(host, req);
122 
123         assertEquals(entry, backing.map.get(key));
124     }
125 
126     @Test
127     public void testFlushContentLocationEntryIfUnSafeRequest()
128             throws Exception {
129         final HttpHost host = new HttpHost("foo.example.com");
130         final HttpRequest req = new HttpPost("/foo");
131         final HttpResponse resp = HttpTestUtils.make200Response();
132         resp.setHeader("Content-Location", "/bar");
133         resp.setHeader(HeaderConstants.ETAG, "\"etag\"");
134         final String key = CacheKeyGenerator.INSTANCE.generateKey(host, new HttpGet("/bar"));
135 
136         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
137            new BasicHeader("Date", DateUtils.formatStandardDate(Instant.now())),
138            new BasicHeader("ETag", "\"old-etag\"")
139         });
140 
141         backing.map.put(key, entry);
142 
143         impl.flushCacheEntriesInvalidatedByExchange(host, req, resp);
144 
145         assertNull(backing.map.get(key));
146     }
147 
148     @Test
149     public void testDoNotFlushContentLocationEntryIfSafeRequest()
150             throws Exception {
151         final HttpHost host = new HttpHost("foo.example.com");
152         final HttpRequest req = new HttpGet("/foo");
153         final HttpResponse resp = HttpTestUtils.make200Response();
154         resp.setHeader("Content-Location", "/bar");
155         final String key = CacheKeyGenerator.INSTANCE.generateKey(host, new HttpGet("/bar"));
156 
157         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry(new Header[] {
158            new BasicHeader("Date", DateUtils.formatStandardDate(Instant.now())),
159            new BasicHeader("ETag", "\"old-etag\"")
160         });
161 
162         backing.map.put(key, entry);
163 
164         impl.flushCacheEntriesInvalidatedByExchange(host, req, resp);
165 
166         assertEquals(entry, backing.map.get(key));
167     }
168 
169     @Test
170     public void testCanFlushCacheEntriesAtUri() throws Exception {
171         final HttpHost host = new HttpHost("foo.example.com");
172         final HttpRequest req = new HttpDelete("/bar");
173         final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
174         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
175 
176         backing.map.put(key, entry);
177 
178         impl.flushCacheEntriesFor(host, req);
179 
180         assertNull(backing.map.get(key));
181     }
182 
183     @Test
184     public void testStoreInCachePutsNonVariantEntryInPlace() throws Exception {
185         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
186         assertFalse(entry.hasVariants());
187         final HttpHost host = new HttpHost("foo.example.com");
188         final HttpRequest req = new HttpGet("http://foo.example.com/bar");
189         final String key = CacheKeyGenerator.INSTANCE.generateKey(host, req);
190 
191         impl.storeInCache(key, host, req, entry);
192         assertSame(entry, backing.map.get(key));
193     }
194 
195     @Test
196     public void testGetCacheEntryReturnsNullOnCacheMiss() throws Exception {
197         final HttpHost host = new HttpHost("foo.example.com");
198         final HttpRequest request = new HttpGet("http://foo.example.com/bar");
199         final HttpCacheEntry result = impl.getCacheEntry(host, request);
200         assertNull(result);
201     }
202 
203     @Test
204     public void testGetCacheEntryFetchesFromCacheOnCacheHitIfNoVariants() throws Exception {
205         final HttpCacheEntry entry = HttpTestUtils.makeCacheEntry();
206         assertFalse(entry.hasVariants());
207         final HttpHost host = new HttpHost("foo.example.com");
208         final HttpRequest request = new HttpGet("http://foo.example.com/bar");
209 
210         final String key = CacheKeyGenerator.INSTANCE.generateKey(host, request);
211 
212         backing.map.put(key,entry);
213 
214         final HttpCacheEntry result = impl.getCacheEntry(host, request);
215         assertSame(entry, result);
216     }
217 
218     @Test
219     public void testGetCacheEntryReturnsNullIfNoVariantInCache() throws Exception {
220         final HttpHost host = new HttpHost("foo.example.com");
221 
222         final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar");
223         origRequest.setHeader("Accept-Encoding","gzip");
224 
225         final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
226         final HttpResponse origResponse = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
227         origResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now()));
228         origResponse.setHeader("Cache-Control", "max-age=3600, public");
229         origResponse.setHeader("ETag", "\"etag\"");
230         origResponse.setHeader("Vary", "Accept-Encoding");
231         origResponse.setHeader("Content-Encoding","gzip");
232 
233         impl.createCacheEntry(host, origRequest, origResponse, buf, Instant.now(), Instant.now());
234 
235         final HttpRequest request = new HttpGet("http://foo.example.com/bar");
236         final HttpCacheEntry result = impl.getCacheEntry(host, request);
237         assertNull(result);
238     }
239 
240     @Test
241     public void testGetCacheEntryReturnsVariantIfPresentInCache() throws Exception {
242         final HttpHost host = new HttpHost("foo.example.com");
243 
244         final HttpRequest origRequest = new HttpGet("http://foo.example.com/bar");
245         origRequest.setHeader("Accept-Encoding","gzip");
246 
247         final ByteArrayBuffer buf = HttpTestUtils.getRandomBuffer(128);
248         final HttpResponse origResponse = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
249         origResponse.setHeader("Date", DateUtils.formatStandardDate(Instant.now()));
250         origResponse.setHeader("Cache-Control", "max-age=3600, public");
251         origResponse.setHeader("ETag", "\"etag\"");
252         origResponse.setHeader("Vary", "Accept-Encoding");
253         origResponse.setHeader("Content-Encoding","gzip");
254 
255         impl.createCacheEntry(host, origRequest, origResponse, buf, Instant.now(), Instant.now());
256 
257         final HttpRequest request = new HttpGet("http://foo.example.com/bar");
258         request.setHeader("Accept-Encoding","gzip");
259         final HttpCacheEntry result = impl.getCacheEntry(host, request);
260         assertNotNull(result);
261     }
262 
263     @Test
264     public void testGetVariantCacheEntriesReturnsEmptySetOnNoVariants() throws Exception {
265         final HttpHost host = new HttpHost("foo.example.com");
266         final HttpRequest request = new HttpGet("http://foo.example.com/bar");
267 
268         final Map<String,Variant> variants = impl.getVariantCacheEntriesWithEtags(host, request);
269 
270         assertNotNull(variants);
271         assertEquals(0, variants.size());
272     }
273 
274     @Test
275     public void testGetVariantCacheEntriesReturnsAllVariants() throws Exception {
276         final HttpHost host = new HttpHost("foo.example.com");
277         final HttpRequest req1 = new HttpGet("http://foo.example.com/bar");
278         req1.setHeader("Accept-Encoding", "gzip");
279 
280         final HttpResponse resp1 = HttpTestUtils.make200Response();
281         resp1.setHeader("Date", DateUtils.formatStandardDate(Instant.now()));
282         resp1.setHeader("Cache-Control", "max-age=3600, public");
283         resp1.setHeader("ETag", "\"etag1\"");
284         resp1.setHeader("Vary", "Accept-Encoding");
285         resp1.setHeader("Content-Encoding","gzip");
286         resp1.setHeader("Vary", "Accept-Encoding");
287 
288         final HttpRequest req2 = new HttpGet("http://foo.example.com/bar");
289         req2.setHeader("Accept-Encoding", "identity");
290 
291         final HttpResponse resp2 = HttpTestUtils.make200Response();
292         resp2.setHeader("Date", DateUtils.formatStandardDate(Instant.now()));
293         resp2.setHeader("Cache-Control", "max-age=3600, public");
294         resp2.setHeader("ETag", "\"etag2\"");
295         resp2.setHeader("Vary", "Accept-Encoding");
296         resp2.setHeader("Content-Encoding","gzip");
297         resp2.setHeader("Vary", "Accept-Encoding");
298 
299         impl.createCacheEntry(host, req1, resp1, null, Instant.now(), Instant.now());
300         impl.createCacheEntry(host, req2, resp2, null, Instant.now(), Instant.now());
301 
302         final Map<String,Variant> variants = impl.getVariantCacheEntriesWithEtags(host, req1);
303 
304         assertNotNull(variants);
305         assertEquals(2, variants.size());
306 
307     }
308 
309 }