View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.server;
20  
21  import java.io.IOException;
22  import java.util.Collections;
23  import java.util.Enumeration;
24  import java.util.HashSet;
25  import java.util.Iterator;
26  import java.util.Map;
27  
28  import javax.servlet.RequestDispatcher;
29  import javax.servlet.ServletException;
30  import javax.servlet.ServletRequest;
31  import javax.servlet.ServletResponse;
32  import javax.servlet.http.HttpServletRequest;
33  import javax.servlet.http.HttpServletResponse;
34  
35  import org.eclipse.jetty.server.handler.ContextHandler;
36  import org.eclipse.jetty.util.Attributes;
37  import org.eclipse.jetty.util.LazyList;
38  import org.eclipse.jetty.util.MultiMap;
39  import org.eclipse.jetty.util.UrlEncoded;
40  
41  /* ------------------------------------------------------------ */
42  /** Servlet RequestDispatcher.
43   * 
44   * 
45   */
46  public class Dispatcher implements RequestDispatcher
47  {
48      public static final String FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
49      public static final String FORWARD_CONTEXT_PATH = "javax.servlet.forward.context_path";
50      public static final String FORWARD_PATH_INFO = "javax.servlet.forward.path_info";
51      public static final String FORWARD_SERVLET_PATH = "javax.servlet.forward.servlet_path";
52      public static final String FORWARD_QUERY_STRING = "javax.servlet.forward.query_string";
53      public static final String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
54      public static final String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
55      public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
56      public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
57      public static final String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
58  
59      public static final String ERROR_EXCEPTION = "javax.servlet.error.exception";
60      public static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
61      public static final String ERROR_MESSAGE = "javax.servlet.error.message";
62      public static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
63      public static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
64      public static final String ERROR_STATUS_CODE = "javax.servlet.error.status_code";
65  
66      /** Dispatch include attribute names */
67      public final static String __INCLUDE_PREFIX="javax.servlet.include.";
68  
69      /** Dispatch include attribute names */
70      public final static String __FORWARD_PREFIX="javax.servlet.forward.";
71  
72      /** JSP attributes */
73      public final static String __JSP_FILE="org.apache.catalina.jsp_file";
74  
75      /* ------------------------------------------------------------ */
76      private final ContextHandler _contextHandler;
77      private final String _uri;
78      private final String _path;
79      private final String _dQuery;
80      private final String _named;
81      
82      /* ------------------------------------------------------------ */
83      /**
84       * @param contextHandler
85       * @param uri
86       * @param pathInContext
87       * @param query
88       */
89      public Dispatcher(ContextHandler contextHandler, String uri, String pathInContext, String query)
90      {
91          _contextHandler=contextHandler;
92          _uri=uri;
93          _path=pathInContext;
94          _dQuery=query;
95          _named=null;
96      }
97  
98  
99      /* ------------------------------------------------------------ */
100     /** Constructor. 
101      * @param contextHandler
102      * @param name
103      */
104     public Dispatcher(ContextHandler contextHandler,String name)
105         throws IllegalStateException
106     {
107         _contextHandler=contextHandler;
108         _named=name;
109         _uri=null;
110         _path=null;
111         _dQuery=null;
112     }
113     
114     /* ------------------------------------------------------------ */
115     /* 
116      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
117      */
118     public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException
119     {
120         forward(request, response, DispatcherType.FORWARD);
121     }
122     
123     /* ------------------------------------------------------------ */
124     /* 
125      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
126      */
127     public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException
128     {
129         forward(request, response, DispatcherType.ERROR);
130     }
131     
132     /* ------------------------------------------------------------ */
133     /* 
134      * @see javax.servlet.RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
135      */
136     public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException
137     {
138         Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest();
139       
140         
141         if (!(request instanceof HttpServletRequest))
142             request = new ServletRequestHttpWrapper(request);
143         if (!(response instanceof HttpServletResponse))
144             response = new ServletResponseHttpWrapper(response);
145             
146         
147         // TODO - allow stream or writer????
148         
149         final DispatcherType old_type = baseRequest.getDispatcherType();
150         final Attributes old_attr=baseRequest.getAttributes();
151         MultiMap old_params=baseRequest.getParameters();
152         try
153         {
154             baseRequest.setDispatcherType(DispatcherType.INCLUDE);
155             baseRequest.getConnection().include();
156             if (_named!=null)
157                 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
158             else 
159             {
160                 String query=_dQuery;
161                 
162                 if (query!=null)
163                 {
164                     // force parameter extraction
165                     if (old_params==null)
166                     {
167                         baseRequest.extractParameters();
168                         old_params=baseRequest.getParameters();
169                     }
170                     
171                     MultiMap parameters=new MultiMap();
172                     UrlEncoded.decodeTo(query,parameters,baseRequest.getCharacterEncoding());
173                     
174                     if (old_params!=null && old_params.size()>0)
175                     {
176                         // Merge parameters.
177                         Iterator iter = old_params.entrySet().iterator();
178                         while (iter.hasNext())
179                         {
180                             Map.Entry entry = (Map.Entry)iter.next();
181                             String name=(String)entry.getKey();
182                             Object values=entry.getValue();
183                             for (int i=0;i<LazyList.size(values);i++)
184                                 parameters.add(name, LazyList.get(values, i));
185                         }
186                     }
187                     baseRequest.setParameters(parameters);
188                 }
189                 
190                 IncludeAttributes attr = new IncludeAttributes(old_attr); 
191                 
192                 attr._requestURI=_uri;
193                 attr._contextPath=_contextHandler.getContextPath();
194                 attr._servletPath=null; // set by ServletHandler
195                 attr._pathInfo=_path;
196                 attr._query=query;
197                 
198                 baseRequest.setAttributes(attr);
199                 
200                 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
201             }
202         }
203         finally
204         {
205             baseRequest.setAttributes(old_attr);
206             baseRequest.getConnection().included();
207             baseRequest.setParameters(old_params);
208             baseRequest.setDispatcherType(old_type);
209         }
210     }
211 
212     
213     /* ------------------------------------------------------------ */
214     /* 
215      * @see javax.servlet.RequestDispatcher#forward(javax.servlet.ServletRequest, javax.servlet.ServletResponse)
216      */
217     protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException
218     {
219         Request baseRequest=(request instanceof Request)?((Request)request):AbstractHttpConnection.getCurrentConnection().getRequest();
220         Response base_response=baseRequest.getResponse();
221         response.resetBuffer();
222         base_response.fwdReset();
223        
224 
225         if (!(request instanceof HttpServletRequest))
226             request = new ServletRequestHttpWrapper(request);
227         if (!(response instanceof HttpServletResponse))
228             response = new ServletResponseHttpWrapper(response);
229         
230         final boolean old_handled=baseRequest.isHandled();
231         final String old_uri=baseRequest.getRequestURI();
232         final String old_context_path=baseRequest.getContextPath();
233         final String old_servlet_path=baseRequest.getServletPath();
234         final String old_path_info=baseRequest.getPathInfo();
235         final String old_query=baseRequest.getQueryString();
236         final Attributes old_attr=baseRequest.getAttributes();
237         final DispatcherType old_type=baseRequest.getDispatcherType();
238         MultiMap<String> old_params=baseRequest.getParameters();
239         
240         try
241         {
242             baseRequest.setHandled(false);
243             baseRequest.setDispatcherType(dispatch);
244             
245             if (_named!=null)
246                 _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
247             else 
248             {
249                 
250                 // process any query string from the dispatch URL
251                 String query=_dQuery;
252                 if (query!=null)
253                 {
254                     // force parameter extraction
255                     if (old_params==null)
256                     {
257                         baseRequest.extractParameters();
258                         old_params=baseRequest.getParameters();
259                     }
260                     
261                     baseRequest.mergeQueryString(query);
262                 }
263                 
264                 ForwardAttributes attr = new ForwardAttributes(old_attr); 
265                 
266                 //If we have already been forwarded previously, then keep using the established 
267                 //original value. Otherwise, this is the first forward and we need to establish the values.
268                 //Note: the established value on the original request for pathInfo and
269                 //for queryString is allowed to be null, but cannot be null for the other values.
270                 if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null)
271                 {
272                     attr._pathInfo=(String)old_attr.getAttribute(FORWARD_PATH_INFO);
273                     attr._query=(String)old_attr.getAttribute(FORWARD_QUERY_STRING);
274                     attr._requestURI=(String)old_attr.getAttribute(FORWARD_REQUEST_URI);
275                     attr._contextPath=(String)old_attr.getAttribute(FORWARD_CONTEXT_PATH);
276                     attr._servletPath=(String)old_attr.getAttribute(FORWARD_SERVLET_PATH);
277                 }
278                 else
279                 {
280                     attr._pathInfo=old_path_info;
281                     attr._query=old_query;
282                     attr._requestURI=old_uri;
283                     attr._contextPath=old_context_path;
284                     attr._servletPath=old_servlet_path;
285                 }     
286                 
287                 baseRequest.setRequestURI(_uri);
288                 baseRequest.setContextPath(_contextHandler.getContextPath());
289                 baseRequest.setServletPath(null);
290                 baseRequest.setPathInfo(_uri);
291                 baseRequest.setAttributes(attr);
292                 
293                 _contextHandler.handle(_path,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response);
294                 
295                 if (!baseRequest.getAsyncContinuation().isAsyncStarted())
296                     commitResponse(response,baseRequest);
297             }
298         }
299         finally
300         {
301             baseRequest.setHandled(old_handled);
302             baseRequest.setRequestURI(old_uri);
303             baseRequest.setContextPath(old_context_path);
304             baseRequest.setServletPath(old_servlet_path);
305             baseRequest.setPathInfo(old_path_info);
306             baseRequest.setAttributes(old_attr);
307             baseRequest.setParameters(old_params);
308             baseRequest.setQueryString(old_query);
309             baseRequest.setDispatcherType(old_type);
310         }
311     }
312 
313 
314     /* ------------------------------------------------------------ */
315     private void commitResponse(ServletResponse response, Request baseRequest) throws IOException
316     {
317         if (baseRequest.getResponse().isWriting())
318         {
319             try
320             {
321                 response.getWriter().close();
322             }
323             catch (IllegalStateException e)
324             {
325                 response.getOutputStream().close();
326             }
327         }
328         else
329         {
330             try
331             {
332                 response.getOutputStream().close();
333             }
334             catch (IllegalStateException e)
335             {
336                 response.getWriter().close();
337             }
338         }
339     }
340 
341 
342     /* ------------------------------------------------------------ */
343     /* ------------------------------------------------------------ */
344     /* ------------------------------------------------------------ */
345     private class ForwardAttributes implements Attributes
346     {
347         final Attributes _attr;
348         
349         String _requestURI;
350         String _contextPath;
351         String _servletPath;
352         String _pathInfo;
353         String _query;
354         
355         ForwardAttributes(Attributes attributes)
356         {
357             _attr=attributes;
358         }
359         
360         /* ------------------------------------------------------------ */
361         public Object getAttribute(String key)
362         {
363             if (Dispatcher.this._named==null)
364             {
365                 if (key.equals(FORWARD_PATH_INFO))    
366                     return _pathInfo;
367                 if (key.equals(FORWARD_REQUEST_URI))  
368                     return _requestURI;
369                 if (key.equals(FORWARD_SERVLET_PATH)) 
370                     return _servletPath;
371                 if (key.equals(FORWARD_CONTEXT_PATH)) 
372                     return _contextPath;
373                 if (key.equals(FORWARD_QUERY_STRING)) 
374                     return _query;
375             }
376             
377             if (key.startsWith(__INCLUDE_PREFIX))
378                 return null;
379             
380             return _attr.getAttribute(key);
381         }
382         
383         /* ------------------------------------------------------------ */
384         public Enumeration getAttributeNames()
385         {
386             HashSet set=new HashSet();
387             Enumeration e=_attr.getAttributeNames();
388             while(e.hasMoreElements())
389             {
390                 String name=(String)e.nextElement();
391                 if (!name.startsWith(__INCLUDE_PREFIX) &&
392                     !name.startsWith(__FORWARD_PREFIX))
393                     set.add(name);
394             }
395             
396             if (_named==null)
397             {
398                 if (_pathInfo!=null)
399                     set.add(FORWARD_PATH_INFO);
400                 else
401                     set.remove(FORWARD_PATH_INFO);
402                 set.add(FORWARD_REQUEST_URI);
403                 set.add(FORWARD_SERVLET_PATH);
404                 set.add(FORWARD_CONTEXT_PATH);
405                 if (_query!=null)
406                     set.add(FORWARD_QUERY_STRING);
407                 else
408                     set.remove(FORWARD_QUERY_STRING);
409             }
410 
411             return Collections.enumeration(set);
412         }
413         
414         /* ------------------------------------------------------------ */
415         public void setAttribute(String key, Object value)
416         {
417             if (_named==null && key.startsWith("javax.servlet."))
418             {
419                 if (key.equals(FORWARD_PATH_INFO))         
420                     _pathInfo=(String)value;
421                 else if (key.equals(FORWARD_REQUEST_URI))  
422                     _requestURI=(String)value;
423                 else if (key.equals(FORWARD_SERVLET_PATH)) 
424                     _servletPath=(String)value;
425                 else if (key.equals(FORWARD_CONTEXT_PATH)) 
426                     _contextPath=(String)value;
427                 else if (key.equals(FORWARD_QUERY_STRING)) 
428                     _query=(String)value;
429                 
430                 else if (value==null)
431                     _attr.removeAttribute(key);
432                 else
433                     _attr.setAttribute(key,value); 
434             }
435             else if (value==null)
436                 _attr.removeAttribute(key);
437             else
438                 _attr.setAttribute(key,value);
439         }
440         
441         /* ------------------------------------------------------------ */
442         @Override
443         public String toString() 
444         {
445             return "FORWARD+"+_attr.toString();
446         }
447 
448         /* ------------------------------------------------------------ */
449         public void clearAttributes()
450         {
451             throw new IllegalStateException();
452         }
453 
454         /* ------------------------------------------------------------ */
455         public void removeAttribute(String name)
456         {
457             setAttribute(name,null);
458         }
459     }
460 
461     /* ------------------------------------------------------------ */
462     private class IncludeAttributes implements Attributes
463     {
464         final Attributes _attr;
465         
466         String _requestURI;
467         String _contextPath;
468         String _servletPath;
469         String _pathInfo;
470         String _query;
471         
472         IncludeAttributes(Attributes attributes)
473         {
474             _attr=attributes;
475         }
476         
477         /* ------------------------------------------------------------ */
478         /* ------------------------------------------------------------ */
479         /* ------------------------------------------------------------ */
480         public Object getAttribute(String key)
481         {
482             if (Dispatcher.this._named==null)
483             {
484                 if (key.equals(INCLUDE_PATH_INFO))    return _pathInfo;
485                 if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath;
486                 if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath;
487                 if (key.equals(INCLUDE_QUERY_STRING)) return _query;
488                 if (key.equals(INCLUDE_REQUEST_URI))  return _requestURI;
489             }
490             else if (key.startsWith(__INCLUDE_PREFIX)) 
491                     return null;
492             
493             
494             return _attr.getAttribute(key);
495         }
496         
497         /* ------------------------------------------------------------ */
498         public Enumeration getAttributeNames()
499         {
500             HashSet set=new HashSet();
501             Enumeration e=_attr.getAttributeNames();
502             while(e.hasMoreElements())
503             {
504                 String name=(String)e.nextElement();
505                 if (!name.startsWith(__INCLUDE_PREFIX))
506                     set.add(name);
507             }
508             
509             if (_named==null)
510             {
511                 if (_pathInfo!=null)
512                     set.add(INCLUDE_PATH_INFO);
513                 else
514                     set.remove(INCLUDE_PATH_INFO);
515                 set.add(INCLUDE_REQUEST_URI);
516                 set.add(INCLUDE_SERVLET_PATH);
517                 set.add(INCLUDE_CONTEXT_PATH);
518                 if (_query!=null)
519                     set.add(INCLUDE_QUERY_STRING);
520                 else
521                     set.remove(INCLUDE_QUERY_STRING);
522             }
523             
524             return Collections.enumeration(set);
525         }
526         
527         /* ------------------------------------------------------------ */
528         public void setAttribute(String key, Object value)
529         {
530             if (_named==null && key.startsWith("javax.servlet."))
531             {
532                 if (key.equals(INCLUDE_PATH_INFO))         _pathInfo=(String)value;
533                 else if (key.equals(INCLUDE_REQUEST_URI))  _requestURI=(String)value;
534                 else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
535                 else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
536                 else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value;
537                 else if (value==null)
538                     _attr.removeAttribute(key);
539                 else
540                     _attr.setAttribute(key,value); 
541             }
542             else if (value==null)
543                 _attr.removeAttribute(key);
544             else
545                 _attr.setAttribute(key,value);
546         }
547         
548         /* ------------------------------------------------------------ */
549         @Override
550         public String toString() 
551         {
552             return "INCLUDE+"+_attr.toString();
553         }
554 
555         /* ------------------------------------------------------------ */
556         public void clearAttributes()
557         {
558             throw new IllegalStateException();
559         }
560 
561         /* ------------------------------------------------------------ */
562         public void removeAttribute(String name)
563         {
564             setAttribute(name,null);
565         }
566     }
567 }