Я проверяю рецепты App Store в Kotlin на стороне сервера, используя Ktor's HttpClient
(версия Ktor 1.2.1).Вот мой код:
class AppStoreClient(
val url: String,
val password: String,
val excludeOldTransactions: Boolean = true
) {
private val objectMapper = ObjectMapperFactory.defaultObjectMapper()
private val client = HttpClient(Apache /* tried with CIO as well */) {
install(JsonFeature) {
serializer = JacksonSerializer()
}
}
suspend fun validate(receipt: String): VerifyReceiptResponse {
val post = client.post<String> {
url(this@AppStoreClient.url)
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
body = VerifyReceiptRequest(
receipt,
password,
excludeOldTransactions
)
}
// client.close()
// Apple does not send Content-Type header ¯\_(ツ)_/¯
// So Ktor's deserialization is not working here and
// I have to manually deserialize the response.
return objectMapper.readValue(post)
}
}
И здесь я проверяю его:
fun main() = runBlocking {
val client = AppStoreClient("https://sandbox.itunes.apple.com/verifyReceipt", "<password>")
println(client.validate("<recipe1>"))
// println(client.validate("<recipe2>"))
// println(client.validate("<recipe3>"))
}
Я получил все ответы (один или три) в выводе, но затем мойприложение просто зависает и никогда не выходит из метода main
.Похоже, runBlocking
все еще чего-то ждет, например client.close
.Действительно, если я закрою клиент после первого запроса, приложение успешно завершится, но это заставит меня создавать клиента при каждом отдельном запросе проверки.Конфигурация конвейера клиента кажется трудоемкой, и AppStoreClient
предназначен для долгоживущего объекта, поэтому я подумал, что клиент может поделиться своим жизненным циклом (возможно, даже с зависимостями).
Является ли io.ktor.client.HttpClient
долгоживущий объект, который можно повторно использовать для нескольких запросов, или я должен создать новый для каждого запроса?
Если да, что я с ним не так делаю, так что runBlocking
зависает?
PS Код работает с Ktor 1.1.1!Это ошибка?
PPS Этот код также зависает:
fun main() {
val client = AppStoreClient("...", "...")
runBlocking {
println(client.validate("..."))
println(client.validate("..."))
println(client.validate("..."))
}
runBlocking {
println(client.validate("..."))
println(client.validate("..."))
println(client.validate("..."))
}
}
Так что я мог бы серьезно подумать о закрытии клиента.