https://kotlinlang.org logo
Title
s

Sam Garfinkel

04/21/2020, 7:56 PM
Can pipeline interceptors handle exceptions thrown further down in the pipeline? I’m trying to add a simple interceptor that retries a call (with a delay) if the call fails. I’d like this to supplement the existing timeout/validation features.
r

rharter

04/22/2020, 12:49 AM
Client or server?
s

Sam Garfinkel

04/22/2020, 2:02 PM
Client.
@rharter Not sure if you had any thoughts? It looks like exceptions that are thrown from an interceptor just stop the pipeline entirely which is kind of undesirable.
r

rharter

04/22/2020, 3:55 PM
Nope, sorry. I use the server but haven’t really used the client, so can’t offer much assistance.
I was going to suggest checking out the StatusPages feature, since it’s basically a glorified exception handler, but then I realized you may have been talking about the client.
s

Sam Garfinkel

04/22/2020, 3:56 PM
Honestly the code is probably similar between each. I’ll take a look.
I do wish there was a better description of the pipeline lifecycle on the docs.
@e5l Any insight on this topic?
e

e5l

04/22/2020, 4:09 PM
Sure. You can wrap
proceed
or
proceedWith
method with
try catch
and it will give you the exception!
s

Sam Garfinkel

04/22/2020, 4:10 PM
I had thought that would work, but this fails (maybe I’m misunderstanding the lifecycle here?):
override fun install(feature: RetryOnFailure, scope: HttpClient) {
            scope.receivePipeline.intercept(HttpReceivePipeline.Before) {
                proceedWith(it)
                throw RuntimeException()
            }

            scope.receivePipeline.intercept(HttpReceivePipeline.After) {
                try {
                    proceedWith(it)
                } catch (e: Exception) {
                    println("Hello world")
                    e.printStackTrace()
                }
            }
        }
e

e5l

04/22/2020, 4:11 PM
It should run from
Before
to
After
, so the
Before
will receive the exception thrown in
After
s

Sam Garfinkel

04/22/2020, 4:13 PM
Ah that’s what I had thought. So proceed literally proceeds down the chain then yields back to earlier stages? Makes sense (if i’m understanding correctly)
e

e5l

04/22/2020, 4:14 PM
Exactly.
s

Sam Garfinkel

04/22/2020, 4:15 PM
Ok cool. The only small wrinkle is that validation is handled in an ad-hoc
BeforeReceive
phase. Somehow I’ll need to figure out how to insert even before that.
e

e5l

04/22/2020, 4:16 PM
The
Pipeline
has special methods to insert phase before and after. So you can create a new phase, insert it, and then intercept.
s

Sam Garfinkel

04/22/2020, 4:17 PM
Right, I’ll do that. Sorry for all of the questions 🙂 just one more: Do exceptions span pipelines or are individual pipelines executed separately? I.e. what’s the relationship between
HttpResponsePipeline
and
HttpReceivePipeline
?
e

e5l

04/22/2020, 4:19 PM
It depends on the exception, but usually, they are executed separately. We haven't had any usecases for that, and If you have one you could file an issue.
s

Sam Garfinkel

04/22/2020, 4:20 PM
The only usecase would be to handle timeouts and validation exceptions in a single interceptor. I’ll need to check exactly which pipeline propagates timeouts as I’m unsure right now.
@e5l I tried several approaches, but all resulted in a
java.lang.ClassCastException: class io.ktor.client.utils.EmptyContent cannot be cast to class io.ktor.client.call.HttpClientCall
when a timeout occurs (via the
Timeout
feature). Here’s the configuration that causes this:
val handleValidation = PipelinePhase("HandleValidation")
scope.requestPipeline.insertPhaseBefore(HttpRequestPipeline.Before, handleValidation)
scope.requestPipeline.intercept(handleValidation) {
    repeat(5) {
        try {
            proceed()
        } catch (e: Exception) {
            delay(100)
            println("retry attempt: $it")
        }
    }
}
@e5l Sorry to keep pinging you, but I’m still having trouble with this exception. Any idea why calling
proceed()
multiple times from the interceptor above would cause this?