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.coyote;
18
19 import java.security.AccessController;
20 import java.security.PrivilegedAction;
21 import java.util.concurrent.atomic.AtomicLong;
22
23 import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
24 import org.apache.tomcat.util.res.StringManager;
25 import org.apache.tomcat.util.security.PrivilegedGetTccl;
26 import org.apache.tomcat.util.security.PrivilegedSetTccl;
27
28 /**
29  * Manages the state transitions for async requests.
30  *
31  * <pre>
32  * The internal states that are used are:
33  * DISPATCHED       - Standard request. Not in Async mode.
34  * STARTING         - ServletRequest.startAsync() has been called from
35  *                    Servlet.service() but service() has not exited.
36  * STARTED          - ServletRequest.startAsync() has been called from
37  *                    Servlet.service() and service() has exited.
38  * READ_WRITE_OP    - Performing an asynchronous read or write.
39  * MUST_COMPLETE    - ServletRequest.startAsync() followed by complete() have
40  *                    been called during a single Servlet.service() method. The
41  *                    complete() will be processed as soon as Servlet.service()
42  *                    exits.
43  * COMPLETE_PENDING - ServletRequest.startAsync() has been called from
44  *                    Servlet.service() but, before service() exited, complete()
45  *                    was called from another thread. The complete() will
46  *                    be processed as soon as Servlet.service() exits.
47  * COMPLETING       - The call to complete() was made once the request was in
48  *                    the STARTED state.
49  * TIMING_OUT       - The async request has timed out and is waiting for a call
50  *                    to complete() or dispatch(). If that isn't made, the error
51  *                    state will be entered.
52  * MUST_DISPATCH    - ServletRequest.startAsync() followed by dispatch() have
53  *                    been called during a single Servlet.service() method. The
54  *                    dispatch() will be processed as soon as Servlet.service()
55  *                    exits.
56  * DISPATCH_PENDING - ServletRequest.startAsync() has been called from
57  *                    Servlet.service() but, before service() exited, dispatch()
58  *                    was called from another thread. The dispatch() will
59  *                    be processed as soon as Servlet.service() exits.
60  * DISPATCHING      - The dispatch is being processed.
61  * MUST_ERROR       - ServletRequest.startAsync() has been called from
62  *                    Servlet.service() but, before service() exited, an I/O
63  *                    error occured on another thread. The container will
64  *                    perform the necessary error handling when
65  *                    Servlet.service() exits.
66  * ERROR            - Something went wrong.
67  *
68  *
69  * The valid state transitions are:
70  *
71  *                  post()                                        dispatched()
72  *    |-------»------------------»---------|    |-------«-----------------------«-----|
73  *    |                                    |    |                                     |
74  *    |                                    |    |        post()                       |
75  *    |               post()              \|/  \|/       dispatched()                 |
76  *    |           |-----»----------------»DISPATCHED«-------------«-------------|     |
77  *    |           |                          | /|\ |                            |     |
78  *    |           |              startAsync()|  |--|timeout()                   |     |
79  *    ^           |                          |                                  |     |
80  *    |           |        complete()        |                  dispatch()      ^     |
81  *    |           |   |--«---------------«-- | ---«--MUST_ERROR--»-----|        |     |
82  *    |           |   |                      |         /|\             |        |     |
83  *    |           ^   |                      |          |              |        |     |
84  *    |           |   |                      |    /-----|error()       |        |     |
85  *    |           |   |                      |   /                     |        ^     |
86  *    |           |  \|/  ST-complete()     \|/ /   ST-dispatch()     \|/       |     |
87  *    |    MUST_COMPLETE«--------«--------STARTING--------»---------»MUST_DISPATCH    |
88  *    |                                    / | \                                      |
89  *    |                                   /  |  \                                     |
90  *    |                    OT-complete() /   |   \    OT-dispatch()                   |
91  *    |   COMPLETE_PENDING«------«------/    |    \-------»---------»DISPATCH_PENDING |
92  *    |          |                           |                           |            |
93  *    |    post()|   timeout()         post()|   post()            post()|  timeout() |
94  *    |          |   |--|                    |  |--|                     |    |--|    |
95  *    |         \|/ \|/ |   complete()      \|/\|/ |   dispatch()       \|/  \|/ |    |
96  *    |--«-----COMPLETING«--------«----------STARTED--------»---------»DISPATCHING----|
97  *            /|\  /|\ /|\                   | /|\ \                   /|\ /|\ /|\
98  *             |    |   |                    |  \   \asyncOperation()   |   |   |
99  *             |    |   |           timeout()|   \   \                  |   |   |
100  *             |    |   |                    |    \   \                 |   |   |
101  *             |    |   |                    |     \   \                |   |   |
102  *             |    |   |                    |      \   \               |   |   |
103  *             |    |   |                    |       \   \              |   |   |
104  *             |    |   |                    |  post()\   \   dispatch()|   |   |
105  *             |    |   |   complete()       |         \ \|/            |   |   |
106  *             |    |   |---«------------«-- | --«---READ_WRITE----»----|   |   |
107  *             |    |                        |                              |   |
108  *             |    |       complete()      \|/         dispatch()          |   |
109  *             |    |------------«-------TIMING_OUT--------»----------------|   |
110  *             |                                                                |
111  *             |            complete()                     dispatch()           |
112  *             |---------------«-----------ERROR--------------»-----------------|
113  *
114  *
115  * Notes: * For clarity, the transitions to ERROR which are valid from every state apart from
116  *          STARTING are not shown.
117  *        * All transitions may happen on either the Servlet.service() thread (ST) or on any
118  *          other thread (OT) unless explicitly marked.
119  * </pre>
120  */

121 class AsyncStateMachine {
122
123     /**
124      * The string manager for this package.
125      */

126     private static final StringManager sm = StringManager.getManager(AsyncStateMachine.class);
127
128     private enum AsyncState {
129         DISPATCHED      (falsefalsefalsefalse),
130         STARTING        (true,  true,  falsefalse),
131         STARTED         (true,  true,  falsefalse),
132         MUST_COMPLETE   (true,  true,  true,  false),
133         COMPLETE_PENDING(true,  true,  falsefalse),
134         COMPLETING      (true,  falsetrue,  false),
135         TIMING_OUT      (true,  true,  falsefalse),
136         MUST_DISPATCH   (true,  true,  falsetrue),
137         DISPATCH_PENDING(true,  true,  falsefalse),
138         DISPATCHING     (true,  falsefalsetrue),
139         READ_WRITE_OP   (true,  true,  falsefalse),
140         MUST_ERROR      (true,  true,  falsefalse),
141         ERROR           (true,  true,  falsefalse);
142
143         private final boolean isAsync;
144         private final boolean isStarted;
145         private final boolean isCompleting;
146         private final boolean isDispatching;
147
148         private AsyncState(boolean isAsync, boolean isStarted, boolean isCompleting,
149                 boolean isDispatching) {
150             this.isAsync = isAsync;
151             this.isStarted = isStarted;
152             this.isCompleting = isCompleting;
153             this.isDispatching = isDispatching;
154         }
155
156         boolean isAsync() {
157             return isAsync;
158         }
159
160         boolean isStarted() {
161             return isStarted;
162         }
163
164         boolean isDispatching() {
165             return isDispatching;
166         }
167
168         boolean isCompleting() {
169             return isCompleting;
170         }
171     }
172
173
174     private volatile AsyncState state = AsyncState.DISPATCHED;
175     private volatile long lastAsyncStart = 0;
176     /*
177      * Tracks the current generation of async processing for this state machine.
178      * The generation is incremented every time async processing is started. The
179      * primary purpose of this is to enable Tomcat to detect and prevent
180      * attempts to process an event for a previous generation with the current
181      * generation as processing such an event usually ends badly:
182      * e.g. CVE-2018-8037.
183      */

184     private final AtomicLong generation = new AtomicLong(0);
185     // Need this to fire listener on complete
186     private AsyncContextCallback asyncCtxt = null;
187     private final AbstractProcessor processor;
188
189
190     AsyncStateMachine(AbstractProcessor processor) {
191         this.processor = processor;
192     }
193
194
195     boolean isAsync() {
196         return state.isAsync();
197     }
198
199     boolean isAsyncDispatching() {
200         return state.isDispatching();
201     }
202
203     boolean isAsyncStarted() {
204         return state.isStarted();
205     }
206
207     boolean isAsyncTimingOut() {
208         return state == AsyncState.TIMING_OUT;
209     }
210
211     boolean isAsyncError() {
212         return state == AsyncState.ERROR;
213     }
214
215     boolean isCompleting() {
216         return state.isCompleting();
217     }
218
219     /**
220      * Obtain the time that this connection last transitioned to async
221      * processing.
222      *
223      * @return The time (as returned by {@link System#currentTimeMillis()}) that
224      *         this connection last transitioned to async
225      */

226     long getLastAsyncStart() {
227         return lastAsyncStart;
228     }
229
230     long getCurrentGeneration() {
231         return generation.get();
232     }
233
234     synchronized void asyncStart(AsyncContextCallback asyncCtxt) {
235         if (state == AsyncState.DISPATCHED) {
236             generation.incrementAndGet();
237             state = AsyncState.STARTING;
238             this.asyncCtxt = asyncCtxt;
239             lastAsyncStart = System.currentTimeMillis();
240         } else {
241             throw new IllegalStateException(
242                     sm.getString("asyncStateMachine.invalidAsyncState",
243                             "asyncStart()", state));
244         }
245     }
246
247     synchronized void asyncOperation() {
248         if (state==AsyncState.STARTED) {
249             state = AsyncState.READ_WRITE_OP;
250         } else {
251             throw new IllegalStateException(
252                     sm.getString("asyncStateMachine.invalidAsyncState",
253                             "asyncOperation()", state));
254         }
255     }
256
257     /*
258      * Async has been processed. Whether or not to enter a long poll depends on
259      * current state. For example, as per SRV.2.3.3.3 can now process calls to
260      * complete() or dispatch().
261      */

262     synchronized SocketState asyncPostProcess() {
263         if (state == AsyncState.COMPLETE_PENDING) {
264             clearNonBlockingListeners();
265             state = AsyncState.COMPLETING;
266             return SocketState.ASYNC_END;
267         } else if (state == AsyncState.DISPATCH_PENDING) {
268             clearNonBlockingListeners();
269             state = AsyncState.DISPATCHING;
270             return SocketState.ASYNC_END;
271         } else  if (state == AsyncState.STARTING || state == AsyncState.READ_WRITE_OP) {
272             state = AsyncState.STARTED;
273             return SocketState.LONG;
274         } else if (state == AsyncState.MUST_COMPLETE || state == AsyncState.COMPLETING) {
275             asyncCtxt.fireOnComplete();
276             state = AsyncState.DISPATCHED;
277             return SocketState.ASYNC_END;
278         } else if (state == AsyncState.MUST_DISPATCH) {
279             state = AsyncState.DISPATCHING;
280             return SocketState.ASYNC_END;
281         } else if (state == AsyncState.DISPATCHING) {
282             state = AsyncState.DISPATCHED;
283             return SocketState.ASYNC_END;
284         } else if (state == AsyncState.STARTED) {
285             // This can occur if an async listener does a dispatch to an async
286             // servlet during onTimeout
287             return SocketState.LONG;
288         } else {
289             throw new IllegalStateException(
290                     sm.getString("asyncStateMachine.invalidAsyncState",
291                             "asyncPostProcess()", state));
292         }
293     }
294
295
296     synchronized boolean asyncComplete() {
297         if (!ContainerThreadMarker.isContainerThread() && state == AsyncState.STARTING) {
298             state = AsyncState.COMPLETE_PENDING;
299             return false;
300         }
301
302         clearNonBlockingListeners();
303         boolean triggerDispatch = false;
304         if (state == AsyncState.STARTING || state == AsyncState.MUST_ERROR) {
305             // Processing is on a container thread so no need to transfer
306             // processing to a new container thread
307             state = AsyncState.MUST_COMPLETE;
308         } else if (state == AsyncState.STARTED) {
309             state = AsyncState.COMPLETING;
310             // A dispatch to a container thread is always required.
311             // If on a non-container thread, need to get back onto a container
312             // thread to complete the processing.
313             // If on a container thread the current request/response are not the
314             // request/response associated with the AsyncContext so need a new
315             // container thread to process the different request/response.
316             triggerDispatch = true;
317         } else if (state == AsyncState.READ_WRITE_OP || state == AsyncState.TIMING_OUT ||
318                 state == AsyncState.ERROR) {
319             // Read/write operations can happen on or off a container thread but
320             // while in this state the call to listener that triggers the
321             // read/write will be in progress on a container thread.
322             // Processing of timeouts and errors can happen on or off a
323             // container thread (on is much more likely) but while in this state
324             // the call that triggers the timeout will be in progress on a
325             // container thread.
326             // The socket will be added to the poller when the container thread
327             // exits the AbstractConnectionHandler.process() method so don't do
328             // a dispatch here which would add it to the poller a second time.
329             state = AsyncState.COMPLETING;
330         } else {
331             throw new IllegalStateException(
332                     sm.getString("asyncStateMachine.invalidAsyncState",
333                             "asyncComplete()", state));
334         }
335         return triggerDispatch;
336     }
337
338
339     synchronized boolean asyncTimeout() {
340         if (state == AsyncState.STARTED) {
341             state = AsyncState.TIMING_OUT;
342             return true;
343         } else if (state == AsyncState.COMPLETING ||
344                 state == AsyncState.DISPATCHING ||
345                 state == AsyncState.DISPATCHED) {
346             // NOOP - App called complete() or dispatch() between the the
347             // timeout firing and execution reaching this point
348             return false;
349         } else {
350             throw new IllegalStateException(
351                     sm.getString("asyncStateMachine.invalidAsyncState",
352                             "asyncTimeout()", state));
353         }
354     }
355
356
357     synchronized boolean asyncDispatch() {
358         if (!ContainerThreadMarker.isContainerThread() && state == AsyncState.STARTING) {
359             state = AsyncState.DISPATCH_PENDING;
360             return false;
361         }
362
363         clearNonBlockingListeners();
364         boolean triggerDispatch = false;
365         if (state == AsyncState.STARTING || state == AsyncState.MUST_ERROR) {
366             // Processing is on a container thread so no need to transfer
367             // processing to a new container thread
368             state = AsyncState.MUST_DISPATCH;
369         } else if (state == AsyncState.STARTED) {
370             state = AsyncState.DISPATCHING;
371             // A dispatch to a container thread is always required.
372             // If on a non-container thread, need to get back onto a container
373             // thread to complete the processing.
374             // If on a container thread the current request/response are not the
375             // request/response associated with the AsyncContext so need a new
376             // container thread to process the different request/response.
377             triggerDispatch = true;
378         } else if (state == AsyncState.READ_WRITE_OP || state == AsyncState.TIMING_OUT ||
379                 state == AsyncState.ERROR) {
380             // Read/write operations can happen on or off a container thread but
381             // while in this state the call to listener that triggers the
382             // read/write will be in progress on a container thread.
383             // Processing of timeouts and errors can happen on or off a
384             // container thread (on is much more likely) but while in this state
385             // the call that triggers the timeout will be in progress on a
386             // container thread.
387             // The socket will be added to the poller when the container thread
388             // exits the AbstractConnectionHandler.process() method so don't do
389             // a dispatch here which would add it to the poller a second time.
390             state = AsyncState.DISPATCHING;
391         } else {
392             throw new IllegalStateException(
393                     sm.getString("asyncStateMachine.invalidAsyncState",
394                             "asyncDispatch()", state));
395         }
396         return triggerDispatch;
397     }
398
399
400     synchronized void asyncDispatched() {
401         if (state == AsyncState.DISPATCHING ||
402                 state == AsyncState.MUST_DISPATCH) {
403             state = AsyncState.DISPATCHED;
404         } else {
405             throw new IllegalStateException(
406                     sm.getString("asyncStateMachine.invalidAsyncState",
407                             "asyncDispatched()", state));
408         }
409     }
410
411
412     synchronized boolean asyncError() {
413         clearNonBlockingListeners();
414         if (state == AsyncState.STARTING) {
415             state = AsyncState.MUST_ERROR;
416         } else {
417             state = AsyncState.ERROR;
418         }
419         return !ContainerThreadMarker.isContainerThread();
420     }
421
422
423     synchronized void asyncRun(Runnable runnable) {
424         if (state == AsyncState.STARTING || state ==  AsyncState.STARTED ||
425                 state == AsyncState.READ_WRITE_OP) {
426             // Execute the runnable using a container thread from the
427             // Connector's thread pool. Use a wrapper to prevent a memory leak
428             ClassLoader oldCL;
429             if (Constants.IS_SECURITY_ENABLED) {
430                 PrivilegedAction<ClassLoader> pa = new PrivilegedGetTccl();
431                 oldCL = AccessController.doPrivileged(pa);
432             } else {
433                 oldCL = Thread.currentThread().getContextClassLoader();
434             }
435             try {
436                 if (Constants.IS_SECURITY_ENABLED) {
437                     PrivilegedAction<Void> pa = new PrivilegedSetTccl(
438                             this.getClass().getClassLoader());
439                     AccessController.doPrivileged(pa);
440                 } else {
441                     Thread.currentThread().setContextClassLoader(
442                             this.getClass().getClassLoader());
443                 }
444
445                 processor.execute(runnable);
446             } finally {
447                 if (Constants.IS_SECURITY_ENABLED) {
448                     PrivilegedAction<Void> pa = new PrivilegedSetTccl(
449                             oldCL);
450                     AccessController.doPrivileged(pa);
451                 } else {
452                     Thread.currentThread().setContextClassLoader(oldCL);
453                 }
454             }
455         } else {
456             throw new IllegalStateException(
457                     sm.getString("asyncStateMachine.invalidAsyncState",
458                             "asyncRun()", state));
459         }
460
461     }
462
463
464     synchronized boolean isAvailable() {
465         if (asyncCtxt == null) {
466             // Async processing has probably been completed in another thread.
467             // Trigger a timeout to make sure the Processor is cleaned up.
468             return false;
469         }
470         return asyncCtxt.isAvailable();
471     }
472
473
474     synchronized void recycle() {
475         // Use lastAsyncStart to determine if this instance has been used since
476         // it was last recycled. If it hasn't there is no need to recycle again
477         // which saves the relatively expensive call to notifyAll()
478         if (lastAsyncStart == 0) {
479             return;
480         }
481         // Ensure in case of error that any non-container threads that have been
482         // paused are unpaused.
483         notifyAll();
484         asyncCtxt = null;
485         state = AsyncState.DISPATCHED;
486         lastAsyncStart = 0;
487     }
488
489
490     private void clearNonBlockingListeners() {
491         processor.getRequest().listener = null;
492         processor.getRequest().getResponse().listener = null;
493     }
494 }
495