Skip to content

Commit

Permalink
KTOR-385 Add Ignored Types to JsonPlugin
Browse files Browse the repository at this point in the history
  • Loading branch information
e5l committed Aug 10, 2022
1 parent 861c112 commit 05d6c8b
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ public final class io/ktor/client/plugins/json/JsonPlugin {
public final class io/ktor/client/plugins/json/JsonPlugin$Config {
public fun <init> ()V
public final fun accept ([Lio/ktor/http/ContentType;)V
public final fun clearIgnoredTypes ()V
public final fun getAcceptContentTypes ()Ljava/util/List;
public final fun getIgnoredTypes ()Ljava/util/Set;
public final fun getReceiveContentTypeMatchers ()Ljava/util/List;
public final fun getSerializer ()Lio/ktor/client/plugins/json/JsonSerializer;
public final fun receive (Lio/ktor/http/ContentTypeMatcher;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,20 @@ import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.client.utils.*
import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.util.*
import io.ktor.utils.io.*
import kotlin.reflect.*

internal val DefaultCommonIgnoredTypes: Set<KClass<*>> = setOf(
ByteArray::class,
String::class,
HttpStatusCode::class,
ByteReadChannel::class,
OutgoingContent::class
)

internal expect val DefaultIgnoredTypes: Set<KClass<*>>

/**
* Platform default serializer.
Expand Down Expand Up @@ -44,6 +56,7 @@ public class JsonPlugin internal constructor(
public val serializer: JsonSerializer,
public val acceptContentTypes: List<ContentType> = listOf(ContentType.Application.Json),
private val receiveContentTypeMatchers: List<ContentTypeMatcher> = listOf(JsonContentTypeMatcher()),
private val ignoredTypes: Set<KClass<*>> = DefaultCommonIgnoredTypes + DefaultIgnoredTypes
) {
internal constructor(config: Config) : this(
config.serializer ?: defaultSerializer(),
Expand All @@ -56,6 +69,11 @@ public class JsonPlugin internal constructor(
*/
@KtorDsl
public class Config {

@PublishedApi
internal val ignoredTypes: MutableSet<KClass<*>> =
(DefaultIgnoredTypes + DefaultCommonIgnoredTypes).toMutableSet()

/**
* Serializer that will be used for serializing requests and deserializing response bodies.
*
Expand Down Expand Up @@ -111,6 +129,29 @@ public class JsonPlugin internal constructor(
public fun receive(matcher: ContentTypeMatcher) {
_receiveContentTypeMatchers += matcher
}

/**
* Adds a type to the list of types that should be ignored by [ContentNegotiation].
*
* The list contains the [HttpStatusCode], [ByteArray], [String] and streaming types by default.
*/
public inline fun <reified T> ignoreType() {
ignoredTypes.add(T::class)
}

/**
* Remove [T] from the list of types that should be ignored by [ContentNegotiation].
*/
public inline fun <reified T> removeIgnoredType() {
ignoredTypes.remove(T::class)
}

/**
* Clear all configured ignored types including defaults.
*/
public fun clearIgnoredTypes() {
ignoredTypes.clear()
}
}

internal fun canHandle(contentType: ContentType): Boolean {
Expand All @@ -131,14 +172,16 @@ public class JsonPlugin internal constructor(
val serializer = config.serializer ?: defaultSerializer()
val allowedContentTypes = config.acceptContentTypes.toList()
val receiveContentTypeMatchers = config.receiveContentTypeMatchers
val ignoredTypes = config.ignoredTypes

return JsonPlugin(serializer, allowedContentTypes, receiveContentTypeMatchers)
return JsonPlugin(serializer, allowedContentTypes, receiveContentTypeMatchers, ignoredTypes)
}

override fun install(plugin: JsonPlugin, scope: HttpClient) {
scope.requestPipeline.intercept(HttpRequestPipeline.Transform) { payload ->
plugin.acceptContentTypes.forEach { context.accept(it) }

if (payload::class in plugin.ignoredTypes) return@intercept
val contentType = context.contentType() ?: return@intercept
if (!plugin.canHandle(contentType)) return@intercept

Expand All @@ -155,7 +198,7 @@ public class JsonPlugin internal constructor(

scope.responsePipeline.intercept(HttpResponsePipeline.Transform) { (info, body) ->
if (body !is ByteReadChannel) return@intercept
if (info.type == ByteReadChannel::class) return@intercept
if (info.type in plugin.ignoredTypes) return@intercept

val contentType = context.response.contentType() ?: return@intercept
if (!plugin.canHandle(contentType)) return@intercept
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

@file:Suppress("DEPRECATION")

package io.ktor.client.plugins.json

import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.mock.*
import io.ktor.client.request.*
import io.ktor.client.utils.*
import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.test.dispatcher.*
import io.ktor.util.reflect.*
import io.ktor.utils.io.core.*
import kotlin.test.*

class JsonPluginMockTest {

@Test
fun testReceiveJsonLikeString() = testSuspend {
val client = HttpClient(MockEngine) {
engine {
addHandler {
respond(
"{}",
headers = buildHeaders {
append(HttpHeaders.ContentType, "application/json")
}
)
}
}
install(JsonPlugin) {
serializer = MockSerializer
}
}

val response = client.get("/").body<String>()
assertEquals("{}", response)
}
}

object MockSerializer : JsonSerializer {
override fun write(data: Any, contentType: ContentType): OutgoingContent {
error("Can't serialize $data")
}

override fun read(type: TypeInfo, body: Input): Any {
error("can't read $type")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ class KotlinxSerializerTest : ClientLoader() {
@Test
fun testStringWithJsonPlugin() = clientTests {
config {
install(JsonPlugin)
install(JsonPlugin) {
removeIgnoredType<String>()
}
defaultRequest {
val contentType = ContentType.parse("application/vnd.string+json")
accept(contentType)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.client.plugins.json

import kotlin.reflect.*

internal actual val DefaultIgnoredTypes: Set<KClass<*>> =
mutableSetOf()
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.client.plugins.json

import java.io.*
import kotlin.reflect.*

internal actual val DefaultIgnoredTypes: Set<KClass<*>> =
mutableSetOf(InputStream::class)
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/*
* Copyright 2014-2022 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package io.ktor.client.plugins.json

import kotlin.reflect.*

internal actual val DefaultIgnoredTypes: Set<KClass<*>> =
mutableSetOf()

0 comments on commit 05d6c8b

Please sign in to comment.