diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 9a8d9eb2c3..0c8caec941 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1 -FROM eclipse-temurin:21.0.5_11-jdk-noble AS dev +FROM eclipse-temurin:21.0.6_7-jdk-noble AS dev ARG USERNAME=developer ARG USER_UID=1001 diff --git a/ktor-io/jvm/src/io/ktor/utils/io/ByteReadChannelOperations.jvm.kt b/ktor-io/jvm/src/io/ktor/utils/io/ByteReadChannelOperations.jvm.kt index 3b5faddd1b..d28a36f8af 100644 --- a/ktor-io/jvm/src/io/ktor/utils/io/ByteReadChannelOperations.jvm.kt +++ b/ktor-io/jvm/src/io/ktor/utils/io/ByteReadChannelOperations.jvm.kt @@ -96,9 +96,8 @@ public suspend fun ByteReadChannel.copyTo(channel: WritableByteChannel, limit: L } } - while (copied < limit) { + while (copied < limit && !isClosedForRead) { read(min = 0, consumer = copy) - if (isClosedForRead) break } closedCause?.let { throw it } @@ -184,19 +183,17 @@ public fun ByteReadChannel.readAvailable(block: (ByteBuffer) -> Int): Int { * * @param min amount of bytes available for read, should be positive or zero * @param consumer to be invoked when at least [min] bytes available for read + * @throws EOFException when there are less than [min] bytes available after the channel is closed */ @OptIn(InternalAPI::class) public suspend inline fun ByteReadChannel.read(min: Int = 1, noinline consumer: (ByteBuffer) -> Unit) { require(min >= 0) { "min should be positive or zero" } - if (availableForRead > 0 && availableForRead >= min) { + if (min > 0) { + if (!awaitContent(min)) { + throw EOFException("Not enough bytes available: required $min but $availableForRead available") + } + readBuffer.read(consumer) + } else if (awaitContent()) { readBuffer.read(consumer) - return - } - - awaitContent() - if (isClosedForRead && min > 0) { - throw EOFException("Not enough bytes available: required $min but $availableForRead available") } - - if (availableForRead > 0) readBuffer.read(consumer) } diff --git a/ktor-io/jvm/test/ByteReadChannelOperationsJvmTest.kt b/ktor-io/jvm/test/ByteReadChannelOperationsJvmTest.kt index 9f930c5301..e78a7ca594 100644 --- a/ktor-io/jvm/test/ByteReadChannelOperationsJvmTest.kt +++ b/ktor-io/jvm/test/ByteReadChannelOperationsJvmTest.kt @@ -4,7 +4,11 @@ import io.ktor.utils.io.* import kotlinx.coroutines.* +import kotlinx.coroutines.test.runTest +import kotlinx.io.EOFException +import org.junit.jupiter.api.assertThrows import kotlin.test.* +import kotlin.test.Test import kotlin.time.Duration.Companion.seconds import kotlin.time.measureTime @@ -100,4 +104,16 @@ class ByteReadChannelOperationsJvmTest { assertTrue(time < 5.seconds, "Expected I/O to be complete in a reasonable time, but it took $time") assertEquals(2_088_890, out.length) } + + @Test + fun readWithGreaterMinThrows() = runTest { + val channel = ByteChannel() + channel.writeByte(1) + channel.close() + assertThrows { + channel.read(2) { + fail("There is only one byte in the channel") + } + } + } }