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.server;
22
23 import java.io.BufferedReader;
24 import java.io.File;
25 import java.io.IOException;
26
27 import java.lang.reflect.Constructor;
28
29 import java.security.Principal;
30
31 import java.util.Enumeration;
32 import java.util.Locale;
33
34 import javax.servlet.RequestDispatcher;
35 import javax.servlet.ServletInputStream;
36 import javax.servlet.http.Cookie;
37 import javax.servlet.http.HttpServletRequest;
38 import javax.servlet.http.HttpSession;
39
40 import org.apache.cactus.ServletURL;
41 import org.apache.cactus.util.ChainedRuntimeException;
42
43 import org.apache.commons.logging.Log;
44 import org.apache.commons.logging.LogFactory;
45
46
47 /**
48 * Abstract wrapper around {@link HttpServletRequest}. This class provides
49 * a common implementation of the wrapper for the different Servlet APIs.
50 * This is an implementation that delegates all the call to the
51 * {@link HttpServletRequest} object passed in the constructor except for
52 * some overidden methods which are use to simulate a URL. This is to be able
53 * to simulate any URL that would have been used to call the test method : if
54 * this was not done, the URL that would be returned (by calling the
55 * {@link HttpServletRequest#getRequestURI()} method or others alike) would be
56 * the URL of the Cactus redirector servlet and not a URL that the test case
57 * want to simulate.
58 *
59 * @version $Id: AbstractHttpServletRequestWrapper.java 292559 2005-09-29 21:36:43Z kenney $
60 */
61 public abstract class AbstractHttpServletRequestWrapper
62 implements HttpServletRequest
63 {
64 /**
65 * The logger.
66 */
67 private static final Log LOGGER =
68 LogFactory.getLog(AbstractHttpServletRequestWrapper.class);
69
70 /**
71 * The real HTTP request.
72 */
73 protected HttpServletRequest request;
74
75 /**
76 * The URL to simulate.
77 */
78 protected ServletURL url;
79
80 /**
81 * Remote IP address to simulate (if any).
82 * @see #setRemoteIPAddress(String)
83 */
84 protected String remoteIPAddress;
85
86 /**
87 * Remote Host name to simulate (if any).
88 * @see #setRemoteHostName(String)
89 */
90 protected String remoteHostName;
91
92 /**
93 * Remote user to simulate (if any).
94 * @see #setRemoteUser(String)
95 */
96 protected String remoteUser;
97
98 // New methods not in the interface --------------------------------------
99
100 /**
101 * Construct an <code>HttpServletRequest</code> instance that delegates
102 * it's method calls to the request object passed as parameter and that
103 * uses the URL passed as parameter to simulate a URL from which the request
104 * would come from.
105 *
106 * @param theRequest the real HTTP request
107 * @param theURL the URL to simulate or <code>null</code> if none
108 */
109 public AbstractHttpServletRequestWrapper(HttpServletRequest theRequest,
110 ServletURL theURL)
111 {
112 this.request = theRequest;
113 this.url = theURL;
114 }
115 /**
116 * {@inheritDoc}
117 * @see HttpServletRequest#newInstance()
118 */
119 public static AbstractHttpServletRequestWrapper newInstance(
120 HttpServletRequest theOriginalRequest, ServletURL theURL)
121 {
122 try
123 {
124 Class clazz = Class.forName(
125 "org.apache.cactus.server.HttpServletRequestWrapper");
126 Object[] args = new Object[] {theOriginalRequest, theURL};
127
128 Constructor constructor = clazz.getConstructor(new Class[] {
129 HttpServletRequest.class, ServletURL.class });
130
131 return (AbstractHttpServletRequestWrapper) constructor.
132 newInstance(args);
133 }
134 catch (Throwable t)
135 {
136 throw new ChainedRuntimeException(
137 "Failed to create HttpServletRequestWrapper", t);
138 }
139 }
140
141
142 /**
143 * @return the original request object
144 */
145 public HttpServletRequest getOriginalRequest()
146 {
147 return this.request;
148 }
149
150 /**
151 * Simulates the remote IP address (ie the client IP address).
152 *
153 * @param theRemoteIPAddress the simulated IP address in string format.
154 * Exemple : "127.0.0.1"
155 */
156 public void setRemoteIPAddress(String theRemoteIPAddress)
157 {
158 this.remoteIPAddress = theRemoteIPAddress;
159 }
160
161 /**
162 * Simulates the remote host name(ie the client host name).
163 *
164 * @param theRemoteHostName the simulated host name in string format.
165 * Exemple : "atlantis"
166 */
167 public void setRemoteHostName(String theRemoteHostName)
168 {
169 this.remoteHostName = theRemoteHostName;
170 }
171
172 /**
173 * Sets the remote user name to simulate.
174 *
175 * @param theRemoteUser the simulated remote user name
176 */
177 public void setRemoteUser(String theRemoteUser)
178 {
179 this.remoteUser = theRemoteUser;
180 }
181
182 // Modified methods ------------------------------------------------------
183
184 /**
185 * @return the context path from the simulated URL or the real context path
186 * if a simulation URL has not been defined. The real context path
187 * will be returned if the context path defined in the simulated
188 * URL has a null value.
189 */
190 public String getContextPath()
191 {
192 String result = this.request.getContextPath();
193
194 if ((this.url != null) && (this.url.getContextPath() != null))
195 {
196 result = this.url.getContextPath();
197 LOGGER.debug("Using simulated context : [" + result + "]");
198 }
199
200 return result;
201 }
202
203 /**
204 * @return the path info from the simulated URL or the real path info
205 * if a simulation URL has not been defined.
206 */
207 public String getPathInfo()
208 {
209 String result;
210
211 if (this.url != null)
212 {
213 result = this.url.getPathInfo();
214 LOGGER.debug("Using simulated PathInfo : [" + result + "]");
215 }
216 else
217 {
218 result = this.request.getPathInfo();
219 }
220
221 return result;
222 }
223
224 /**
225 * @return the server name from the simulated URL or the real server name
226 * if a simulation URL has not been defined. If the server name
227 * defined in the simulation URL is null, return the real server
228 * name.
229 */
230 public String getServerName()
231 {
232 String result = this.request.getServerName();
233
234 if ((this.url != null) && (this.url.getHost() != null))
235 {
236 result = this.url.getHost();
237 LOGGER.debug("Using simulated server name : [" + result + "]");
238 }
239
240 return result;
241 }
242
243 /**
244 * @return the server port number from the simulated URL or the real server
245 * port number if a simulation URL has not been defined. If no
246 * port is defined in the simulation URL, then port 80 is returned.
247 * If the server name has been defined with a null value in
248 * in the simulation URL, return the real server port.
249 */
250 public int getServerPort()
251 {
252 int result = this.request.getServerPort();
253
254 if ((this.url != null) && (this.url.getServerName() != null))
255 {
256 result = (this.url.getPort() == -1) ? 80 : this.url.getPort();
257 LOGGER.debug("Using simulated server port : [" + result + "]");
258 }
259
260 return result;
261 }
262
263 /**
264 * @return the URI from the simulated URL or the real URI
265 * if a simulation URL has not been defined.
266 */
267 public String getRequestURI()
268 {
269 String result;
270
271 if (this.url != null)
272 {
273 result = getContextPath()
274 + ((getServletPath() == null) ? "" : getServletPath())
275 + ((getPathInfo() == null) ? "" : getPathInfo());
276
277 LOGGER.debug("Using simulated request URI : [" + result + "]");
278 }
279 else
280 {
281 result = this.request.getRequestURI();
282 }
283
284 return result;
285 }
286
287 /**
288 * @return the servlet path from the simulated URL or the real servlet path
289 * if a simulation URL has not been defined. The real servlet path
290 * will be returned if the servlet path defined in the simulated
291 * URL has a null value.
292 */
293 public String getServletPath()
294 {
295 String result = this.request.getServletPath();
296
297 if ((this.url != null) && (this.url.getServletPath() != null))
298 {
299 result = this.url.getServletPath();
300 LOGGER.debug("Using simulated servlet path : [" + result + "]");
301 }
302
303 return result;
304 }
305
306 /**
307 * @return any extra path information after the servlet name but
308 * before the query string, and translates it to a real path.
309 * Takes into account the simulated URL (if any).
310 */
311 public String getPathTranslated()
312 {
313 String pathTranslated;
314
315 if ((this.url != null) && (this.url.getPathInfo() != null))
316 {
317 String pathInfo = this.url.getPathInfo();
318
319 // If getRealPath returns null then getPathTranslated should also
320 // return null (see section SRV.4.5 of the Servlet 2.3 spec).
321 if (this.request.getRealPath("/") == null)
322 {
323 pathTranslated = null;
324 }
325 else
326 {
327 // Compute the translated path using the root real path
328 String newPathInfo = (pathInfo.startsWith("/")
329 ? pathInfo.substring(1) : pathInfo);
330
331 if (this.request.getRealPath("/").endsWith("/"))
332 {
333 pathTranslated = this.request.getRealPath("/")
334 + newPathInfo.replace('/', File.separatorChar);
335 }
336 else
337 {
338 pathTranslated = this.request.getRealPath("/")
339 + File.separatorChar + newPathInfo.replace('/',
340 File.separatorChar);
341 }
342 }
343 }
344 else
345 {
346 pathTranslated = this.request.getPathTranslated();
347 }
348
349 return pathTranslated;
350 }
351
352 /**
353 * @return the query string from the simulated URL or the real query
354 * string if a simulation URL has not been defined.
355 */
356 public String getQueryString()
357 {
358 String result;
359
360 if (this.url != null)
361 {
362 result = this.url.getQueryString();
363 LOGGER.debug("Using simulated query string : [" + result + "]");
364 }
365 else
366 {
367 result = this.request.getQueryString();
368 }
369
370 return result;
371 }
372
373 /**
374 * @param thePath the path to the resource
375 * @return a wrapped request dispatcher instead of the real one, so that
376 * forward() and include() calls will use the wrapped dispatcher
377 * passing it the *original* request [this is needed for some
378 * servlet engine like Tomcat 3.x which do not support the new
379 * mechanism introduced by Servlet 2.3 Filters].
380 * @see HttpServletRequest#getRequestDispatcher(String)
381 */
382 public RequestDispatcher getRequestDispatcher(String thePath)
383 {
384 // I hate it, but we have to write some logic here ! Ideally we
385 // shouldn't have to do this as it is supposed to be done by the servlet
386 // engine. However as we are simulating the request URL, we have to
387 // provide it ... This is where we can see the limitation of Cactus
388 // (it has to mock some parts of the servlet engine) !
389 if (thePath == null)
390 {
391 return null;
392 }
393
394 RequestDispatcher dispatcher = null;
395 String fullPath;
396
397 // The spec says that the path can be relative, in which case it will
398 // be relative to the request. So for relative paths, we need to take
399 // into account the simulated URL (ServletURL).
400 if (thePath.startsWith("/"))
401 {
402 fullPath = thePath;
403 }
404 else
405 {
406 String pI = getPathInfo();
407
408 if (pI == null)
409 {
410 fullPath = catPath(getServletPath(), thePath);
411 }
412 else
413 {
414 fullPath = catPath(getServletPath() + pI, thePath);
415 }
416
417 if (fullPath == null)
418 {
419 return null;
420 }
421 }
422
423 LOGGER.debug("Computed full path : [" + fullPath + "]");
424
425 dispatcher = new RequestDispatcherWrapper(
426 this.request.getRequestDispatcher(fullPath));
427
428 return dispatcher;
429 }
430
431 /**
432 * Will concatenate 2 paths, normalising it. For example :
433 * ( /a/b/c + d = /a/b/d, /a/b/c + ../d = /a/d ). Code borrowed from
434 * Tomcat 3.2.2 !
435 *
436 * @param theLookupPath the first part of the path
437 * @param thePath the part to add to the lookup path
438 * @return the concatenated thePath or null if an error occurs
439 */
440 private String catPath(String theLookupPath, String thePath)
441 {
442 // Cut off the last slash and everything beyond
443 int index = theLookupPath.lastIndexOf("/");
444
445 theLookupPath = theLookupPath.substring(0, index);
446
447 // Deal with .. by chopping dirs off the lookup thePath
448 while (thePath.startsWith("../"))
449 {
450 if (theLookupPath.length() > 0)
451 {
452 index = theLookupPath.lastIndexOf("/");
453 theLookupPath = theLookupPath.substring(0, index);
454 }
455 else
456 {
457 // More ..'s than dirs, return null
458 return null;
459 }
460
461 index = thePath.indexOf("../") + 3;
462 thePath = thePath.substring(index);
463 }
464
465 return theLookupPath + "/" + thePath;
466 }
467
468 /**
469 * @return the simulated remote IP address if any or the real one.
470 *
471 * @see HttpServletRequest#getRemoteAddr()
472 */
473 public String getRemoteAddr()
474 {
475 String remoteIPAddress;
476
477 if (this.remoteIPAddress != null)
478 {
479 remoteIPAddress = this.remoteIPAddress;
480 }
481 else
482 {
483 remoteIPAddress = this.request.getRemoteAddr();
484 }
485
486 return remoteIPAddress;
487 }
488
489 /**
490 * @return the simulated remote host name if any or the real one.
491 *
492 * @see HttpServletRequest#getRemoteHost()
493 */
494 public String getRemoteHost()
495 {
496 String remoteHostName;
497
498 if (this.remoteHostName != null)
499 {
500 remoteHostName = this.remoteHostName;
501 }
502 else
503 {
504 remoteHostName = this.request.getRemoteHost();
505 }
506
507 return remoteHostName;
508 }
509
510 /**
511 * @return the simulated remote user name if any or the real one.
512 *
513 * @see HttpServletRequest#getRemoteUser()
514 */
515 public String getRemoteUser()
516 {
517 String remoteUser;
518
519 if (this.remoteUser != null)
520 {
521 remoteUser = this.remoteUser;
522 }
523 else
524 {
525 remoteUser = this.request.getRemoteUser();
526 }
527
528 return remoteUser;
529 }
530
531 // Not modified methods --------------------------------------------------
532
533 /**
534 * {@inheritDoc}
535 * @see HttpServletRequest#isRequestedSessionIdFromURL()
536 */
537 public boolean isRequestedSessionIdFromURL()
538 {
539 return this.request.isRequestedSessionIdFromURL();
540 }
541
542 /**
543 * {@inheritDoc}
544 * @see HttpServletRequest#isRequestedSessionIdFromUrl()
545 */
546 public boolean isRequestedSessionIdFromUrl()
547 {
548 return this.request.isRequestedSessionIdFromURL();
549 }
550
551 /**
552 * {@inheritDoc}
553 * @see HttpServletRequest#isUserInRole(String)
554 */
555 public boolean isUserInRole(String theRole)
556 {
557 return this.request.isUserInRole(theRole);
558 }
559
560 /**
561 * {@inheritDoc}
562 * @see HttpServletRequest#isRequestedSessionIdValid()
563 */
564 public boolean isRequestedSessionIdValid()
565 {
566 return this.request.isRequestedSessionIdValid();
567 }
568
569 /**
570 * {@inheritDoc}
571 * @see HttpServletRequest#isRequestedSessionIdFromCookie()
572 */
573 public boolean isRequestedSessionIdFromCookie()
574 {
575 return this.request.isRequestedSessionIdFromCookie();
576 }
577
578 /**
579 * {@inheritDoc}
580 * @see HttpServletRequest#getLocales()
581 */
582 public Enumeration getLocales()
583 {
584 return this.request.getLocales();
585 }
586
587 /**
588 * {@inheritDoc}
589 * @see HttpServletRequest#getHeader(String)
590 */
591 public String getHeader(String theName)
592 {
593 return this.request.getHeader(theName);
594 }
595
596 /**
597 * {@inheritDoc}
598 * @see HttpServletRequest#getHeaders(String)
599 */
600 public Enumeration getHeaders(String theName)
601 {
602 return this.request.getHeaders(theName);
603 }
604
605 /**
606 * {@inheritDoc}
607 * @see HttpServletRequest#getHeaderNames()
608 */
609 public Enumeration getHeaderNames()
610 {
611 return this.request.getHeaderNames();
612 }
613
614 /**
615 * {@inheritDoc}
616 * @see HttpServletRequest#getScheme()
617 */
618 public String getScheme()
619 {
620 return this.request.getScheme();
621 }
622
623 /**
624 * {@inheritDoc}
625 * @see HttpServletRequest#getAuthType()
626 */
627 public String getAuthType()
628 {
629 return this.request.getAuthType();
630 }
631
632 /**
633 * {@inheritDoc}
634 * @see HttpServletRequest#getRealPath(String)
635 */
636 public String getRealPath(String thePath)
637 {
638 return this.request.getRealPath(thePath);
639 }
640
641 /**
642 * {@inheritDoc}
643 * @see HttpServletRequest#getSession()
644 */
645 public HttpSession getSession()
646 {
647 return this.request.getSession();
648 }
649
650 /**
651 * {@inheritDoc}
652 * @see HttpServletRequest#getSession(boolean)
653 */
654 public HttpSession getSession(boolean isCreate)
655 {
656 return this.request.getSession(isCreate);
657 }
658
659 /**
660 * {@inheritDoc}
661 * @see HttpServletRequest#getReader()
662 */
663 public BufferedReader getReader() throws IOException
664 {
665 return this.request.getReader();
666 }
667
668 /**
669 * {@inheritDoc}
670 * @see HttpServletRequest#getContentLength()
671 */
672 public int getContentLength()
673 {
674 return this.request.getContentLength();
675 }
676
677 /**
678 * {@inheritDoc}
679 * @see HttpServletRequest#getParameterValues(String)
680 */
681 public String[] getParameterValues(String theName)
682 {
683 return this.request.getParameterValues(theName);
684 }
685
686 /**
687 * {@inheritDoc}
688 * @see HttpServletRequest#getContentType()
689 */
690 public String getContentType()
691 {
692 return this.request.getContentType();
693 }
694
695 /**
696 * {@inheritDoc}
697 * @see HttpServletRequest#getLocale()
698 */
699 public Locale getLocale()
700 {
701 return this.request.getLocale();
702 }
703
704 /**
705 * {@inheritDoc}
706 * @see HttpServletRequest#removeAttribute(String)
707 */
708 public void removeAttribute(String theName)
709 {
710 this.request.removeAttribute(theName);
711 }
712
713 /**
714 * {@inheritDoc}
715 * @see HttpServletRequest#getParameter(String)
716 */
717 public String getParameter(String theName)
718 {
719 return this.request.getParameter(theName);
720 }
721
722 /**
723 * {@inheritDoc}
724 * @see HttpServletRequest#getInputStream()
725 */
726 public ServletInputStream getInputStream() throws IOException
727 {
728 return this.request.getInputStream();
729 }
730
731 /**
732 * {@inheritDoc}
733 * @see HttpServletRequest#getUserPrincipal()
734 */
735 public Principal getUserPrincipal()
736 {
737 return this.request.getUserPrincipal();
738 }
739
740 /**
741 * {@inheritDoc}
742 * @see HttpServletRequest#isSecure()
743 */
744 public boolean isSecure()
745 {
746 return this.request.isSecure();
747 }
748
749 /**
750 * {@inheritDoc}
751 * @see HttpServletRequest#getCharacterEncoding()
752 */
753 public String getCharacterEncoding()
754 {
755 return this.request.getCharacterEncoding();
756 }
757
758 /**
759 * {@inheritDoc}
760 * @see HttpServletRequest#getParameterNames()
761 */
762 public Enumeration getParameterNames()
763 {
764 return this.request.getParameterNames();
765 }
766
767 /**
768 * {@inheritDoc}
769 * @see HttpServletRequest#getMethod()
770 */
771 public String getMethod()
772 {
773 return this.request.getMethod();
774 }
775
776 /**
777 * {@inheritDoc}
778 * @see HttpServletRequest#setAttribute(String, Object)
779 */
780 public void setAttribute(String theName, Object theAttribute)
781 {
782 this.request.setAttribute(theName, theAttribute);
783 }
784
785 /**
786 * {@inheritDoc}
787 * @see HttpServletRequest#getAttribute(String)
788 */
789 public Object getAttribute(String theName)
790 {
791 return this.request.getAttribute(theName);
792 }
793
794 /**
795 * {@inheritDoc}
796 * @see HttpServletRequest#getIntHeader(String)
797 */
798 public int getIntHeader(String theName)
799 {
800 return this.request.getIntHeader(theName);
801 }
802
803 /**
804 * {@inheritDoc}
805 * @see HttpServletRequest#getDateHeader(String)
806 */
807 public long getDateHeader(String theName)
808 {
809 return this.request.getDateHeader(theName);
810 }
811
812 /**
813 * {@inheritDoc}
814 * @see HttpServletRequest#getAttributeNames()
815 */
816 public Enumeration getAttributeNames()
817 {
818 return this.request.getAttributeNames();
819 }
820
821 /**
822 * {@inheritDoc}
823 * @see HttpServletRequest#getRequestedSessionId()
824 */
825 public String getRequestedSessionId()
826 {
827 return this.request.getRequestedSessionId();
828 }
829
830 /**
831 * {@inheritDoc}
832 * @see HttpServletRequest#getCookies()
833 */
834 public Cookie[] getCookies()
835 {
836 return this.request.getCookies();
837 }
838
839 /**
840 * {@inheritDoc}
841 * @see HttpServletRequest#getProtocol()
842 */
843 public String getProtocol()
844 {
845 return this.request.getProtocol();
846 }
847 }