View Javadoc

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