https://kotlinlang.org logo
#ktor
Title
# ktor
b

borisdamato

03/13/2023, 4:40 PM
Hi all, how can I create a client plugin/interceptor that reads the response with
bodyAsText
without interfering with other plugins that might want to do the same? At the moment I see that the body can only be read once, after which it gets consumed. Is there a way to prevent that? Thanks
c

CLOVIS

03/13/2023, 4:47 PM
I believe this is what you want: https://ktor.io/docs/double-receive.html
b

borisdamato

03/13/2023, 4:56 PM
Is this applicable to Ktor client too?
I'm already using
call.save()
following the implementation in ktor's default RequestValidator, I think that should have a similar effect, as it reads the body and copies it in a
SavedHttpCall
, which also sets
allowDoubleReceive: Boolean = true
on the call itself, but I'm still unable to read the body twice
my understanding is that
SavedHttpResponse
creates a new
ByteReadChannel
based on the saved body every time
response.content
is called, but it doesn't seem to be working for me. When I try to read
bodyAsText
again, I get a
CancellationException
saying that the parent job was completed
the response coroutineContext's job is completed as soon as the body is read and I can't find a way to prevent that
Unless I'm missing something I'm starting to think there's something wrong in the api, the
finally
block for
bodyNullable
always completes the response coroutine job, and there's no other way to access the saved responseBody in the
SavedHttpCall
because
getResponseContent
is protected and that's the only one creating a new ByteReadChannel with the saved content
a

Aleksei Tirman [JB]

03/14/2023, 7:36 AM
Can you describe what problem is to be solved by the interceptor/plugin?
b

borisdamato

03/14/2023, 9:36 AM
@Aleksei Tirman [JB] The usecase is the combination of 2 interceptors handling failure responses: 1. The first one is ready the body via
bodyAsText()
and logging it to our backend logging service 2. The second one is my version of the
HttpResponseValidator
where I convert the response into my internal exception which contains the deserialized error (via
body<MyError>()
+ the raw response body via
bodyAsText()
). For instance, for interceptor no.2, reading the body via
bodyAsText()
makes the following
body<MyError>()
throw the cancelled exception even if I transformed my call in a
SavedHttpCall
by calling
save()
in advance, and I assume that's because
bodyNullable()
completes the job even for
SavedHttpResponse
From what I understand, being able to do something like that appears to be the point of
SavedHttpCall
, and I can see that it overrides
getResponseContent()
by always creating a new
ByteReadChannel
with the saved response content for that purpose, but then the fact that executing
bodyNullable
always completes the scope's job defeats its purpose.
And from what I can see using
bodyNullable
is the only way I can access the saved response content (because of the
getResponseContent
), as the other APIs are not public
a

Aleksei Tirman [JB]

03/14/2023, 2:58 PM
Calling the
body
method in a
HttpResponseValidator
consumes a response body as described in KTOR-4225.
b

borisdamato

03/14/2023, 3:16 PM
Thanks, good to know it's being worked on, I'm not using HttpResponseValidator directly, but I think this still applies in my case as I'm following a similar implementation
38 Views