question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Please provide a way to bind objects to the request and acces them from anywhere

See original GitHub issue

In Ktor, accessing the incoming call is easy: just use the call field. However, this field is not global, it is provided by the REST API builder.

Assume you are building a microservice with Ktor. You have your REST endpoints, a service layer, and some repositories - a typical 3-level enterprise stack. You do authentication via JWT. All of that works nicely with Ktor.

Now we would like to call another microservice from one of our services. To do so, we require the JWT token which was passed to the call. We’re in the middle of a service-layer method, probably 5 or 6 function calls deep into the JVM call stack. The call object (which contains the token in its header) is way out of reach. Passing it down as an argument through all the functions in the service layer would be a major pain as well.

Coming from the Spring framework, for a case like this, you have the SecurityContext. You would call the static method SecurityContextHolder.getContext() and get the exact instance of the context which is associated with your current call. This is accomplished via a ThreadLocal<SecurityContext> that is hidden inside the SecurityContextHolder. When a call is received, Spring generates the security context, and clears it when call processing is done.

Other prominent use cases for request-attached data are:

  • Sessions maintained by O/R-mappers (e.g. JPA)
  • Database transactions
  • Access control decisions

As far as I know, Ktor at the moment provides no such facilities. Mimicking the spring approach directly is also not feasible, as coroutines and ThreadLocal in general do not play nice with each other.

I’ve found this article which outlines an approach for storing such data in the CoroutineContext. However, this has some issues as well:

  • Coroutine contexts are, to my knowledge, not nested by default. So whenever I start a sub-coroutine (e.g. via async), the context of the parent coroutine (the one which invoked async()) is not visible to the child (the one which was produced by async()). This is in general very unfortunate, because the programmer A) needs to be aware that the context contains data which might be needed downstream and B) needs to pass the data along explicitly in the coroutine generator function.
  • The coroutine context API is very convoluted. I just tried to follow the article, the developer experience is honestly quite bad (in essence it’s just a hash map, with type safety factored in). It feels like I have to write a lot of code to accomplish something that is simple, and that is also an often-needed feature. The upshot is: ideally I shouldn’t have to touch it as a Ktor user.

The Ktor-side API which I would imagine is something like this:

object CallContext {

    suspend fun get(key: String): Any { ... }
    suspend fun set(key: String, value: Any): Any { ... }

}

… and ideally the programmer should not have to explicitly pass along coroutine contexts. The functions are suspending to force the caller to use them from within a coroutine (this allows the implementation to make use of the coroutineContext). I’m not super experienced with coroutines (yet), so maybe there is a way to do it like this even today. Maybe Ktor already has something like this?

Please feel free to correct me if I took a wrong turn somewhere. I’m still quite new to all of this.

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:4
  • Comments:7 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
oleg-larshincommented, Aug 10, 2020

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

0reactions
cy6erGn0mcommented, Jan 2, 2021

Spring had the SecurityContextHolder for a long time, which is globally acessible in an (internally managed) ThreadLocal. They’re doing just fine with it, never had any complaints about it. The real issue is that (to my knowledge) there’s no such thing as a CoroutineLocal.

Actually, there is the context. But the problem is that it is only available in suspend functions. So it should be coroutine context + thread locals with ThreadLocalElement support that could do the trick.

We are at vacation here so I don’t have a dev environment so can’t verify. But the IDEA is like the following (JVM only):

class MyContext
private val threadLocal = ThreadLocal<MyContext>(null)

suspend fun withMyContext(block: suspend () -> Unit) {
    val myContext = MyContext()
    threadLocal.set(myContext)
    val ctx = coroutineContext + ThreadLocalElement(threadLocal)
    withContext(ctx) {
        block()
    }
}

val currentContext: MyContext
    get() = threadLocal.get() ?: error("No current context")
  • add a global interceptor to wrap every call into withMyContext
Read more comments on GitHub >

github_iconTop Results From Across the Web

Function binding - The Modern JavaScript Tutorial
The task is quite typical – we want to pass an object method somewhere else (here – to the scheduler) where it will...
Read more >
How to bind custom context to Jersey request - Stack Overflow
This will allow you to do @Inject RestContext ctx; and ensure that only one instance lives per request. You can inject it into...
Read more >
Understanding Call, Bind and Apply Methods in JavaScript
Here, I am calling the printName() method using the person object, so the this keyword inside the method refers to the person object....
Read more >
Getting Out of Binding Situations in JavaScript - A List Apart
It happens whenever you're accessing a method through a reference instead of directly through its owner object. The method loses its implicit binding, ......
Read more >
Autodiscover service in Exchange Server - Microsoft Learn
The Mailbox server now provides Client Access services, ... And SCP objects in AD DS provide an easy way for domain-joined clients to...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found