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