https://kotlinlang.org logo
e

Elka

10/15/2020, 2:39 PM
Hello, I am trying to build a multi-platform client that retries the request automatically when the token expired and 401 is returned from the server. For that I am using a sample code by moko-network. The refresh token api is being triggered but when it retries the  request using 
request
 , I never get a 
result
 as if some “deadlock” occurred.
Copy code
val requestBuilder = HttpRequestBuilder().takeFrom(context.request)
val result: HttpResponse = context.client!!.request(requestBuilder)
Any idea? Anyone implemented a RefreshToken feature that works with latest Ktor?
r

ribesg

10/15/2020, 2:41 PM
I created a Ktor Client
Feature
for that.
Copy code
override fun install(feature: AuthFeature, scope: HttpClient) {
    scope.requestPipeline.intercept(HttpRequestPipeline.State) {
        if (context.usesAuthentication) {
            val token = feature.authTokenService.getToken() ?: return@intercept
            context.headers[HttpHeaders.Authorization] = "Bearer $token"
        }
    }

    val circuitBreaker = AttributeKey<Unit>("my-auth-request")
    scope.feature(HttpSend)!!.intercept { origin, context ->
        if (origin.response.status != HttpStatusCode.Unauthorized) return@intercept origin
        if (origin.request.attributes.contains(circuitBreaker)) return@intercept origin
        if (!feature.authTokenService.refreshToken()) return@intercept origin
        val token = feature.authTokenService.getToken() ?: return@intercept origin
        val request = HttpRequestBuilder()
        request.takeFromWithExecutionContext(context)
        request.headers[HttpHeaders.Authorization] = "Bearer $token"
        request.attributes.put(circuitBreaker, Unit)
        return@intercept execute(request)
    }
}
e

Elka

10/16/2020, 6:07 AM
Thanks @ribesg. But the
takeFromWithExecutionContext
is internal in Ktor
1.4
r

ribesg

10/16/2020, 9:03 AM
@Elka no it’s not.
Copy code
/**
     * Mutates [this] copying all the data from another [builder] using it as base.
     */
    @InternalAPI
    public fun takeFromWithExecutionContext(builder: HttpRequestBuilder): HttpRequestBuilder {
        executionContext = builder.executionContext
        return takeFrom(builder)
    }
It’s
public
e

Elka

10/18/2020, 3:32 PM
The code you shared works thanks… The idea is that
HttpSend
Feature tracks the network call and cancels it when it is modified… Canceling it is necessary otherwise the new network call doesn’t kickstart.
2 Views