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.mockito.Mockito.mock;
30  import static org.mockito.Mockito.verify;
31  import static org.mockito.Mockito.when;
32  
33  import org.apache.http.Header;
34  import org.apache.http.HttpHost;
35  import org.apache.http.HttpRequest;
36  import org.apache.http.HttpVersion;
37  import org.apache.http.client.cache.HttpCacheEntry;
38  import org.apache.http.client.methods.HttpGet;
39  import org.apache.http.message.BasicHeader;
40  import org.apache.http.message.BasicHttpRequest;
41  import org.junit.Assert;
42  import org.junit.Before;
43  import org.junit.Test;
44  
45  @SuppressWarnings({"boxing","static-access"}) // this is test code
46  public class TestCacheKeyGenerator {
47  
48      private static final BasicHttpRequest REQUEST_FULL_EPISODES = new BasicHttpRequest("GET",
49              "/full_episodes");
50      private static final BasicHttpRequest REQUEST_ROOT = new BasicHttpRequest("GET", "/");
51  
52      CacheKeyGenerator extractor;
53      private HttpHost defaultHost;
54      private HttpCacheEntry mockEntry;
55      private HttpRequest mockRequest;
56  
57      @Before
58      public void setUp() throws Exception {
59          defaultHost = new HttpHost("foo.example.com");
60          mockEntry = mock(HttpCacheEntry.class);
61          mockRequest = mock(HttpRequest.class);
62          extractor = new CacheKeyGenerator();
63      }
64  
65      @Test
66      public void testExtractsUriFromAbsoluteUriInRequest() {
67          final HttpHost host = new HttpHost("bar.example.com");
68          final HttpRequest req = new HttpGet("http://foo.example.com/");
69          Assert.assertEquals("http://foo.example.com:80/", extractor.getURI(host, req));
70      }
71  
72      @Test
73      public void testGetURIWithDefaultPortAndScheme() {
74          Assert.assertEquals("http://www.comcast.net:80/", extractor.getURI(new HttpHost(
75                  "www.comcast.net"), REQUEST_ROOT));
76  
77          Assert.assertEquals("http://www.fancast.com:80/full_episodes", extractor.getURI(new HttpHost(
78                  "www.fancast.com"), REQUEST_FULL_EPISODES));
79      }
80  
81      @Test
82      public void testGetURIWithDifferentScheme() {
83          Assert.assertEquals("https://www.comcast.net:443/", extractor.getURI(new HttpHost(
84                  "www.comcast.net", -1, "https"), REQUEST_ROOT));
85  
86          Assert.assertEquals("myhttp://www.fancast.com/full_episodes", extractor.getURI(
87                  new HttpHost("www.fancast.com", -1, "myhttp"), REQUEST_FULL_EPISODES));
88      }
89  
90      @Test
91      public void testGetURIWithDifferentPort() {
92          Assert.assertEquals("http://www.comcast.net:8080/", extractor.getURI(new HttpHost(
93                  "www.comcast.net", 8080), REQUEST_ROOT));
94  
95          Assert.assertEquals("http://www.fancast.com:9999/full_episodes", extractor.getURI(
96                  new HttpHost("www.fancast.com", 9999), REQUEST_FULL_EPISODES));
97      }
98  
99      @Test
100     public void testGetURIWithDifferentPortAndScheme() {
101         Assert.assertEquals("https://www.comcast.net:8080/", extractor.getURI(new HttpHost(
102                 "www.comcast.net", 8080, "https"), REQUEST_ROOT));
103 
104         Assert.assertEquals("myhttp://www.fancast.com:9999/full_episodes", extractor.getURI(
105                 new HttpHost("www.fancast.com", 9999, "myhttp"), REQUEST_FULL_EPISODES));
106     }
107 
108     @Test
109     public void testGetURIWithQueryParameters() {
110         Assert.assertEquals("http://www.comcast.net:80/?foo=bar", extractor.getURI(new HttpHost(
111                 "www.comcast.net", -1, "http"), new BasicHttpRequest("GET", "/?foo=bar")));
112         Assert.assertEquals("http://www.fancast.com:80/full_episodes?foo=bar", extractor.getURI(
113                 new HttpHost("www.fancast.com", -1, "http"), new BasicHttpRequest("GET",
114                         "/full_episodes?foo=bar")));
115     }
116 
117     @Test
118     public void testGetVariantURIWithNoVaryHeaderReturnsNormalURI() {
119         final String theURI = "theURI";
120         when(mockEntry.hasVariants()).thenReturn(false);
121         extractor = new CacheKeyGenerator() {
122             @Override
123             public String getURI(final HttpHost h, final HttpRequest req) {
124                 Assert.assertSame(defaultHost, h);
125                 Assert.assertSame(mockRequest, req);
126                 return theURI;
127             }
128         };
129 
130         final String result = extractor.getVariantURI(defaultHost, mockRequest, mockEntry);
131         verify(mockEntry).hasVariants();
132         Assert.assertSame(theURI, result);
133     }
134 
135     @Test
136     public void testGetVariantURIWithSingleValueVaryHeaderPrepends() {
137         final String theURI = "theURI";
138         final Header[] varyHeaders = { new BasicHeader("Vary", "Accept-Encoding") };
139         final Header[] encHeaders = { new BasicHeader("Accept-Encoding", "gzip") };
140 
141         extractor = new CacheKeyGenerator() {
142             @Override
143             public String getURI(final HttpHost h, final HttpRequest req) {
144                 Assert.assertSame(defaultHost, h);
145                 Assert.assertSame(mockRequest, req);
146                 return theURI;
147             }
148         };
149         when(mockEntry.hasVariants()).thenReturn(true);
150         when(mockEntry.getHeaders("Vary")).thenReturn(varyHeaders);
151         when(mockRequest.getHeaders("Accept-Encoding")).thenReturn(
152                 encHeaders);
153 
154         final String result = extractor.getVariantURI(defaultHost, mockRequest, mockEntry);
155 
156         verify(mockEntry).hasVariants();
157         verify(mockEntry).getHeaders("Vary");
158         verify(mockRequest).getHeaders("Accept-Encoding");
159         Assert.assertEquals("{Accept-Encoding=gzip}" + theURI, result);
160     }
161 
162     @Test
163     public void testGetVariantURIWithMissingRequestHeader() {
164         final String theURI = "theURI";
165         final Header[] noHeaders = new Header[0];
166         final Header[] varyHeaders = { new BasicHeader("Vary", "Accept-Encoding") };
167         extractor = new CacheKeyGenerator() {
168             @Override
169             public String getURI(final HttpHost h, final HttpRequest req) {
170                 Assert.assertSame(defaultHost, h);
171                 Assert.assertSame(mockRequest, req);
172                 return theURI;
173             }
174         };
175         when(mockEntry.hasVariants()).thenReturn(true);
176         when(mockEntry.getHeaders("Vary")).thenReturn(varyHeaders);
177         when(mockRequest.getHeaders("Accept-Encoding"))
178                 .thenReturn(noHeaders);
179 
180         final String result = extractor.getVariantURI(defaultHost, mockRequest, mockEntry);
181 
182         verify(mockEntry).hasVariants();
183         verify(mockEntry).getHeaders("Vary");
184         verify(mockRequest).getHeaders("Accept-Encoding");
185         Assert.assertEquals("{Accept-Encoding=}" + theURI, result);
186     }
187 
188     @Test
189     public void testGetVariantURIAlphabetizesWithMultipleVaryingHeaders() {
190         final String theURI = "theURI";
191         final Header[] varyHeaders = { new BasicHeader("Vary", "User-Agent, Accept-Encoding") };
192         final Header[] encHeaders = { new BasicHeader("Accept-Encoding", "gzip") };
193         final Header[] uaHeaders = { new BasicHeader("User-Agent", "browser") };
194         extractor = new CacheKeyGenerator() {
195             @Override
196             public String getURI(final HttpHost h, final HttpRequest req) {
197                 Assert.assertSame(defaultHost, h);
198                 Assert.assertSame(mockRequest, req);
199                 return theURI;
200             }
201         };
202         when(mockEntry.hasVariants()).thenReturn(true);
203         when(mockEntry.getHeaders("Vary")).thenReturn(varyHeaders);
204         when(mockRequest.getHeaders("Accept-Encoding")).thenReturn(
205                 encHeaders);
206         when(mockRequest.getHeaders("User-Agent")).thenReturn(uaHeaders);
207 
208         final String result = extractor.getVariantURI(defaultHost, mockRequest, mockEntry);
209 
210         verify(mockEntry).hasVariants();
211         verify(mockEntry).getHeaders("Vary");
212         verify(mockRequest).getHeaders("Accept-Encoding");
213         verify(mockRequest).getHeaders("User-Agent");
214         Assert.assertEquals("{Accept-Encoding=gzip&User-Agent=browser}" + theURI, result);
215     }
216 
217     @Test
218     public void testGetVariantURIHandlesMultipleVaryHeaders() {
219         final String theURI = "theURI";
220         final Header[] varyHeaders = { new BasicHeader("Vary", "User-Agent"),
221                 new BasicHeader("Vary", "Accept-Encoding") };
222         final Header[] encHeaders = { new BasicHeader("Accept-Encoding", "gzip") };
223         final Header[] uaHeaders = { new BasicHeader("User-Agent", "browser") };
224         extractor = new CacheKeyGenerator() {
225             @Override
226             public String getURI(final HttpHost h, final HttpRequest req) {
227                 Assert.assertSame(defaultHost, h);
228                 Assert.assertSame(mockRequest, req);
229                 return theURI;
230             }
231         };
232         when(mockEntry.hasVariants()).thenReturn(true);
233         when(mockEntry.getHeaders("Vary")).thenReturn(varyHeaders);
234         when(mockRequest.getHeaders("Accept-Encoding")).thenReturn(encHeaders);
235         when(mockRequest.getHeaders("User-Agent")).thenReturn(uaHeaders);
236 
237         final String result = extractor.getVariantURI(defaultHost, mockRequest, mockEntry);
238 
239         verify(mockEntry).hasVariants();
240         verify(mockEntry).getHeaders("Vary");
241         verify(mockRequest).getHeaders("Accept-Encoding");
242         verify(mockRequest).getHeaders("User-Agent");
243         Assert.assertEquals("{Accept-Encoding=gzip&User-Agent=browser}" + theURI, result);
244     }
245 
246     @Test
247     public void testGetVariantURIHandlesMultipleLineRequestHeaders() {
248         final String theURI = "theURI";
249         final Header[] varyHeaders = { new BasicHeader("Vary", "User-Agent, Accept-Encoding") };
250         final Header[] encHeaders = { new BasicHeader("Accept-Encoding", "gzip"),
251                 new BasicHeader("Accept-Encoding", "deflate") };
252         final Header[] uaHeaders = { new BasicHeader("User-Agent", "browser") };
253         extractor = new CacheKeyGenerator() {
254             @Override
255             public String getURI(final HttpHost h, final HttpRequest req) {
256                 Assert.assertSame(defaultHost, h);
257                 Assert.assertSame(mockRequest, req);
258                 return theURI;
259             }
260         };
261         when(mockEntry.hasVariants()).thenReturn(true);
262         when(mockEntry.getHeaders("Vary")).thenReturn(varyHeaders);
263         when(mockRequest.getHeaders("Accept-Encoding")).thenReturn(encHeaders);
264         when(mockRequest.getHeaders("User-Agent")).thenReturn(uaHeaders);
265 
266         final String result = extractor.getVariantURI(defaultHost, mockRequest, mockEntry);
267 
268         verify(mockEntry).hasVariants();
269         verify(mockEntry).getHeaders("Vary");
270         verify(mockRequest).getHeaders("Accept-Encoding");
271         verify(mockRequest).getHeaders("User-Agent");
272         Assert
273                 .assertEquals("{Accept-Encoding=gzip%2C+deflate&User-Agent=browser}" + theURI,
274                         result);
275     }
276 
277     /*
278      * "When comparing two URIs to decide if they match or not, a client
279      * SHOULD use a case-sensitive octet-by-octet comparison of the entire
280      * URIs, with these exceptions:
281      * - A port that is empty or not given is equivalent to the default
282      * port for that URI-reference;
283      * - Comparisons of host names MUST be case-insensitive;
284      * - Comparisons of scheme names MUST be case-insensitive;
285      * - An empty abs_path is equivalent to an abs_path of "/".
286      * Characters other than those in the 'reserved' and 'unsafe' sets
287      * (see RFC 2396 [42]) are equivalent to their '"%" HEX HEX' encoding."
288      *
289      * http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.2.3
290      */
291     @Test
292     public void testEmptyPortEquivalentToDefaultPortForHttp() {
293         final HttpHost host1 = new HttpHost("foo.example.com:");
294         final HttpHost host2 = new HttpHost("foo.example.com:80");
295         final HttpRequest req = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
296         Assert.assertEquals(extractor.getURI(host1, req), extractor.getURI(host2, req));
297     }
298 
299     @Test
300     public void testEmptyPortEquivalentToDefaultPortForHttps() {
301         final HttpHost host1 = new HttpHost("foo.example.com", -1, "https");
302         final HttpHost host2 = new HttpHost("foo.example.com", 443, "https");
303         final HttpRequest req = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
304         final String uri1 = extractor.getURI(host1, req);
305         final String uri2 = extractor.getURI(host2, req);
306         Assert.assertEquals(uri1, uri2);
307     }
308 
309     @Test
310     public void testEmptyPortEquivalentToDefaultPortForHttpsAbsoluteURI() {
311         final HttpHost host = new HttpHost("foo.example.com", -1, "https");
312         final HttpGet get1 = new HttpGet("https://bar.example.com:/");
313         final HttpGet get2 = new HttpGet("https://bar.example.com:443/");
314         final String uri1 = extractor.getURI(host, get1);
315         final String uri2 = extractor.getURI(host, get2);
316         Assert.assertEquals(uri1, uri2);
317     }
318 
319     @Test
320     public void testNotProvidedPortEquivalentToDefaultPortForHttpsAbsoluteURI() {
321         final HttpHost host = new HttpHost("foo.example.com", -1, "https");
322         final HttpGet get1 = new HttpGet("https://bar.example.com/");
323         final HttpGet get2 = new HttpGet("https://bar.example.com:443/");
324         final String uri1 = extractor.getURI(host, get1);
325         final String uri2 = extractor.getURI(host, get2);
326         Assert.assertEquals(uri1, uri2);
327     }
328 
329     @Test
330     public void testNotProvidedPortEquivalentToDefaultPortForHttp() {
331         final HttpHost host1 = new HttpHost("foo.example.com");
332         final HttpHost host2 = new HttpHost("foo.example.com:80");
333         final HttpRequest req = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
334         Assert.assertEquals(extractor.getURI(host1, req), extractor.getURI(host2, req));
335     }
336 
337     @Test
338     public void testHostNameComparisonsAreCaseInsensitive() {
339         final HttpHost host1 = new HttpHost("foo.example.com");
340         final HttpHost host2 = new HttpHost("FOO.EXAMPLE.COM");
341         final HttpRequest req = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
342         Assert.assertEquals(extractor.getURI(host1, req), extractor.getURI(host2, req));
343     }
344 
345     @Test
346     public void testSchemeNameComparisonsAreCaseInsensitive() {
347         final HttpHost host1 = new HttpHost("foo.example.com", -1, "http");
348         final HttpHost host2 = new HttpHost("foo.example.com", -1, "HTTP");
349         final HttpRequest req = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
350         Assert.assertEquals(extractor.getURI(host1, req), extractor.getURI(host2, req));
351     }
352 
353     @Test
354     public void testEmptyAbsPathIsEquivalentToSlash() {
355         final HttpHost host = new HttpHost("foo.example.com");
356         final HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
357         final HttpRequest req2 = new HttpGet("http://foo.example.com");
358         Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
359     }
360 
361     @Test
362     public void testExtraDotSegmentsAreIgnored() {
363         final HttpHost host = new HttpHost("foo.example.com");
364         final HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
365         final HttpRequest req2 = new HttpGet("http://foo.example.com/./");
366         Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
367     }
368 
369     @Test
370     public void testExtraDotDotSegmentsAreIgnored() {
371         final HttpHost host = new HttpHost("foo.example.com");
372         final HttpRequest req1 = new BasicHttpRequest("GET", "/", HttpVersion.HTTP_1_1);
373         final HttpRequest req2 = new HttpGet("http://foo.example.com/.././../");
374         Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
375     }
376 
377     @Test
378     public void testIntermidateDotDotSegementsAreEquivalent() {
379         final HttpHost host = new HttpHost("foo.example.com");
380         final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html", HttpVersion.HTTP_1_1);
381         final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/../home.html", HttpVersion.HTTP_1_1);
382         Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
383     }
384 
385     @Test
386     public void testIntermidateEncodedDotDotSegementsAreEquivalent() {
387         final HttpHost host = new HttpHost("foo.example.com");
388         final HttpRequest req1 = new BasicHttpRequest("GET", "/home.html", HttpVersion.HTTP_1_1);
389         final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith%2F../home.html", HttpVersion.HTTP_1_1);
390         Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
391     }
392 
393     @Test
394     public void testIntermidateDotSegementsAreEquivalent() {
395         final HttpHost host = new HttpHost("foo.example.com");
396         final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html", HttpVersion.HTTP_1_1);
397         final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/./home.html", HttpVersion.HTTP_1_1);
398         Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
399     }
400 
401     @Test
402     public void testEquivalentPathEncodingsAreEquivalent() {
403         final HttpHost host = new HttpHost("foo.example.com");
404         final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html", HttpVersion.HTTP_1_1);
405         final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith/home.html", HttpVersion.HTTP_1_1);
406         Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
407     }
408 
409     @Test
410     public void testEquivalentExtraPathEncodingsAreEquivalent() {
411         final HttpHost host = new HttpHost("foo.example.com");
412         final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home.html", HttpVersion.HTTP_1_1);
413         final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith%2Fhome.html", HttpVersion.HTTP_1_1);
414         Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
415     }
416 
417     @Test
418     public void testEquivalentExtraPathEncodingsWithPercentAreEquivalent() {
419         final HttpHost host = new HttpHost("foo.example.com");
420         final HttpRequest req1 = new BasicHttpRequest("GET", "/~smith/home%20folder.html", HttpVersion.HTTP_1_1);
421         final HttpRequest req2 = new BasicHttpRequest("GET", "/%7Esmith%2Fhome%20folder.html", HttpVersion.HTTP_1_1);
422         Assert.assertEquals(extractor.getURI(host, req1), extractor.getURI(host, req2));
423     }
424 }