Hi. We upgraded Ktor from version 1 to 2. We use O...
# ktor
e
Hi. We upgraded Ktor from version 1 to 2. We use OkHttp engine. After the upgrade post/put with large JSON data is sent as
Transfer-Encoding: chunked
. Are there any ways to prevent this? The server we communicate with doesn't support it
Actually it doesn't have to be large. This snippet will be chunked:
Copy code
suspend fun main() {
    val client = HttpClient(OkHttp) {
install(ContentNegotiation) {
            jackson { }
        }
    }
    client.put("<http://localhost:1337/data>") {
        contentType(ContentType.Application.Json)
        setBody("")
    }
}
If I remove
jackson
it will not be chunked
2022-09-01_14-48.png
It seems to be fixed in 2.1.0. Looking at the release notes I'm not sure what was the fix, revert of ContentNegotiation or truncated array in ContentEncoding? https://github.com/ktorio/ktor/releases/tag/2.1.0
a
It isn’t fixed in Ktor 2.1.0 because the behavior you described isn’t buggy. To solve your problem you can write an implementation of the
ContentConverter
that will serialize objects to
OutgoingContent
with known content length. Here is an example:
Copy code
class MyJacksonConverter(private val objectmapper: ObjectMapper = jacksonObjectMapper()): ContentConverter {
    private val originalConverter = JacksonConverter(objectmapper)

    override suspend fun deserialize(charset: Charset, typeInfo: TypeInfo, content: ByteReadChannel): Any? {
        return originalConverter.deserialize(charset, typeInfo, content)
    }

    override suspend fun serializeNullable(
        contentType: ContentType,
        charset: Charset,
        typeInfo: TypeInfo,
        value: Any?
    ): OutgoingContent {
        val writer = StringWriter()
        objectmapper.writeValue(writer, value)
        return TextContent(writer.toString(), contentType)
    }
}
Here is an example usage:
Copy code
suspend fun main() {
    val client = HttpClient(OkHttp) {
        install(ContentNegotiation) {
            register(ContentType.Application.Json, MyJacksonConverter())
        }
    }
    val r = <http://client.post|client.post>("<https://httpbin.org/post>") {
        contentType(ContentType.Application.Json)
        setBody(Example(123))
    }

    println(r.bodyAsText())
}

data class Example(val x: Int)
e
But it has been fixed in 2.1.0, they behave differently. On 2.0.3 even
""
is chunked, but on 2.1.0 it is never chunked
a
Yes, because in 2.1.0 a string body is ignored by default in the
ContentNegotiation
plugin.
An object with a custom type is serialized the same way with both versions.
e
Oh snap, you are correct. Tested with data class now, it is indeed chunked. It is not with
gson
though
Your snippet also probably works, I just don't know how to use it since I need
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
and I don't know where to put this setting. With the data I use it will fail without that config.
gson
seems to do this by default. I'm not sure if we could just swap to gson
a
You can create an instance of Jackson’s mapper, configure it and pass it to the constructor of
MyJacksonConverter
:
Copy code
val mapper = ObjectMapper()
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)

val client = HttpClient(OkHttp) {
    install(ContentNegotiation) {
        register(ContentType.Application.Json, MyJacksonConverter(mapper))
    }
}
e
That works. Thanks! 🙂