You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2019/10/11 12:46:34 UTC
[tomcat] branch 7.0.x updated: asyncStarted should be false once
complete/dispatch in onTimeout
This is an automated email from the ASF dual-hosted git repository.
markt pushed a commit to branch 7.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/7.0.x by this push:
new 528032c asyncStarted should be false once complete/dispatch in onTimeout
528032c is described below
commit 528032c68cb711287eb437069cceb005f8ba32b6
Author: Mark Thomas <ma...@apache.org>
AuthorDate: Thu Oct 10 16:57:55 2019 +0100
asyncStarted should be false once complete/dispatch in onTimeout
---
java/org/apache/coyote/AsyncStateMachine.java | 25 +++++++++++---
.../apache/catalina/core/TestAsyncContextImpl.java | 38 +++++++++++++++++++---
webapps/docs/changelog.xml | 6 ++++
3 files changed, 61 insertions(+), 8 deletions(-)
diff --git a/java/org/apache/coyote/AsyncStateMachine.java b/java/org/apache/coyote/AsyncStateMachine.java
index 0c1bcec..8e2d8db 100644
--- a/java/org/apache/coyote/AsyncStateMachine.java
+++ b/java/org/apache/coyote/AsyncStateMachine.java
@@ -274,8 +274,7 @@ public class AsyncStateMachine<S> {
private synchronized boolean doComplete() {
boolean triggerDispatch = false;
- if (state == AsyncState.STARTING || state == AsyncState.TIMING_OUT ||
- state == AsyncState.ERROR) {
+ if (state == AsyncState.STARTING || state == AsyncState.ERROR) {
// Processing is on a container thread so no need to transfer
// processing to a new container thread
state = AsyncState.MUST_COMPLETE;
@@ -288,6 +287,14 @@ public class AsyncStateMachine<S> {
// request/response associated with the AsyncContext so need a new
// container thread to process the different request/response.
triggerDispatch = true;
+ } else if (state == AsyncState.TIMING_OUT) {
+ // Processing of timeouts can happen on or off a container thread
+ // (on is much more likely) but while in this state the call that
+ // triggers the timeout will be in progress on a container thread.
+ // The socket will be added to the poller when the container thread
+ // exits the AbstractConnectionHandler.process() method so don't do
+ // a dispatch here which would add it to the poller a second time.
+ state = AsyncState.COMPLETING;
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
@@ -327,8 +334,7 @@ public class AsyncStateMachine<S> {
private synchronized boolean doDispatch() {
boolean triggerDispatch = false;
- if (state == AsyncState.STARTING || state == AsyncState.TIMING_OUT ||
- state == AsyncState.ERROR) {
+ if (state == AsyncState.STARTING || state == AsyncState.ERROR) {
// Processing is on a container thread so no need to transfer
// processing to a new container thread
state = AsyncState.MUST_DISPATCH;
@@ -341,6 +347,17 @@ public class AsyncStateMachine<S> {
// request/response associated with the AsyncContext so need a new
// container thread to process the different request/response.
triggerDispatch = true;
+ } else if (state == AsyncState.TIMING_OUT) {
+ // Read/write operations can happen on or off a container thread but
+ // while in this state the call to listener that triggers the
+ // read/write will be in progress on a container thread.
+ // Processing of timeouts can happen on or off a container thread
+ // (on is much more likely) but while in this state the call that
+ // triggers the timeout will be in progress on a container thread.
+ // The socket will be added to the poller when the container thread
+ // exits the AbstractConnectionHandler.process() method so don't do
+ // a dispatch here which would add it to the poller a second time.
+ state = AsyncState.DISPATCHING;
} else {
throw new IllegalStateException(
sm.getString("asyncStateMachine.invalidAsyncState",
diff --git a/test/org/apache/catalina/core/TestAsyncContextImpl.java b/test/org/apache/catalina/core/TestAsyncContextImpl.java
index a8cb064..2319ca7 100644
--- a/test/org/apache/catalina/core/TestAsyncContextImpl.java
+++ b/test/org/apache/catalina/core/TestAsyncContextImpl.java
@@ -552,19 +552,25 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
alv.validateAccessLog(1, 200, timeoutDelay,
timeoutDelay + TIMEOUT_MARGIN + REQUEST_TIME);
}
+
+ Assert.assertTrue(timeout.isAsyncStartedCorrect());
}
private static class TimeoutServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private final Boolean completeOnTimeout;
- private final String dispatchUrl;
+ private final TrackingListener trackingListener;
public static final long ASYNC_TIMEOUT = 3000;
public TimeoutServlet(Boolean completeOnTimeout, String dispatchUrl) {
this.completeOnTimeout = completeOnTimeout;
- this.dispatchUrl = dispatchUrl;
+ if (completeOnTimeout == null) {
+ this.trackingListener = null;
+ } else {
+ this.trackingListener = new TrackingListener(false, completeOnTimeout.booleanValue(), dispatchUrl);
+ }
}
@Override
@@ -576,13 +582,19 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
ac.setTimeout(ASYNC_TIMEOUT);
if (completeOnTimeout != null) {
- ac.addListener(new TrackingListener(false,
- completeOnTimeout.booleanValue(), dispatchUrl));
+ ac.addListener(trackingListener);
}
} else {
resp.getWriter().print("FAIL: Async unsupported");
}
}
+
+ public boolean isAsyncStartedCorrect() {
+ if (trackingListener == null) {
+ return true;
+ }
+ return trackingListener.isAsyncStartedCorrect();
+ }
}
@Test
@@ -824,6 +836,7 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
private final boolean completeOnError;
private final boolean completeOnTimeout;
private final String dispatchUrl;
+ private boolean asyncStartedCorrect = true;
public TrackingListener(boolean completeOnError,
boolean completeOnTimeout, String dispatchUrl) {
@@ -839,27 +852,44 @@ public class TestAsyncContextImpl extends TomcatBaseTest {
@Override
public void onTimeout(AsyncEvent event) throws IOException {
+ boolean expectedAsyncStarted = true;
+
TestAsyncContextImpl.track("onTimeout-");
if (completeOnTimeout){
event.getAsyncContext().complete();
+ expectedAsyncStarted = false;
}
if (dispatchUrl != null) {
event.getAsyncContext().dispatch(dispatchUrl);
+ expectedAsyncStarted = false;
}
+
+ ServletRequest req = event.getSuppliedRequest();
+ asyncStartedCorrect = (expectedAsyncStarted == req.isAsyncStarted());
}
@Override
public void onError(AsyncEvent event) throws IOException {
+ boolean expectedAsyncStarted = true;
+
TestAsyncContextImpl.track("onError-");
if (completeOnError) {
event.getAsyncContext().complete();
+ expectedAsyncStarted = false;
}
+
+ ServletRequest req = event.getSuppliedRequest();
+ asyncStartedCorrect = (expectedAsyncStarted == req.isAsyncStarted());
}
@Override
public void onStartAsync(AsyncEvent event) throws IOException {
TestAsyncContextImpl.track("onStartAsync-");
}
+
+ public boolean isAsyncStartedCorrect() {
+ return asyncStartedCorrect;
+ }
}
private static class StickyTrackingListener extends TrackingListener {
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 44c1aad..f02589e 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -66,6 +66,12 @@
<bug>63814</bug>: Do not set server socket timeout with negative
values in NIO. (remm)
</fix>
+ <fix>
+ Ensure that <code>ServletRequest.isAsyncStarted()</code> returns
+ <code>false</code> once <code>AsyncContext.complete()</code> or
+ <code>AsyncContext.dispatch()</code> has been called during
+ <code>AsyncListener.onTimeout()</code>. (markt)
+ </fix>
</changelog>
</subsection>
</section>
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org