Affects: : Spring Web 6.0.0-RC2
The methods annotated with @HttpExchange
work great using Reactor, but unfortunately it's currently unable to use suspend
functions in its method signatures.
This is a sample repository, with instructions to reproduce the issue: https://github.com/renatomrcosta/boot3-playground
When using the HttpExchange annotations, the following reactor sample works as expected:
interface ReactorClient {
@GetExchange("/delay/5")
fun getWithDelay(): Mono<String>
}
But its Kotlin Coroutines counterpart will fail during a call:
interface CoroutinesClient {
@GetExchange("/delay/5")
suspend fun getWithDelay(): String
}
This is due to the fact that the @HttpExchange will try to pass the continuation argument of the compiled suspend call to be handled during invocation, and it will fail to parse it:
java.lang.IllegalStateException: Could not resolve parameter [0] in public abstract java.lang.Object com.example.boot3playground.CoroutinesClient.getWithDelay(kotlin.coroutines.Continuation<? super java.lang.String>): No suitable resolver
at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-6.0.0-RC2.jar:6.0.0-RC2]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
*__checkpoint ⇢ Handler com.example.boot3playground.Controllo#hello(Continuation) [DispatcherHandler]
*__checkpoint ⇢ org.springframework.web.filter.reactive.ServerHttpObservationFilter [DefaultWebFilterChain]
*__checkpoint ⇢ HTTP GET "/hello" [ExceptionHandlingWebHandler]
Original Stack Trace:
at org.springframework.util.Assert.state(Assert.java:76) ~[spring-core-6.0.0-RC2.jar:6.0.0-RC2]
at org.springframework.web.service.invoker.HttpServiceMethod.applyArguments(HttpServiceMethod.java:119) ~[spring-web-6.0.0-RC2.jar:6.0.0-RC2]
at org.springframework.web.service.invoker.HttpServiceMethod.invoke(HttpServiceMethod.java:104) ~[spring-web-6.0.0-RC2.jar:6.0.0-RC2]
at org.springframework.web.service.invoker.HttpServiceProxyFactory$HttpServiceMethodInterceptor.invoke(HttpServiceProxyFactory.java:354) ~[spring-web-6.0.0-RC2.jar:6.0.0-RC2]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) ~[spring-aop-6.0.0-RC2.jar:6.0.0-RC2]
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:218) ~[spring-aop-6.0.0-RC2.jar:6.0.0-RC2]
at jdk.proxy3/jdk.proxy3.$Proxy68.getWithDelay(Unknown Source) ~[na:na]
at com.example.boot3playground.Controllo.hello$suspendImpl(Boot3PlaygroundApplication.kt:29) ~[main/:na]
at com.example.boot3playground.Controllo.hello(Boot3PlaygroundApplication.kt) ~[main/:na]
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:578) ~[na:na]
at kotlin.reflect.jvm.internal.calls.CallerImpl$Method.callMethod(CallerImpl.kt:97) ~[kotlin-reflect-1.7.20.jar:1.7.20-release-201(1.7.20)]
at kotlin.reflect.jvm.internal.calls.CallerImpl$Method$Instance.call(CallerImpl.kt:113) ~[kotlin-reflect-1.7.20.jar:1.7.20-release-201(1.7.20)]
at kotlin.reflect.jvm.internal.KCallableImpl.call(KCallableImpl.kt:108) ~[kotlin-reflect-1.7.20.jar:1.7.20-release-201(1.7.20)]
at kotlin.reflect.full.KCallables.callSuspend(KCallables.kt:56) ~[kotlin-reflect-1.7.20.jar:1.7.20-release-201(1.7.20)]
at org.springframework.core.CoroutinesUtils.lambda$invokeSuspendingFunction$2(CoroutinesUtils.java:103) ~[spring-core-6.0.0-RC2.jar:6.0.0-RC2]
at kotlin.coroutines.intrinsics.IntrinsicsKt__IntrinsicsJvmKt$createCoroutineUnintercepted$$inlined$createCoroutineFromSuspendFunction$IntrinsicsKt__IntrinsicsJvmKt$4.invokeSuspend(IntrinsicsJvm.kt:205) ~[kotlin-stdlib-1.7.20.jar:1.7.20-release-201(1.7.20)]
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) ~[kotlin-stdlib-1.7.20.jar:1.7.20-release-201(1.7.20)]
at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:367) ~[kotlinx-coroutines-core-jvm-1.6.4.jar:na]
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:30) ~[kotlinx-coroutines-core-jvm-1.6.4.jar:na]
at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable$default(Cancellable.kt:25) ~[kotlinx-coroutines-core-jvm-1.6.4.jar:na]
at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:110) ~[kotlinx-coroutines-core-jvm-1.6.4.jar:na]
at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:126) ~[kotlinx-coroutines-core-jvm-1.6.4.jar:na]
at kotlinx.coroutines.reactor.MonoKt.monoInternal$lambda-2(Mono.kt:90) ~[kotlinx-coroutines-reactor-1.6.4.jar:na]
at reactor.core.publisher.MonoCreate.subscribe(MonoCreate.java:58) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.secondComplete(MonoFlatMap.java:245) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoFlatMap$FlatMapInner.onNext(MonoFlatMap.java:305) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxOnErrorResume$ResumeSubscriber.onNext(FluxOnErrorResume.java:79) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:200) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.complete(MonoIgnoreThen.java:292) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onNext(MonoIgnoreThen.java:187) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:158) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoZip$ZipCoordinator.signal(MonoZip.java:291) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoZip$ZipInner.onNext(MonoZip.java:472) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2543) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoZip$ZipInner.onSubscribe(MonoZip.java:464) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onSubscribe(MonoPeekTerminal.java:152) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoJust.subscribe(MonoJust.java:55) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoZip$ZipCoordinator.request(MonoZip.java:215) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.request(MonoFlatMap.java:194) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onSubscribe(MonoIgnoreThen.java:134) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onSubscribe(MonoFlatMap.java:117) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:124) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:240) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.onComplete(MonoIgnoreThen.java:203) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:189) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.Operators.complete(Operators.java:137) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoZip.subscribe(MonoZip.java:120) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.Mono.subscribe(Mono.java:4400) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:82) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.innerNext(FluxConcatMapNoPrefetch.java:258) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:863) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:129) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.onNext(MonoPeekTerminal.java:180) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2543) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoPeekTerminal$MonoTerminalPeekSubscriber.request(MonoPeekTerminal.java:139) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:171) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.request(Operators.java:2303) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.request(FluxConcatMapNoPrefetch.java:338) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoNext$NextSubscriber.request(MonoNext.java:108) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:2339) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:2213) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoNext$NextSubscriber.onSubscribe(MonoNext.java:70) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxConcatMapNoPrefetch$FluxConcatMapNoPrefetchSubscriber.onSubscribe(FluxConcatMapNoPrefetch.java:164) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:165) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:87) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.Mono.subscribe(Mono.java:4400) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:55) ~[reactor-core-3.5.0-RC1.jar:3.5.0-RC1]
at reactor.netty.http.server.HttpServer$HttpServerHandle.onStateChange(HttpServer.java:997) ~[reactor-netty-http-1.1.0-RC1.jar:1.1.0-RC1]
at reactor.netty.ReactorNetty$CompositeConnectionObserver.onStateChange(ReactorNetty.java:680) ~[reactor-netty-core-1.1.0-RC1.jar:1.1.0-RC1]
at reactor.netty.transport.ServerTransport$ChildObserver.onStateChange(ServerTransport.java:477) ~[reactor-netty-core-1.1.0-RC1.jar:1.1.0-RC1]
at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:573) ~[reactor-netty-http-1.1.0-RC1.jar:1.1.0-RC1]
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:113) ~[reactor-netty-core-1.1.0-RC1.jar:1.1.0-RC1]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:220) ~[reactor-netty-http-1.1.0-RC1.jar:1.1.0-RC1]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:336) ~[netty-codec-4.1.84.Final.jar:4.1.84.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:308) ~[netty-codec-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:788) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.84.Final.jar:4.1.84.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.84.Final.jar:4.1.84.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.84.Final.jar:4.1.84.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.84.Final.jar:4.1.84.Final]
at java.base/java.lang.Thread.run(Thread.java:1589) ~[na:na]
A naive solution would be to filter out the ContinuationImpl
object passed in the arguments in org.springframework.web.service.invoker.HttpServiceMethod#invoke
, or maybe better yet to have an argument resolver that can ignore the Continuation object at org.springframework.web.service.invoker.HttpServiceMethod#applyArguments
but I must admit I'm not very familiar with the inner workings of Spring to propose a more thorough approach.
Supposing the continuation is filtered out, the org.springframework.web.service.invoker.HttpServiceMethod.ResponseFunction#execute
function would have to use a non blocking API of some sort, I suppose as well.
Ultimately, it would be great to be able to simply add a suspend modifier, such as in the @HttpMapping annotations to get coroutines support out of the box.
Comment From: sdeleuze
This is more complex than adding a ContinuationArgumentResolver
since this involves the proxied invocation of a suspended function, I think it depends on #22462.
Comment From: sdeleuze
Superseded by the PR #29527.
Comment From: renatomrcosta
For future reference: Was fixed in https://github.com/spring-projects/spring-framework/commit/cb63164593f4f2a028645c71669caf9fbc8cf658, and is working as intended since Spring 6.0.5.
Sample working code: https://github.com/renatomrcosta/boot3-playground/commit/5eb809bdfab0df4b33624139f616df9eb5b1623f