Коммит 90121c25 создал по автору Leonid Stashevsky's avatar Leonid Stashevsky Зафиксировано автором Leonid Stashevsky
Просмотр файлов

Update to HttpStatement

владелец 7c9b2b58
......@@ -48,14 +48,11 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "me.champeau.gradle:jmh-gradle-plugin:$jmh_plugin_version"
classpath "org.jetbrains.kotlinx:kotlinx.benchmark.gradle:$benchmarks_version"
classpath "kotlinx.team:kotlinx.team.infra:$infra_version"
classpath "io.spring.gradle:dependency-management-plugin:$spring_dependency_management_version"
}
}
apply plugin: 'kotlinx.team.infra'
ext.configuredVersion = project.hasProperty("releaseVersion") ? project.releaseVersion : project.version
ext.globalM2 = "$buildDir/m2"
ext.publishLocal = project.hasProperty("publishLocal")
......
......@@ -6,6 +6,7 @@ version=1.3.0-beta-2-SNAPSHOT
kotlin.incremental.js=true
kotlin.incremental.multiplatform=true
kotlin.parallel.tasks.in.project=true
org.gradle.parallel=true
# gradle
org.gradle.daemon=true
......@@ -13,10 +14,10 @@ org.gradle.jvmargs=-Xmx2g -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError
org.jetbrains.kotlin.native.jvmArgs=-XX:TieredStopAtLevel=1
# kotlin
kotlin_version=1.3.60-eap-143
kotlin_version=1.3.60-release-155
kotlin.native.ignoreDisabledTargets=true
# kotlin libraries
infra_version=0.1.0-dev-50
benchmarks_version=0.2.0-dev-5
serialization_version=0.14.0-1.3.60-eap-76
......@@ -59,6 +60,6 @@ mocha_teamcity_reporter_version=2.5.2
source_map_support_version=0.5.11
text_encoding_version=0.7.0
node_fetch_version=2.6.0
abort_controller_version=3.0.0
ws_version=6.2.1
org.gradle.parallel=true
......@@ -11,6 +11,9 @@ kotlin {
js {
nodejs {
testTask {
useMocha {
timeout = 10000
}
debug = false
}
}
......@@ -50,10 +53,8 @@ kotlin {
}
jsTest.dependencies {
api "org.jetbrains.kotlin:kotlin-test-js:$kotlin_version"
// api(npm("puppeteer", "*"))
api(npm("puppeteer", "*"))
// api(npm("source-map-support", source_map_support_version))
api(npm("node-fetch", node_fetch_version))
api(npm("ws", ws_version))
}
}
}
......@@ -9,6 +9,7 @@ import io.ktor.client.*
import io.ktor.client.engine.*
import io.ktor.client.request.*
import io.ktor.client.response.*
import io.ktor.client.statement.*
import io.ktor.client.tests.utils.*
import io.ktor.http.*
import io.ktor.network.tls.certificates.*
......@@ -103,8 +104,9 @@ class AndroidHttpsTest : TestWithKtor() {
fun external(): Unit = runBlocking {
val client = HttpClient(Android)
val response = client.get<HttpResponse>("https://kotlinlang.org")
assertEquals(HttpStatusCode.OK, response.status)
client.get<HttpStatement>("https://kotlinlang.org").execute { response ->
assertEquals(HttpStatusCode.OK, response.status)
}
}
@Test
......
......@@ -5,7 +5,6 @@
package io.ktor.client.engine.android
import io.ktor.client.tests.*
import org.junit.*
class AndroidCookiesTest : CookiesTest(Android)
......
......@@ -12,6 +12,4 @@ class ApacheMultithreadedTest : MultithreadedTest(Apache)
class ApacheBuildersTest : BuildersTest(Apache)
class ApacheFeaturesTest : FeaturesTest(Apache)
class ApacheHttpClientTest : HttpClientTest(Apache)
......@@ -6,7 +6,7 @@ package io.ktor.client.engine.cio
import io.ktor.application.*
import io.ktor.client.request.*
import io.ktor.client.response.*
import io.ktor.client.statement.*
import io.ktor.client.tests.utils.*
import io.ktor.http.*
import io.ktor.network.tls.*
......@@ -138,7 +138,7 @@ class CIOHttpsTest : TestWithKtor() {
@Test
fun external(): Unit = clientTest(CIO) {
test { client ->
client.get<HttpResponse>("https://kotlinlang.org").use { response ->
client.get<HttpStatement>("https://kotlinlang.org").execute { response ->
assertEquals(HttpStatusCode.OK, response.status)
}
}
......@@ -184,7 +184,7 @@ class CIOHttpsTest : TestWithKtor() {
var received = 0
client.async {
repeat(testSize) {
client.get<HttpResponse>("https://www.facebook.com").use { response ->
client.get<HttpStatement>("https://www.facebook.com").execute { response ->
assertTrue(response.status.isSuccess())
received++
}
......
......@@ -7,6 +7,7 @@ package io.ktor.client.engine.cio
import io.ktor.application.*
import io.ktor.client.request.*
import io.ktor.client.response.*
import io.ktor.client.statement.*
import io.ktor.client.tests.utils.*
import io.ktor.http.*
import io.ktor.http.content.*
......@@ -14,6 +15,7 @@ import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlin.io.use
import kotlin.test.*
class CIORequestTest : TestWithKtor() {
......@@ -38,9 +40,9 @@ class CIORequestTest : TestWithKtor() {
test { client ->
val headerValue = "x".repeat(testSize)
client.get<HttpResponse>(port = serverPort) {
client.get<HttpStatement>(port = serverPort) {
header("LongHeader", headerValue)
}.use { response ->
}.execute { response ->
assertEquals(headerValue, response.headers["LongHeader"])
}
}
......
......@@ -12,6 +12,4 @@ class CIOMultithreadedTest : MultithreadedTest(CIO)
class CIOBuildersTest : BuildersTest(CIO)
class CIOFeaturesTest : FeaturesTest(CIO)
class CIOHttpClientTest : HttpClientTest(CIO)
......@@ -6,9 +6,9 @@ package io.ktor.client.engine.cio
import io.ktor.application.*
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.network.tls.certificates.*
import io.ktor.response.*
import io.ktor.routing.*
......@@ -39,16 +39,20 @@ class ConnectErrorsTest {
@Test
fun testConnectAfterConnectionErrors(): Unit = runBlocking<Unit> {
HttpClient(CIO.config {
maxConnectionsCount = 1
endpoint.connectTimeout = SOCKET_CONNECT_TIMEOUT
endpoint.connectRetryAttempts = 3
}).use { client ->
val client = HttpClient(CIO) {
engine {
maxConnectionsCount = 1
endpoint.connectTimeout = SOCKET_CONNECT_TIMEOUT
endpoint.connectRetryAttempts = 3
}
}
client.use {
serverSocket.close()
repeat(5) {
try {
client.call("http://localhost:${serverSocket.localPort}/").close()
client.request<HttpResponse>("http://localhost:${serverSocket.localPort}/")
fail("Shouldn't reach here")
} catch (_: java.net.ConnectException) {
}
......
......@@ -5,6 +5,10 @@ description = "Ktor http client"
val ideaActive: Boolean by project
val coroutines_version: String by project
val node_fetch_version: String by project
val abort_controller_version: String by project
val ws_version: String by project
kotlin.sourceSets {
commonMain {
dependencies {
......@@ -19,6 +23,14 @@ kotlin.sourceSets {
}
}
jsMain {
dependencies {
api(npm("node-fetch", node_fetch_version))
api(npm("abort-controller", abort_controller_version))
api(npm("ws", ws_version))
}
}
commonTest {
dependencies {
// api(project(":ktor-client:ktor-client-tests"))
......
......@@ -8,11 +8,11 @@ import io.ktor.client.call.*
import io.ktor.client.engine.*
import io.ktor.client.features.*
import io.ktor.client.request.*
import io.ktor.client.response.*
import io.ktor.client.statement.*
import io.ktor.util.*
import io.ktor.utils.io.core.*
import kotlinx.atomicfu.*
import kotlinx.coroutines.*
import io.ktor.utils.io.core.*
import kotlin.coroutines.*
/**
......@@ -137,6 +137,16 @@ class HttpClient(
/**
* Creates a new [HttpRequest] from a request [data] and a specific client [call].
*/
@Deprecated(
"Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(builder)] instead.",
level = DeprecationLevel.ERROR,
replaceWith = ReplaceWith(
"this.request<HttpResponse>(builder)",
"io.ktor.client.statement.*"
)
)
@InternalAPI
suspend fun execute(builder: HttpRequestBuilder): HttpClientCall =
requestPipeline.execute(builder, builder.body) as HttpClientCall
......
......@@ -6,12 +6,11 @@ package io.ktor.client.call
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.response.*
import io.ktor.client.statement.*
import io.ktor.util.*
import io.ktor.utils.io.*
import kotlinx.atomicfu.*
import kotlinx.coroutines.*
import io.ktor.utils.io.*
import io.ktor.utils.io.core.*
import kotlin.coroutines.*
import kotlin.reflect.*
......@@ -36,7 +35,7 @@ internal fun HttpClientCall(
*/
open class HttpClientCall internal constructor(
val client: HttpClient
) : CoroutineScope, Closeable {
) : CoroutineScope {
private val received = atomic(false)
override val coroutineContext: CoroutineContext get() = response.coroutineContext
......@@ -58,15 +57,6 @@ open class HttpClientCall internal constructor(
lateinit var response: HttpResponse
internal set
/**
* Configuration for the [response].
*/
@Deprecated(
message = "responseConfig is deprecated. Consider using [Charsets] config instead",
level = DeprecationLevel.ERROR
)
val responseConfig: HttpResponseConfig = client.engineConfig.response
/**
* Tries to receive the payload of the [response] as an specific [expectedType].
* Returns [response] if [expectedType] is [HttpResponse].
......@@ -89,28 +79,14 @@ open class HttpClientCall internal constructor(
throw NoTransformationFoundException(from, to)
}
if (result is ByteReadChannel) {
return response.channelWithCloseHandling()
}
if (result !is Closeable && result !is HttpRequest) {
close()
}
return result
}
catch (cause: Throwable) {
close()
} catch (cause: Throwable) {
response.cancel("Receive failed", cause)
throw cause
}
}
/**
* Closes the underlying [response].
*/
override fun close() {
response.close()
}
override fun toString(): String = "HttpClientCall[${request.url}, ${response.status}]"
companion object {
/**
......@@ -142,8 +118,17 @@ data class HttpEngineCall(val request: HttpRequest, val response: HttpResponse)
* Constructs a [HttpClientCall] from this [HttpClient] and with the specified [HttpRequestBuilder]
* configured inside the [block].
*/
@Deprecated(
"Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(block)] in instead.",
level = DeprecationLevel.ERROR,
replaceWith = ReplaceWith(
"this.request<HttpResponse>(block)",
"io.ktor.client.request.request",
"io.ktor.client.statement.*"
)
)
suspend fun HttpClient.call(block: suspend HttpRequestBuilder.() -> Unit = {}): HttpClientCall =
execute(HttpRequestBuilder().apply { block() })
error("Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(block)] in instead.")
/**
* Tries to receive the payload of the [response] as an specific type [T].
......
......@@ -6,11 +6,10 @@ package io.ktor.client.call
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.response.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.util.*
import io.ktor.util.date.*
import kotlinx.coroutines.*
import io.ktor.utils.io.*
import io.ktor.utils.io.core.*
import kotlin.coroutines.*
......@@ -35,13 +34,9 @@ internal class SavedHttpResponse(
override val headers: Headers = origin.headers
override val coroutineContext: CoroutineContext = origin.coroutineContext + Job()
override val coroutineContext: CoroutineContext = origin.coroutineContext
override val content: ByteReadChannel = ByteReadChannel(body)
override fun close() {
(coroutineContext[Job] as CompletableJob).complete()
}
}
/**
......
......@@ -6,12 +6,8 @@ package io.ktor.client.call
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.response.*
import io.ktor.http.*
import io.ktor.http.content.*
import kotlinx.coroutines.*
import kotlinx.coroutines.CancellationException
import io.ktor.utils.io.*
@Suppress("KDocMissingDocumentation")
......@@ -27,38 +23,44 @@ class UnsupportedUpgradeProtocolException(
* Constructs a [HttpClientCall] from this [HttpClient] and
* with the specified HTTP request [builder].
*/
suspend fun HttpClient.call(builder: HttpRequestBuilder): HttpClientCall = call { takeFrom(builder) }
@Deprecated(
"Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(builder)] instead.",
level = DeprecationLevel.ERROR,
replaceWith = ReplaceWith("this.request<HttpResponse>(builder)", "io.ktor.client.statement.*")
)
suspend fun HttpClient.call(builder: HttpRequestBuilder): HttpClientCall =
error("Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(builder)] instead.")
/**
* Constructs a [HttpClientCall] from this [HttpClient],
* an [url] and an optional [block] configuring a [HttpRequestBuilder].
*/
@Deprecated(
"Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(urlString, block)] instead.",
level = DeprecationLevel.ERROR,
replaceWith = ReplaceWith(
"this.request<HttpResponse>(urlString, block)", "io.ktor.client.statement.*"
)
)
suspend fun HttpClient.call(
urlString: String,
block: suspend HttpRequestBuilder.() -> Unit = {}
): HttpClientCall = call {
url.takeFrom(urlString)
block()
}
): HttpClientCall = error(
"Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(urlString, block)] instead."
)
/**
* Constructs a [HttpClientCall] from this [HttpClient],
* an [url] and an optional [block] configuring a [HttpRequestBuilder].
*/
@Deprecated(
"Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(url, block)] instead.",
level = DeprecationLevel.ERROR,
replaceWith = ReplaceWith("this.request<HttpResponse>(url, block)", "io.ktor.client.statement.*")
)
suspend fun HttpClient.call(
url: Url,
block: suspend HttpRequestBuilder.() -> Unit = {}
): HttpClientCall = call {
this.url.takeFrom(url)
block()
}
internal fun HttpResponse.channelWithCloseHandling(): ByteReadChannel = writer {
try {
content.joinTo(channel, closeOnEnd = true)
} catch (cause: CancellationException) {
this@channelWithCloseHandling.cancel(cause)
} finally {
this@channelWithCloseHandling.close()
}
}.channel
): HttpClientCall = error(
"Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(url, block)] instead."
)
......@@ -12,6 +12,10 @@ import io.ktor.util.*
import io.ktor.utils.io.core.*
import kotlinx.coroutines.*
import kotlin.coroutines.*
import kotlin.native.concurrent.*
@SharedImmutable
private val CALL_COROUTINE = CoroutineName("call-context")
/**
* Base interface use to define engines for [HttpClient].
......@@ -84,7 +88,7 @@ interface HttpClientEngine : CoroutineScope, Closeable {
*/
private suspend fun createCallContext(parentJob: Job): CoroutineContext {
val callJob = Job(parentJob)
val callContext = this@HttpClientEngine.coroutineContext + callJob + CoroutineName("call-context")
val callContext = this@HttpClientEngine.coroutineContext + callJob + CALL_COROUTINE
attachToUserJob(callJob)
......
......@@ -15,19 +15,6 @@ import kotlinx.coroutines.*
*/
@HttpClientDsl
open class HttpClientEngineConfig {
/**
* The [CoroutineDispatcher] that will be used for the client requests.
*/
@Deprecated(
"Binary compatibility.",
level = DeprecationLevel.HIDDEN
)
var dispatcher: CoroutineDispatcher?
get() = null
set(_) {
throw UnsupportedOperationException("Custom dispatcher is deprecated. Use threadsCount instead.")
}
/**
* Network threads count advice.
*/
......@@ -40,11 +27,6 @@ open class HttpClientEngineConfig {
@KtorExperimentalAPI
var pipelining: Boolean = false
/**
* Configuration for http response.
*/
val response: HttpResponseConfig = HttpResponseConfig()
/**
* Proxy address to use. Use system proxy by default.
*
......@@ -52,4 +34,11 @@ open class HttpClientEngineConfig {
*/
@KtorExperimentalAPI
var proxy: ProxyConfig? = null
@Deprecated(
"Response config is deprecated. See [HttpPlainText] feature for charset configuration",
level = DeprecationLevel.ERROR
)
val response: Nothing get() =
error("Unbound [HttpClientCall] is deprecated. Consider using [request<HttpResponse>(block)] in instead.")
}
......@@ -6,9 +6,8 @@ package io.ktor.client.features
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.response.*
import io.ktor.util.*
import io.ktor.utils.io.core.*
import io.ktor.client.statement.*
import kotlin.jvm.*
private val ValidateMark = AttributeKey<Unit>("ValidateMark")
......@@ -24,21 +23,16 @@ fun HttpClientConfig<*>.addDefaultResponseValidation() {
val originCall = response.call
if (statusCode < 300 || originCall.attributes.contains(ValidateMark)) return@validateResponse
response.use {
val exceptionCall = originCall.save().apply {
attributes.put(ValidateMark, Unit)
}
val exceptionResponse = exceptionCall.response
when (statusCode) {
in 300..399 -> throw RedirectResponseException(exceptionResponse)
in 400..499 -> throw ClientRequestException(exceptionResponse)
in 500..599 -> throw ServerResponseException(exceptionResponse)
}
val exceptionCall = originCall.save().apply {
attributes.put(ValidateMark, Unit)
}
if (statusCode >= 600) {
throw ResponseException(exceptionResponse)
}
val exceptionResponse = exceptionCall.response
when (statusCode) {
in 300..399 -> throw RedirectResponseException(exceptionResponse)
in 400..499 -> throw ClientRequestException(exceptionResponse)
in 500..599 -> throw ServerResponseException(exceptionResponse)
else -> throw ResponseException(exceptionResponse)
}
}
}
......
......@@ -6,11 +6,14 @@ package io.ktor.client.features
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.response.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.utils.io.*
import io.ktor.utils.io.CancellationException
import io.ktor.utils.io.cancel
import io.ktor.utils.io.core.*
import kotlinx.coroutines.*
/**
* Install default transformers.
......@@ -29,8 +32,7 @@ fun HttpClient.defaultTransformers() {
val contentType = context.headers[HttpHeaders.ContentType]?.let {
context.headers.remove(HttpHeaders.ContentType)
ContentType.parse(it)
}
?: ContentType.Text.Plain
} ?: ContentType.Text.Plain
proceedWith(TextContent(body, contentType))
}
......@@ -48,27 +50,37 @@ fun HttpClient.defaultTransformers() {
when (info.type) {
Unit::class -> {
response.close()
body.cancel()
proceedWith(HttpResponseContainer(info, Unit))
}
Int::class -> {
proceedWith(HttpResponseContainer(info, body.readRemaining().readText().toInt()))
}
ByteReadPacket::class,
Input::class -> {
try {
proceedWith(HttpResponseContainer(info, body.readRemaining()))
} finally {
response.close()
}
proceedWith(HttpResponseContainer(info, body.readRemaining()))
}
ByteArray::class -> {
try {
val readRemaining = body.readRemaining(contentLength)
proceedWith(HttpResponseContainer(info, readRemaining.readBytes()))
} finally {
response.close()
}
val readRemaining = body.readRemaining(contentLength)
proceedWith(HttpResponseContainer(info, readRemaining.readBytes()))
}
ByteReadChannel::class -> {
val channel: ByteReadChannel = writer {
try {
body.copyTo(channel, limit = Long.MAX_VALUE)
} catch (cause: CancellationException) {
response.cancel(cause)
} catch (cause: Throwable) {
response.cancel("Receive failed", cause)
} finally {
response.complete()
}
}.channel
proceedWith(HttpResponseContainer(info, channel))
}
HttpStatusCode::class -> {
response.close()
body.cancel()
proceedWith(HttpResponseContainer(info, response.status))
}
}
......
......@@ -6,7 +6,7 @@ package io.ktor.client.features
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.response.*
import io.ktor.client.statement.*
import io.ktor.util.*
import io.ktor.util.pipeline.*
......
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать