View Javadoc

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