ktorio / ktor

Framework for quickly creating connected applications in Kotlin with minimal effort
https://ktor.io
Apache License 2.0
12.86k stars 1.04k forks source link

Failed to skip delimiter: actual bytes differ from delimiter bytes #1426

Closed sedovalx closed 3 years ago

sedovalx commented 4 years ago

Ktor Version and Engine Used (client or server and name)

    ktor_version = '1.2.5'

    compile "io.ktor:ktor-server-core:$ktor_version"
    compile "io.ktor:ktor-server-jetty:$ktor_version"
    compile "io.ktor:ktor-websockets:$ktor_version"
    compile "io.ktor:ktor-html-builder:$ktor_version"
    compile("io.ktor:ktor-auth:$ktor_version") {
        exclude group: 'junit'
    }
    compile "io.ktor:ktor-jackson:$ktor_version"
    compile "io.ktor:ktor-client-core:$ktor_version"
    compile "io.ktor:ktor-client-apache:$ktor_version"
    compile "io.ktor:ktor-client-cio:$ktor_version"
    api "io.ktor:ktor-client-json:$ktor_version"
    api "io.ktor:ktor-client-jackson:$ktor_version"

Describe the bug The exception happens when a client code tries to upload a ByteArray to the server. The client code:

fun example(url: String, accessToken: String, bytes: ByteArray) {
    runBlocking {
        HttpClient(Apache.config {
            customizeClient {
                setThreadFactory(NamedThreadFactory("Ktor-client-apache-Automation-RemoteClient-", daemon = true))
                setDefaultIOReactorConfig(IOReactorConfig.custom().setIoThreadCount(4).build())
            }
        }).use { client ->
            val data = formData {
                append("jar", "user.jar") {
                    writeFully(bytes, 0, bytes.size)
                }
            }
            client.submitFormWithBinaryData<HttpResponse>(url, data) {
                header(HttpHeaders.Authorization, "Bearer $accessToken")
            }.use { response ->
                if (!response.status.isSuccess()) {
                    error("Failed to save jar url $url responded with ${response.status}: ${response.readText()}")
                }
            }
        }
    }
}

Server error stacktrace:

java.io.IOException: Failed to skip delimiter: actual bytes differ from delimiter bytes
    at kotlinx.coroutines.io.DelimitedKt.tryEnsureDelimiter(Delimited.kt:144)
    at kotlinx.coroutines.io.DelimitedKt.access$tryEnsureDelimiter(Delimited.kt:1)
    at kotlinx.coroutines.io.DelimitedKt$skipDelimiter$2.invoke(Delimited.kt:47)
    at kotlinx.coroutines.io.DelimitedKt$skipDelimiter$2.invoke(Delimited.kt)
    at kotlinx.coroutines.io.ByteBufferChannel.lookAhead(ByteBufferChannel.kt:1815)
    at kotlinx.coroutines.io.DelimitedKt.skipDelimiter(Delimited.kt:46)
    at io.ktor.http.cio.MultipartKt.boundary(Multipart.kt:160)
    at io.ktor.http.cio.MultipartKt$parseMultipart$1.invokeSuspend(Multipart.kt:282)
    at    (Coroutine boundary. ( )
    at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1.invokeSuspend(OrgRoutes.kt:89)
    at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:106)
    at libraries.coroutines.extra.CoroutineBuildersCommonKt$withContext$2.invokeSuspend(CoroutineBuildersCommon.kt:44)
    at io.ktor.routing.Routing.executeResult(Routing.kt:147)
    at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:99)
    at io.ktor.features.StatusPages$interceptCall$2.invokeSuspend(StatusPages.kt:98)
    at io.ktor.features.StatusPages.interceptCall(StatusPages.kt:97)
    at io.ktor.features.StatusPages$Feature$install$2.invokeSuspend(StatusPages.kt:137)
    at io.ktor.features.CallLogging$Feature$install$2.invokeSuspend(CallLogging.kt:139)
    at io.ktor.server.engine.DefaultEnginePipelineKt$defaultEnginePipeline$2.invokeSuspend(DefaultEnginePipeline.kt:118)
    at io.ktor.server.jetty.JettyKtorHandler$handle$2.invokeSuspend(JettyKtorHandler.kt:117)
Caused by: java.io.IOException: Failed to skip delimiter: actual bytes differ from delimiter bytes
    at kotlinx.coroutines.io.DelimitedKt.tryEnsureDelimiter(Delimited.kt:144)
    at kotlinx.coroutines.io.DelimitedKt.access$tryEnsureDelimiter(Delimited.kt:1)
    at kotlinx.coroutines.io.DelimitedKt$skipDelimiter$2.invoke(Delimited.kt:47)
    at kotlinx.coroutines.io.DelimitedKt$skipDelimiter$2.invoke(Delimited.kt)
    at kotlinx.coroutines.io.ByteBufferChannel.lookAhead(ByteBufferChannel.kt:1815)
    at kotlinx.coroutines.io.DelimitedKt.skipDelimiter(Delimited.kt:46)
    at io.ktor.http.cio.MultipartKt.boundary(Multipart.kt:160)
    at io.ktor.http.cio.MultipartKt$parseMultipart$1.invokeSuspend(Multipart.kt:282)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedKt.resumeCancellable(Dispatched.kt:457)
    at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
    at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
    at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:154)
    at kotlinx.coroutines.channels.ProduceKt.produce(Produce.kt:98)
    at kotlinx.coroutines.channels.ProduceKt.produce$default(Produce.kt:92)
    at io.ktor.http.cio.MultipartKt.parseMultipart(Multipart.kt:268)
    at io.ktor.http.cio.MultipartKt.parseMultipart(Multipart.kt:244)
    at io.ktor.http.cio.CIOMultipartDataBase.<init>(CIOMultipartData.kt:30)
    at io.ktor.http.cio.CIOMultipartDataBase.<init>(CIOMultipartData.kt:28)
    at io.ktor.server.engine.DefaultTransformKt.multiPartData(DefaultTransform.kt:85)
    at io.ktor.server.engine.DefaultTransformKt.access$multiPartData(DefaultTransform.kt:1)
    at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$2.invokeSuspend(DefaultTransform.kt:48)
    at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$2.invoke(DefaultTransform.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
    at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
    at io.ktor.request.ApplicationReceiveFunctionsKt.receive(ApplicationReceiveFunctions.kt:118)
    at circlet.pipelines.PipelinesRoutesKt.handleUserJarSaveRequest(PipelinesRoutes.kt:277)
    at circlet.pipelines.PipelinesRoutesKt$pipelines$1$9$1.invokeSuspend(PipelinesRoutes.kt:65)
    at circlet.pipelines.PipelinesRoutesKt$pipelines$1$9$1.invoke(PipelinesRoutes.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1$invokeSuspend$$inlined$with$1.invokeSuspend(MDC.kt:98)
    at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1$invokeSuspend$$inlined$with$1.invoke(MDC.kt)
    at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
    at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:156)
    at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
    at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1.invokeSuspend(OrgRoutes.kt:89)
    at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1.invoke(OrgRoutes.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:106)
    at io.ktor.features.ContentNegotiation$Feature$install$1.invoke(ContentNegotiation.kt)
    at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:268)
    at io.ktor.util.pipeline.SuspendFunctionGun.access$loop(PipelineContext.kt:67)
    at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
    at circlet.platform.server.ServerKt$platformMain$$inlined$maybeMeasure$lambda$1$2$1.invokeSuspend(Server.kt:117)
    at circlet.platform.server.ServerKt$platformMain$$inlined$maybeMeasure$lambda$1$2$1.invoke(Server.kt)
    at libraries.coroutines.extra.CoroutineBuildersCommonKt$withContext$2.invokeSuspend(CoroutineBuildersCommon.kt:44)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTask.run(Dispatched.kt:241)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at java.base/java.lang.Thread.run(Thread.java:834)
e5l commented 4 years ago

Hi @sedovalx, thanks for the report. Could you check it with 1.3.0-beta-2?

dmitrievanthony commented 4 years ago

Hi @sedovalx,

I tried to reproduce it using simplified code (1.2.5 and master), but I could not.

I used the following code for server:

fun main() {
    embeddedServer(Jetty, port = 7654) {
        routing {
            post("/handler") {
                val multipart = call.receiveMultipart()
                val out = arrayListOf<String>()
                multipart.forEachPart { part ->
                    out += when (part) {
                        is PartData.FormItem -> {
                            "FormItem(${part.name},${part.value})"
                        }
                        is PartData.FileItem -> {
                            val bytes = part.streamProvider().readBytes()
                            "FileItem(${part.name},${part.originalFileName},${hex(bytes)})"
                        }
                        is PartData.BinaryItem -> {
                            "BinaryItem(${part.name},${hex(part.provider().readBytes())})"
                        }
                    }

                    part.dispose()
                }
                call.respondText(out.joinToString("; "))
            }
        }
    }.start(true)
}

And the following for the client:

suspend fun main() {
    HttpClient(Apache).use { client ->
        val bytes = ByteArray(1024 * 1024)
        val data = formData {
            append("jar", "user.jar") {
                writeFully(bytes, 0, bytes.size)
            }
        }
        client.submitFormWithBinaryData<HttpResponse>("http://localhost:7654/handler", data).use { response ->
            if (!response.status.isSuccess()) {
                error("Failed to save jar, server responded with ${response.status}: ${response.readText()}")
            }
        }
    }
}

Could you provide a reproducer that doesn't require additional modules/code and reproduce the problem?

sedovalx commented 4 years ago

@dmitrievanthony I would love to but doubt I can. The error happened from time to time, seems to be flaky. I don't have ideas what may impact the correct behavior. Recently we updated to ktor 1.3.1. I will post an update here if/when I face this issue again.

dmitrievanthony commented 4 years ago

Thanks, @sedovalx, we appreciate it a lot.

sedovalx commented 4 years ago

The problem reproduced on 1.3.2

java.io.IOException: Failed to skip delimiter: actual bytes differ from delimiter bytes
  at io.ktor.utils.io.DelimitedKt.tryEnsureDelimiter(Delimited.kt:144)
  at io.ktor.utils.io.DelimitedKt.access$tryEnsureDelimiter(Delimited.kt:1)
  at io.ktor.utils.io.DelimitedKt$skipDelimiter$2.invoke(Delimited.kt:47)
  at io.ktor.utils.io.DelimitedKt$skipDelimiter$2.invoke(Delimited.kt)
  at io.ktor.utils.io.ByteBufferChannel.lookAhead(ByteBufferChannel.kt:1859)
  at io.ktor.utils.io.DelimitedKt.skipDelimiter(Delimited.kt:46)
  at io.ktor.http.cio.MultipartKt.skipBoundary(Multipart.kt:201)
  at io.ktor.http.cio.MultipartKt$parseMultipart$1.invokeSuspend(Multipart.kt:325)
  at    (Coroutine boundary. ( )
  at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1.invokeSuspend(OrgRoutes.kt:83)
  at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:107)
  at libraries.coroutines.extra.CoroutineBuildersCommonKt$withContext$2.invokeSuspend(CoroutineBuildersCommon.kt:44)
  at io.ktor.routing.Routing.executeResult(Routing.kt:147)
  at io.ktor.routing.Routing.interceptor(Routing.kt:34)
  at io.ktor.routing.Routing$Feature$install$1.invokeSuspend(Routing.kt:99)
Caused by: java.io.IOException: Failed to skip delimiter: actual bytes differ from delimiter bytes
  at io.ktor.utils.io.DelimitedKt.tryEnsureDelimiter(Delimited.kt:144)
  at io.ktor.utils.io.DelimitedKt.access$tryEnsureDelimiter(Delimited.kt:1)
  at io.ktor.utils.io.DelimitedKt$skipDelimiter$2.invoke(Delimited.kt:47)
  at io.ktor.utils.io.DelimitedKt$skipDelimiter$2.invoke(Delimited.kt)
  at io.ktor.utils.io.ByteBufferChannel.lookAhead(ByteBufferChannel.kt:1859)
  at io.ktor.utils.io.DelimitedKt.skipDelimiter(Delimited.kt:46)
  at io.ktor.http.cio.MultipartKt.skipBoundary(Multipart.kt:201)
  at io.ktor.http.cio.MultipartKt$parseMultipart$1.invokeSuspend(Multipart.kt:325)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:314)
  at kotlinx.coroutines.intrinsics.CancellableKt.startCoroutineCancellable(Cancellable.kt:26)
  at kotlinx.coroutines.CoroutineStart.invoke(CoroutineStart.kt:109)
  at kotlinx.coroutines.AbstractCoroutine.start(AbstractCoroutine.kt:158)
  at kotlinx.coroutines.channels.ProduceKt.produce(Produce.kt:98)
  at kotlinx.coroutines.channels.ProduceKt.produce$default(Produce.kt:92)
  at io.ktor.http.cio.MultipartKt.parseMultipart(Multipart.kt:311)
  at io.ktor.http.cio.MultipartKt.parseMultipart(Multipart.kt:287)
  at io.ktor.http.cio.CIOMultipartDataBase.<init>(CIOMultipartData.kt:30)
  at io.ktor.http.cio.CIOMultipartDataBase.<init>(CIOMultipartData.kt:28)
  at io.ktor.server.engine.DefaultTransformKt.multiPartData(DefaultTransform.kt:99)
  at io.ktor.server.engine.DefaultTransformKt.access$multiPartData(DefaultTransform.kt:1)
  at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$2.invokeSuspend(DefaultTransform.kt:50)
  at io.ktor.server.engine.DefaultTransformKt$installDefaultTransformations$2.invoke(DefaultTransform.kt)
  at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
  at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
  at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:183)
  at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
  at io.ktor.request.ApplicationReceiveFunctionsKt.receive(ApplicationReceiveFunctions.kt:110)
  at circlet.pipelines.PipelinesRoutesKt.handleUserScriptSaveRequest(PipelinesRoutes.kt:296)
  at circlet.pipelines.PipelinesRoutesKt$pipelines$1$3$1$1.invokeSuspend(PipelinesRoutes.kt:56)
  at circlet.pipelines.PipelinesRoutesKt$pipelines$1$3$1$1.invoke(PipelinesRoutes.kt)
  at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
  at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
  at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1$invokeSuspend$$inlined$with$1.invokeSuspend(MDC.kt:98)
  at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1$invokeSuspend$$inlined$with$1.invoke(MDC.kt)
  at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:91)
  at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:154)
  at kotlinx.coroutines.BuildersKt.withContext(Unknown Source)
  at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1.invokeSuspend(OrgRoutes.kt:83)
  at circlet.server.application.OrgRoutesKt$orgKeyInterceptor$1.invoke(OrgRoutes.kt)
  at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
  at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
  at io.ktor.features.ContentNegotiation$Feature$install$1.invokeSuspend(ContentNegotiation.kt:107)
  at io.ktor.features.ContentNegotiation$Feature$install$1.invoke(ContentNegotiation.kt)
  at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:318)
  at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:163)
  at circlet.platform.server.ServerKt$platformMain$$inlined$maybeMeasure$lambda$1$2$1.invokeSuspend(Server.kt:138)
  at circlet.platform.server.ServerKt$platformMain$$inlined$maybeMeasure$lambda$1$2$1.invoke(Server.kt)
  at libraries.coroutines.extra.CoroutineBuildersCommonKt$withContext$2.invokeSuspend(CoroutineBuildersCommon.kt:44)
  at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
  at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
  at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
  at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
  at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
  at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
  at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
  at java.base/java.lang.Thread.run(Thread.java:834)

Server code where exception happens

suspend fun handleUserScriptSaveRequest(call: ApplicationCall) {
    val scriptHash = call.parameters["scriptHash"]
    if (scriptHash == null) {
        call.respond(HttpStatusCode.BadRequest)
        return
    }

    val saver = call.orgKey.component<CompilationDataSaver>()
    call.receiveMultipart().apply {
        while (true) {
            val part = readPart() ?: break
            try {
                when (part) {
                    is PartData.FileItem -> {
                        try {
                            when (part.name) {
                                "jar" -> {
                                    val jarContent = part.streamProvider().use { it.readBytes() }
                                    saver.saveJar(scriptHash, jarContent)
                                }
                                "metadata" -> {
                                    val metadataContent = part.streamProvider().use { it.readBytes() }
                                    saver.saveMetadata(scriptHash, metadataContent)
                                }
                                else -> {
                                    call.respond(HttpStatusCode.BadRequest)
                                    return
                                }
                            }
                        }
                        catch (ex: Throwable) {
                            call.respond(HttpStatusCode.BadRequest)
                            return
                        }
                    }
                    else -> {
                        call.respond(HttpStatusCode.BadRequest)
                        return
                    }
                }
            }
            finally {
                part.dispose()
            }
        }
    }

    call.respond(HttpStatusCode.OK)
}

Can't say for sure where exactly it happens as line numbers in the stack trace don't match.

And the client code:

    private fun uploadExample(url: String, accessToken: String, content: String) {
        runBlocking {
            HttpClient(Apache.config {
                customizeClient {
                    setThreadFactory(NamedThreadFactory("Ktor-client-apache-Automation-RemoteClient-", daemon = true))
                    setDefaultIOReactorConfig(IOReactorConfig.custom().setIoThreadCount(4).build())
                }
            }).use { client ->
                val data =
                    formData {
                        append("metadata", "script.metadata", bodyBuilder = { writeStringUtf8(content) })
                    }
                client.submitFormWithBinaryData<HttpResponse>(url, data) {
                    header(HttpHeaders.Authorization, "Bearer $accessToken")
                }.also { response ->
                    if (!response.status.isSuccess()) {
                        error("Failed to save jar url $url responded with ${response.status}: ${response.readText()}")
                    }
                }
            }
        }
    }
dmitrievanthony commented 4 years ago

Thanks, @sedovalx,

Could you please say if this issue reproduces every time or it's fluky?

I tried to write a simpler reproducer, but I still can't get any results. Could you please check the following code in your environment?

import io.ktor.application.*
import io.ktor.http.*
import io.ktor.http.content.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.jetty.*

/*
 * Copyright 2014-2020 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

fun main() {
    embeddedServer(Jetty, port = 9876) {
        routing {
            post("/") {
                call.receiveMultipart().apply {
                    while (true) {
                        val part = readPart() ?: break
                        try {
                            when (part) {
                                is PartData.FileItem -> {
                                    try {
                                        when (part.name) {
                                            "jar" -> {
                                                part.streamProvider().use { it.readBytes() }
                                            }
                                            "metadata" -> {
                                                part.streamProvider().use { it.readBytes() }
                                            }
                                            else -> {
                                                call.respond(HttpStatusCode.BadRequest)
                                            }
                                        }
                                    }
                                    catch (ex: Throwable) {
                                        call.respond(HttpStatusCode.BadRequest)
                                    }
                                }
                                else -> {
                                    call.respond(HttpStatusCode.BadRequest)
                                }
                            }
                        }
                        finally {
                            part.dispose()
                        }
                    }
                }

                call.respond(HttpStatusCode.OK)
            }
        }
    }.start(true)
}
import io.ktor.client.*
import io.ktor.client.engine.*
import io.ktor.client.engine.apache.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.*
import org.apache.http.impl.nio.reactor.*
import java.util.concurrent.ThreadFactory

/*
 * Copyright 2014-2020 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
 */

suspend fun main() {
    HttpClient(Apache.config {
        customizeClient {
            setThreadFactory(ThreadFactory { Thread(it) })
            setDefaultIOReactorConfig(IOReactorConfig.custom().setIoThreadCount(4).build())
        }
    }).use { client ->
        val data =
            formData {
                append("metadata", "script.metadata", bodyBuilder = { writeStringUtf8(String(ByteArray(10 * 1024 * 1024))) })
            }

        repeat(1000) {
            client.submitFormWithBinaryData<HttpResponse>("http://localhost:9876/", data) {
            header(HttpHeaders.Authorization, "Bearer MY_TOKEN")
            }.also { response ->
                if (!response.status.isSuccess()) {
                    error("Failed to save jar url responded with ${response.status}: ${response.readText()}")
                }
            }
        }
    }
}
sedovalx commented 4 years ago

The error is flaky and rarely reproduces. We can add diagnostic logging or something in our code if you will come with a suggestion.

oleg-larshin commented 4 years ago

Please check the following ticket on YouTrack for follow-ups to this issue. GitHub issues will be closed in the coming weeks.

e5l commented 3 years ago

Please reopen if the problem is still active