// ======================================================================== // $Id: Dispatcher.java,v 1.92 2005/12/12 18:03:31 gregwilkins Exp $ // Copyright 199-2004 Mort Bay Consulting Pty. Ltd. // ------------------------------------------------------------------------ // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ======================================================================== package org.browsermob.proxy.jetty.jetty.servlet; import org.apache.commons.logging.Log; import org.browsermob.proxy.jetty.http.HttpConnection; import org.browsermob.proxy.jetty.http.PathMap; import org.browsermob.proxy.jetty.log.LogFactory; import org.browsermob.proxy.jetty.util.*; import javax.servlet.*; import javax.servlet.http.*; import java.io.IOException; import java.io.PrintWriter; import java.util.*; /* ------------------------------------------------------------ */ /** Servlet RequestDispatcher. * * @version $Id: Dispatcher.java,v 1.92 2005/12/12 18:03:31 gregwilkins Exp $ * @author Greg Wilkins (gregw) */ public class Dispatcher implements RequestDispatcher { static Log log = LogFactory.getLog(Dispatcher.class); /* ------------------------------------------------------------ */ /** Dispatch types */ public static final int __DEFAULT=0; public static final int __REQUEST=1; public static final int __FORWARD=2; public static final int __INCLUDE=4; public static final int __ERROR=8; public static final int __ALL=15; /** Dispatch include attribute names */ public final static String __INCLUDE_REQUEST_URI= "javax.servlet.include.request_uri"; public final static String __INCLUDE_CONTEXT_PATH= "javax.servlet.include.context_path"; public final static String __INCLUDE_SERVLET_PATH= "javax.servlet.include.servlet_path"; public final static String __INCLUDE_PATH_INFO= "javax.servlet.include.path_info"; public final static String __INCLUDE_QUERY_STRING= "javax.servlet.include.query_string"; /** Dispatch include attribute names */ public final static String __FORWARD_REQUEST_URI= "javax.servlet.forward.request_uri"; public final static String __FORWARD_CONTEXT_PATH= "javax.servlet.forward.context_path"; public final static String __FORWARD_SERVLET_PATH= "javax.servlet.forward.servlet_path"; public final static String __FORWARD_PATH_INFO= "javax.servlet.forward.path_info"; public final static String __FORWARD_QUERY_STRING= "javax.servlet.forward.query_string"; public final static StringMap __managedAttributes = new StringMap(); static { __managedAttributes.put(__INCLUDE_REQUEST_URI,__INCLUDE_REQUEST_URI); __managedAttributes.put(__INCLUDE_CONTEXT_PATH,__INCLUDE_CONTEXT_PATH); __managedAttributes.put(__INCLUDE_SERVLET_PATH,__INCLUDE_SERVLET_PATH); __managedAttributes.put(__INCLUDE_PATH_INFO,__INCLUDE_PATH_INFO); __managedAttributes.put(__INCLUDE_QUERY_STRING,__INCLUDE_QUERY_STRING); __managedAttributes.put(__FORWARD_REQUEST_URI,__FORWARD_REQUEST_URI); __managedAttributes.put(__FORWARD_CONTEXT_PATH,__FORWARD_CONTEXT_PATH); __managedAttributes.put(__FORWARD_SERVLET_PATH,__FORWARD_SERVLET_PATH); __managedAttributes.put(__FORWARD_PATH_INFO,__FORWARD_PATH_INFO); __managedAttributes.put(__FORWARD_QUERY_STRING,__FORWARD_QUERY_STRING); } ServletHandler _servletHandler; ServletHolder _holder=null; String _pathSpec; String _uriInContext; String _pathInContext; String _query; /* ------------------------------------------------------------ */ /** Constructor. /** Constructor. * @param servletHandler * @param uriInContext Encoded uriInContext * @param pathInContext Encoded pathInContext * @param query * @exception IllegalStateException */ Dispatcher(ServletHandler servletHandler, String uriInContext, String pathInContext, String query, Map.Entry entry) throws IllegalStateException { if(log.isDebugEnabled())log.debug("Dispatcher for "+servletHandler+","+uriInContext+","+query); _servletHandler=servletHandler; _uriInContext=uriInContext; _pathInContext=pathInContext; _query=query; _pathSpec=(String)entry.getKey(); _holder = (ServletHolder)entry.getValue(); } /* ------------------------------------------------------------ */ /** Constructor. * @param servletHandler * @param name */ Dispatcher(ServletHandler servletHandler,String name) throws IllegalStateException { _servletHandler=servletHandler; _holder=_servletHandler.getServletHolder(name); if (_holder==null) throw new IllegalStateException("No named servlet handler in context"); } /* ------------------------------------------------------------ */ public boolean isNamed() { return _pathInContext==null; } /* ------------------------------------------------------------ */ public void include(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { dispatch(servletRequest,servletResponse,Dispatcher.__INCLUDE); } /* ------------------------------------------------------------ */ public void forward(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException,IOException { dispatch(servletRequest,servletResponse,Dispatcher.__FORWARD); } /* ------------------------------------------------------------ */ void error(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException,IOException { dispatch(servletRequest,servletResponse,Dispatcher.__ERROR); } /* ------------------------------------------------------------ */ void dispatch(ServletRequest servletRequest, ServletResponse servletResponse, int type) throws ServletException,IOException { HttpServletRequest httpServletRequest=(HttpServletRequest)servletRequest; HttpServletResponse httpServletResponse=(HttpServletResponse)servletResponse; HttpConnection httpConnection= _servletHandler.getHttpContext().getHttpConnection(); ServletHttpRequest servletHttpRequest= (ServletHttpRequest)httpConnection.getRequest().getWrapper(); // wrap the request and response DispatcherRequest request = new DispatcherRequest(httpServletRequest, servletHttpRequest, type); DispatcherResponse response = new DispatcherResponse(request, httpServletResponse); if (type==Dispatcher.__FORWARD) servletResponse.resetBuffer(); // Merge parameters String query=_query; MultiMap parameters=null; if (query!=null) { Map old_params = httpServletRequest.getParameterMap(); // Add the parameters parameters=new MultiMap(); UrlEncoded.decodeTo(query,parameters,request.getCharacterEncoding()); if (old_params!=null && old_params.size()>0) { // Merge old parameters. Iterator iter = old_params.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); String name=(String)entry.getKey(); String[] values=(String[])entry.getValue(); for (int i=0;i<values.length;i++) parameters.add(name, values[i]); } } request.setParameters(parameters); String old_query=httpServletRequest.getQueryString(); if (old_query!=null) request.setQuery(query+"&"+old_query); else request.setQuery(query); } Object old_scope = null; try { if (request.crossContext()) { // Setup new _context old_scope= _servletHandler.getHttpContext() .enterContextScope(httpConnection.getRequest(),httpConnection.getResponse()); } if (isNamed()) { // No further modifications required. if (_servletHandler instanceof WebApplicationHandler) { JSR154Filter filter = ((WebApplicationHandler)_servletHandler).getJsr154Filter(); if (filter!=null && filter.isUnwrappedDispatchSupported()) { filter.setDispatch(request, response); _servletHandler.dispatch(null,httpServletRequest,httpServletResponse,_holder, type); } else _servletHandler.dispatch(null,request,response,_holder, type); } else _servletHandler.dispatch(null,request,response,_holder, type); } else { // Adjust servlet paths request.setPaths(_servletHandler.getHttpContext().getContextPath(), PathMap.pathMatch(_pathSpec,_pathInContext), PathMap.pathInfo(_pathSpec,_pathInContext)); // are we wrap over or wrap under if (_servletHandler instanceof WebApplicationHandler) { JSR154Filter filter = ((WebApplicationHandler)_servletHandler).getJsr154Filter(); if (filter!=null && filter.isUnwrappedDispatchSupported()) { filter.setDispatch(request, response); _servletHandler.dispatch(_pathInContext,httpServletRequest,httpServletResponse,_holder, type); } else _servletHandler.dispatch(_pathInContext,request,response,_holder, type); } else _servletHandler.dispatch(_pathInContext,request,response,_holder, type); if (type!=Dispatcher.__INCLUDE) response.close(); else if (response.isFlushNeeded()) response.flushBuffer(); } } finally { // restore _context if (request.crossContext()) _servletHandler.getHttpContext() .leaveContextScope(httpConnection.getRequest(), httpConnection.getResponse(), old_scope); } } /* ------------------------------------------------------------ */ public String toString() { return "Dispatcher["+_pathSpec+","+_holder+"]"; } /* ------------------------------------------------------------ */ /** Dispatch type from name */ public static int type(String type) { if ("request".equalsIgnoreCase(type)) return __REQUEST; if ("forward".equalsIgnoreCase(type)) return __FORWARD; if ("include".equalsIgnoreCase(type)) return __INCLUDE; if ("error".equalsIgnoreCase(type)) return __ERROR; throw new IllegalArgumentException(type); } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class DispatcherRequest extends HttpServletRequestWrapper { DispatcherResponse _response; int _filterType; String _contextPath; String _servletPath; String _pathInfo; MultiMap _parameters; HashMap _attributes; boolean _xContext; HttpSession _xSession; ServletHttpRequest _servletHttpRequest; String _query; /* ------------------------------------------------------------ */ DispatcherRequest(HttpServletRequest httpServletRequest, ServletHttpRequest servletHttpRequest, int filterType) { super(httpServletRequest); _servletHttpRequest=servletHttpRequest; _filterType=filterType; // Is this being dispatched to a different context? _xContext= servletHttpRequest.getServletHandler()!=_servletHandler; if (_xContext) { // Look for an existing or requested session ID. HttpSession session=httpServletRequest.getSession(false); String session_id=(session==null) ?httpServletRequest.getRequestedSessionId() :session.getId(); // Look for that session in new context to access it. if (session_id!=null) { _xSession=_servletHandler.getHttpSession(session_id); if (_xSession!=null) ((SessionManager.Session)_xSession).access(); } } } /* ------------------------------------------------------------ */ boolean crossContext() { return _xContext; } /* ------------------------------------------------------------ */ void setPaths(String cp,String sp, String pi) { _contextPath = (cp.length()==1 && cp.charAt(0)=='/')?"":cp; _servletPath=sp; _pathInfo=pi; } /* ------------------------------------------------------------ */ void setQuery(String q) { this._query=q; } /* ------------------------------------------------------------ */ int getFilterType() { return _filterType; } /* ------------------------------------------------------------ */ String getPathInContext() { if (_pathInContext!=null) return _pathInContext; else return URI.addPaths(getServletPath(),getPathInfo()); } /* ------------------------------------------------------------ */ public String getRequestURI() { if (_filterType==Dispatcher.__INCLUDE || isNamed()) return super.getRequestURI(); return URI.addPaths(_contextPath,_uriInContext); } /* ------------------------------------------------------------ */ public StringBuffer getRequestURL() { if (_filterType==Dispatcher.__INCLUDE || isNamed()) return super.getRequestURL(); StringBuffer buf = getRootURL(); if (_contextPath.length()>0) buf.append(_contextPath); buf.append(_uriInContext); return buf; } /* ------------------------------------------------------------ */ public String getPathTranslated() { String info=getPathInfo(); if (info==null) return null; return getRealPath(info); } /* ------------------------------------------------------------ */ StringBuffer getRootURL() { StringBuffer buf = super.getRequestURL(); int d=3; for (int i=0;i<buf.length();i++) { if (buf.charAt(i)=='/' && --d==0) { buf.setLength(i); break; } } return buf; } /* ------------------------------------------------------------ */ public String getContextPath() { return(_filterType==Dispatcher.__INCLUDE||isNamed())?super.getContextPath():_contextPath; } /* ------------------------------------------------------------ */ public String getServletPath() { return(_filterType==Dispatcher.__INCLUDE||isNamed())?super.getServletPath():_servletPath; } /* ------------------------------------------------------------ */ public String getPathInfo() { return(_filterType==Dispatcher.__INCLUDE||isNamed())?super.getPathInfo():_pathInfo; } /* ------------------------------------------------------------ */ public String getQueryString() { if (this._query==null) return super.getQueryString(); return this._query; } /* ------------------------------------------------------------ */ void setParameters(MultiMap parameters) { _parameters=parameters; } /* -------------------------------------------------------------- */ public Enumeration getParameterNames() { if (_parameters==null) return super.getParameterNames(); return Collections.enumeration(_parameters.keySet()); } /* -------------------------------------------------------------- */ public String getParameter(String name) { if (_parameters==null) return super.getParameter(name); return (String)_parameters.getValue(name,0); } /* -------------------------------------------------------------- */ public String[] getParameterValues(String name) { if (_parameters==null) return super.getParameterValues(name); List l =_parameters.getValues(name); if (l==null) return null; return (String[])l.toArray(new String[l.size()]); } /* -------------------------------------------------------------- */ public Map getParameterMap() { if (_parameters==null) return super.getParameterMap(); return _parameters.toStringArrayMap(); } /* ------------------------------------------------------------ */ public void setAttribute(String name, Object value) { if (__managedAttributes.containsKey(name)) { if (_attributes==null) _attributes=new HashMap(3); _attributes.put(name,value); } else super.setAttribute(name,value); } /* ------------------------------------------------------------ */ public Object getAttribute(String name) { if (_attributes!=null && _attributes.containsKey(name)) return _attributes.get(name); if (_filterType==Dispatcher.__INCLUDE && !isNamed()) { if (name.equals(__INCLUDE_PATH_INFO)) return _pathInfo; if (name.equals(__INCLUDE_REQUEST_URI)) return URI.addPaths(_contextPath,_uriInContext); if (name.equals(__INCLUDE_SERVLET_PATH)) return _servletPath; if (name.equals(__INCLUDE_CONTEXT_PATH)) return _contextPath; if (name.equals(__INCLUDE_QUERY_STRING)) return Dispatcher.this._query; } else { if (name.equals(__INCLUDE_PATH_INFO)) return null; if (name.equals(__INCLUDE_REQUEST_URI)) return null; if (name.equals(__INCLUDE_SERVLET_PATH)) return null; if (name.equals(__INCLUDE_CONTEXT_PATH)) return null; if (name.equals(__INCLUDE_QUERY_STRING)) return null; } if (_filterType!=Dispatcher.__INCLUDE && !isNamed()) { if (name.equals(__FORWARD_PATH_INFO)) return _servletHttpRequest.getPathInfo(); if (name.equals(__FORWARD_REQUEST_URI)) return _servletHttpRequest.getRequestURI(); if (name.equals(__FORWARD_SERVLET_PATH)) return _servletHttpRequest.getServletPath(); if (name.equals(__FORWARD_CONTEXT_PATH)) return _servletHttpRequest.getContextPath(); if (name.equals(__FORWARD_QUERY_STRING)) return _servletHttpRequest.getQueryString(); } return super.getAttribute(name); } /* ------------------------------------------------------------ */ public Enumeration getAttributeNames() { HashSet set=new HashSet(); Enumeration e=super.getAttributeNames(); while (e.hasMoreElements()) set.add(e.nextElement()); if (_filterType==Dispatcher.__INCLUDE && !isNamed()) { 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 (Dispatcher.this._query!=null) set.add(__INCLUDE_QUERY_STRING); else set.remove(__INCLUDE_QUERY_STRING); } else { set.remove(__INCLUDE_PATH_INFO); set.remove(__INCLUDE_REQUEST_URI); set.remove(__INCLUDE_SERVLET_PATH); set.remove(__INCLUDE_CONTEXT_PATH); set.remove(__INCLUDE_QUERY_STRING); } if (_filterType!=Dispatcher.__INCLUDE && !isNamed()) { if (_servletHttpRequest.getPathInfo()!=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 (_servletHttpRequest.getQueryString()!=null) set.add(__FORWARD_QUERY_STRING); else set.remove(__FORWARD_QUERY_STRING); } if (_attributes!=null) set.addAll(_attributes.keySet()); return Collections.enumeration(set); } /* ------------------------------------------------------------ */ /** * If the request attribute "org.mortbay.jetty.servlet.Dispatcher.shared_session" is set, then * sessions are shared in cross context dispatch. Watch out for class loading issues! */ public HttpSession getSession(boolean create) { if (_xContext) { if (_xSession==null) { if (getAttribute("org.browsermob.proxy.jetty.jetty.servlet.Dispatcher.shared_session") != null) _xSession= super.getSession(create); else { log.debug("Ctx dispatch session"); String rsid=getRequestedSessionId(); if (rsid==null) { HttpSession session=super.getSession(false); if (session!=null) rsid=session.getId(); } _xSession=_servletHandler.getHttpSession(rsid); if (create && _xSession==null) { _xSession=_servletHandler.newHttpSession(this); Cookie cookie = _servletHandler.getSessionManager().getSessionCookie(_xSession, isSecure()); if (cookie!=null) _servletHttpRequest.getHttpRequest().getHttpResponse().addSetCookie(cookie); } } } return _xSession; } else return super.getSession(create); } /* ------------------------------------------------------------ */ public boolean isRequestedSessionIdValid() { if (_xContext) { String requestedSessionid = super.getRequestedSessionId(); if (requestedSessionid != null) { HttpSession session = getSession(false); if (session != null) { return ((AbstractSessionManager.Session)session).isValid() && requestedSessionid.equals(session.getId()); } } return false; } else { return super.isRequestedSessionIdValid(); } } /* ------------------------------------------------------------ */ public HttpSession getSession() { return getSession(true); } /* ------------------------------------------------------------ */ public String getRealPath(String path) { return _servletHandler.getServletContext().getRealPath(path); } /* ------------------------------------------------------------ */ public RequestDispatcher getRequestDispatcher(String url) { if (url == null) return null; if (!url.startsWith("/")) { String relTo=URI.addPaths(getServletPath(),getPathInfo()); int slash=relTo.lastIndexOf("/"); if (slash>1) relTo=relTo.substring(0,slash+1); else relTo="/"; url=URI.addPaths(relTo,url); } return _servletHandler.getServletContext().getRequestDispatcher(url); } public String getMethod() { if (this._filterType==Dispatcher.__ERROR) return org.browsermob.proxy.jetty.http.HttpRequest.__GET; return super.getMethod(); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ class DispatcherResponse extends HttpServletResponseWrapper { DispatcherRequest _request; private ServletOutputStream _out=null; private PrintWriter _writer=null; private boolean _flushNeeded=false; private boolean _include; /* ------------------------------------------------------------ */ DispatcherResponse(DispatcherRequest request, HttpServletResponse response) { super(response); _request=request; request._response=this; _include=_request._filterType==Dispatcher.__INCLUDE; } /* ------------------------------------------------------------ */ public ServletOutputStream getOutputStream() throws IOException { if (_writer!=null) throw new IllegalStateException("getWriter called"); if (_out==null) { try {_out=super.getOutputStream();} catch(IllegalStateException e) { LogSupport.ignore(log,e); _flushNeeded=true; _out=new ServletOut(new WriterOutputStream(super.getWriter())); } } if (_include) _out=new DontCloseServletOut(_out); return _out; } /* ------------------------------------------------------------ */ public PrintWriter getWriter() throws IOException { if (_out!=null) throw new IllegalStateException("getOutputStream called"); if (_writer==null) { try{_writer=super.getWriter();} catch(IllegalStateException e) { LogSupport.ignore(log, e); _flushNeeded=true; _writer = new ServletWriter(super.getOutputStream(), getCharacterEncoding()); } } if (_include) _writer=new DontCloseWriter(_writer); return _writer; } /* ------------------------------------------------------------ */ boolean isFlushNeeded() { return _flushNeeded; } /* ------------------------------------------------------------ */ public void flushBuffer() throws IOException { if (_writer!=null) _writer.flush(); if (_out!=null) _out.flush(); super.flushBuffer(); } /* ------------------------------------------------------------ */ public void close() throws IOException { if (_writer!=null) _writer.close(); if (_out!=null) _out.close(); } /* ------------------------------------------------------------ */ public void setLocale(Locale locale) { if (!_include) super.setLocale(locale); } /* ------------------------------------------------------------ */ public void sendError(int status, String message) throws IOException { if (_request._filterType!=Dispatcher.__ERROR && !_include) super.sendError(status,message); } /* ------------------------------------------------------------ */ public void sendError(int status) throws IOException { if (_request._filterType!=Dispatcher.__ERROR && !_include) super.sendError(status); } /* ------------------------------------------------------------ */ public void sendRedirect(String url) throws IOException { if (!_include) { if (!url.startsWith("http:/")&&!url.startsWith("https:/")) { StringBuffer buf = _request.getRootURL(); if (url.startsWith("/")) buf.append(URI.canonicalPath(url)); else buf.append(URI.canonicalPath(URI.addPaths(URI.parentPath(_request.getRequestURI()),url))); url=buf.toString(); } super.sendRedirect(url); } } /* ------------------------------------------------------------ */ public void setDateHeader(String name, long value) { if (!_include) super.setDateHeader(name,value); } /* ------------------------------------------------------------ */ public void setHeader(String name, String value) { if (!_include) super.setHeader(name,value); } /* ------------------------------------------------------------ */ public void setIntHeader(String name, int value) { if (!_include) super.setIntHeader(name,value); } /* ------------------------------------------------------------ */ public void addHeader(String name, String value) { if (!_include) super.addHeader(name,value); } /* ------------------------------------------------------------ */ public void addDateHeader(String name, long value) { if (!_include) super.addDateHeader(name,value); } /* ------------------------------------------------------------ */ public void addIntHeader(String name, int value) { if (!_include) super.addIntHeader(name,value); } /* ------------------------------------------------------------ */ public void setStatus(int status) { if (_request._filterType!=Dispatcher.__ERROR && !_include) super.setStatus(status); } /* ------------------------------------------------------------ */ /** * The default behavior of this method is to call setStatus(int sc, String sm) * on the wrapped response object. * * @deprecated As of version 2.1 of the Servlet spec. * To set a status code * use <code>setStatus(int)</code>, to send an error with a description * use <code>sendError(int, String)</code>. * * @param status the status code * @param message the status message */ public void setStatus(int status, String message) { if (_request._filterType!=Dispatcher.__ERROR && !_include) super.setStatus(status,message); } /* ------------------------------------------------------------ */ public void setContentLength(int len) { if (!_include) super.setContentLength(len); } /* ------------------------------------------------------------ */ public void setContentType(String contentType) { if (!_include) super.setContentType(contentType); } /* ------------------------------------------------------------ */ public void addCookie(Cookie cookie) { if (!_include) super.addCookie(cookie); } } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class DontCloseWriter extends PrintWriter { DontCloseWriter(PrintWriter writer) { super(writer); } public void close() {} } /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */ private class DontCloseServletOut extends ServletOut { DontCloseServletOut(ServletOutputStream output) { super(output); } public void close() throws IOException {} } };