]> WPIA git - gigi.git/blob - lib/jetty/org/eclipse/jetty/servlet/Invoker.java
Merge "Update notes about password security"
[gigi.git] / lib / jetty / org / eclipse / jetty / servlet / Invoker.java
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.servlet;
20
21 import java.io.IOException;
22 import java.util.Enumeration;
23 import java.util.HashMap;
24 import java.util.Locale;
25 import java.util.Map;
26
27 import javax.servlet.ServletContext;
28 import javax.servlet.ServletException;
29 import javax.servlet.UnavailableException;
30 import javax.servlet.http.HttpServlet;
31 import javax.servlet.http.HttpServletRequest;
32 import javax.servlet.http.HttpServletRequestWrapper;
33 import javax.servlet.http.HttpServletResponse;
34
35 import org.eclipse.jetty.server.Dispatcher;
36 import org.eclipse.jetty.server.Handler;
37 import org.eclipse.jetty.server.HttpChannel;
38 import org.eclipse.jetty.server.Request;
39 import org.eclipse.jetty.server.handler.ContextHandler;
40 import org.eclipse.jetty.server.handler.HandlerWrapper;
41 import org.eclipse.jetty.util.ArrayUtil;
42 import org.eclipse.jetty.util.URIUtil;
43 import org.eclipse.jetty.util.log.Log;
44 import org.eclipse.jetty.util.log.Logger;
45
46 /* ------------------------------------------------------------ */
47 /**  Dynamic Servlet Invoker.
48  * This servlet invokes anonymous servlets that have not been defined
49  * in the web.xml or by other means. The first element of the pathInfo
50  * of a request passed to the envoker is treated as a servlet name for
51  * an existing servlet, or as a class name of a new servlet.
52  * This servlet is normally mapped to /servlet/*
53  * This servlet support the following initParams:
54  * <PRE>
55  *  nonContextServlets       If false, the invoker can only load
56  *                           servlets from the contexts classloader.
57  *                           This is false by default and setting this
58  *                           to true may have security implications.
59  *
60  *  verbose                  If true, log dynamic loads
61  *
62  *  *                        All other parameters are copied to the
63  *                           each dynamic servlet as init parameters
64  * </PRE>
65  * @version $Id: Invoker.java 4780 2009-03-17 15:36:08Z jesse $
66  *
67  */
68 public class Invoker extends HttpServlet
69 {
70     private static final Logger LOG = Log.getLogger(Invoker.class);
71
72
73     private ContextHandler _contextHandler;
74     private ServletHandler _servletHandler;
75     private Map.Entry<String, ServletHolder> _invokerEntry;
76     private Map<String, String> _parameters;
77     private boolean _nonContextServlets;
78     private boolean _verbose;
79
80     /* ------------------------------------------------------------ */
81     public void init()
82     {
83         ServletContext config=getServletContext();
84         _contextHandler=((ContextHandler.Context)config).getContextHandler();
85
86         Handler handler=_contextHandler.getHandler();
87         while (handler!=null && !(handler instanceof ServletHandler) && (handler instanceof HandlerWrapper))
88             handler=((HandlerWrapper)handler).getHandler();
89         _servletHandler = (ServletHandler)handler;
90         Enumeration<String> e = getInitParameterNames();
91         while(e.hasMoreElements())
92         {
93             String param=e.nextElement();
94             String value=getInitParameter(param);
95             String lvalue=value.toLowerCase(Locale.ENGLISH);
96             if ("nonContextServlets".equals(param))
97             {
98                 _nonContextServlets=value.length()>0 && lvalue.startsWith("t");
99             }
100             if ("verbose".equals(param))
101             {
102                 _verbose=value.length()>0 && lvalue.startsWith("t");
103             }
104             else
105             {
106                 if (_parameters==null)
107                     _parameters=new HashMap<String, String>();
108                 _parameters.put(param,value);
109             }
110         }
111     }
112
113     /* ------------------------------------------------------------ */
114     protected void service(HttpServletRequest request, HttpServletResponse response)
115         throws ServletException, IOException
116     {
117         // Get the requested path and info
118         boolean included=false;
119         String servlet_path=(String)request.getAttribute(Dispatcher.INCLUDE_SERVLET_PATH);
120         if (servlet_path==null)
121             servlet_path=request.getServletPath();
122         else
123             included=true;
124         String path_info = (String)request.getAttribute(Dispatcher.INCLUDE_PATH_INFO);
125         if (path_info==null)
126             path_info=request.getPathInfo();
127
128         // Get the servlet class
129         String servlet = path_info;
130         if (servlet==null || servlet.length()<=1 )
131         {
132             response.sendError(404);
133             return;
134         }
135
136
137         int i0=servlet.charAt(0)=='/'?1:0;
138         int i1=servlet.indexOf('/',i0);
139         servlet=i1<0?servlet.substring(i0):servlet.substring(i0,i1);
140
141         // look for a named holder
142         ServletHolder[] holders = _servletHandler.getServlets();
143         ServletHolder holder = getHolder (holders, servlet);
144
145         if (holder!=null)
146         {
147             // Found a named servlet (from a user's web.xml file) so
148             // now we add a mapping for it
149             if (LOG.isDebugEnabled())
150                 LOG.debug("Adding servlet mapping for named servlet:"+servlet+":"+URIUtil.addPaths(servlet_path,servlet)+"/*");
151             ServletMapping mapping = new ServletMapping();
152             mapping.setServletName(servlet);
153             mapping.setPathSpec(URIUtil.addPaths(servlet_path,servlet)+"/*");
154             _servletHandler.setServletMappings(ArrayUtil.addToArray(_servletHandler.getServletMappings(), mapping, ServletMapping.class));
155         }
156         else
157         {
158             // look for a class mapping
159             if (servlet.endsWith(".class"))
160                 servlet=servlet.substring(0,servlet.length()-6);
161             if (servlet==null || servlet.length()==0)
162             {
163                 response.sendError(404);
164                 return;
165             }
166
167             synchronized(_servletHandler)
168             {
169                 // find the entry for the invoker (me)
170                  _invokerEntry=_servletHandler.getHolderEntry(servlet_path);
171
172                 // Check for existing mapping (avoid threaded race).
173                 String path=URIUtil.addPaths(servlet_path,servlet);
174                 Map.Entry<String, ServletHolder> entry = _servletHandler.getHolderEntry(path);
175
176                 if (entry!=null && !entry.equals(_invokerEntry))
177                 {
178                     // Use the holder
179                     holder=(ServletHolder)entry.getValue();
180                 }
181                 else
182                 {
183                     // Make a holder
184                     if (LOG.isDebugEnabled())
185                         LOG.debug("Making new servlet="+servlet+" with path="+path+"/*");
186                     holder=_servletHandler.addServletWithMapping(servlet, path+"/*");
187
188                     if (_parameters!=null)
189                         holder.setInitParameters(_parameters);
190
191                     try {holder.start();}
192                     catch (Exception e)
193                     {
194                         LOG.debug(e);
195                         throw new UnavailableException(e.toString());
196                     }
197
198                     // Check it is from an allowable classloader
199                     if (!_nonContextServlets)
200                     {
201                         Object s=holder.getServlet();
202
203                         if (_contextHandler.getClassLoader()!=
204                             s.getClass().getClassLoader())
205                         {
206                             try
207                             {
208                                 holder.stop();
209                             }
210                             catch (Exception e)
211                             {
212                                 LOG.ignore(e);
213                             }
214
215                             LOG.warn("Dynamic servlet "+s+
216                                          " not loaded from context "+
217                                          request.getContextPath());
218                             throw new UnavailableException("Not in context");
219                         }
220                     }
221
222                     if (_verbose && LOG.isDebugEnabled())
223                         LOG.debug("Dynamic load '"+servlet+"' at "+path);
224                 }
225             }
226         }
227
228         if (holder!=null)
229         {
230             final Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest();
231             holder.handle(baseRequest,
232                     new InvokedRequest(request,included,servlet,servlet_path,path_info),
233                           response);
234         }
235         else
236         {
237             LOG.info("Can't find holder for servlet: "+servlet);
238             response.sendError(404);
239         }
240
241
242     }
243
244     /* ------------------------------------------------------------ */
245     class InvokedRequest extends HttpServletRequestWrapper
246     {
247         String _servletPath;
248         String _pathInfo;
249         boolean _included;
250
251         /* ------------------------------------------------------------ */
252         InvokedRequest(HttpServletRequest request,
253                 boolean included,
254                 String name,
255                 String servletPath,
256                 String pathInfo)
257         {
258             super(request);
259             _included=included;
260             _servletPath=URIUtil.addPaths(servletPath,name);
261             _pathInfo=pathInfo.substring(name.length()+1);
262             if (_pathInfo.length()==0)
263                 _pathInfo=null;
264         }
265
266         /* ------------------------------------------------------------ */
267         public String getServletPath()
268         {
269             if (_included)
270                 return super.getServletPath();
271             return _servletPath;
272         }
273
274         /* ------------------------------------------------------------ */
275         public String getPathInfo()
276         {
277             if (_included)
278                 return super.getPathInfo();
279             return _pathInfo;
280         }
281
282         /* ------------------------------------------------------------ */
283         public Object getAttribute(String name)
284         {
285             if (_included)
286             {
287                 if (name.equals(Dispatcher.INCLUDE_REQUEST_URI))
288                     return URIUtil.addPaths(URIUtil.addPaths(getContextPath(),_servletPath),_pathInfo);
289                 if (name.equals(Dispatcher.INCLUDE_PATH_INFO))
290                     return _pathInfo;
291                 if (name.equals(Dispatcher.INCLUDE_SERVLET_PATH))
292                     return _servletPath;
293             }
294             return super.getAttribute(name);
295         }
296     }
297
298
299     private ServletHolder getHolder(ServletHolder[] holders, String servlet)
300     {
301         if (holders == null)
302             return null;
303
304         ServletHolder holder = null;
305         for (int i=0; holder==null && i<holders.length; i++)
306         {
307             if (holders[i].getName().equals(servlet))
308             {
309                 holder = holders[i];
310             }
311         }
312         return holder;
313     }
314 }