View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2016 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     /**
220      * <p>Pushes a secondary resource identified by this dispatcher.</p>
221      *
222      * @param request the primary request
223      * @deprecated Use {@link Request#getPushBuilder()} instead
224      */
225     @Deprecated
226     public void push(ServletRequest request)
227     {
228         Request baseRequest = Request.getBaseRequest(request);
229         HttpFields fields = new HttpFields(baseRequest.getHttpFields());
230 
231         String query=baseRequest.getQueryString();
232         if (_uri.hasQuery())
233         {
234             if (query==null)
235                 query=_uri.getQuery();
236             else
237                 query=query+"&"+_uri.getQuery(); // TODO is this correct semantic?
238         }
239 
240         HttpURI uri = HttpURI.createHttpURI(request.getScheme(),request.getServerName(),request.getServerPort(),_uri.getPath(),baseRequest.getHttpURI().getParam(),query,null);
241 
242         MetaData.Request push = new MetaData.Request(HttpMethod.GET.asString(),uri,baseRequest.getHttpVersion(),fields);
243 
244         baseRequest.getHttpChannel().getHttpTransport().push(push);
245     }
246 
247     @Override
248     public String toString()
249     {
250         return String.format("Dispatcher@0x%x{%s,%s}",hashCode(),_named,_uri);
251     }
252 
253     private void commitResponse(ServletResponse response, Request baseRequest) throws IOException
254     {
255         if (baseRequest.getResponse().isWriting())
256         {
257             try
258             {
259                 response.getWriter().close();
260             }
261             catch (IllegalStateException e)
262             {
263                 response.getOutputStream().close();
264             }
265         }
266         else
267         {
268             try
269             {
270                 response.getOutputStream().close();
271             }
272             catch (IllegalStateException e)
273             {
274                 response.getWriter().close();
275             }
276         }
277     }
278 
279     private class ForwardAttributes implements Attributes
280     {
281         final Attributes _attr;
282 
283         String _requestURI;
284         String _contextPath;
285         String _servletPath;
286         String _pathInfo;
287         String _query;
288 
289         ForwardAttributes(Attributes attributes)
290         {
291             _attr=attributes;
292         }
293 
294         /* ------------------------------------------------------------ */
295         @Override
296         public Object getAttribute(String key)
297         {
298             if (Dispatcher.this._named==null)
299             {
300                 if (key.equals(FORWARD_PATH_INFO))
301                     return _pathInfo;
302                 if (key.equals(FORWARD_REQUEST_URI))
303                     return _requestURI;
304                 if (key.equals(FORWARD_SERVLET_PATH))
305                     return _servletPath;
306                 if (key.equals(FORWARD_CONTEXT_PATH))
307                     return _contextPath;
308                 if (key.equals(FORWARD_QUERY_STRING))
309                     return _query;
310             }
311 
312             if (key.startsWith(__INCLUDE_PREFIX))
313                 return null;
314 
315             return _attr.getAttribute(key);
316         }
317 
318         @Override
319         public Enumeration<String> getAttributeNames()
320         {
321             HashSet<String> set=new HashSet<>();
322             Enumeration<String> e=_attr.getAttributeNames();
323             while(e.hasMoreElements())
324             {
325                 String name=e.nextElement();
326                 if (!name.startsWith(__INCLUDE_PREFIX) &&
327                     !name.startsWith(__FORWARD_PREFIX))
328                     set.add(name);
329             }
330 
331             if (_named==null)
332             {
333                 if (_pathInfo!=null)
334                     set.add(FORWARD_PATH_INFO);
335                 else
336                     set.remove(FORWARD_PATH_INFO);
337                 set.add(FORWARD_REQUEST_URI);
338                 set.add(FORWARD_SERVLET_PATH);
339                 set.add(FORWARD_CONTEXT_PATH);
340                 if (_query!=null)
341                     set.add(FORWARD_QUERY_STRING);
342                 else
343                     set.remove(FORWARD_QUERY_STRING);
344             }
345 
346             return Collections.enumeration(set);
347         }
348 
349         @Override
350         public void setAttribute(String key, Object value)
351         {
352             if (_named==null && key.startsWith("javax.servlet."))
353             {
354                 if (key.equals(FORWARD_PATH_INFO))
355                     _pathInfo=(String)value;
356                 else if (key.equals(FORWARD_REQUEST_URI))
357                     _requestURI=(String)value;
358                 else if (key.equals(FORWARD_SERVLET_PATH))
359                     _servletPath=(String)value;
360                 else if (key.equals(FORWARD_CONTEXT_PATH))
361                     _contextPath=(String)value;
362                 else if (key.equals(FORWARD_QUERY_STRING))
363                     _query=(String)value;
364 
365                 else if (value==null)
366                     _attr.removeAttribute(key);
367                 else
368                     _attr.setAttribute(key,value);
369             }
370             else if (value==null)
371                 _attr.removeAttribute(key);
372             else
373                 _attr.setAttribute(key,value);
374         }
375 
376         @Override
377         public String toString()
378         {
379             return "FORWARD+"+_attr.toString();
380         }
381 
382         @Override
383         public void clearAttributes()
384         {
385             throw new IllegalStateException();
386         }
387 
388         @Override
389         public void removeAttribute(String name)
390         {
391             setAttribute(name,null);
392         }
393     }
394 
395     private class IncludeAttributes implements Attributes
396     {
397         final Attributes _attr;
398 
399         String _requestURI;
400         String _contextPath;
401         String _servletPath;
402         String _pathInfo;
403         String _query;
404 
405         IncludeAttributes(Attributes attributes)
406         {
407             _attr=attributes;
408         }
409 
410         @Override
411         public Object getAttribute(String key)
412         {
413             if (Dispatcher.this._named==null)
414             {
415                 if (key.equals(INCLUDE_PATH_INFO))    return _pathInfo;
416                 if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath;
417                 if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath;
418                 if (key.equals(INCLUDE_QUERY_STRING)) return _query;
419                 if (key.equals(INCLUDE_REQUEST_URI))  return _requestURI;
420             }
421             else if (key.startsWith(__INCLUDE_PREFIX))
422                     return null;
423 
424 
425             return _attr.getAttribute(key);
426         }
427 
428         @Override
429         public Enumeration<String> getAttributeNames()
430         {
431             HashSet<String> set=new HashSet<>();
432             Enumeration<String> e=_attr.getAttributeNames();
433             while(e.hasMoreElements())
434             {
435                 String name=e.nextElement();
436                 if (!name.startsWith(__INCLUDE_PREFIX))
437                     set.add(name);
438             }
439 
440             if (_named==null)
441             {
442                 if (_pathInfo!=null)
443                     set.add(INCLUDE_PATH_INFO);
444                 else
445                     set.remove(INCLUDE_PATH_INFO);
446                 set.add(INCLUDE_REQUEST_URI);
447                 set.add(INCLUDE_SERVLET_PATH);
448                 set.add(INCLUDE_CONTEXT_PATH);
449                 if (_query!=null)
450                     set.add(INCLUDE_QUERY_STRING);
451                 else
452                     set.remove(INCLUDE_QUERY_STRING);
453             }
454 
455             return Collections.enumeration(set);
456         }
457 
458         @Override
459         public void setAttribute(String key, Object value)
460         {
461             if (_named==null && key.startsWith("javax.servlet."))
462             {
463                 if (key.equals(INCLUDE_PATH_INFO))         _pathInfo=(String)value;
464                 else if (key.equals(INCLUDE_REQUEST_URI))  _requestURI=(String)value;
465                 else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value;
466                 else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value;
467                 else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value;
468                 else if (value==null)
469                     _attr.removeAttribute(key);
470                 else
471                     _attr.setAttribute(key,value);
472             }
473             else if (value==null)
474                 _attr.removeAttribute(key);
475             else
476                 _attr.setAttribute(key,value);
477         }
478 
479         @Override
480         public String toString()
481         {
482             return "INCLUDE+"+_attr.toString();
483         }
484 
485         @Override
486         public void clearAttributes()
487         {
488             throw new IllegalStateException();
489         }
490 
491         @Override
492         public void removeAttribute(String name)
493         {
494             setAttribute(name,null);
495         }
496     }
497 }