// // ======================================================================== // Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // All rights reserved. This program and the accompanying materials // are made available under the terms of the Eclipse Public License v1.0 // and Apache License v2.0 which accompanies this distribution. // // The Eclipse Public License is available at // http://www.eclipse.org/legal/epl-v10.html // // The Apache License v2.0 is available at // http://www.opensource.org/licenses/apache2.0.php // // You may elect to redistribute this code under either of these licenses. // ======================================================================== // package org.eclipse.jetty.server; import java.io.IOException; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import javax.servlet.DispatcherType; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpURI; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.MultiMap; public class Dispatcher implements RequestDispatcher { public final static String __ERROR_DISPATCH="org.eclipse.jetty.server.Dispatcher.ERROR"; /** Dispatch include attribute names */ public final static String __INCLUDE_PREFIX="javax.servlet.include."; /** Dispatch include attribute names */ public final static String __FORWARD_PREFIX="javax.servlet.forward."; private final ContextHandler _contextHandler; private final HttpURI _uri; private final String _pathInContext; private final String _named; public Dispatcher(ContextHandler contextHandler, HttpURI uri, String pathInContext) { _contextHandler=contextHandler; _uri=uri; _pathInContext=pathInContext; _named=null; } public Dispatcher(ContextHandler contextHandler, String name) throws IllegalStateException { _contextHandler=contextHandler; _uri=null; _pathInContext=null; _named=name; } @Override public void forward(ServletRequest request, ServletResponse response) throws ServletException, IOException { forward(request, response, DispatcherType.FORWARD); } public void error(ServletRequest request, ServletResponse response) throws ServletException, IOException { try { request.setAttribute(__ERROR_DISPATCH,Boolean.TRUE); forward(request, response, DispatcherType.ERROR); } finally { request.setAttribute(__ERROR_DISPATCH,null); } } @Override public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException { Request baseRequest=Request.getBaseRequest(request); if (!(request instanceof HttpServletRequest)) request = new ServletRequestHttpWrapper(request); if (!(response instanceof HttpServletResponse)) response = new ServletResponseHttpWrapper(response); final DispatcherType old_type = baseRequest.getDispatcherType(); final Attributes old_attr=baseRequest.getAttributes(); final MultiMap<String> old_query_params=baseRequest.getQueryParameters(); try { baseRequest.setDispatcherType(DispatcherType.INCLUDE); baseRequest.getResponse().include(); if (_named!=null) { _contextHandler.handle(_named,baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); } else { IncludeAttributes attr = new IncludeAttributes(old_attr); attr._requestURI=_uri.getPath(); attr._contextPath=_contextHandler.getContextPath(); attr._servletPath=null; // set by ServletHandler attr._pathInfo=_pathInContext; attr._query=_uri.getQuery(); if (attr._query!=null) baseRequest.mergeQueryParameters(baseRequest.getQueryString(),attr._query, false); baseRequest.setAttributes(attr); _contextHandler.handle(_pathInContext, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); } } finally { baseRequest.setAttributes(old_attr); baseRequest.getResponse().included(); baseRequest.setQueryParameters(old_query_params); baseRequest.resetParameters(); baseRequest.setDispatcherType(old_type); } } protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException { Request baseRequest=Request.getBaseRequest(request); Response base_response=baseRequest.getResponse(); base_response.resetForForward(); if (!(request instanceof HttpServletRequest)) request = new ServletRequestHttpWrapper(request); if (!(response instanceof HttpServletResponse)) response = new ServletResponseHttpWrapper(response); final HttpURI old_uri=baseRequest.getHttpURI(); final String old_context_path=baseRequest.getContextPath(); final String old_servlet_path=baseRequest.getServletPath(); final String old_path_info=baseRequest.getPathInfo(); final MultiMap<String> old_query_params=baseRequest.getQueryParameters(); final Attributes old_attr=baseRequest.getAttributes(); final DispatcherType old_type=baseRequest.getDispatcherType(); try { baseRequest.setDispatcherType(dispatch); if (_named!=null) { _contextHandler.handle(_named, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); } else { ForwardAttributes attr = new ForwardAttributes(old_attr); //If we have already been forwarded previously, then keep using the established //original value. Otherwise, this is the first forward and we need to establish the values. //Note: the established value on the original request for pathInfo and //for queryString is allowed to be null, but cannot be null for the other values. if (old_attr.getAttribute(FORWARD_REQUEST_URI) != null) { attr._pathInfo=(String)old_attr.getAttribute(FORWARD_PATH_INFO); attr._query=(String)old_attr.getAttribute(FORWARD_QUERY_STRING); attr._requestURI=(String)old_attr.getAttribute(FORWARD_REQUEST_URI); attr._contextPath=(String)old_attr.getAttribute(FORWARD_CONTEXT_PATH); attr._servletPath=(String)old_attr.getAttribute(FORWARD_SERVLET_PATH); } else { attr._pathInfo=old_path_info; attr._query=old_uri.getQuery(); attr._requestURI=old_uri.getPath(); attr._contextPath=old_context_path; attr._servletPath=old_servlet_path; } HttpURI uri = new HttpURI(old_uri.getScheme(),old_uri.getHost(),old_uri.getPort(), _uri.getPath(),_uri.getParam(),_uri.getQuery(),_uri.getFragment()); baseRequest.setHttpURI(uri); baseRequest.setContextPath(_contextHandler.getContextPath()); baseRequest.setServletPath(null); baseRequest.setPathInfo(_pathInContext); if (_uri.getQuery()!=null || old_uri.getQuery()!=null) baseRequest.mergeQueryParameters(old_uri.getQuery(),_uri.getQuery(), true); baseRequest.setAttributes(attr); _contextHandler.handle(_pathInContext, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); if (!baseRequest.getHttpChannelState().isAsync()) commitResponse(response,baseRequest); } } finally { baseRequest.setHttpURI(old_uri); baseRequest.setContextPath(old_context_path); baseRequest.setServletPath(old_servlet_path); baseRequest.setPathInfo(old_path_info); baseRequest.setQueryParameters(old_query_params); baseRequest.resetParameters(); baseRequest.setAttributes(old_attr); baseRequest.setDispatcherType(old_type); } } @Override public String toString() { return String.format("Dispatcher@0x%x{%s,%s}",hashCode(),_named,_uri); } @SuppressWarnings("Duplicates") private void commitResponse(ServletResponse response, Request baseRequest) throws IOException, ServletException { if (baseRequest.getResponse().isWriting()) { try { // Try closing Writer first (based on knowledge in Response obj) response.getWriter().close(); } catch (IllegalStateException e1) { try { // Try closing OutputStream as alternate route // This path is possible due to badly behaving Response wrappers response.getOutputStream().close(); } catch(IllegalStateException e2) { ServletException servletException = new ServletException("Unable to commit the response", e2); servletException.addSuppressed(e1); throw servletException; } } } else { try { // Try closing OutputStream first (based on knowledge in Response obj) response.getOutputStream().close(); } catch (IllegalStateException e1) { try { // Try closing Writer as alternate route // This path is possible due to badly behaving Response wrappers response.getWriter().close(); } catch(IllegalStateException e2) { ServletException servletException = new ServletException("Unable to commit the response", e2); servletException.addSuppressed(e1); throw servletException; } } } } private class ForwardAttributes implements Attributes { final Attributes _attr; String _requestURI; String _contextPath; String _servletPath; String _pathInfo; String _query; ForwardAttributes(Attributes attributes) { _attr=attributes; } /* ------------------------------------------------------------ */ @Override public Object getAttribute(String key) { if (Dispatcher.this._named==null) { if (key.equals(FORWARD_PATH_INFO)) return _pathInfo; if (key.equals(FORWARD_REQUEST_URI)) return _requestURI; if (key.equals(FORWARD_SERVLET_PATH)) return _servletPath; if (key.equals(FORWARD_CONTEXT_PATH)) return _contextPath; if (key.equals(FORWARD_QUERY_STRING)) return _query; } if (key.startsWith(__INCLUDE_PREFIX)) return null; return _attr.getAttribute(key); } @Override public Enumeration<String> getAttributeNames() { HashSet<String> set=new HashSet<>(); Enumeration<String> e=_attr.getAttributeNames(); while(e.hasMoreElements()) { String name=e.nextElement(); if (!name.startsWith(__INCLUDE_PREFIX) && !name.startsWith(__FORWARD_PREFIX)) set.add(name); } if (_named==null) { if (_pathInfo!=null) set.add(FORWARD_PATH_INFO); else set.remove(FORWARD_PATH_INFO); set.add(FORWARD_REQUEST_URI); set.add(FORWARD_SERVLET_PATH); set.add(FORWARD_CONTEXT_PATH); if (_query!=null) set.add(FORWARD_QUERY_STRING); else set.remove(FORWARD_QUERY_STRING); } return Collections.enumeration(set); } @Override public void setAttribute(String key, Object value) { if (_named==null && key.startsWith("javax.servlet.")) { if (key.equals(FORWARD_PATH_INFO)) _pathInfo=(String)value; else if (key.equals(FORWARD_REQUEST_URI)) _requestURI=(String)value; else if (key.equals(FORWARD_SERVLET_PATH)) _servletPath=(String)value; else if (key.equals(FORWARD_CONTEXT_PATH)) _contextPath=(String)value; else if (key.equals(FORWARD_QUERY_STRING)) _query=(String)value; else if (value==null) _attr.removeAttribute(key); else _attr.setAttribute(key,value); } else if (value==null) _attr.removeAttribute(key); else _attr.setAttribute(key,value); } @Override public String toString() { return "FORWARD+"+_attr.toString(); } @Override public void clearAttributes() { throw new IllegalStateException(); } @Override public void removeAttribute(String name) { setAttribute(name,null); } } private class IncludeAttributes implements Attributes { final Attributes _attr; String _requestURI; String _contextPath; String _servletPath; String _pathInfo; String _query; IncludeAttributes(Attributes attributes) { _attr=attributes; } @Override public Object getAttribute(String key) { if (Dispatcher.this._named==null) { if (key.equals(INCLUDE_PATH_INFO)) return _pathInfo; if (key.equals(INCLUDE_SERVLET_PATH)) return _servletPath; if (key.equals(INCLUDE_CONTEXT_PATH)) return _contextPath; if (key.equals(INCLUDE_QUERY_STRING)) return _query; if (key.equals(INCLUDE_REQUEST_URI)) return _requestURI; } else if (key.startsWith(__INCLUDE_PREFIX)) return null; return _attr.getAttribute(key); } @Override public Enumeration<String> getAttributeNames() { HashSet<String> set=new HashSet<>(); Enumeration<String> e=_attr.getAttributeNames(); while(e.hasMoreElements()) { String name=e.nextElement(); if (!name.startsWith(__INCLUDE_PREFIX)) set.add(name); } if (_named==null) { if (_pathInfo!=null) set.add(INCLUDE_PATH_INFO); else set.remove(INCLUDE_PATH_INFO); set.add(INCLUDE_REQUEST_URI); set.add(INCLUDE_SERVLET_PATH); set.add(INCLUDE_CONTEXT_PATH); if (_query!=null) set.add(INCLUDE_QUERY_STRING); else set.remove(INCLUDE_QUERY_STRING); } return Collections.enumeration(set); } @Override public void setAttribute(String key, Object value) { if (_named==null && key.startsWith("javax.servlet.")) { if (key.equals(INCLUDE_PATH_INFO)) _pathInfo=(String)value; else if (key.equals(INCLUDE_REQUEST_URI)) _requestURI=(String)value; else if (key.equals(INCLUDE_SERVLET_PATH)) _servletPath=(String)value; else if (key.equals(INCLUDE_CONTEXT_PATH)) _contextPath=(String)value; else if (key.equals(INCLUDE_QUERY_STRING)) _query=(String)value; else if (value==null) _attr.removeAttribute(key); else _attr.setAttribute(key,value); } else if (value==null) _attr.removeAttribute(key); else _attr.setAttribute(key,value); } @Override public String toString() { return "INCLUDE+"+_attr.toString(); } @Override public void clearAttributes() { throw new IllegalStateException(); } @Override public void removeAttribute(String name) { setAttribute(name,null); } } }