1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.springframework.web.util;
18
19 import java.io.UnsupportedEncodingException;
20 import java.net.URLDecoder;
21 import java.util.LinkedHashMap;
22 import java.util.Map;
23 import java.util.Map.Entry;
24 import java.util.Properties;
25 import javax.servlet.http.HttpServletRequest;
26
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29
30 import org.springframework.util.LinkedMultiValueMap;
31 import org.springframework.util.MultiValueMap;
32 import org.springframework.util.StringUtils;
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public class UrlPathHelper {
49
50
51
52
53
54
55 private static final String WEBSPHERE_URI_ATTRIBUTE = "com.ibm.websphere.servlet.uri_non_decoded";
56
57 private static final Log logger = LogFactory.getLog(UrlPathHelper.class);
58
59 static volatile Boolean websphereComplianceFlag;
60
61
62 private boolean alwaysUseFullPath = false;
63
64 private boolean urlDecode = true;
65
66 private boolean removeSemicolonContent = true;
67
68 private String defaultEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING;
69
70
71
72
73
74
75
76
77 public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
78 this.alwaysUseFullPath = alwaysUseFullPath;
79 }
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95 public void setUrlDecode(boolean urlDecode) {
96 this.urlDecode = urlDecode;
97 }
98
99
100
101
102
103 public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
104 this.removeSemicolonContent = removeSemicolonContent;
105 }
106
107
108
109
110 public boolean shouldRemoveSemicolonContent() {
111 return this.removeSemicolonContent;
112 }
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127 public void setDefaultEncoding(String defaultEncoding) {
128 this.defaultEncoding = defaultEncoding;
129 }
130
131
132
133
134 protected String getDefaultEncoding() {
135 return this.defaultEncoding;
136 }
137
138
139
140
141
142
143
144
145
146
147
148 public String getLookupPathForRequest(HttpServletRequest request) {
149
150 if (this.alwaysUseFullPath) {
151 return getPathWithinApplication(request);
152 }
153
154 String rest = getPathWithinServletMapping(request);
155 if (!"".equals(rest)) {
156 return rest;
157 }
158 else {
159 return getPathWithinApplication(request);
160 }
161 }
162
163
164
165
166
167
168
169
170
171
172
173
174 public String getPathWithinServletMapping(HttpServletRequest request) {
175 String pathWithinApp = getPathWithinApplication(request);
176 String servletPath = getServletPath(request);
177 String path = getRemainingPath(pathWithinApp, servletPath, false);
178 if (path != null) {
179
180 return path;
181 }
182 else {
183
184 String pathInfo = request.getPathInfo();
185 if (pathInfo != null) {
186
187
188 return pathInfo;
189 }
190 if (!this.urlDecode) {
191
192
193
194
195 path = getRemainingPath(decodeInternal(request, pathWithinApp), servletPath, false);
196 if (path != null) {
197 return pathWithinApp;
198 }
199 }
200
201 return servletPath;
202 }
203 }
204
205
206
207
208
209
210
211 public String getPathWithinApplication(HttpServletRequest request) {
212 String contextPath = getContextPath(request);
213 String requestUri = getRequestUri(request);
214 String path = getRemainingPath(requestUri, contextPath, true);
215 if (path != null) {
216
217 return (StringUtils.hasText(path) ? path : "/");
218 }
219 else {
220 return requestUri;
221 }
222 }
223
224
225
226
227
228
229
230 private String getRemainingPath(String requestUri, String mapping, boolean ignoreCase) {
231 int index1 = 0;
232 int index2 = 0;
233 for (; (index1 < requestUri.length()) && (index2 < mapping.length()); index1++, index2++) {
234 char c1 = requestUri.charAt(index1);
235 char c2 = mapping.charAt(index2);
236 if (c1 == ';') {
237 index1 = requestUri.indexOf('/', index1);
238 if (index1 == -1) {
239 return null;
240 }
241 c1 = requestUri.charAt(index1);
242 }
243 if (c1 == c2) {
244 continue;
245 }
246 if (ignoreCase && (Character.toLowerCase(c1) == Character.toLowerCase(c2))) {
247 continue;
248 }
249 return null;
250 }
251 if (index2 != mapping.length()) {
252 return null;
253 }
254 if (index1 == requestUri.length()) {
255 return "";
256 }
257 else if (requestUri.charAt(index1) == ';') {
258 index1 = requestUri.indexOf('/', index1);
259 }
260 return (index1 != -1 ? requestUri.substring(index1) : "");
261 }
262
263
264
265
266
267
268
269
270
271
272
273
274 public String getRequestUri(HttpServletRequest request) {
275 String uri = (String) request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE);
276 if (uri == null) {
277 uri = request.getRequestURI();
278 }
279 return decodeAndCleanUriString(request, uri);
280 }
281
282
283
284
285
286
287
288
289
290 public String getContextPath(HttpServletRequest request) {
291 String contextPath = (String) request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE);
292 if (contextPath == null) {
293 contextPath = request.getContextPath();
294 }
295 if ("/".equals(contextPath)) {
296
297 contextPath = "";
298 }
299 return decodeRequestString(request, contextPath);
300 }
301
302
303
304
305
306
307
308
309
310 public String getServletPath(HttpServletRequest request) {
311 String servletPath = (String) request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE);
312 if (servletPath == null) {
313 servletPath = request.getServletPath();
314 }
315 if (servletPath.length() > 1 && servletPath.endsWith("/") && shouldRemoveTrailingServletPathSlash(request)) {
316
317
318
319 servletPath = servletPath.substring(0, servletPath.length() - 1);
320 }
321 return servletPath;
322 }
323
324
325
326
327
328
329 public String getOriginatingRequestUri(HttpServletRequest request) {
330 String uri = (String) request.getAttribute(WEBSPHERE_URI_ATTRIBUTE);
331 if (uri == null) {
332 uri = (String) request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE);
333 if (uri == null) {
334 uri = request.getRequestURI();
335 }
336 }
337 return decodeAndCleanUriString(request, uri);
338 }
339
340
341
342
343
344
345
346
347
348 public String getOriginatingContextPath(HttpServletRequest request) {
349 String contextPath = (String) request.getAttribute(WebUtils.FORWARD_CONTEXT_PATH_ATTRIBUTE);
350 if (contextPath == null) {
351 contextPath = request.getContextPath();
352 }
353 return decodeRequestString(request, contextPath);
354 }
355
356
357
358
359
360
361
362 public String getOriginatingServletPath(HttpServletRequest request) {
363 String servletPath = (String) request.getAttribute(WebUtils.FORWARD_SERVLET_PATH_ATTRIBUTE);
364 if (servletPath == null) {
365 servletPath = request.getServletPath();
366 }
367 return servletPath;
368 }
369
370
371
372
373
374
375
376 public String getOriginatingQueryString(HttpServletRequest request) {
377 if ((request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE) != null) ||
378 (request.getAttribute(WebUtils.ERROR_REQUEST_URI_ATTRIBUTE) != null)) {
379 return (String) request.getAttribute(WebUtils.FORWARD_QUERY_STRING_ATTRIBUTE);
380 }
381 else {
382 return request.getQueryString();
383 }
384 }
385
386
387
388
389 private String decodeAndCleanUriString(HttpServletRequest request, String uri) {
390 uri = removeSemicolonContent(uri);
391 uri = decodeRequestString(request, uri);
392 return uri;
393 }
394
395
396
397
398
399
400
401
402
403
404
405
406
407 public String decodeRequestString(HttpServletRequest request, String source) {
408 if (this.urlDecode) {
409 return decodeInternal(request, source);
410 }
411 return source;
412 }
413
414 @SuppressWarnings("deprecation")
415 private String decodeInternal(HttpServletRequest request, String source) {
416 String enc = determineEncoding(request);
417 try {
418 return UriUtils.decode(source, enc);
419 }
420 catch (UnsupportedEncodingException ex) {
421 if (logger.isWarnEnabled()) {
422 logger.warn("Could not decode request string [" + source + "] with encoding '" + enc +
423 "': falling back to platform default encoding; exception message: " + ex.getMessage());
424 }
425 return URLDecoder.decode(source);
426 }
427 }
428
429
430
431
432
433
434
435
436
437
438
439 protected String determineEncoding(HttpServletRequest request) {
440 String enc = request.getCharacterEncoding();
441 if (enc == null) {
442 enc = getDefaultEncoding();
443 }
444 return enc;
445 }
446
447
448
449
450
451
452
453
454 public String removeSemicolonContent(String requestUri) {
455 return this.removeSemicolonContent ?
456 removeSemicolonContentInternal(requestUri) : removeJsessionid(requestUri);
457 }
458
459 private String removeSemicolonContentInternal(String requestUri) {
460 int semicolonIndex = requestUri.indexOf(';');
461 while (semicolonIndex != -1) {
462 int slashIndex = requestUri.indexOf('/', semicolonIndex);
463 String start = requestUri.substring(0, semicolonIndex);
464 requestUri = (slashIndex != -1) ? start + requestUri.substring(slashIndex) : start;
465 semicolonIndex = requestUri.indexOf(';', semicolonIndex);
466 }
467 return requestUri;
468 }
469
470 private String removeJsessionid(String requestUri) {
471 int startIndex = requestUri.toLowerCase().indexOf(";jsessionid=");
472 if (startIndex != -1) {
473 int endIndex = requestUri.indexOf(';', startIndex + 12);
474 String start = requestUri.substring(0, startIndex);
475 requestUri = (endIndex != -1) ? start + requestUri.substring(endIndex) : start;
476 }
477 return requestUri;
478 }
479
480
481
482
483
484
485
486
487
488
489
490
491 public Map<String, String> decodePathVariables(HttpServletRequest request, Map<String, String> vars) {
492 if (this.urlDecode) {
493 return vars;
494 }
495 else {
496 Map<String, String> decodedVars = new LinkedHashMap<String, String>(vars.size());
497 for (Entry<String, String> entry : vars.entrySet()) {
498 decodedVars.put(entry.getKey(), decodeInternal(request, entry.getValue()));
499 }
500 return decodedVars;
501 }
502 }
503
504
505
506
507
508
509
510
511
512
513
514
515 public MultiValueMap<String, String> decodeMatrixVariables(HttpServletRequest request, MultiValueMap<String, String> vars) {
516 if (this.urlDecode) {
517 return vars;
518 }
519 else {
520 MultiValueMap<String, String> decodedVars = new LinkedMultiValueMap <String, String>(vars.size());
521 for (String key : vars.keySet()) {
522 for (String value : vars.get(key)) {
523 decodedVars.add(key, decodeInternal(request, value));
524 }
525 }
526 return decodedVars;
527 }
528 }
529
530 private boolean shouldRemoveTrailingServletPathSlash(HttpServletRequest request) {
531 if (request.getAttribute(WEBSPHERE_URI_ATTRIBUTE) == null) {
532
533
534
535 return false;
536 }
537 if (websphereComplianceFlag == null) {
538 ClassLoader classLoader = UrlPathHelper.class.getClassLoader();
539 String className = "com.ibm.ws.webcontainer.WebContainer";
540 String methodName = "getWebContainerProperties";
541 String propName = "com.ibm.ws.webcontainer.removetrailingservletpathslash";
542 boolean flag = false;
543 try {
544 Class<?> cl = classLoader.loadClass(className);
545 Properties prop = (Properties) cl.getMethod(methodName).invoke(null);
546 flag = Boolean.parseBoolean(prop.getProperty(propName));
547 }
548 catch (Throwable ex) {
549 if (logger.isDebugEnabled()) {
550 logger.debug("Could not introspect WebSphere web container properties: " + ex);
551 }
552 }
553 websphereComplianceFlag = flag;
554 }
555
556
557 return !websphereComplianceFlag;
558 }
559
560 }