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