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  import java.util.Date;
30  import java.util.HashMap;
31  import java.util.Iterator;
32  import java.util.Map;
33  
34  import org.apache.hc.client5.http.cache.HeaderConstants;
35  import org.apache.hc.client5.http.cache.HttpCacheEntry;
36  import org.apache.hc.client5.http.utils.DateUtils;
37  import org.apache.hc.core5.function.Factory;
38  import org.apache.hc.core5.http.Header;
39  import org.apache.hc.core5.http.HeaderElement;
40  import org.apache.hc.core5.http.HttpRequest;
41  import org.apache.hc.core5.http.message.BasicHeader;
42  import org.apache.hc.core5.http.message.BasicHttpRequest;
43  import org.apache.hc.core5.http.message.MessageSupport;
44  import org.apache.hc.core5.http.support.BasicRequestBuilder;
45  import org.junit.Assert;
46  import org.junit.Before;
47  import org.junit.Test;
48  
49  public class TestConditionalRequestBuilder {
50  
51      private ConditionalRequestBuilder<HttpRequest> impl;
52      private HttpRequest request;
53  
54      @Before
55      public void setUp() throws Exception {
56          impl = new ConditionalRequestBuilder<>(new Factory<HttpRequest, HttpRequest>() {
57  
58              @Override
59              public HttpRequest create(final HttpRequest request) {
60                  return BasicRequestBuilder.copy(request).build();
61              }
62  
63          });
64          request = new BasicHttpRequest("GET", "/");
65      }
66  
67      @Test
68      public void testBuildConditionalRequestWithLastModified() {
69          final String theMethod = "GET";
70          final String theUri = "/theuri";
71          final String lastModified = "this is my last modified date";
72  
73          final HttpRequest basicRequest = new BasicHttpRequest(theMethod, theUri);
74          basicRequest.addHeader("Accept-Encoding", "gzip");
75  
76          final Header[] headers = new Header[] {
77                  new BasicHeader("Date", DateUtils.formatDate(new Date())),
78                  new BasicHeader("Last-Modified", lastModified) };
79  
80          final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
81          final HttpRequest newRequest = impl.buildConditionalRequest(basicRequest, cacheEntry);
82  
83          Assert.assertEquals(theMethod, newRequest.getMethod());
84          Assert.assertEquals(theUri, newRequest.getRequestUri());
85          Assert.assertEquals(2, newRequest.getHeaders().length);
86  
87          Assert.assertEquals("Accept-Encoding", newRequest.getHeaders()[0].getName());
88          Assert.assertEquals("gzip", newRequest.getHeaders()[0].getValue());
89  
90          Assert.assertEquals("If-Modified-Since", newRequest.getHeaders()[1].getName());
91          Assert.assertEquals(lastModified, newRequest.getHeaders()[1].getValue());
92      }
93  
94      @Test
95      public void testConditionalRequestForEntryWithLastModifiedAndEtagIncludesBothAsValidators()
96              throws Exception {
97          final Date now = new Date();
98          final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
99          final Date twentySecondsAgo = new Date(now.getTime() - 20 * 1000L);
100         final String lmDate = DateUtils.formatDate(twentySecondsAgo);
101         final String etag = "\"etag\"";
102         final Header[] headers = {
103             new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
104             new BasicHeader("Last-Modified", lmDate),
105             new BasicHeader("ETag", etag)
106         };
107         final HttpRequest basicRequest = new BasicHttpRequest("GET", "/");
108         final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
109         final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry);
110         Assert.assertEquals(lmDate,
111                 result.getFirstHeader("If-Modified-Since").getValue());
112         Assert.assertEquals(etag,
113                 result.getFirstHeader("If-None-Match").getValue());
114     }
115 
116     @Test
117     public void testBuildConditionalRequestWithETag() {
118         final String theMethod = "GET";
119         final String theUri = "/theuri";
120         final String theETag = "this is my eTag";
121 
122         final HttpRequest basicRequest = new BasicHttpRequest(theMethod, theUri);
123         basicRequest.addHeader("Accept-Encoding", "gzip");
124 
125         final Header[] headers = new Header[] {
126                 new BasicHeader("Date", DateUtils.formatDate(new Date())),
127                 new BasicHeader("Last-Modified", DateUtils.formatDate(new Date())),
128                 new BasicHeader("ETag", theETag) };
129 
130         final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(headers);
131 
132         final HttpRequest newRequest = impl.buildConditionalRequest(basicRequest, cacheEntry);
133 
134         Assert.assertEquals(theMethod, newRequest.getMethod());
135         Assert.assertEquals(theUri, newRequest.getRequestUri());
136 
137         Assert.assertEquals(3, newRequest.getHeaders().length);
138 
139         Assert.assertEquals("Accept-Encoding", newRequest.getHeaders()[0].getName());
140         Assert.assertEquals("gzip", newRequest.getHeaders()[0].getValue());
141 
142         Assert.assertEquals("If-None-Match", newRequest.getHeaders()[1].getName());
143         Assert.assertEquals(theETag, newRequest.getHeaders()[1].getValue());
144     }
145 
146     @Test
147     public void testCacheEntryWithMustRevalidateDoesEndToEndRevalidation() throws Exception {
148         final HttpRequest basicRequest = new BasicHttpRequest("GET","/");
149         final Date now = new Date();
150         final Date elevenSecondsAgo = new Date(now.getTime() - 11 * 1000L);
151         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
152         final Date nineSecondsAgo = new Date(now.getTime() - 9 * 1000L);
153 
154         final Header[] cacheEntryHeaders = new Header[] {
155                 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
156                 new BasicHeader("ETag", "\"etag\""),
157                 new BasicHeader("Cache-Control","max-age=5, must-revalidate") };
158         final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders);
159 
160         final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry);
161 
162         boolean foundMaxAge0 = false;
163 
164         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL);
165         while (it.hasNext()) {
166             final HeaderElement elt = it.next();
167             if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) {
168                 foundMaxAge0 = true;
169             }
170         }
171         Assert.assertTrue(foundMaxAge0);
172     }
173 
174     @Test
175     public void testCacheEntryWithProxyRevalidateDoesEndToEndRevalidation() throws Exception {
176         final HttpRequest basicRequest = new BasicHttpRequest("GET", "/");
177         final Date now = new Date();
178         final Date elevenSecondsAgo = new Date(now.getTime() - 11 * 1000L);
179         final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L);
180         final Date nineSecondsAgo = new Date(now.getTime() - 9 * 1000L);
181 
182         final Header[] cacheEntryHeaders = new Header[] {
183                 new BasicHeader("Date", DateUtils.formatDate(tenSecondsAgo)),
184                 new BasicHeader("ETag", "\"etag\""),
185                 new BasicHeader("Cache-Control","max-age=5, proxy-revalidate") };
186         final HttpCacheEntry cacheEntry = HttpTestUtils.makeCacheEntry(elevenSecondsAgo, nineSecondsAgo, cacheEntryHeaders);
187 
188         final HttpRequest result = impl.buildConditionalRequest(basicRequest, cacheEntry);
189 
190         boolean foundMaxAge0 = false;
191         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL);
192         while (it.hasNext()) {
193             final HeaderElement elt = it.next();
194             if ("max-age".equalsIgnoreCase(elt.getName()) && "0".equals(elt.getValue())) {
195                 foundMaxAge0 = true;
196             }
197         }
198         Assert.assertTrue(foundMaxAge0);
199     }
200 
201     @Test
202     public void testBuildUnconditionalRequestUsesGETMethod()
203         throws Exception {
204         final HttpRequest result = impl.buildUnconditionalRequest(request);
205         Assert.assertEquals("GET", result.getMethod());
206     }
207 
208     @Test
209     public void testBuildUnconditionalRequestUsesRequestUri()
210         throws Exception {
211         final String uri = "/theURI";
212         request = new BasicHttpRequest("GET", uri);
213         final HttpRequest result = impl.buildUnconditionalRequest(request);
214         Assert.assertEquals(uri, result.getRequestUri());
215     }
216 
217     @Test
218     public void testBuildUnconditionalRequestAddsCacheControlNoCache()
219         throws Exception {
220         final HttpRequest result = impl.buildUnconditionalRequest(request);
221         boolean ccNoCacheFound = false;
222         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.CACHE_CONTROL);
223         while (it.hasNext()) {
224             final HeaderElement elt = it.next();
225             if ("no-cache".equals(elt.getName())) {
226                 ccNoCacheFound = true;
227             }
228         }
229         Assert.assertTrue(ccNoCacheFound);
230     }
231 
232     @Test
233     public void testBuildUnconditionalRequestAddsPragmaNoCache()
234         throws Exception {
235         final HttpRequest result = impl.buildUnconditionalRequest(request);
236         boolean ccNoCacheFound = false;
237         final Iterator<HeaderElement> it = MessageSupport.iterate(result, HeaderConstants.PRAGMA);
238         while (it.hasNext()) {
239             final HeaderElement elt = it.next();
240             if ("no-cache".equals(elt.getName())) {
241                 ccNoCacheFound = true;
242             }
243         }
244         Assert.assertTrue(ccNoCacheFound);
245     }
246 
247     @Test
248     public void testBuildUnconditionalRequestDoesNotUseIfRange()
249         throws Exception {
250         request.addHeader("If-Range","\"etag\"");
251         final HttpRequest result = impl.buildUnconditionalRequest(request);
252         Assert.assertNull(result.getFirstHeader("If-Range"));
253     }
254 
255     @Test
256     public void testBuildUnconditionalRequestDoesNotUseIfMatch()
257         throws Exception {
258         request.addHeader("If-Match","\"etag\"");
259         final HttpRequest result = impl.buildUnconditionalRequest(request);
260         Assert.assertNull(result.getFirstHeader("If-Match"));
261     }
262 
263     @Test
264     public void testBuildUnconditionalRequestDoesNotUseIfNoneMatch()
265         throws Exception {
266         request.addHeader("If-None-Match","\"etag\"");
267         final HttpRequest result = impl.buildUnconditionalRequest(request);
268         Assert.assertNull(result.getFirstHeader("If-None-Match"));
269     }
270 
271     @Test
272     public void testBuildUnconditionalRequestDoesNotUseIfUnmodifiedSince()
273         throws Exception {
274         request.addHeader("If-Unmodified-Since", DateUtils.formatDate(new Date()));
275         final HttpRequest result = impl.buildUnconditionalRequest(request);
276         Assert.assertNull(result.getFirstHeader("If-Unmodified-Since"));
277     }
278 
279     @Test
280     public void testBuildUnconditionalRequestDoesNotUseIfModifiedSince()
281         throws Exception {
282         request.addHeader("If-Modified-Since", DateUtils.formatDate(new Date()));
283         final HttpRequest result = impl.buildUnconditionalRequest(request);
284         Assert.assertNull(result.getFirstHeader("If-Modified-Since"));
285     }
286 
287     @Test
288     public void testBuildUnconditionalRequestCarriesOtherRequestHeaders()
289         throws Exception {
290         request.addHeader("User-Agent","MyBrowser/1.0");
291         final HttpRequest result = impl.buildUnconditionalRequest(request);
292         Assert.assertEquals("MyBrowser/1.0",
293                 result.getFirstHeader("User-Agent").getValue());
294     }
295 
296     @Test
297     public void testBuildConditionalRequestFromVariants() throws Exception {
298         final String etag1 = "\"123\"";
299         final String etag2 = "\"456\"";
300         final String etag3 = "\"789\"";
301 
302         final Map<String,Variant> variantEntries = new HashMap<>();
303         variantEntries.put(etag1, new Variant("A", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag1) })));
304         variantEntries.put(etag2, new Variant("B", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag2) })));
305         variantEntries.put(etag3, new Variant("C", HttpTestUtils.makeCacheEntry(new Header[] { new BasicHeader("ETag", etag3) })));
306 
307         final HttpRequest conditional = impl.buildConditionalRequestFromVariants(request, variantEntries);
308 
309         // seems like a lot of work, but necessary, check for existence and exclusiveness
310         String ifNoneMatch = conditional.getFirstHeader(HeaderConstants.IF_NONE_MATCH).getValue();
311         Assert.assertTrue(ifNoneMatch.contains(etag1));
312         Assert.assertTrue(ifNoneMatch.contains(etag2));
313         Assert.assertTrue(ifNoneMatch.contains(etag3));
314         ifNoneMatch = ifNoneMatch.replace(etag1, "");
315         ifNoneMatch = ifNoneMatch.replace(etag2, "");
316         ifNoneMatch = ifNoneMatch.replace(etag3, "");
317         ifNoneMatch = ifNoneMatch.replace(",","");
318         ifNoneMatch = ifNoneMatch.replace(" ", "");
319         Assert.assertEquals(ifNoneMatch, "");
320     }
321 
322 }