View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.juneau.rest.util;
18  
19  import static org.apache.juneau.commons.utils.StringUtils.*;
20  import static org.apache.juneau.junit.bct.BctAssertions.*;
21  import static org.apache.juneau.rest.util.RestUtils.*;
22  import static org.junit.jupiter.api.Assertions.*;
23  
24  import java.io.*;
25  import java.util.*;
26  
27  import org.apache.juneau.*;
28  import org.apache.juneau.parser.ParseException;
29  import org.apache.juneau.rest.mock.*;
30  import org.apache.juneau.urlencoding.*;
31  import org.junit.jupiter.api.*;
32  
33  class RestUtils_Test extends TestBase {
34  
35  	//------------------------------------------------------------------------------------------------------------------
36  	// decode(String)
37  	//------------------------------------------------------------------------------------------------------------------
38  
39  	@Test void a01_testDecode() {
40  		assertNull(urlDecode(null));
41  		assertEquals("foo/bar baz  bing", urlDecode("foo%2Fbar+baz++bing"));
42  	}
43  
44  	//------------------------------------------------------------------------------------------------------------------
45  	// encode(String)
46  	//------------------------------------------------------------------------------------------------------------------
47  
48  	@Test void b01_testEncode() {
49  		assertNull(urlEncode(null));
50  		assertEquals("foo%2Fbar+baz++bing", urlEncode("foo/bar baz  bing"));
51  		assertEquals("foobar", urlEncode("foobar"));
52  		assertEquals("+", urlEncode(" "));
53  		assertEquals("%2F", urlEncode("/"));
54  	}
55  
56  	//------------------------------------------------------------------------------------------------------------------
57  	// trimSlashes(String)
58  	//------------------------------------------------------------------------------------------------------------------
59  
60  	@Test void d01_testTrimSlashes() {
61  		assertNull(trimSlashes(null));
62  		assertEquals("", trimSlashes(""));
63  		assertEquals("", trimSlashes("/"));
64  		assertEquals("", trimSlashes("//"));
65  		assertEquals("foo/bar", trimSlashes("foo/bar"));
66  		assertEquals("foo/bar", trimSlashes("foo/bar//"));
67  		assertEquals("foo/bar", trimSlashes("/foo/bar//"));
68  		assertEquals("foo/bar", trimSlashes("//foo/bar//"));
69  	}
70  
71  	//------------------------------------------------------------------------------------------------------------------
72  	// trimTrailingSlashes(String)
73  	//------------------------------------------------------------------------------------------------------------------
74  
75  	@Test void e01_testTrimTrailingSlashes() {
76  		assertNull(trimTrailingSlashes((String)null));
77  		assertEquals("", trimTrailingSlashes(""));
78  		assertEquals("", trimTrailingSlashes("/"));
79  		assertEquals("", trimTrailingSlashes("//"));
80  		assertEquals("foo/bar", trimTrailingSlashes("foo/bar"));
81  		assertEquals("foo/bar", trimTrailingSlashes("foo/bar//"));
82  		assertEquals("/foo/bar", trimTrailingSlashes("/foo/bar//"));
83  		assertEquals("//foo/bar", trimTrailingSlashes("//foo/bar//"));
84  	}
85  
86  	//------------------------------------------------------------------------------------------------------------------
87  	// getHttpResponseText(int)
88  	//------------------------------------------------------------------------------------------------------------------
89  
90  	@Test void i01_testGetHttpResponseText() {
91  		assertEquals("OK", getHttpResponseText(200));
92  		assertEquals("Created", getHttpResponseText(201));
93  		assertEquals("Accepted", getHttpResponseText(202));
94  		assertEquals("No Content", getHttpResponseText(204));
95  		assertEquals("Moved Permanently", getHttpResponseText(301));
96  		assertEquals("Temporary Redirect", getHttpResponseText(302));
97  		assertEquals("Not Modified", getHttpResponseText(304));
98  		assertEquals("Bad Request", getHttpResponseText(400));
99  		assertEquals("Unauthorized", getHttpResponseText(401));
100 		assertEquals("Forbidden", getHttpResponseText(403));
101 		assertEquals("Not Found", getHttpResponseText(404));
102 		assertEquals("Method Not Allowed", getHttpResponseText(405));
103 		assertEquals("Internal Server Error", getHttpResponseText(500));
104 		assertEquals("Not Implemented", getHttpResponseText(501));
105 		assertEquals("Service Unavailable", getHttpResponseText(503));
106 		assertEquals("Gateway Timeout", getHttpResponseText(504));
107 	}
108 
109 	@Test void i02_testGetHttpResponseTextInvalid() {
110 		assertNull(getHttpResponseText(0));
111 		assertNull(getHttpResponseText(99));
112 		assertNull(getHttpResponseText(600));
113 		assertNull(getHttpResponseText(-1));
114 	}
115 
116 	//------------------------------------------------------------------------------------------------------------------
117 	// getPathInfoUndecoded(HttpServletRequest)
118 	//------------------------------------------------------------------------------------------------------------------
119 
120 	@Test void j01_testGetPathInfoUndecoded_basic() {
121 		var req = MockServletRequest.create("GET", "/foo/bar");
122 		assertEquals("/foo/bar", getPathInfoUndecoded(req));
123 	}
124 
125 	@Test void j02_testGetPathInfoUndecoded_withContextPath() {
126 		var req = MockServletRequest.create("GET", "http://localhost:8080/context/foo/bar")
127 			.contextPath("/context");
128 		assertEquals("/foo/bar", getPathInfoUndecoded(req));
129 	}
130 
131 	@Test void j03_testGetPathInfoUndecoded_withServletPath() {
132 		var req = MockServletRequest.create("GET", "http://localhost:8080/api/foo/bar")
133 			.servletPath("/api");
134 		assertEquals("/foo/bar", getPathInfoUndecoded(req));
135 	}
136 
137 	@Test void j04_testGetPathInfoUndecoded_withContextAndServletPath() {
138 		var req = MockServletRequest.create("GET", "http://localhost:8080/context/api/foo/bar")
139 			.contextPath("/context")
140 			.servletPath("/api");
141 		assertEquals("/foo/bar", getPathInfoUndecoded(req));
142 	}
143 
144 	@Test void j05_testGetPathInfoUndecoded_noPathInfo() {
145 		var req = MockServletRequest.create("GET", "http://localhost:8080/context/api")
146 			.contextPath("/context")
147 			.servletPath("/api");
148 		assertNull(getPathInfoUndecoded(req));
149 	}
150 
151 	@Test void j06_testGetPathInfoUndecoded_encodedCharacters() {
152 		var req = MockServletRequest.create("GET", "/foo%2Fbar%20baz");
153 		assertEquals("/foo%2Fbar%20baz", getPathInfoUndecoded(req));
154 	}
155 
156 	@Test void j07_testGetPathInfoUndecoded_rootPath() {
157 		var req = MockServletRequest.create("GET", "/");
158 		assertEquals("/", getPathInfoUndecoded(req));
159 	}
160 
161 	@Test void j08_testGetPathInfoUndecoded_emptyContextAndServlet() {
162 		var req = MockServletRequest.create("GET", "/foo/bar")
163 			.contextPath("")
164 			.servletPath("");
165 		assertEquals("/foo/bar", getPathInfoUndecoded(req));
166 	}
167 
168 	//------------------------------------------------------------------------------------------------------------------
169 	// parseIfJson(String)
170 	//------------------------------------------------------------------------------------------------------------------
171 
172 	@Test void k01_testParseIfJson_null() throws Exception {
173 		assertNull(parseIfJson(null));
174 	}
175 
176 	@Test void k02_testParseIfJson_plainString() throws Exception {
177 		assertEquals("hello world", parseIfJson("hello world"));
178 		assertEquals("foo", parseIfJson("foo"));
179 		assertEquals("", parseIfJson(""));
180 		assertEquals("123abc", parseIfJson("123abc"));
181 	}
182 
183 	@Test void k03_testParseIfJson_jsonObject() throws Exception {
184 		var result = parseIfJson("{\"name\":\"John\",\"age\":30}");
185 		assertTrue(result instanceof Map);
186 		var map = (Map<String, Object>) result;
187 		assertEquals("John", map.get("name"));
188 		assertEquals(30, map.get("age"));
189 	}
190 
191 	@Test void k04_testParseIfJson_jsonObjectEmpty() throws Exception {
192 		var result = parseIfJson("{}");
193 		assertTrue(result instanceof Map);
194 		var map = (Map<String, Object>) result;
195 		assertTrue(map.isEmpty());
196 	}
197 
198 	@Test void k05_testParseIfJson_jsonArray() throws Exception {
199 		var result = parseIfJson("[1,2,3]");
200 		assertTrue(result instanceof List);
201 		var list = (List<Object>) result;
202 		assertEquals(3, list.size());
203 		assertEquals(1, list.get(0));
204 		assertEquals(2, list.get(1));
205 		assertEquals(3, list.get(2));
206 	}
207 
208 	@Test void k06_testParseIfJson_jsonArrayEmpty() throws Exception {
209 		var result = parseIfJson("[]");
210 		assertTrue(result instanceof List);
211 		var list = (List<Object>) result;
212 		assertTrue(list.isEmpty());
213 	}
214 
215 	@Test void k07_testParseIfJson_jsonBoolean() throws Exception {
216 		assertEquals(true, parseIfJson("true"));
217 		assertEquals(false, parseIfJson("false"));
218 	}
219 
220 	@Test void k08_testParseIfJson_jsonNull() throws Exception {
221 		assertNull(parseIfJson("null"));
222 	}
223 
224 	@Test void k09_testParseIfJson_jsonNumber() throws Exception {
225 		assertEquals(123, parseIfJson("123"));
226 		var result = parseIfJson("123.45");
227 		assertTrue(result instanceof Number);
228 		assertEquals(123.45, ((Number)result).doubleValue(), 0.001);
229 		assertEquals(-42, parseIfJson("-42"));
230 	}
231 
232 	@Test void k10_testParseIfJson_jsonWithWhitespace() throws Exception {
233 		var result = parseIfJson("  {\"key\":\"value\"}  ");
234 		assertTrue(result instanceof Map);
235 		var map = (Map<String, Object>) result;
236 		assertEquals("value", map.get("key"));
237 	}
238 
239 	@Test void k11_testParseIfJson_invalidJson() {
240 		// These strings are detected as JSON by isProbablyJson (start with {/} or [/]) but are invalid
241 		assertThrows(ParseException.class, () -> parseIfJson("{key:xxx}"));
242 		assertThrows(ParseException.class, () -> parseIfJson("{key:invalid value}"));
243 	}
244 
245 	@Test void k12_testParseIfJson_nestedStructures() throws Exception {
246 		var result = parseIfJson("{\"items\":[1,2,3],\"nested\":{\"a\":1,\"b\":2}}");
247 		assertTrue(result instanceof Map);
248 		var map = (Map<String, Object>) result;
249 		assertTrue(map.get("items") instanceof List);
250 		assertTrue(map.get("nested") instanceof Map);
251 	}
252 
253 	@Test void k13_testParseIfJson_singleQuotedString() throws Exception {
254 		var result = parseIfJson("'test string'");
255 		assertEquals("test string", result);
256 	}
257 
258 	//------------------------------------------------------------------------------------------------------------------
259 	// parseQuery(String)
260 	//------------------------------------------------------------------------------------------------------------------
261 
262 	@Test void g01_testParseQuery_null() {
263 		var m = parseQuery((String)null);
264 		assertTrue(m.isEmpty());
265 	}
266 
267 	@Test void g02_testParseQuery_emptyString() {
268 		var m = parseQuery("");
269 		assertTrue(m.isEmpty());
270 	}
271 
272 	@Test void g03_testParseQuery_whitespaceOnly() {
273 		var m = parseQuery("   ");
274 		assertTrue(m.isEmpty());
275 	}
276 
277 	//------------------------------------------------------------------------------------------------------------------
278 	// parseQuery(Reader)
279 	//------------------------------------------------------------------------------------------------------------------
280 
281 	@Test void g04_testParseQuery_readerNull() {
282 		var m = parseQuery((Reader)null);
283 		assertTrue(m.isEmpty());
284 	}
285 
286 	@Test void g05_testParseQuery_readerEmpty() throws Exception {
287 		var m = parseQuery(new StringReader(""));
288 		assertTrue(m.isEmpty());
289 	}
290 
291 	@Test void g06_testParseQuery_readerWhitespaceOnly() throws Exception {
292 		var m = parseQuery(new StringReader("   "));
293 		assertTrue(m.isEmpty());
294 	}
295 
296 	@Test void g07_testParseQuery_readerValid() throws Exception {
297 		var m = parseQuery(new StringReader("f1=v1&f2=v2"));
298 		assertEquals("v1", m.get("f1").get(0));
299 		assertEquals("v2", m.get("f2").get(0));
300 	}
301 
302 	//------------------------------------------------------------------------------------------------------------------
303 	// Test URL-encoded strings parsed into plain-text values using UrlEncodingParser.parseIntoSimpleMap().
304 	//------------------------------------------------------------------------------------------------------------------
305 
306 	@Test void g04_testParseIntoSimpleMap() {
307 		var s = "?f1=,()=&f2a=$b(true)&f2b=true&f3a=$n(123)&f3b=123&f4=$s(foo)";
308 		var m = parseQuery(s);
309 		assertEquals(",()=", m.get("f1").get(0));
310 		assertEquals("$b(true)", m.get("f2a").get(0));
311 		assertEquals("true", m.get("f2b").get(0));
312 		assertEquals("$n(123)", m.get("f3a").get(0));
313 		assertEquals("123", m.get("f3b").get(0));
314 		assertEquals("$s(foo)", m.get("f4").get(0));
315 
316 		s = "f1=v1&=";
317 		m = parseQuery(s);
318 		assertEquals("v1", m.get("f1").get(0));
319 		assertEquals("", m.get("").get(0));
320 
321 		s = "f1=v1&f2&f3";
322 		m = parseQuery(s);
323 		assertEquals("v1", m.get("f1").get(0));
324 		assertTrue(m.containsKey("f2"));
325 		assertTrue(m.containsKey("f3"));
326 		assertNull(m.get("f2"));
327 		assertNull(m.get("f3"));
328 	}
329 
330 	//------------------------------------------------------------------------------------------------------------------
331 	// Test parsing URL-encoded strings with multiple values.
332 	//------------------------------------------------------------------------------------------------------------------
333 
334 	@Test void h01_testParseIntoSimpleMapMultiValues() {
335 		var s = "?f1&f1&f2&f2=abc&f2=def&f2";
336 		var m = parseQuery(s);
337 		assertBean(m, "f1,f2", "<null>,[abc,def]");
338 	}
339 
340 	@Test void h02_testEmptyString() throws Exception {
341 		var p = UrlEncodingParser.DEFAULT;
342 
343 		var s = "";
344 		var b = p.parse(s, B.class);
345 		assertEquals("f1", b.f1);
346 	}
347 
348 	public static class B {
349 		public String f1 = "f1";
350 	}
351 
352 	//------------------------------------------------------------------------------------------------------------------
353 	// toValidContextPath(String)
354 	//------------------------------------------------------------------------------------------------------------------
355 
356 	@Test void m01_testToValidContextPath_null() {
357 		assertEquals("", toValidContextPath(null));
358 	}
359 
360 	@Test void m02_testToValidContextPath_empty() {
361 		assertEquals("", toValidContextPath(""));
362 	}
363 
364 	@Test void m03_testToValidContextPath_root() {
365 		assertEquals("", toValidContextPath("/"));
366 	}
367 
368 	@Test void m04_testToValidContextPath_noLeadingSlash() {
369 		assertEquals("/api", toValidContextPath("api"));
370 	}
371 
372 	@Test void m05_testToValidContextPath_withLeadingSlash() {
373 		assertEquals("/api", toValidContextPath("/api"));
374 	}
375 
376 	@Test void m06_testToValidContextPath_trailingSlash() {
377 		assertEquals("/api", toValidContextPath("api/"));
378 	}
379 
380 	@Test void m07_testToValidContextPath_bothSlashes() {
381 		assertEquals("/api", toValidContextPath("/api/"));
382 	}
383 
384 	@Test void m08_testToValidContextPath_multipleTrailingSlashes() {
385 		assertEquals("/api", toValidContextPath("/api///"));
386 	}
387 
388 	@Test void m09_testToValidContextPath_nestedPath() {
389 		assertEquals("/api/users", toValidContextPath("/api/users"));
390 	}
391 
392 	@Test void m10_testToValidContextPath_nestedPathTrailingSlash() {
393 		assertEquals("/api/users", toValidContextPath("/api/users/"));
394 	}
395 
396 	@Test void m11_testToValidContextPath_onlySlashes() {
397 		assertEquals("", toValidContextPath("///"));
398 	}
399 
400 	//------------------------------------------------------------------------------------------------------------------
401 	// validatePathInfo(String)
402 	//------------------------------------------------------------------------------------------------------------------
403 
404 	@Test void n01_testValidatePathInfo_null() {
405 		assertNull(validatePathInfo(null));
406 	}
407 
408 	@Test void n02_testValidatePathInfo_valid() {
409 		assertEquals("/users", validatePathInfo("/users"));
410 	}
411 
412 	@Test void n03_testValidatePathInfo_validNested() {
413 		assertEquals("/users/123", validatePathInfo("/users/123"));
414 	}
415 
416 	@Test void n04_testValidatePathInfo_empty() {
417 		assertThrows(RuntimeException.class, () -> validatePathInfo(""));
418 	}
419 
420 	@Test void n05_testValidatePathInfo_noLeadingSlash() {
421 		assertThrows(RuntimeException.class, () -> validatePathInfo("users"));
422 	}
423 
424 	@Test void n06_testValidatePathInfo_noLeadingSlashNested() {
425 		assertThrows(RuntimeException.class, () -> validatePathInfo("users/123"));
426 	}
427 
428 	//------------------------------------------------------------------------------------------------------------------
429 	// validateServletPath(String)
430 	//------------------------------------------------------------------------------------------------------------------
431 
432 	@Test void o01_testValidateServletPath_empty() {
433 		assertEquals("", validateServletPath(""));
434 	}
435 
436 	@Test void o02_testValidateServletPath_valid() {
437 		assertEquals("/api", validateServletPath("/api"));
438 	}
439 
440 	@Test void o03_testValidateServletPath_validNested() {
441 		assertEquals("/api/users", validateServletPath("/api/users"));
442 	}
443 
444 	@Test void o04_testValidateServletPath_null() {
445 		assertThrows(RuntimeException.class, () -> validateServletPath(null));
446 	}
447 
448 	@Test void o05_testValidateServletPath_root() {
449 		assertThrows(RuntimeException.class, () -> validateServletPath("/"));
450 	}
451 
452 	@Test void o06_testValidateServletPath_noLeadingSlash() {
453 		assertThrows(RuntimeException.class, () -> validateServletPath("api"));
454 	}
455 
456 	@Test void o07_testValidateServletPath_trailingSlash() {
457 		assertThrows(RuntimeException.class, () -> validateServletPath("/api/"));
458 	}
459 
460 	@Test void o08_testValidateServletPath_noLeadingSlashTrailingSlash() {
461 		assertThrows(RuntimeException.class, () -> validateServletPath("api/"));
462 	}
463 
464 	@Test void o09_testValidateServletPath_nestedTrailingSlash() {
465 		assertThrows(RuntimeException.class, () -> validateServletPath("/api/users/"));
466 	}
467 }