Skip to content

Commit f38fb52

Browse files
authored
[async] Disable default timeout system for all async requests (javalin#1823)
1 parent 8314ce7 commit f38fb52

File tree

3 files changed

+22
-2
lines changed

3 files changed

+22
-2
lines changed

javalin/src/main/java/io/javalin/http/servlet/JavalinServlet.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@ class JavalinServlet(val cfg: JavalinConfig) : HttpServlet() {
6060

6161
private fun JavalinServletContext.handleUserFuture() {
6262
val userFutureSupplier = userFutureSupplier!!.also { userFutureSupplier = null } // nullcheck in handleSync
63+
if (!isAsync()) startAsyncAndAddDefaultTimeoutListeners() // start async if not already started
64+
6365
val userFuture = handleTask { userFutureSupplier.get() } ?: return handleSync() // get future from supplier or handle error
64-
if (!isAsync()) startAsyncAndAddDefaultTimeoutListeners()
65-
req().asyncContext.addListener(onTimeout = { userFuture.cancel(true) })
66+
req().asyncContext.addListener(onTimeout = { userFuture.cancel(true) }) // cancel user's future if timeout occurs
67+
6668
userFuture
6769
.thenApply { handleSync() }
6870
.exceptionally {

javalin/src/main/java/io/javalin/http/util/AsyncUtil.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.javalin.http.util
22

3+
import io.javalin.config.HttpConfig
34
import io.javalin.http.Context
45
import io.javalin.util.ConcurrencyUtil
56
import io.javalin.util.function.ThrowingRunnable
@@ -16,8 +17,15 @@ internal object AsyncUtil {
1617

1718
val defaultExecutor = ConcurrencyUtil.executorService("JavalinDefaultAsyncThreadPool")
1819

20+
/**
21+
* Utility method that executes [task] asynchronously using [executor] ([defaultExecutor] by default).
22+
* It also provides custom timeout handling via [onTimeout] callback registered directly on underlying [CompletableFuture],
23+
* so global [HttpConfig.asyncTimeout] does not affect this particular task.
24+
*/
1925
fun submitAsyncTask(context: Context, executor: ExecutorService?, timeout: Long, onTimeout: Runnable?, task: ThrowingRunnable<Exception>): Unit =
2026
context.future {
27+
context.req().asyncContext.timeout = 0 // we're using cf timeouts below, so we need to disable default jetty timeout listener
28+
2129
CompletableFuture.runAsync({ task.run() }, executor ?: defaultExecutor)
2230
.let { if (timeout > 0) it.orTimeout(timeout, MILLISECONDS) else it }
2331
.let { if (onTimeout == null) it else it.exceptionally { exception ->

javalin/src/test/java/io/javalin/TestFuture.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,16 @@ internal class TestFuture {
5050
assertThat(http.getBody("/")).isEqualTo("Success")
5151
}
5252

53+
@Test
54+
fun `async context can be used in future supplier`() = TestUtil.test { app, http ->
55+
app.get("/") { ctx ->
56+
ctx.future {
57+
completedFuture(ctx.req().asyncContext.timeout).thenApply { ctx.result(it.toString()) }
58+
}
59+
}
60+
assertThat(http.getBody("/")).isEqualTo(app.cfg.http.asyncTimeout.toString())
61+
}
62+
5363
}
5464

5565
@Nested

0 commit comments

Comments
 (0)