Skip to content

Commit 3ed981e

Browse files
committed
[UNDERTOW-1870] Backport of RFE with property instead of API change
1 parent e869e9f commit 3ed981e

File tree

4 files changed

+211
-1
lines changed

4 files changed

+211
-1
lines changed

servlet/src/main/java/io/undertow/servlet/spec/AsyncContextImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public class AsyncContextImpl implements AsyncContext {
8787

8888
//todo: make default configurable
8989
private volatile long timeout = 30000;
90-
90+
public static final String ASYNC_CONTEXT_TIMEOUT = "io.undertow.servlet.ASYNC_CONTEXT_TIMEOUT";
9191
private volatile XnioExecutor.Key timeoutKey;
9292

9393
private boolean dispatched;
@@ -106,6 +106,7 @@ public AsyncContextImpl(final HttpServerExchange exchange, final ServletRequest
106106
this.servletRequestContext = servletRequestContext;
107107
this.requestSupplied = requestSupplied;
108108
this.previousAsyncContext = previousAsyncContext;
109+
this.timeout = Integer.parseInt(System.getProperty(ASYNC_CONTEXT_TIMEOUT, timeout+""));
109110
initiatingThread = Thread.currentThread();
110111
exchange.dispatch(SameThreadExecutor.INSTANCE, new Runnable() {
111112
@Override
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2025 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package io.undertow.servlet.test.listener.request.async.onTimeout.property;
20+
21+
import java.io.IOException;
22+
23+
import io.undertow.servlet.spec.AsyncContextImpl;
24+
import jakarta.servlet.AsyncContext;
25+
import jakarta.servlet.ServletException;
26+
import jakarta.servlet.http.HttpServlet;
27+
import jakarta.servlet.http.HttpServletRequest;
28+
import jakarta.servlet.http.HttpServletResponse;
29+
30+
/**
31+
* @author baranowb
32+
*/
33+
public class AsyncServlet extends HttpServlet {
34+
35+
public static final String TEST_TIMEOUT="timeout";
36+
public static final String TIMEOUT_START_TSTAMP = "tstamp";
37+
@Override
38+
protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
39+
long timeout = Long.parseLong((String)req.getParameter(TEST_TIMEOUT));
40+
System.setProperty(AsyncContextImpl.ASYNC_CONTEXT_TIMEOUT, timeout+"");
41+
req.setAttribute(TIMEOUT_START_TSTAMP, System.currentTimeMillis());
42+
AsyncContext ctx = req.startAsync();
43+
ctx.addListener(new SimpleAsyncListener());
44+
Thread t = new Thread(new Runnable() {
45+
@Override
46+
public void run() {
47+
try {
48+
Thread.sleep(timeout+3000L);
49+
} catch (InterruptedException e) {
50+
throw new RuntimeException(e);
51+
}
52+
System.setProperty(AsyncContextImpl.ASYNC_CONTEXT_TIMEOUT, "30000");
53+
}
54+
});
55+
t.start();
56+
}
57+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2025 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package io.undertow.servlet.test.listener.request.async.onTimeout.property;
19+
20+
import java.io.IOException;
21+
22+
import org.apache.http.HttpResponse;
23+
import org.apache.http.client.methods.HttpGet;
24+
import org.junit.Assert;
25+
import org.junit.BeforeClass;
26+
import org.junit.Test;
27+
import org.junit.runner.RunWith;
28+
29+
import io.undertow.server.handlers.PathHandler;
30+
import io.undertow.servlet.api.DeploymentInfo;
31+
import io.undertow.servlet.api.DeploymentManager;
32+
import io.undertow.servlet.api.ListenerInfo;
33+
import io.undertow.servlet.api.ServletContainer;
34+
import io.undertow.servlet.api.ServletInfo;
35+
import io.undertow.servlet.test.listener.request.async.onTimeout.SimpleRequestListener;
36+
import io.undertow.servlet.test.util.TestClassIntrospector;
37+
import io.undertow.testutils.DefaultServer;
38+
import io.undertow.testutils.TestHttpClient;
39+
import io.undertow.util.StatusCodes;
40+
import jakarta.servlet.ServletException;
41+
42+
@RunWith(DefaultServer.class)
43+
public class DefaultAsyncTimeoutTestCase {
44+
@BeforeClass
45+
public static void setup() throws ServletException {
46+
47+
final PathHandler root = new PathHandler();
48+
final ServletContainer container = ServletContainer.Factory.newInstance();
49+
50+
ServletInfo a = new ServletInfo("asyncServlet", AsyncServlet.class)
51+
.setAsyncSupported(true)
52+
.addMapping("/async");
53+
54+
DeploymentInfo builder = new DeploymentInfo()
55+
.setClassLoader(DefaultAsyncTimeoutTestCase.class.getClassLoader())
56+
.setContextPath("/servletContext")
57+
.setClassIntrospecter(TestClassIntrospector.INSTANCE)
58+
.setDeploymentName("servletContext.war")
59+
.addServlets(a)
60+
.addListener(new ListenerInfo(SimpleRequestListener.class));
61+
62+
DeploymentManager manager = container.addDeployment(builder);
63+
manager.deploy();
64+
root.addPrefixPath(builder.getContextPath(), manager.start());
65+
66+
DefaultServer.setRootHandler(root);
67+
}
68+
69+
@Test
70+
public void testDefaultTimeout() throws IOException {
71+
TestHttpClient client = new TestHttpClient();
72+
try {
73+
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/async?"+AsyncServlet.TEST_TIMEOUT+"=30000");
74+
HttpResponse response = client.execute(get);
75+
Assert.assertEquals(StatusCodes.OK, response.getStatusLine().getStatusCode());
76+
Assert.assertFalse(SimpleRequestListener.hasNestedInvocationOccured());
77+
} finally {
78+
client.getConnectionManager().shutdown();
79+
}
80+
}
81+
82+
@Test
83+
public void testNewDefaultTimeout() throws IOException {
84+
TestHttpClient client = new TestHttpClient();
85+
try {
86+
HttpGet get = new HttpGet(DefaultServer.getDefaultServerURL() + "/servletContext/async?"+AsyncServlet.TEST_TIMEOUT+"=20000");
87+
HttpResponse response = client.execute(get);
88+
Assert.assertEquals(StatusCodes.OK, response.getStatusLine().getStatusCode());
89+
Assert.assertFalse(SimpleRequestListener.hasNestedInvocationOccured());
90+
} finally {
91+
client.getConnectionManager().shutdown();
92+
}
93+
}
94+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* JBoss, Home of Professional Open Source.
3+
* Copyright 2025 Red Hat, Inc., and individual contributors
4+
* as indicated by the @author tags.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package io.undertow.servlet.test.listener.request.async.onTimeout.property;
19+
20+
import io.undertow.util.StatusCodes;
21+
22+
import java.io.IOException;
23+
24+
import jakarta.servlet.AsyncEvent;
25+
import jakarta.servlet.AsyncListener;
26+
import jakarta.servlet.http.HttpServletResponse;
27+
28+
public class SimpleAsyncListener implements AsyncListener {
29+
30+
public static final String HEADER="TIMER_HEADER";
31+
@Override
32+
public void onComplete(AsyncEvent event) throws IOException {
33+
}
34+
35+
@Override
36+
public void onTimeout(AsyncEvent event) throws IOException {
37+
HttpServletResponse response = (HttpServletResponse) event.getSuppliedResponse();
38+
final long startTstamp = (long) event.getSuppliedRequest().getAttribute(AsyncServlet.TIMEOUT_START_TSTAMP);
39+
long timeout = Long.parseLong((String) event.getSuppliedRequest().getParameter(AsyncServlet.TEST_TIMEOUT));
40+
long elapsed = System.currentTimeMillis() - startTstamp;
41+
response.setHeader(HEADER, (System.currentTimeMillis() - startTstamp) + "");
42+
if (Math.abs(timeout - elapsed) < 2000) {
43+
response.setStatus(StatusCodes.OK);
44+
} else {
45+
response.setStatus(StatusCodes.CONFLICT);
46+
}
47+
event.getAsyncContext().complete();
48+
}
49+
50+
@Override
51+
public void onError(AsyncEvent event) throws IOException {
52+
}
53+
54+
@Override
55+
public void onStartAsync(AsyncEvent event) throws IOException {
56+
}
57+
58+
}

0 commit comments

Comments
 (0)