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
27
28
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
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
65
66
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
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
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
135 checkConvertInvalid("http:/foo/bar");
136 }
137
138
139
140
141
142
143
144 @org.junit.Test
145 public void testResolveValid() throws IOException
146 {
147
148
149
150
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
174 checkResolveValid("http://a/b/c/G","G");
175 }
176
177
178
179
180
181
182
183 @org.junit.Test
184 public void testResolveInvalid() throws IOException
185 {
186
187
188
189
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
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
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
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 }