View Javadoc
1   package de.juplo.httpresources;
2   
3   
4   import java.io.IOException;
5   import java.net.URI;
6   import java.time.Clock;
7   import java.time.ZoneId;
8   
9   import mockit.Mocked;
10  import org.junit.jupiter.api.DisplayName;
11  import org.junit.jupiter.api.Test;
12  import org.slf4j.Logger;
13  import org.slf4j.LoggerFactory;
14  
15  import static de.juplo.httpresources.TestUtil.*;
16  import static org.assertj.core.api.Assertions.fail;
17  import static org.junit.Assert.assertEquals;
18  import static org.junit.Assert.assertNotNull;
19  import static org.mockito.ArgumentMatchers.*;
20  import static org.mockito.Mockito.*;
21  
22  import org.springframework.cache.support.NoOpCache;
23  
24  
25  /**
26   * Tests for the methods {@link HttpResources#getResource(java.net.URI)}
27   * and {@link HttpResources#convert(java.lang.String)}
28   * @author Kai Moritz
29   */
30  public class HttpResourcesTest
31  {
32    private final static Logger LOG =
33        LoggerFactory.getLogger(HttpResourcesTest.class);
34  
35    String base = "http://a/b/c/d;p?q";
36  
37  
38    @Test
39    public void testGetResource()
40    {
41      LOG.info("<-- start of test-case");
42  
43      HttpResourceFetcher fetcher = mock(HttpResourceFetcher.class);
44      Clock clock = mock(Clock.class);
45  
46      HttpResources httpResources = new HttpResources(fetcher, clock);
47      URI uri = URI.create("http://foo/bar");
48  
49      // Everything is possible, nothing is necessary: Only defines behavior!
50      when(fetcher.fetch(any(), any())).thenReturn(DATA_NOT_EXPIRED);
51  
52      HttpResource resource = httpResources.getResource(uri);
53  
54      assertNotNull(resource);
55      assertEquals(HttpResource.class, resource.getClass());
56      assertEquals(uri, resource.getURI());
57      assertEquals(clock, resource.clock);
58      assertEquals(DATA_NOT_EXPIRED, resource.data);
59  
60      verify(fetcher).fetch(eq(uri), eq(HttpData.NOT_FETCHED));
61    }
62  
63    /**
64     * This test only checks the integration of the used methods
65     * Detailed tests for {@link URI}-handling are implemented in
66     * {@link HttpResourcesUriHandlingTest}.
67     */
68    @Test
69    @DisplayName("createRelative()")
70    public void testCreateRelative(@Mocked HttpResourceFetcher fetcher) throws Exception
71    {
72      Clock clock = Clock.fixed(NOW.toInstant(), ZoneId.of("GMT"));
73      HttpResources resources = new HttpResources(fetcher, clock);
74      HttpResource base = new HttpResource(resources, fetcher, clock, HttpResources.convert("http://a/b/c/d;p?q"));
75      HttpResource relative;
76  
77      relative = base.createRelative("g;x?y#s");
78      assertEquals(URI.create("http://a/b/c/g;x?y#s"), relative.getURI());
79      relative = base.createRelative("/g");
80      assertEquals(URI.create("http://a/b/c/g"), relative.getURI());
81      relative = base.createRelative("//g;x?y#s");
82      assertEquals(URI.create("http://a/b/c/?y#s"), relative.getURI());
83      relative = base.createRelative("../../g");
84      assertEquals(URI.create("http://a/b/c/g"), relative.getURI());
85    }
86  
87    @org.junit.Test
88    public void testConvertValid() throws Exception
89    {
90      // These examples are derived from RFC 2396 (page 29)
91      checkConvertValid("g", "g");
92      checkConvertValid("g", "./g");
93      checkConvertValid("g/", "g/");
94      checkConvertValid("/g", "/g");
95      checkConvertValid("//g/", "//g");
96      checkConvertValid("?y", "?y");
97      checkConvertValid("//g/?y", "//g?y"); // The extra slash needed in our use-case
98      checkConvertValid("//g/h?y", "//g/h?y");
99      checkConvertValid("g?y", "g?y");
100     checkConvertValid("//g/h?y", "//g/h?y");
101     checkConvertValid("#s", "#s");
102     checkConvertValid("g#s", "g#s");
103     checkConvertValid("g?y#s", "g?y#s");
104     checkConvertValid(";x", ";x");
105     checkConvertValid("g;x", "g;x");
106     checkConvertValid("g;x?y#s", "g;x?y#s");
107     checkConvertValid("", ".");
108     checkConvertValid("", "./");
109     checkConvertValid("..", "..");
110     checkConvertValid("../", "../");
111     checkConvertValid("../g", "../g");
112     checkConvertValid("../..", "../..");
113     checkConvertValid("../../", "../../");
114     checkConvertValid("../../g", "../../g");
115     checkConvertValid("http://g/", "http://g");
116     checkConvertValid("http://g/h", "http://g/h");
117     checkConvertValid("https://g/", "https://g");
118     checkConvertValid("https://g/h", "https://g/h");
119 
120     // Additionally examples for lower-case conversions
121     checkConvertValid("//g/", "//G");
122     checkConvertValid("//g/H/i/J", "//G/H/i/J");
123     checkConvertValid("http://g/", "HTtP://G");
124     checkConvertValid("http://g/H", "hTTP://G/H");
125     checkConvertValid("http://g.com/H", "hTTP://G.cOM/H");
126     checkConvertValid("https://g/", "HTTPs://G");
127     checkConvertValid("https://g/H", "HTTPs://G/H");
128     checkConvertValid("https://g.com/H/i/J", "HTTPs://g.COM/H/i/J");
129   }
130 
131   @org.junit.Test
132   public void testConvertInvalid() throws Exception
133   {
134     // Examples for invalid urls
135     checkConvertInvalid("http:/foo/bar");
136   }
137 
138   /**
139    * Checks the resolving of normal examples, with enabled enforcment of a
140    * relative path.
141    * @see https://www.ietf.org/rfc/rfc2396.txt
142    * @throws IOException
143    */
144   @org.junit.Test
145   public void testResolveValid() throws IOException
146   {
147     // These examples are taken from RFC 2396 (page 29), but the outcome is
148     // different, because the resolved path is forcefully turned into a
149     // relative URL.
150     //            "https:h","https:h"  << Not accepted, because opaque
151     checkResolveValid("http://a/b/c/g","g");
152     checkResolveValid("http://a/b/c/g","./g");
153     checkResolveValid("http://a/b/c/g/","g/");
154     checkResolveValid("http://a/b/c/g","/g");
155     checkResolveValid("http://a/b/c/g;x?y#s","/g;x?y#s");
156     checkResolveValid("http://a/b/c/?y","?y");
157     checkResolveValid("http://a/b/c/g?y","g?y");
158     checkResolveValid("http://a/b/c/d;p?q#s","#s");
159     checkResolveValid("http://a/b/c/g#s","g#s");
160     checkResolveValid("http://a/b/c/g?y#s","g?y#s");
161     checkResolveValid("http://a/b/c/;x",";x");
162     checkResolveValid("http://a/b/c/g;x","g;x");
163     checkResolveValid("http://a/b/c/g;x?y#s","g;x?y#s");
164     checkResolveValid("http://a/b/c/",".");
165     checkResolveValid("http://a/b/c/","./");
166     checkResolveValid("http://a/b/c/","..");
167     checkResolveValid("http://a/b/c/","../");
168     checkResolveValid("http://a/b/c/g","../g");
169     checkResolveValid("http://a/b/c/","../..");
170     checkResolveValid("http://a/b/c/","../../");
171     checkResolveValid("http://a/b/c/g","../../g");
172 
173     // Additionally examples for lower-case conversions
174     checkResolveValid("http://a/b/c/G","G");
175   }
176 
177   /**
178    * Checks the resolving of normal examples, with enabled enforcment of a
179    * relative path.
180    * @see https://www.ietf.org/rfc/rfc2396.txt
181    * @throws IOException
182    */
183   @org.junit.Test
184   public void testResolveInvalid() throws IOException
185   {
186     // These examples are taken from RFC 2396 (page 29), but the outcome is
187     // different, because the resolved path is forcefully turned into a
188     // relative URL.
189     //            "https:h","https:h"  << Not accepted, because opaque
190     checkResolveInvalid("http://a/b/c/","//g");
191     checkResolveInvalid("http://a/b/c/?y#s","//g;x?y#s"); // << g;x actually is an authority, because ';' is an allowed character! See RFC 2396 Chapter 3.2
192     checkResolveInvalid("http://a/b/c/?y#s","//g/;x?y#s");
193 
194     // Additionally examples for lower-case conversions
195     checkResolveInvalid("http://a/b/c/","//G");
196     checkResolveInvalid("http://a/b/c/","HTTPS://G");
197     checkResolveInvalid("http://a/b/c/H","HTTPS://G/H");
198 
199     // Additionally examples for absolute URI's
200     checkResolveInvalid("http://a/b/c/","https://g");
201     checkResolveInvalid("http://a/b/c/","https://g/.");
202     checkResolveInvalid("http://a/b/c/","https://g/./");
203     checkResolveInvalid("http://a/b/c/h","https://g/./h");
204     checkResolveInvalid("http://a/b/c/h/","https://g/./h/.");
205     checkResolveInvalid("http://a/b/c/h/","https://g/./h/./");
206     checkResolveInvalid("http://a/b/c/","https://g/./h/..");
207     checkResolveInvalid("http://a/b/c/","https://g/./h/../");
208     checkResolveInvalid("http://a/b/c/h","https://g/../h");
209     checkResolveInvalid("http://a/b/c/h/","https://g/../h/.");
210     checkResolveInvalid("http://a/b/c/h/","https://g/../h/./");
211     checkResolveInvalid("http://a/b/c/","https://g/../h/..");
212     checkResolveInvalid("http://a/b/c/","https://g/../h/../");
213     checkResolveInvalid("http://a/b/c/","http://h@g");
214     checkResolveInvalid("http://a/b/c/","http://g:1");
215     checkResolveInvalid("http://a/b/c/","http://h@g:1");
216     checkResolveInvalid("http://a/b/c/i","http://h@g:1/i");
217 
218     // Additionally examples for relative URI's with authority
219     checkResolveInvalid("http://a/b/c/","//g");
220     checkResolveInvalid("http://a/b/c/","//g/.");
221     checkResolveInvalid("http://a/b/c/","//g/./");
222     checkResolveInvalid("http://a/b/c/h","//g/./h");
223     checkResolveInvalid("http://a/b/c/h/","//g/./h/.");
224     checkResolveInvalid("http://a/b/c/h/","//g/./h/./");
225     checkResolveInvalid("http://a/b/c/","//g/./h/..");
226     checkResolveInvalid("http://a/b/c/","//g/./h/../");
227     checkResolveInvalid("http://a/b/c/h","//g/../h");
228     checkResolveInvalid("http://a/b/c/h/","//g/../h/.");
229     checkResolveInvalid("http://a/b/c/h/","//g/../h/./");
230     checkResolveInvalid("http://a/b/c/","//g/../h/..");
231     checkResolveInvalid("http://a/b/c/","//g/../h/../");
232     checkResolveInvalid("http://a/b/c/","//h@g");
233     checkResolveInvalid("http://a/b/c/","//g:1");
234     checkResolveInvalid("http://a/b/c/","//h@g:1");
235     checkResolveInvalid("http://a/b/c/i","//h@g:1/i");
236   }
237 
238   @org.junit.Test
239   public void testHostWithoutTrailingSlash() throws IOException {
240     checkResolveValid("http://localhost:1234/relative/path", "relative/path", "http://localhost:1234");
241     checkResolveValid("https://localhost:1234/relative/path", "relative/path", "https://localhost:1234");
242     checkResolveValid("//localhost:1234/relative/path", "relative/path", "//localhost:1234");
243     checkResolveValid("http://localhost:1234/absolute/path", "/absolute/path", "http://localhost:1234");
244     checkResolveValid("https://localhost:1234/absolute/path", "/absolute/path", "https://localhost:1234");
245     checkResolveValid("//localhost:1234/absolute/path", "/absolute/path", "//localhost:1234");
246   }
247 
248 
249   private void checkResolveValid(String result, String path)
250       throws
251       IOException
252   {
253     checkResolveValid(result, path, base);
254     URI relative = HttpResources.convert(path);
255   }
256 
257   private void checkResolveValid(String result, String path, String host)
258       throws
259       IOException
260   {
261     assertEquals(
262         URI.create(result),
263         HttpResources.resolve(URI.create(path), URI.create(host)));
264   }
265 
266   private void checkResolveInvalid(String result, String path)
267       throws
268       IOException
269   {
270     checkResolveInvalid(result, path, base);
271     URI relative = HttpResources.convert(path);
272   }
273 
274   private void checkResolveInvalid(String result, String path, String host)
275       throws
276       IOException
277   {
278     try
279     {
280       URI resolved = HttpResources.resolve(URI.create(path), URI.create(host));
281       fail("Resolved invalid relative path " + path + " against " + host + " as " + resolved);
282     }
283     catch (IOException e)
284     {
285     }
286   }
287 
288   private void checkConvertValid(String result, String url)
289   {
290     assertEquals(result, HttpResources.convert(url).toString());
291   }
292 
293   private void checkConvertInvalid(String url)
294   {
295     try
296     {
297       URI converted = HttpResources.convert(url);
298       fail("Converted invalid url path " + url + " to URI as " + converted);
299     }
300     catch (IllegalArgumentException e)
301     {
302     }
303   }
304 }