Skip to content

Commit

Permalink
KTOR-7683 Fix for empty response with respondSource
Browse files Browse the repository at this point in the history
  • Loading branch information
bjhham committed Nov 26, 2024
1 parent d783338 commit a92e5ac
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 15 deletions.
2 changes: 1 addition & 1 deletion ktor-io/api/ktor-io.api
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public final class io/ktor/utils/io/ByteWriteChannelOperationsKt {
public static final fun join (Lio/ktor/utils/io/ChannelJob;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun write (Lio/ktor/utils/io/ByteWriteChannel;ILkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun write$default (Lio/ktor/utils/io/ByteWriteChannel;ILkotlin/jvm/functions/Function3;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun writeBuffer (Lio/ktor/utils/io/ByteWriteChannel;Lkotlinx/io/Source;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun writeBuffer (Lio/ktor/utils/io/ByteWriteChannel;Lkotlinx/io/RawSource;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun writeByte (Lio/ktor/utils/io/ByteWriteChannel;BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun writeByteArray (Lio/ktor/utils/io/ByteWriteChannel;[BLkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun writeFully (Lio/ktor/utils/io/ByteWriteChannel;[BIILkotlin/coroutines/Continuation;)Ljava/lang/Object;
Expand Down
2 changes: 1 addition & 1 deletion ktor-io/api/ktor-io.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ final suspend fun (io.ktor.utils.io/ByteReadChannel).io.ktor.utils.io/toByteArra
final suspend fun (io.ktor.utils.io/ByteWriteChannel).io.ktor.utils.io/awaitFreeSpace() // io.ktor.utils.io/awaitFreeSpace|[email protected](){}[0]
final suspend fun (io.ktor.utils.io/ByteWriteChannel).io.ktor.utils.io/flushIfNeeded() // io.ktor.utils.io/flushIfNeeded|[email protected](){}[0]
final suspend fun (io.ktor.utils.io/ByteWriteChannel).io.ktor.utils.io/write(kotlin/Int = ..., kotlin/Function3<kotlin/ByteArray, kotlin/Int, kotlin/Int, kotlin/Int>): kotlin/Int // io.ktor.utils.io/write|[email protected](kotlin.Int;kotlin.Function3<kotlin.ByteArray,kotlin.Int,kotlin.Int,kotlin.Int>){}[0]
final suspend fun (io.ktor.utils.io/ByteWriteChannel).io.ktor.utils.io/writeBuffer(kotlinx.io/Source) // io.ktor.utils.io/writeBuffer|[email protected](kotlinx.io.Source){}[0]
final suspend fun (io.ktor.utils.io/ByteWriteChannel).io.ktor.utils.io/writeBuffer(kotlinx.io/RawSource) // io.ktor.utils.io/writeBuffer|[email protected](kotlinx.io.RawSource){}[0]
final suspend fun (io.ktor.utils.io/ByteWriteChannel).io.ktor.utils.io/writeByte(kotlin/Byte) // io.ktor.utils.io/writeByte|[email protected](kotlin.Byte){}[0]
final suspend fun (io.ktor.utils.io/ByteWriteChannel).io.ktor.utils.io/writeByteArray(kotlin/ByteArray) // io.ktor.utils.io/writeByteArray|[email protected](kotlin.ByteArray){}[0]
final suspend fun (io.ktor.utils.io/ByteWriteChannel).io.ktor.utils.io/writeFully(kotlin/ByteArray, kotlin/Int = ..., kotlin/Int = ...) // io.ktor.utils.io/writeFully|[email protected](kotlin.ByteArray;kotlin.Int;kotlin.Int){}[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import kotlinx.io.*
import kotlinx.io.Buffer
import kotlinx.io.unsafe.*
import kotlin.coroutines.*
import kotlin.jvm.*

@OptIn(InternalAPI::class)
public suspend fun ByteWriteChannel.writeByte(value: Byte) {
Expand Down Expand Up @@ -62,7 +61,7 @@ public suspend fun ByteWriteChannel.writeFully(value: ByteArray, startIndex: Int
}

@OptIn(InternalAPI::class)
public suspend fun ByteWriteChannel.writeBuffer(value: Source) {
public suspend fun ByteWriteChannel.writeBuffer(value: RawSource) {
writeBuffer.transferFrom(value)
flushIfNeeded()
}
Expand Down
4 changes: 2 additions & 2 deletions ktor-server/ktor-server-core/api/ktor-server-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -1135,8 +1135,8 @@ public final class io/ktor/server/response/ApplicationResponseFunctionsKt {
public static synthetic fun respondRedirect$default (Lio/ktor/server/application/ApplicationCall;Lio/ktor/http/Url;ZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static synthetic fun respondRedirect$default (Lio/ktor/server/application/ApplicationCall;Ljava/lang/String;ZLkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static synthetic fun respondRedirect$default (Lio/ktor/server/application/ApplicationCall;ZLkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun respondSource (Lio/ktor/server/application/ApplicationCall;Lkotlinx/io/Source;Lio/ktor/http/ContentType;Lio/ktor/http/HttpStatusCode;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun respondSource$default (Lio/ktor/server/application/ApplicationCall;Lkotlinx/io/Source;Lio/ktor/http/ContentType;Lio/ktor/http/HttpStatusCode;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun respondSource (Lio/ktor/server/application/ApplicationCall;Lkotlinx/io/RawSource;Lio/ktor/http/ContentType;Lio/ktor/http/HttpStatusCode;Ljava/lang/Long;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun respondSource$default (Lio/ktor/server/application/ApplicationCall;Lkotlinx/io/RawSource;Lio/ktor/http/ContentType;Lio/ktor/http/HttpStatusCode;Ljava/lang/Long;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
public static final fun respondText (Lio/ktor/server/application/ApplicationCall;Lio/ktor/http/ContentType;Lio/ktor/http/HttpStatusCode;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static final fun respondText (Lio/ktor/server/application/ApplicationCall;Ljava/lang/String;Lio/ktor/http/ContentType;Lio/ktor/http/HttpStatusCode;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
public static synthetic fun respondText$default (Lio/ktor/server/application/ApplicationCall;Lio/ktor/http/ContentType;Lio/ktor/http/HttpStatusCode;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
Expand Down
2 changes: 1 addition & 1 deletion ktor-server/ktor-server-core/api/ktor-server-core.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -1816,7 +1816,7 @@ final suspend fun (io.ktor.server.application/ApplicationCall).io.ktor.server.re
final suspend fun (io.ktor.server.application/ApplicationCall).io.ktor.server.response/respondBytesWriter(io.ktor.http/ContentType? = ..., io.ktor.http/HttpStatusCode? = ..., kotlin/Long? = ..., kotlin.coroutines/SuspendFunction1<io.ktor.utils.io/ByteWriteChannel, kotlin/Unit>) // io.ktor.server.response/respondBytesWriter|[email protected](io.ktor.http.ContentType?;io.ktor.http.HttpStatusCode?;kotlin.Long?;kotlin.coroutines.SuspendFunction1<io.ktor.utils.io.ByteWriteChannel,kotlin.Unit>){}[0]
final suspend fun (io.ktor.server.application/ApplicationCall).io.ktor.server.response/respondRedirect(io.ktor.http/Url, kotlin/Boolean = ...) // io.ktor.server.response/respondRedirect|[email protected](io.ktor.http.Url;kotlin.Boolean){}[0]
final suspend fun (io.ktor.server.application/ApplicationCall).io.ktor.server.response/respondRedirect(kotlin/String, kotlin/Boolean = ...) // io.ktor.server.response/respondRedirect|[email protected](kotlin.String;kotlin.Boolean){}[0]
final suspend fun (io.ktor.server.application/ApplicationCall).io.ktor.server.response/respondSource(kotlinx.io/Source, io.ktor.http/ContentType? = ..., io.ktor.http/HttpStatusCode? = ...) // io.ktor.server.response/respondSource|[email protected](kotlinx.io.Source;io.ktor.http.ContentType?;io.ktor.http.HttpStatusCode?){}[0]
final suspend fun (io.ktor.server.application/ApplicationCall).io.ktor.server.response/respondSource(kotlinx.io/RawSource, io.ktor.http/ContentType? = ..., io.ktor.http/HttpStatusCode? = ..., kotlin/Long? = ...) // io.ktor.server.response/respondSource|[email protected](kotlinx.io.RawSource;io.ktor.http.ContentType?;io.ktor.http.HttpStatusCode?;kotlin.Long?){}[0]
final suspend fun (io.ktor.server.application/ApplicationCall).io.ktor.server.response/respondText(io.ktor.http/ContentType? = ..., io.ktor.http/HttpStatusCode? = ..., kotlin.coroutines/SuspendFunction0<kotlin/String>) // io.ktor.server.response/respondText|[email protected](io.ktor.http.ContentType?;io.ktor.http.HttpStatusCode?;kotlin.coroutines.SuspendFunction0<kotlin.String>){}[0]
final suspend fun (io.ktor.server.application/ApplicationCall).io.ktor.server.response/respondText(kotlin/String, io.ktor.http/ContentType? = ..., io.ktor.http/HttpStatusCode? = ..., kotlin/Function1<io.ktor.http.content/OutgoingContent, kotlin/Unit> = ...) // io.ktor.server.response/respondText|[email protected](kotlin.String;io.ktor.http.ContentType?;io.ktor.http.HttpStatusCode?;kotlin.Function1<io.ktor.http.content.OutgoingContent,kotlin.Unit>){}[0]
final suspend fun <#A: kotlin/Any> (io.ktor.server.application/ApplicationCall).io.ktor.server.request/receive(kotlin.reflect/KClass<#A>): #A // io.ktor.server.request/receive|[email protected](kotlin.reflect.KClass<0:0>){0§<kotlin.Any>}[0]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,13 +157,15 @@ public suspend fun ApplicationCall.respondBytes(
* @param source The binary data source of the content to be responded with.
* @param contentType An optional [ContentType], unspecified by default
* @param status An optional [HttpStatusCode], default is [HttpStatusCode.OK]
* @param contentLength An optional value included in the Content-Length header, also truncating content
*/
public suspend fun ApplicationCall.respondSource(
source: Source,
source: RawSource,
contentType: ContentType? = null,
status: HttpStatusCode? = null,
contentLength: Long? = null,
) {
respond(ChannelWriterContent({ writeBuffer(source) }, contentType, status, source.remaining))
respond(ChannelWriterContent({ writeBuffer(source) }, contentType, status, contentLength))
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.test.base.*
import io.ktor.utils.io.*
import io.ktor.utils.io.core.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.io.InputStream
import kotlinx.coroutines.*
import kotlinx.io.asSource
import kotlinx.io.buffered
import java.io.*
import kotlin.test.*
import kotlin.io.use
import kotlin.text.toByteArray

abstract class ContentTestSuite<TEngine : ApplicationEngine, TConfiguration : ApplicationEngine.Configuration>(
hostFactory: ApplicationEngineFactory<TEngine, TConfiguration>
Expand Down Expand Up @@ -816,6 +817,41 @@ abstract class ContentTestSuite<TEngine : ApplicationEngine, TConfiguration : Ap
}
}

@Test
fun respondSourceFromInputStream() = runTest {
val testString = "test".repeat(100)
createAndStartServer {
get("/raw") {
call.respondSource(
testString.toByteArray().inputStream().asSource(),
contentType = ContentType.Text.Plain
)
}
get("/buffered") {
call.respondSource(
testString.toByteArray().inputStream().asSource().buffered(),
contentType = ContentType.Text.Plain
)
}
get("/truncated") {
call.respondSource(
testString.toByteArray().inputStream().asSource(),
contentType = ContentType.Text.Plain,
contentLength = 20
)
}
}
withUrl("/raw") {
assertEquals(testString, bodyAsText(), "Unbuffered input stream content mismatch")
}
withUrl("/buffered") {
assertEquals(testString, bodyAsText(), "Buffered input stream content mismatch")
}
withUrl("/truncated") {
assertEquals(testString.substring(0, 20), bodyAsText(), "Buffered input stream content mismatch")
}
}

companion object {
const val classesDir: String = "build/classes/"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.ktor.server.testing.*
import io.ktor.util.reflect.*
import io.ktor.utils.io.*
import io.ktor.utils.io.charsets.*
import io.ktor.utils.io.core.remaining
import kotlinx.io.*
import kotlin.test.*

Expand Down Expand Up @@ -105,7 +106,11 @@ class RespondFunctionsTest {
routing {
get("text") {
val source = Buffer().also { it.writeString("Hello") }
call.respondSource(source, contentType = ContentType.Text.Plain)
call.respondSource(
source,
contentType = ContentType.Text.Plain,
contentLength = source.remaining
)
}
}

Expand Down

0 comments on commit a92e5ac

Please sign in to comment.