1 /*
2 * ========================================================================
3 *
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * 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, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * ========================================================================
20 */
21 package org.apache.cactus;
22
23 import javax.servlet.http.HttpServletRequest;
24
25 import org.apache.cactus.internal.server.ServletUtil;
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28
29 /**
30 * Simulate an HTTP URL by breaking it into its different parts.
31 * <br><code><pre><b>
32 * URL = "http://" + serverName (including port) + requestURI ? queryString<br>
33 * requestURI = contextPath + servletPath + pathInfo
34 * </b></pre></code>
35 * From the Servlet 2.2 specification :<br>
36 * <code><pre><ul><li><b>Context Path</b>: The path prefix associated with the
37 * ServletContext that this servlet is a part of. If this context is the
38 * default context rooted at the base of the web server's URL namespace, this
39 * path will be an empty string. Otherwise, this path starts with a "/"
40 * character but does not end with a "/" character.</li>
41 * <li><b>Servlet Path</b>: The path section that directly corresponds to the
42 * mapping which activated this request. This path starts with a "/"
43 * character.</li>
44 * <li><b>PathInfo</b>: The part of the request path that is not part of the
45 * Context Path or the Servlet Path.</li></ul></pre></code>
46 * From the Servlet 2.3 specification :<br>
47 * <code><pre><ul><li><b>Context Path</b>: The path prefix associated with the
48 * ServletContext that this servlet is a part of. If this context is the
49 * default context rooted at the base of the web server's URL namespace, this
50 * path will be an empty string. Otherwise, this path starts with a "/"
51 * character but does not end with a "/" character.</li>
52 * <li><b>Servlet Path</b>: The path section that directly corresponds to the
53 * mapping which activated this request. This path starts with a "/"
54 * character <b>except in the case where the request is matched with the
55 * "/*" pattern, in which case it is the empty string</b>.</li>
56 * <li><b>PathInfo</b>: The part of the request path that is not part of the
57 * Context Path or the Servlet Path. <b>It is either null if there is no
58 * extra path, or is a string with a leading "/"</b>.</li></ul></pre></code>
59 *
60 * @version $Id: ServletURL.java 238991 2004-05-22 11:34:50Z vmassol $
61 */
62 public class ServletURL
63 {
64 /**
65 * Name of the parameter in the HTTP request that represents the protocol
66 * (HTTP, HTTPS, etc) in the URL to simulate. The name is voluntarily long
67 * so that it will not clash with a user-defined parameter.
68 */
69 public static final String URL_PROTOCOL_PARAM = "Cactus_URL_Protocol";
70
71 /**
72 * Name of the parameter in the HTTP request that represents the Server
73 * name (+ port) in the URL to simulate. The name is voluntarily long so
74 * that it will not clash with a user-defined parameter.
75 */
76 public static final String URL_SERVER_NAME_PARAM = "Cactus_URL_Server";
77
78 /**
79 * Name of the parameter in the HTTP request that represents the context
80 * path in the URL to simulate. The name is voluntarily long so that it
81 * will not clash with a user-defined parameter.
82 */
83 public static final String URL_CONTEXT_PATH_PARAM =
84 "Cactus_URL_ContextPath";
85
86 /**
87 * Name of the parameter in the HTTP request that represents the Servlet
88 * Path in the URL to simulate. The name is voluntarily long so that it
89 * will not clash with a user-defined parameter.
90 */
91 public static final String URL_SERVLET_PATH_PARAM =
92 "Cactus_URL_ServletPath";
93
94 /**
95 * Name of the parameter in the HTTP request that represents the Path Info
96 * in the URL to simulate. The name is voluntarily long so that it will not
97 * clash with a user-defined parameter.
98 */
99 public static final String URL_PATH_INFO_PARAM = "Cactus_URL_PathInfo";
100
101 /**
102 * Name of the parameter in the HTTP request that represents the Query
103 * String in the URL to simulate. The name is voluntarily long so that it
104 * will not clash with a user-defined parameter.
105 */
106 public static final String URL_QUERY_STRING_PARAM =
107 "Cactus_URL_QueryString";
108
109 /**
110 * Http protocol.
111 */
112 public static final String PROTOCOL_HTTP = "http";
113
114 /**
115 * Https protocol.
116 */
117 public static final String PROTOCOL_HTTPS = "https";
118
119 /**
120 * Default port of the HTTP protocol.
121 */
122 private static final int DEFAULT_PORT_HTTP = 80;
123
124 /**
125 * Default port of HTTP over SSL.
126 */
127 private static final int DEFAULT_PORT_HTTPS = 443;
128
129 /**
130 * The logger.
131 */
132 private static final Log LOGGER = LogFactory.getLog(ServletURL.class);
133
134 /**
135 * The server name to simulate (including port number).
136 */
137 private String serverName;
138
139 /**
140 * The context path to simulate.
141 */
142 private String contextPath;
143
144 /**
145 * The servlet path to simulate.
146 */
147 private String servletPath;
148
149 /**
150 * The Path Info to simulate.
151 */
152 private String pathInfo;
153
154 /**
155 * The Query string.
156 */
157 private String queryString;
158
159 /**
160 * The protocol to use. Default to HTTP.
161 */
162 private String protocol = PROTOCOL_HTTP;
163
164 /**
165 * Default constructor. Need to call the different setters to make this
166 * a valid object.
167 */
168 public ServletURL()
169 {
170 }
171
172 /**
173 * Creates the URL to simulate.
174 *
175 * @param theProtocol the protocol to simulate (either
176 * <code>ServletURL.PROTOCOL_HTTP</code> or
177 * <code>ServletURL.PROTOCOL_HTTPS</code>.
178 * @param theServerName the server name (and port) in the URL to simulate,
179 * i.e. this is the name that will be returned by the
180 * <code>HttpServletRequest.getServerName()</code> and
181 * <code>HttpServletRequest.getServerPort()</code>. Can
182 * be null. If null, then the server name and port from
183 * the Servlet Redirector will be returned.
184 * @param theContextPath the webapp context path in the URL to simulate,
185 * i.e. this is the name that will be returned by the
186 * <code>HttpServletRequest.getContextPath()</code>.
187 * Can be null. If null, then the context from the
188 * Servlet Redirector will be used.
189 * Format: "/" + name or an empty string for the
190 * default context. Must not end with a "/" character.
191 * @param theServletPath the servlet path in the URL to simulate,
192 * i.e. this is the name that will be returned by the
193 * <code>HttpServletRequest.getServletPath()</code>.
194 * Can be null. If null, then the servlet path from
195 * the Servlet Redirector will be used.
196 * Format : "/" + name or an empty string.
197 * @param thePathInfo the path info in the URL to simulate, i.e. this is
198 * the name that will be returned by the
199 * <code>HttpServletRequest.getPathInfo()</code>. Can
200 * be null. Format : "/" + name.
201 * @param theQueryString the Query string in the URL to simulate, i.e. this
202 * is the string that will be returned by the
203 * <code>HttpServletResquest.getQueryString()</code>.
204 * Can be null.
205 */
206 public ServletURL(String theProtocol, String theServerName,
207 String theContextPath, String theServletPath, String thePathInfo,
208 String theQueryString)
209 {
210 setProtocol(theProtocol);
211 setServerName(theServerName);
212 setContextPath(theContextPath);
213 setServletPath(theServletPath);
214 setPathInfo(thePathInfo);
215 setQueryString(theQueryString);
216 }
217
218 /**
219 * Creates the URL to simulate, using the default HTTP protocol.
220 *
221 * @param theServerName the server name (and port) in the URL to simulate,
222 * i.e. this is the name that will be returned by the
223 * <code>HttpServletRequest.getServerName()</code> and
224 * <code>HttpServletRequest.getServerPort()</code>. Can
225 * be null. If null, then the server name and port from
226 * the Servlet Redirector will be returned.
227 * @param theContextPath the webapp context path in the URL to simulate,
228 * i.e. this is the name that will be returned by the
229 * <code>HttpServletRequest.getContextPath()</code>.
230 * Can be null. If null, then the context from the
231 * Servlet Redirector will be used.
232 * Format: "/" + name or an empty string for the
233 * default context. Must not end with a "/" character.
234 * @param theServletPath the servlet path in the URL to simulate,
235 * i.e. this is the name that will be returned by the
236 * <code>HttpServletRequest.getServletPath()</code>.
237 * Can be null. If null, then the servlet path from
238 * the Servlet Redirector will be used.
239 * Format : "/" + name or an empty string.
240 * @param thePathInfo the path info in the URL to simulate, i.e. this is
241 * the name that will be returned by the
242 * <code>HttpServletRequest.getPathInfo()</code>. Can
243 * be null. Format : "/" + name.
244 * @param theQueryString the Query string in the URL to simulate, i.e. this
245 * is the string that will be returned by the
246 * <code>HttpServletResquest.getQueryString()</code>.
247 * Can be null.
248 */
249 public ServletURL(String theServerName, String theContextPath,
250 String theServletPath, String thePathInfo, String theQueryString)
251 {
252 this(PROTOCOL_HTTP, theServerName, theContextPath, theServletPath,
253 thePathInfo, theQueryString);
254 }
255
256 /**
257 * @return the protocol used to connect to the URL (HTTP, HTTPS, etc).
258 */
259 public String getProtocol()
260 {
261 return this.protocol;
262 }
263
264 /**
265 * Sets the protocol to simulate (either
266 * <code>ServletURL.PROTOCOL_HTTP</code> or
267 * <code>ServletURL.PROTOCOL_HTTPS</code>. If parameter is null then
268 * PROTOCOL_HTTP is assumed.
269 *
270 * @param theProtocol the protocol to simulate
271 */
272 public void setProtocol(String theProtocol)
273 {
274 // Only HTTP and HTTPS are currently supported.
275 if ((!theProtocol.equals(PROTOCOL_HTTP))
276 && (!theProtocol.equals(PROTOCOL_HTTPS)))
277 {
278 throw new RuntimeException("Invalid protocol [" + theProtocol
279 + "]. Currently supported protocols are ["
280 + PROTOCOL_HTTP + "] and ["
281 + PROTOCOL_HTTPS + "].");
282 }
283
284 this.protocol = theProtocol;
285 }
286
287 /**
288 * @return the simulated URL server name (including the port number)
289 */
290 public String getServerName()
291 {
292 return this.serverName;
293 }
294
295 /**
296 * Sets the server name (and port) in the URL to simulate, ie this is the
297 * name that will be returned by the
298 * <code>HttpServletRequest.getServerName()</code> and
299 * <code>HttpServletRequest.getServerPort()</code>. Does not need to be
300 * set. If not set or null, then the server name and port from the Servlet
301 * Redirector will be returned.
302 *
303 * @param theServerName the server name and port (ex:
304 * "jakarta.apache.org:80")
305 */
306 public void setServerName(String theServerName)
307 {
308 this.serverName = theServerName;
309 }
310
311 /**
312 * Returns the host name.
313 *
314 * <p>
315 * The host name is extracted from the specified server name (as in
316 * <code><strong>jakarta.apache.org</strong>:80</code>). If the server
317 * name has not been set, this method will return <code>null</code>.
318 * </p>
319 *
320 * @return the simulated URL server name (excluding the port number)
321 */
322 public String getHost()
323 {
324 String host = getServerName();
325
326 if (host != null)
327 {
328 int pos = host.indexOf(":");
329
330 if (pos > 0)
331 {
332 host = host.substring(0, pos);
333 }
334 }
335
336 return host;
337 }
338
339 /**
340 * Returns the port.
341 *
342 * <p>
343 * The port is extracted from the specified server name (as in
344 * <code>jakarta.apache.org:<strong>80</strong></code>). If the server
345 * name doesn't contain a port number, the default port number is returned
346 * (80 for HTTP, 443 for HTTP over SSL). If a port number is specified but
347 * illegal, or the server name has not been set, this method will return
348 * -1.
349 * </p>
350 *
351 * @return the simulated port number or -1 if an illegal port has been
352 * specified
353 */
354 public int getPort()
355 {
356 int port = -1;
357
358 if (getServerName() != null)
359 {
360 int pos = getServerName().indexOf(":");
361
362 if (pos < 0)
363 {
364 // the server name doesn't contain a port specification, so use
365 // the default port for the protocol
366 port = getDefaultPort();
367 }
368 else
369 {
370 // parse the port encoded in the server name
371 try
372 {
373 port = Integer.parseInt(getServerName().substring(pos + 1));
374 if (port < 0)
375 {
376 port = -1;
377 }
378 }
379 catch (NumberFormatException e)
380 {
381 port = -1;
382 }
383 }
384 }
385
386 return port;
387 }
388
389 /**
390 * @return the simulated URL context path
391 */
392 public String getContextPath()
393 {
394 return this.contextPath;
395 }
396
397 /**
398 * Sets the webapp context path in the URL to simulate, ie this is the
399 * name that will be returned by the
400 * <code>HttpServletRequest.getContextPath()</code>. If not set, the
401 * context from the Servlet Redirector will be returned. Format: "/" +
402 * name or an empty string for the default context. If not an empty
403 * string the last character must not be "/".
404 *
405 * @param theContextPath the context path to simulate
406 */
407 public void setContextPath(String theContextPath)
408 {
409 if ((theContextPath != null) && (theContextPath.length() > 0))
410 {
411 if (!theContextPath.startsWith("/"))
412 {
413 throw new IllegalArgumentException("The Context Path must"
414 + " start with a \"/\" character.");
415 }
416 if (theContextPath.endsWith("/"))
417 {
418 throw new IllegalArgumentException("The Context Path must not"
419 + " end with a \"/\" character.");
420 }
421 }
422
423 this.contextPath = theContextPath;
424 }
425
426 /**
427 * @return the simulated URL servlet path
428 */
429 public String getServletPath()
430 {
431 return this.servletPath;
432 }
433
434 /**
435 * Sets the servlet path in the URL to simulate, ie this is the name that
436 * will be returned by the <code>HttpServletRequest.getServletPath()</code>.
437 * If null then the servlet path from the Servlet Redirector will be
438 * returned. Format : "/" + name or an empty string.
439 *
440 * @param theServletPath the servlet path to simulate
441 */
442 public void setServletPath(String theServletPath)
443 {
444 if ((theServletPath != null) && (theServletPath.length() > 0))
445 {
446 if (!theServletPath.startsWith("/"))
447 {
448 throw new IllegalArgumentException("The Servlet Path must"
449 + " start with a \"/\" character.");
450 }
451 }
452
453 this.servletPath = theServletPath;
454 }
455
456 /**
457 * @return the simulated URL path info
458 */
459 public String getPathInfo()
460 {
461 return this.pathInfo;
462 }
463
464 /**
465 * Sets the path info in the URL to simulate, ie this is the name that will
466 * be returned by the <code>HttpServletRequest.getPathInfo()</code>.
467 * If null then no path info will be set (and the Path Info from the
468 * Servlet Redirector will <b>not</b> be used).
469 * Format : "/" + name.
470 *
471 * @param thePathInfo the path info to simulate
472 */
473 public void setPathInfo(String thePathInfo)
474 {
475 if ((thePathInfo != null) && (thePathInfo.length() == 0))
476 {
477 throw new IllegalArgumentException("The Path Info must"
478 + " not be an empty string. Use null if you don't"
479 + " want to have a path info.");
480 }
481 else if (thePathInfo != null)
482 {
483 if (!thePathInfo.startsWith("/"))
484 {
485 throw new IllegalArgumentException("The Path Info must"
486 + " start with a \"/\" character.");
487 }
488 }
489
490 this.pathInfo = thePathInfo;
491 }
492
493 /**
494 * @return the simulated Query String
495 */
496 public String getQueryString()
497 {
498 return this.queryString;
499 }
500
501 /**
502 * Sets the Query string in the URL to simulate, ie this is the string that
503 * will be returned by the
504 * <code>HttpServletResquest.getQueryString()</code>. If not set, the
505 * query string from the Servlet Redirector will be returned.
506 *
507 * @param theQueryString the query string to simulate
508 */
509 public void setQueryString(String theQueryString)
510 {
511 this.queryString = theQueryString;
512 }
513
514 /**
515 * @return the path (contextPath + servletPath + pathInfo) or null if
516 * not set
517 */
518 public String getPath()
519 {
520 String path;
521
522 path = (getContextPath() == null) ? "" : getContextPath();
523 path += ((getServletPath() == null) ? "" : getServletPath());
524 path += ((getPathInfo() == null) ? "" : getPathInfo());
525
526 if (path.length() == 0)
527 {
528 path = null;
529 }
530
531 return path;
532 }
533
534 /**
535 * Saves the current URL to a <code>WebRequest</code> object.
536 *
537 * @param theRequest the object to which the current URL should be saved to
538 */
539 public void saveToRequest(WebRequest theRequest)
540 {
541 // Note: All these pareameters are passed in the URL. This is to allow
542 // the user to send whatever he wants in the request body. For example
543 // a file, ...
544 theRequest.addParameter(URL_PROTOCOL_PARAM, getProtocol(),
545 WebRequest.GET_METHOD);
546
547 if (getServerName() != null)
548 {
549 theRequest.addParameter(URL_SERVER_NAME_PARAM, getServerName(),
550 WebRequest.GET_METHOD);
551 }
552
553 if (getContextPath() != null)
554 {
555 theRequest.addParameter(URL_CONTEXT_PATH_PARAM, getContextPath(),
556 WebRequest.GET_METHOD);
557 }
558
559 if (getServletPath() != null)
560 {
561 theRequest.addParameter(URL_SERVLET_PATH_PARAM, getServletPath(),
562 WebRequest.GET_METHOD);
563 }
564
565 if (getPathInfo() != null)
566 {
567 theRequest.addParameter(URL_PATH_INFO_PARAM, getPathInfo(),
568 WebRequest.GET_METHOD);
569 }
570
571 if (getQueryString() != null)
572 {
573 theRequest.addParameter(URL_QUERY_STRING_PARAM, getQueryString(),
574 WebRequest.GET_METHOD);
575 }
576 }
577
578 /**
579 * Creates a <code>ServletURL</code> object by loading it's values from the
580 * HTTP request.
581 *
582 * @param theRequest the incoming HTTP request.
583 * @return the <code>ServletURL</code> object unserialized from the HTTP
584 * request
585 */
586 public static ServletURL loadFromRequest(HttpServletRequest theRequest)
587 {
588 String qString = theRequest.getQueryString();
589 boolean isDefined = false;
590
591 ServletURL url = new ServletURL();
592
593 String protocol = ServletUtil.getQueryStringParameter(qString,
594 URL_PROTOCOL_PARAM);
595
596 if (protocol != null)
597 {
598 isDefined = true;
599 url.setProtocol(protocol);
600 }
601
602 String serverName = ServletUtil.getQueryStringParameter(qString,
603 URL_SERVER_NAME_PARAM);
604
605 if (serverName != null)
606 {
607 isDefined = true;
608 url.setServerName(serverName);
609 }
610
611 String contextPath = ServletUtil.getQueryStringParameter(qString,
612 URL_CONTEXT_PATH_PARAM);
613
614 if (contextPath != null)
615 {
616 isDefined = true;
617 url.setContextPath(contextPath);
618 }
619
620 String servletPath = ServletUtil.getQueryStringParameter(qString,
621 URL_SERVLET_PATH_PARAM);
622
623 if (servletPath != null)
624 {
625 isDefined = true;
626 url.setServletPath(servletPath);
627 }
628
629 String pathInfo = ServletUtil.getQueryStringParameter(qString,
630 URL_PATH_INFO_PARAM);
631
632 if (pathInfo != null)
633 {
634 isDefined = true;
635 url.setPathInfo(pathInfo);
636 }
637
638 String queryString = ServletUtil.getQueryStringParameter(qString,
639 URL_QUERY_STRING_PARAM);
640
641 if (queryString != null)
642 {
643 isDefined = true;
644 url.setQueryString(queryString);
645 }
646
647 if (!isDefined)
648 {
649 LOGGER.debug("Undefined simulation URL");
650 url = null;
651 }
652 else
653 {
654 LOGGER.debug("Simulation URL = [" + url + "]");
655 }
656
657 return url;
658 }
659
660 /**
661 * @return a string representation
662 */
663 public String toString()
664 {
665 StringBuffer buffer = new StringBuffer();
666
667 buffer.append("protocol = [" + getProtocol() + "], ");
668 buffer.append("host name = [" + getHost() + "], ");
669 buffer.append("port = [" + getPort() + "], ");
670 buffer.append("context path = [" + getContextPath() + "], ");
671 buffer.append("servlet path = [" + getServletPath() + "], ");
672 buffer.append("path info = [" + getPathInfo() + "], ");
673 buffer.append("query string = [" + getQueryString() + "]");
674
675 return buffer.toString();
676 }
677
678 /**
679 * Returns the default port for the protocol.
680 *
681 * @return the default port (80 for HTTP, 443 for HTTP over SSL)
682 */
683 private int getDefaultPort()
684 {
685 if (PROTOCOL_HTTPS.equals(getProtocol()))
686 {
687 return DEFAULT_PORT_HTTPS;
688 }
689 else
690 {
691 return DEFAULT_PORT_HTTP;
692 }
693 }
694
695 }