Repository: ktorio/ktor
File: ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpEngine.kt (Kotlin)

Code before:
                    ?.adapt(requestData, status, headers, body, requestData.body, callContext)
                    ?: body
            else -> body
        }

        return HttpResponseData(status, requestTime, headers, version, responseBody, callContext)
    }

    private companion object {
        /**
         * It's an artificial prototype object to be used to create actual clients and eliminate the following issue:
         * https://github.com/square/okhttp/issues/3372.
         */
        val okHttpClientPrototype: OkHttpClient by lazy {
            OkHttpClient.Builder().build()
        }
    }

    private fun createOkHttpClient(timeoutExtension: HttpTimeoutConfig?): OkHttpClient {
        val builder = (config.preconfigured ?: okHttpClientPrototype).newBuilder()

        if (config.preconfigured == null) {
            builder.dispatcher(Dispatcher())
        }
        builder.apply(config.config)
        config.proxy?.let { builder.proxy(it) }
        timeoutExtension?.let {
            builder.setupTimeoutAttributes(it)
        }

        return builder.build()
    }
}

@OptIn(DelicateCoroutinesApi::class, InternalCoroutinesApi::class)
private fun BufferedSource.toChannel(context: CoroutineContext, requestData: HttpRequestData): ByteReadChannel =
    GlobalScope.writer(context) {
        use { source ->
            var lastRead = 0
            while (source.isOpen && context.isActive && lastRead >= 0) {
                channel.write { buffer ->
                    lastRead = try {
                        source.read(buffer)
                    } catch (cause: Throwable) {
                        val cancelOrCloseCause =
                            kotlin.runCatching { context.job.getCancellationException() }.getOrNull() ?: cause
                        throw mapExceptions(cancelOrCloseCause, requestData)
                    }
                }
                channel.flush()
            }

Review comment (suggestion):
Probably we should just drop this line as `OkHttp` already [creates `Dispatcher` by default](https://github.com/square/okhttp/blob/parent-5.3.2/okhttp/src/commonJvmAndroid/kotlin/okhttp3/OkHttpClient.kt#L587):
```kotlin
internal var dispatcher: Dispatcher = Dispatcher()
```

Code after:
        val responseBody: Any = when (body) {
            is ByteReadChannel ->
                requestData.attributes
                    .getOrNull(ResponseAdapterAttributeKey)
                    ?.adapt(requestData, status, headers, body, requestData.body, callContext)
                    ?: body
            else -> body
        }

        return HttpResponseData(status, requestTime, headers, version, responseBody, callContext)
    }

    private companion object {
        /**
         * It's an artificial prototype object to be used to create actual clients and eliminate the following issue:
         * https://github.com/square/okhttp/issues/3372.
         */
        val okHttpClientPrototype: OkHttpClient by lazy {
            OkHttpClient.Builder().build()
        }
    }

    private fun createOkHttpClient(timeoutExtension: HttpTimeoutConfig?): OkHttpClient {
        val builder = (config.preconfigured ?: okHttpClientPrototype).newBuilder()
        if (config.preconfigured == null) {
            builder.dispatcher(Dispatcher())
        }
        builder.apply(config.config)
        config.proxy?.let { builder.proxy(it) }
        timeoutExtension?.let {
            builder.setupTimeoutAttributes(it)
        }

        return builder.build()
    }
}

@OptIn(DelicateCoroutinesApi::class, InternalCoroutinesApi::class)
private fun BufferedSource.toChannel(context: CoroutineContext, requestData: HttpRequestData): ByteReadChannel =
    GlobalScope.writer(context) {
        use { source ->
            var lastRead = 0
            while (source.isOpen && context.isActive && lastRead >= 0) {
                channel.write { buffer ->
                    lastRead = try {
                        source.read(buffer)
                    } catch (cause: Throwable) {
                        val cancelOrCloseCause =
                            kotlin.runCatching { context.job.getCancellationException() }.getOrNull() ?: cause
                        throw mapExceptions(cancelOrCloseCause, requestData)
                    }