1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.catalina.core;
18
19 import java.io.IOException;
20 import java.security.Principal;
21 import java.security.PrivilegedActionException;
22 import java.util.Set;
23
24 import javax.servlet.Filter;
25 import javax.servlet.FilterChain;
26 import javax.servlet.Servlet;
27 import javax.servlet.ServletException;
28 import javax.servlet.ServletRequest;
29 import javax.servlet.ServletResponse;
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpServletResponse;
32
33 import org.apache.catalina.Globals;
34 import org.apache.catalina.security.SecurityUtil;
35 import org.apache.tomcat.util.ExceptionUtils;
36 import org.apache.tomcat.util.res.StringManager;
37
38 /**
39  * Implementation of <code>javax.servlet.FilterChain</code> used to manage
40  * the execution of a set of filters for a particular request.  When the
41  * set of defined filters has all been executed, the next call to
42  * <code>doFilter()</code> will execute the servlet's <code>service()</code>
43  * method itself.
44  *
45  * @author Craig R. McClanahan
46  */

47 public final class ApplicationFilterChain implements FilterChain {
48
49     // Used to enforce requirements of SRV.8.2 / SRV.14.2.5.1
50     private static final ThreadLocal<ServletRequest> lastServicedRequest;
51     private static final ThreadLocal<ServletResponse> lastServicedResponse;
52
53     static {
54         if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
55             lastServicedRequest = new ThreadLocal<>();
56             lastServicedResponse = new ThreadLocal<>();
57         } else {
58             lastServicedRequest = null;
59             lastServicedResponse = null;
60         }
61     }
62
63     // -------------------------------------------------------------- Constants
64
65
66     public static final int INCREMENT = 10;
67
68
69     // ----------------------------------------------------- Instance Variables
70
71     /**
72      * Filters.
73      */

74     private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
75
76
77     /**
78      * The int which is used to maintain the current position
79      * in the filter chain.
80      */

81     private int pos = 0;
82
83
84     /**
85      * The int which gives the current number of filters in the chain.
86      */

87     private int n = 0;
88
89
90     /**
91      * The servlet instance to be executed by this chain.
92      */

93     private Servlet servlet = null;
94
95
96     /**
97      * Does the associated servlet instance support async processing?
98      */

99     private boolean servletSupportsAsync = false;
100
101     /**
102      * The string manager for our package.
103      */

104     private static final StringManager sm =
105       StringManager.getManager(Constants.Package);
106
107
108     /**
109      * Static class array used when the SecurityManager is turned on and
110      * <code>doFilter</code> is invoked.
111      */

112     private static final Class<?>[] classType = new Class[]{
113         ServletRequest.class, ServletResponse.class, FilterChain.class};
114
115     /**
116      * Static class array used when the SecurityManager is turned on and
117      * <code>service</code> is invoked.
118      */

119     private static final Class<?>[] classTypeUsedInService = new Class[]{
120         ServletRequest.class, ServletResponse.class};
121
122
123     // ---------------------------------------------------- FilterChain Methods
124
125     /**
126      * Invoke the next filter in this chain, passing the specified request
127      * and response.  If there are no more filters in this chain, invoke
128      * the <code>service()</code> method of the servlet itself.
129      *
130      * @param request The servlet request we are processing
131      * @param response The servlet response we are creating
132      *
133      * @exception IOException if an input/output error occurs
134      * @exception ServletException if a servlet exception occurs
135      */

136     @Override
137     public void doFilter(ServletRequest request, ServletResponse response)
138         throws IOException, ServletException {
139
140         if( Globals.IS_SECURITY_ENABLED ) {
141             final ServletRequest req = request;
142             final ServletResponse res = response;
143             try {
144                 java.security.AccessController.doPrivileged(
145                     new java.security.PrivilegedExceptionAction<Void>() {
146                         @Override
147                         public Void run()
148                             throws ServletException, IOException {
149                             internalDoFilter(req,res);
150                             return null;
151                         }
152                     }
153                 );
154             } catch( PrivilegedActionException pe) {
155                 Exception e = pe.getException();
156                 if (e instanceof ServletException)
157                     throw (ServletException) e;
158                 else if (e instanceof IOException)
159                     throw (IOException) e;
160                 else if (e instanceof RuntimeException)
161                     throw (RuntimeException) e;
162                 else
163                     throw new ServletException(e.getMessage(), e);
164             }
165         } else {
166             internalDoFilter(request,response);
167         }
168     }
169
170     private void internalDoFilter(ServletRequest request,
171                                   ServletResponse response)
172         throws IOException, ServletException {
173
174         // Call the next filter if there is one
175         if (pos < n) {
176             ApplicationFilterConfig filterConfig = filters[pos++];
177             try {
178                 Filter filter = filterConfig.getFilter();
179
180                 if (request.isAsyncSupported() && "false".equalsIgnoreCase(
181                         filterConfig.getFilterDef().getAsyncSupported())) {
182                     request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
183                 }
184                 if( Globals.IS_SECURITY_ENABLED ) {
185                     final ServletRequest req = request;
186                     final ServletResponse res = response;
187                     Principal principal =
188                         ((HttpServletRequest) req).getUserPrincipal();
189
190                     Object[] args = new Object[]{req, res, this};
191                     SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
192                 } else {
193                     filter.doFilter(request, response, this);
194                 }
195             } catch (IOException | ServletException | RuntimeException e) {
196                 throw e;
197             } catch (Throwable e) {
198                 e = ExceptionUtils.unwrapInvocationTargetException(e);
199                 ExceptionUtils.handleThrowable(e);
200                 throw new ServletException(sm.getString("filterChain.filter"), e);
201             }
202             return;
203         }
204
205         // We fell off the end of the chain -- call the servlet instance
206         try {
207             if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
208                 lastServicedRequest.set(request);
209                 lastServicedResponse.set(response);
210             }
211
212             if (request.isAsyncSupported() && !servletSupportsAsync) {
213                 request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
214                         Boolean.FALSE);
215             }
216             // Use potentially wrapped request from this point
217             if ((request instanceof HttpServletRequest) &&
218                     (response instanceof HttpServletResponse) &&
219                     Globals.IS_SECURITY_ENABLED ) {
220                 final ServletRequest req = request;
221                 final ServletResponse res = response;
222                 Principal principal =
223                     ((HttpServletRequest) req).getUserPrincipal();
224                 Object[] args = new Object[]{req, res};
225                 SecurityUtil.doAsPrivilege("service",
226                                            servlet,
227                                            classTypeUsedInService,
228                                            args,
229                                            principal);
230             } else {
231                 servlet.service(request, response);
232             }
233         } catch (IOException | ServletException | RuntimeException e) {
234             throw e;
235         } catch (Throwable e) {
236             e = ExceptionUtils.unwrapInvocationTargetException(e);
237             ExceptionUtils.handleThrowable(e);
238             throw new ServletException(sm.getString("filterChain.servlet"), e);
239         } finally {
240             if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
241                 lastServicedRequest.set(null);
242                 lastServicedResponse.set(null);
243             }
244         }
245     }
246
247
248     /**
249      * The last request passed to a servlet for servicing from the current
250      * thread.
251      *
252      * @return The last request to be serviced.
253      */

254     public static ServletRequest getLastServicedRequest() {
255         return lastServicedRequest.get();
256     }
257
258
259     /**
260      * The last response passed to a servlet for servicing from the current
261      * thread.
262      *
263      * @return The last response to be serviced.
264      */

265     public static ServletResponse getLastServicedResponse() {
266         return lastServicedResponse.get();
267     }
268
269
270     // -------------------------------------------------------- Package Methods
271
272     /**
273      * Add a filter to the set of filters that will be executed in this chain.
274      *
275      * @param filterConfig The FilterConfig for the servlet to be executed
276      */

277     void addFilter(ApplicationFilterConfig filterConfig) {
278
279         // Prevent the same filter being added multiple times
280         for(ApplicationFilterConfig filter:filters)
281             if(filter==filterConfig)
282                 return;
283
284         if (n == filters.length) {
285             ApplicationFilterConfig[] newFilters =
286                 new ApplicationFilterConfig[n + INCREMENT];
287             System.arraycopy(filters, 0, newFilters, 0, n);
288             filters = newFilters;
289         }
290         filters[n++] = filterConfig;
291
292     }
293
294
295     /**
296      * Release references to the filters and wrapper executed by this chain.
297      */

298     void release() {
299         for (int i = 0; i < n; i++) {
300             filters[i] = null;
301         }
302         n = 0;
303         pos = 0;
304         servlet = null;
305         servletSupportsAsync = false;
306     }
307
308
309     /**
310      * Prepare for reuse of the filters and wrapper executed by this chain.
311      */

312     void reuse() {
313         pos = 0;
314     }
315
316
317     /**
318      * Set the servlet that will be executed at the end of this chain.
319      *
320      * @param servlet The Wrapper for the servlet to be executed
321      */

322     void setServlet(Servlet servlet) {
323         this.servlet = servlet;
324     }
325
326
327     void setServletSupportsAsync(boolean servletSupportsAsync) {
328         this.servletSupportsAsync = servletSupportsAsync;
329     }
330
331
332     /**
333      * Identifies the Filters, if any, in this FilterChain that do not support
334      * async.
335      *
336      * @param result The Set to which the fully qualified class names of each
337      *               Filter in this FilterChain that does not support async will
338      *               be added
339      */

340     public void findNonAsyncFilters(Set<String> result) {
341         for (int i = 0; i < n ; i++) {
342             ApplicationFilterConfig filter = filters[i];
343             if ("false".equalsIgnoreCase(filter.getFilterDef().getAsyncSupported())) {
344                 result.add(filter.getFilterClass());
345             }
346         }
347     }
348 }
349