micronaut-projects / micronaut-core

Micronaut Application Framework
http://micronaut.io
Apache License 2.0
6k stars 1.04k forks source link

ServerRequestContext.currentRequest() is not available when accessing in a nested block of runBlocking() and async() #5555

Open Alex100 opened 3 years ago

Alex100 commented 3 years ago

Using coroutines with Micronaut and AWS Lambdas enforced us to scope controller functions with runBlocking (see discussion https://github.com/micronaut-projects/micronaut-aws/issues/1079). This results in a problem where the currentRequest is null if ServerRequestContext.currentRequest() is called in the inner block of runBlocking and async(Dispatchers.IO)

Task List

Steps to Reproduce

@Controller("/")
class MyController() {
    @Get("/test")
    fun test() = runBlocking {

        val async0 = async(Dispatchers.IO) {
            delay(1000)
            val currentRequest = ServerRequestContext.currentRequest<HttpRequest<*>>().orElse(null)
            if (currentRequest == null) "fail" else "success"
        }

        val async1 = async(Dispatchers.IO) {
            delay(1000)
            val currentRequest = ServerRequestContext.currentRequest<HttpRequest<*>>().orElse(null)
            if (currentRequest == null) "fail" else "success"
        }

        var response: String?
        val millis = measureTimeMillis { response = async0.await() + "|" + async1.await() }
        "$response in $millis ms"
    }
}
  1. Call curl http://localhost:8080/test

Expected Behaviour

success|success in 1002 ms

Actual Behaviour

fail|fail in 1002 ms

Environment Information

jameskleeh commented 3 years ago

I understand there may be some use cases impacted here but using ServerRequestContext.currentRequest<HttpRequest<*>>().orElse(null) should never be done in a controller method like your example. You can simply inject the request into the method and use it.

Alex100 commented 3 years ago

This is only a minimal working example that reproduces the problem. In our real use case, we use HttpResponse.ok() in a HttpClientFilter. The underlying AWS implementation of HttpResponseFactory calls then ServerRequestContext.currentRequest() (see: https://github.com/micronaut-projects/micronaut-aws/blob/222f6025a66500d692ba1de570de8f186993df0d/function-aws-api-proxy/src/main/java/io/micronaut/function/aws/proxy/model/factory/MicronautAwsProxyResponseFactory.java line 56).