From 3424862ce38f196f9124b549e18127ecba5aa73e Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Tue, 1 Oct 2024 11:16:01 +0200 Subject: [PATCH 01/14] Add generic parameter to ServerSentEvent --- .../client/plugins/sse/ClientSSESession.kt | 6 +- .../src/io/ktor/client/plugins/sse/SSE.kt | 7 +- .../client/plugins/sse/SSEClientContent.kt | 3 +- .../io/ktor/client/plugins/sse/SSEConfig.kt | 12 ++- .../io/ktor/client/plugins/sse/builders.kt | 101 ++++++++++++------ .../common/src/io/ktor/sse/ServerSentEvent.kt | 4 +- 6 files changed, 92 insertions(+), 41 deletions(-) diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt index e554ee5619b..0eaf2c92e4f 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt @@ -12,11 +12,11 @@ import kotlinx.coroutines.flow.* /** * A Server-sent events session. */ -public interface SSESession : CoroutineScope { +public interface SSESession : CoroutineScope { /** * An incoming server-sent events flow. */ - public val incoming: Flow + public val incoming: Flow> } /** @@ -24,4 +24,4 @@ public interface SSESession : CoroutineScope { * * @property call associated with session. */ -public class ClientSSESession(public val call: HttpClientCall, delegate: SSESession) : SSESession by delegate +public class ClientSSESession(public val call: HttpClientCall, delegate: SSESession) : SSESession by delegate diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt index ecb623f9d37..5e56b929c06 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt @@ -31,7 +31,7 @@ public data object SSECapability : HttpClientEngineCapability * val client = HttpClient { * install(SSE) * } - * client.sse { + * client.sse { * val event = incoming.receive() * } * ``` @@ -41,6 +41,7 @@ public val SSE: ClientPlugin = createClientPlugin( name = "SSE", createConfiguration = ::SSEConfig ) { + val deserializer = pluginConfig.deserialize val reconnectionTime = pluginConfig.reconnectionTime val showCommentEvents = pluginConfig.showCommentEvents val showRetryEvents = pluginConfig.showRetryEvents @@ -52,6 +53,7 @@ public val SSE: ClientPlugin = createClientPlugin( LOGGER.trace("Sending SSE request ${request.url}") request.setCapability(SSECapability, Unit) + val localDeserializer = getAttributeValue(request, deserializerAttr) val localReconnectionTime = getAttributeValue(request, reconnectionTimeAttr) val localShowCommentEvents = getAttributeValue(request, showCommentEventsAttr) val localShowRetryEvents = getAttributeValue(request, showRetryEventsAttr) @@ -59,6 +61,7 @@ public val SSE: ClientPlugin = createClientPlugin( request.attributes.put(ResponseAdapterAttributeKey, SSEClientResponseAdapter()) content.contentType?.let { request.contentType(it) } SSEClientContent( + localDeserializer ?: deserializer, localReconnectionTime ?: reconnectionTime, localShowCommentEvents ?: showCommentEvents, localShowRetryEvents ?: showRetryEvents, @@ -88,7 +91,7 @@ public val SSE: ClientPlugin = createClientPlugin( message = "Expected Content-Type ${ContentType.Text.EventStream} but was $contentType" ) } - if (session !is SSESession) { + if (session !is SSESession<*>) { throw SSEClientException( response, message = "Expected ${SSESession::class.simpleName} content but was $session" diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEClientContent.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEClientContent.kt index 4cd67aa3a5f..b43fc0cf78a 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEClientContent.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEClientContent.kt @@ -11,6 +11,7 @@ import kotlin.time.* @InternalAPI public class SSEClientContent( + public val deserializer: (String) -> Any, public val reconnectionTime: Duration, public val showCommentEvents: Boolean, public val showRetryEvents: Boolean, @@ -27,6 +28,6 @@ public class SSEClientContent( override fun toString(): String = "SSEClientContent" override fun copy(delegate: OutgoingContent): SSEClientContent { - return SSEClientContent(reconnectionTime, showCommentEvents, showRetryEvents, delegate) + return SSEClientContent(deserializer, reconnectionTime, showCommentEvents, showRetryEvents, delegate) } } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEConfig.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEConfig.kt index e52f296a547..6bd1f27491b 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEConfig.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEConfig.kt @@ -13,6 +13,7 @@ import kotlin.time.Duration.Companion.milliseconds public class SSEConfig { internal var showCommentEvents = false internal var showRetryEvents = false + internal var deserialize: (String) -> Any = { s -> s } /** * The reconnection time. If the connection to the server is lost, @@ -23,16 +24,23 @@ public class SSEConfig { public var reconnectionTime: Duration = 3000.milliseconds /** - * Add events consisting only of comments in the incoming flow. + * Adds events consisting only of comments in the incoming flow. */ public fun showCommentEvents() { showCommentEvents = true } /** - * Add events consisting only of the retry field in the incoming flow. + * Adds events consisting only of the retry field in the incoming flow. */ public fun showRetryEvents() { showRetryEvents = true } + + /** + * Configures deserialization logic for transforming field `data` of `ServerSentEvent` into desired data object. + */ + public fun deserialize(deserialize: (String) -> Any) { + this.deserialize = deserialize + } } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt index e8c4e8f82bc..253913d81bd 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt @@ -17,6 +17,7 @@ internal val sseRequestAttr = AttributeKey("SSERequestFlag") internal val reconnectionTimeAttr = AttributeKey("SSEReconnectionTime") internal val showCommentEventsAttr = AttributeKey("SSEShowCommentEvents") internal val showRetryEventsAttr = AttributeKey("SSEShowRetryEvents") +internal val deserializerAttr = AttributeKey<(String) -> Any>("SSEDeserializer") /** * Installs the [SSE] plugin using the [config] as configuration. @@ -30,18 +31,20 @@ public fun HttpClientConfig<*>.SSE(config: SSEConfig.() -> Unit) { /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEventsSession( +public suspend fun HttpClient.serverSentEventsSession( + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit -): ClientSSESession { +): ClientSSESession { plugin(SSE) - val sessionDeferred = CompletableDeferred() + val sessionDeferred = CompletableDeferred>() val statement = prepareRequest { block() addAttribute(sseRequestAttr, true) + addAttribute(deserializerAttr, deserialize) addAttribute(reconnectionTimeAttr, reconnectionTime) addAttribute(showCommentEventsAttr, showCommentEvents) addAttribute(showRetryEventsAttr, showRetryEvents) @@ -49,7 +52,7 @@ public suspend fun HttpClient.serverSentEventsSession( @Suppress("SuspendFunctionOnCoroutineScope") launch { try { - statement.body { session -> + statement.body, Unit> { session -> sessionDeferred.complete(session) } } catch (cause: CancellationException) { @@ -64,16 +67,17 @@ public suspend fun HttpClient.serverSentEventsSession( /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEventsSession( +public suspend fun HttpClient.serverSentEventsSession( scheme: String? = null, host: String? = null, port: Int? = null, path: String? = null, + deserialize: ((String) -> T)?, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit = {} -): ClientSSESession = serverSentEventsSession(reconnectionTime, showCommentEvents, showRetryEvents) { +): ClientSSESession = serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents) { url(scheme, host, port, path) block() } @@ -81,13 +85,14 @@ public suspend fun HttpClient.serverSentEventsSession( /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEventsSession( +public suspend fun HttpClient.serverSentEventsSession( urlString: String, + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit = {} -): ClientSSESession = serverSentEventsSession(reconnectionTime, showCommentEvents, showRetryEvents) { +): ClientSSESession = serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents) { url.takeFrom(urlString) block() } @@ -95,14 +100,15 @@ public suspend fun HttpClient.serverSentEventsSession( /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEvents( +public suspend fun HttpClient.serverSentEvents( request: HttpRequestBuilder.() -> Unit, + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, - block: suspend ClientSSESession.() -> Unit + block: suspend ClientSSESession.() -> Unit ) { - val session = serverSentEventsSession(reconnectionTime, showCommentEvents, showRetryEvents, request) + val session = serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents, request) try { block(session) } catch (cause: CancellationException) { @@ -117,22 +123,24 @@ public suspend fun HttpClient.serverSentEvents( /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEvents( +public suspend fun HttpClient.serverSentEvents( scheme: String? = null, host: String? = null, port: Int? = null, path: String? = null, + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, request: HttpRequestBuilder.() -> Unit = {}, - block: suspend ClientSSESession.() -> Unit + block: suspend ClientSSESession.() -> Unit ) { serverSentEvents( { url(scheme, host, port, path) request() }, + deserialize, reconnectionTime, showCommentEvents, showRetryEvents, @@ -143,19 +151,21 @@ public suspend fun HttpClient.serverSentEvents( /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEvents( +public suspend fun HttpClient.serverSentEvents( urlString: String, + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, request: HttpRequestBuilder.() -> Unit = {}, - block: suspend ClientSSESession.() -> Unit + block: suspend ClientSSESession.() -> Unit ) { serverSentEvents( { url.takeFrom(urlString) request() }, + deserialize, reconnectionTime, showCommentEvents, showRetryEvents, @@ -166,77 +176,106 @@ public suspend fun HttpClient.serverSentEvents( /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.sseSession( +public suspend fun HttpClient.sseSession( + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit -): ClientSSESession = serverSentEventsSession(reconnectionTime, showCommentEvents, showRetryEvents, block) +): ClientSSESession = + serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents, block) /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.sseSession( +public suspend fun HttpClient.sseSession( scheme: String? = null, host: String? = null, port: Int? = null, path: String? = null, + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit = {} -): ClientSSESession = - serverSentEventsSession(scheme, host, port, path, reconnectionTime, showCommentEvents, showRetryEvents, block) +): ClientSSESession = + serverSentEventsSession( + scheme, + host, + port, + path, + deserialize, + reconnectionTime, + showCommentEvents, + showRetryEvents, + block + ) /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.sseSession( +public suspend fun HttpClient.sseSession( urlString: String, + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit = {} -): ClientSSESession = serverSentEventsSession(urlString, reconnectionTime, showCommentEvents, showRetryEvents, block) +): ClientSSESession = + serverSentEventsSession(urlString, deserialize, reconnectionTime, showCommentEvents, showRetryEvents, block) /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.sse( +public suspend fun HttpClient.sse( request: HttpRequestBuilder.() -> Unit, + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, - block: suspend ClientSSESession.() -> Unit -): Unit = serverSentEvents(request, reconnectionTime, showCommentEvents, showRetryEvents, block) + block: suspend ClientSSESession.() -> Unit +): Unit = serverSentEvents(request, deserialize, reconnectionTime, showCommentEvents, showRetryEvents, block) /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.sse( +public suspend fun HttpClient.sse( scheme: String? = null, host: String? = null, port: Int? = null, path: String? = null, request: HttpRequestBuilder.() -> Unit = {}, + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, - block: suspend ClientSSESession.() -> Unit + block: suspend ClientSSESession.() -> Unit ): Unit = - serverSentEvents(scheme, host, port, path, reconnectionTime, showCommentEvents, showRetryEvents, request, block) + serverSentEvents( + scheme, + host, + port, + path, + deserialize, + reconnectionTime, + showCommentEvents, + showRetryEvents, + request, + block + ) /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.sse( +public suspend fun HttpClient.sse( urlString: String, request: HttpRequestBuilder.() -> Unit = {}, + deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, - block: suspend ClientSSESession.() -> Unit -): Unit = serverSentEvents(urlString, reconnectionTime, showCommentEvents, showRetryEvents, request, block) + block: suspend ClientSSESession.() -> Unit +): Unit = serverSentEvents(urlString, deserialize, reconnectionTime, showCommentEvents, showRetryEvents, request, block) private fun HttpRequestBuilder.addAttribute(attributeKey: AttributeKey, value: T?) { if (value != null) { diff --git a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt index ad1cf960b56..ea7a0b810c9 100644 --- a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt +++ b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt @@ -15,8 +15,8 @@ import io.ktor.utils.io.* * @property retry reconnection time, in milliseconds to wait before reconnecting. * @property comments comment lines starting with a ':' character. */ -public class ServerSentEvent( - public val data: String? = null, +public class ServerSentEvent( + public val data: T? = null, public val event: String? = null, public val id: String? = null, public val retry: Long? = null, From c631e9a5e2cdeaef7f50ae0b096b75ba36f131c9 Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Tue, 1 Oct 2024 23:54:23 +0200 Subject: [PATCH 02/14] Add client support --- .../test/server/tests/ServerSentEvents.kt | 24 ++- .../plugins/sse/DefaultClientSSESession.kt | 17 +- .../src/io/ktor/client/request/HttpRequest.kt | 4 +- .../ktor/client/engine/okhttp/OkHttpEngine.kt | 9 +- .../client/engine/okhttp/OkHttpSSESession.kt | 11 +- .../tests/plugins/SSESessionParserTest.kt | Bin 6887 -> 8368 bytes .../tests/plugins/ServerSentEventsTest.kt | 159 ++++++++++++++++-- .../common/src/io/ktor/sse/ServerSentEvent.kt | 8 +- 8 files changed, 199 insertions(+), 33 deletions(-) diff --git a/buildSrc/src/main/kotlin/test/server/tests/ServerSentEvents.kt b/buildSrc/src/main/kotlin/test/server/tests/ServerSentEvents.kt index 33589b9d9e9..d5720ffc4cd 100644 --- a/buildSrc/src/main/kotlin/test/server/tests/ServerSentEvents.kt +++ b/buildSrc/src/main/kotlin/test/server/tests/ServerSentEvents.kt @@ -84,13 +84,35 @@ internal fun Application.serverSentEvents() { emit(SseEvent("Hello")) } ) - } get("/content-type-text-plain") { call.response.header(HttpHeaders.ContentType, ContentType.Text.Plain.toString()) call.respond(HttpStatusCode.OK) } + + get("/person") { + val times = call.parameters["times"]?.toInt() ?: 1 + call.respondSseEvents( + flow { + repeat(times) { + emit(SseEvent(data = "Name $it", event = "event $it", id = "$it")) + } + } + ) + } + + get("/json") { + val data = """{ "id": 1, + "firstName": "Jet", + "lastName": "Brains" + }""".trimIndent() + call.respondSseEvents( + flow { + emit(SseEvent(data = data)) + } + ) + } } } } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt index 6febafae7f2..e6979d0cca2 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt @@ -10,11 +10,12 @@ import kotlinx.coroutines.flow.* import kotlin.coroutines.* @OptIn(InternalAPI::class) -public class DefaultClientSSESession( +public class DefaultClientSSESession( content: SSEClientContent, + private val deserializer: (String) -> T, private var input: ByteReadChannel, override val coroutineContext: CoroutineContext, -) : SSESession { +) : SSESession { private var lastEventId: String? = null private var reconnectionTimeMillis = content.reconnectionTime.inWholeMilliseconds private val showCommentEvents = content.showCommentEvents @@ -31,10 +32,10 @@ public class DefaultClientSSESession( } } - override val incoming: Flow + override val incoming: Flow> get() = _incoming - private suspend fun ByteReadChannel.parseEvent(): ServerSentEvent? { + private suspend fun ByteReadChannel.parseEvent(): ServerSentEvent? { val data = StringBuilder() val comments = StringBuilder() var eventType: String? = null @@ -55,7 +56,7 @@ public class DefaultClientSSESession( this@DefaultClientSSESession.lastEventId = lastEventId val event = ServerSentEvent( - if (wasData) data.toText() else null, + if (wasData) deserializer(data.toText()) else null, eventType, lastEventId, curRetry, @@ -105,13 +106,13 @@ public class DefaultClientSSESession( private fun StringBuilder.toText() = toString().removeSuffix(END_OF_LINE) - private fun ServerSentEvent.isEmpty() = + private fun ServerSentEvent.isEmpty() = data == null && id == null && event == null && retry == null && comments == null - private fun ServerSentEvent.isCommentsEvent() = + private fun ServerSentEvent.isCommentsEvent() = data == null && event == null && id == null && retry == null && comments != null - private fun ServerSentEvent.isRetryEvent() = + private fun ServerSentEvent.isRetryEvent() = data == null && event == null && id == null && comments == null && retry != null } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/request/HttpRequest.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/request/HttpRequest.kt index a55c6b4395d..ef2b1b11cc1 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/request/HttpRequest.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/request/HttpRequest.kt @@ -339,8 +339,10 @@ public class SSEClientResponseAdapter : ResponseAdapter { status == HttpStatusCode.OK && contentType?.withoutParameters() == ContentType.Text.EventStream ) { + outgoingContent as SSEClientContent DefaultClientSSESession( - outgoingContent as SSEClientContent, + outgoingContent, + outgoingContent.deserializer, responseBody, callContext ) diff --git a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpEngine.kt b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpEngine.kt index d4eba7134c9..0342f346f1e 100644 --- a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpEngine.kt +++ b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpEngine.kt @@ -66,7 +66,7 @@ public class OkHttpEngine(override val config: OkHttpConfig) : HttpClientEngineB return when { data.isUpgradeRequest() -> executeWebSocketRequest(requestEngine, engineRequest, callContext) - data.isSseRequest() -> executeServerSendEventsRequest(requestEngine, engineRequest, callContext) + data.isSseRequest() -> executeServerSendEventsRequest(requestEngine, engineRequest, callContext, data) else -> executeHttpRequest(requestEngine, engineRequest, callContext, data) } } @@ -96,13 +96,16 @@ public class OkHttpEngine(override val config: OkHttpConfig) : HttpClientEngineB private suspend fun executeServerSendEventsRequest( engine: OkHttpClient, engineRequest: Request, - callContext: CoroutineContext + callContext: CoroutineContext, + requestData: HttpRequestData ): HttpResponseData { val requestTime = GMTDate() + val deserializer = (requestData.body as SSEClientContent).deserializer val session = OkHttpSSESession( engine, engineRequest, - callContext + callContext, + deserializer ) val originResponse = session.originResponse.await() diff --git a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt index f62eab234ee..1a88c86a2f3 100644 --- a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt +++ b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt @@ -14,18 +14,19 @@ import okhttp3.* import okhttp3.sse.* import kotlin.coroutines.* -internal class OkHttpSSESession( +internal class OkHttpSSESession( engine: OkHttpClient, engineRequest: Request, override val coroutineContext: CoroutineContext, -) : SSESession, EventSourceListener() { + private val deserializer: (String) -> T +) : SSESession, EventSourceListener() { private val serverSentEventsSource = EventSources.createFactory(engine).newEventSource(engineRequest, this) internal val originResponse: CompletableDeferred = CompletableDeferred() - private val _incoming = Channel(8) + private val _incoming = Channel>(8) - override val incoming: Flow + override val incoming: Flow> get() = _incoming.receiveAsFlow() override fun onOpen(eventSource: EventSource, response: Response) { @@ -33,7 +34,7 @@ internal class OkHttpSSESession( } override fun onEvent(eventSource: EventSource, id: String?, type: String?, data: String) { - _incoming.trySendBlocking(ServerSentEvent(data, type, id)) + _incoming.trySendBlocking(ServerSentEvent(deserializer(data), type, id)) } override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) { diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/SSESessionParserTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/SSESessionParserTest.kt index 1715926351fa3dbaa6eb3b6b30043c7653089bac..f758f484a3b61600afbc3cf753693559bf473638 100644 GIT binary patch delta 1288 zcmb7D-D=c86vi8W3SJaiO+;iEdXdN)EaJ^tchPMGi)^7=DoAdI&8(fWnXEID*0qGZ zf^|T>^A!YNq2N1sp2FwhtY)K+aOH`NQ3MPk?n>#YTd5n){PQGyi^Zealzfyc;T?`rTkH`s+LN%>aot-CNL!>rFf{_vP%-Ik~TAD%=0ST z+5USL-%pF9UYaDO0ae}zjX9o+Kia{9O^sU@gTrJZroEI3RlG5zZyVgU6V!(`*YdM_ zU!J;f<}JTggk#S2%OG@^Cd%jmSHCg#{6H(&4bWsVo`U0Aj&6mxdN(3`lc^~^ozc2^*yC}>4ksc&pp@z)+P0Xyrh)N#7VAhn77xFKOEXLLsfaT?%DIr?d~pC_y6)M q=^M(66_z!XmlN0NmZWXZx$2h68l+(?Ke`9<)VXoZPTTo#>+BEi_)}c~ delta 89 zcmdns_}p~DgUQm2ZJUoWiil5ckWHBULN<@jrL-uqBr`uxFUmE@UuW`uIVm-T;*9+A tpwyD0O4qW~ypm!CTZNLM(o`J<*W7}VO6UB%5}>H&WC!`I$tUHX0{|DDB2fSU diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt index 5d3fcfc5229..c0ce667cd20 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt @@ -20,9 +20,10 @@ import io.ktor.utils.io.* import io.ktor.utils.io.charsets.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* +import kotlinx.serialization.* +import kotlinx.serialization.json.* import kotlin.coroutines.* import kotlin.test.* -import kotlin.test.assertFailsWith class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { @@ -30,12 +31,12 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { fun testExceptionIfSseIsNotInstalled() = testSuspend { val client = HttpClient() assertFailsWith { - client.serverSentEventsSession() + client.serverSentEventsSession {} }.apply { assertContains(message!!, SSE.key.name) } assertFailsWith { - client.serverSentEvents {} + client.serverSentEvents {} }.apply { assertContains(message!!, SSE.key.name) } @@ -63,7 +64,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } test { client -> - val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello") + val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello") session.incoming.single().apply { assertEquals("0", id) assertEquals("hello 0", event) @@ -85,7 +86,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> coroutineScope { launch { - val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello?times=100") + val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello?times=100") var size = 0 session.incoming.collectIndexed { i, it -> assertEquals("$i", it.id) @@ -100,7 +101,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { session.cancel() } launch { - val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello?times=50") + val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello?times=50") var size = 0 session.incoming.collectIndexed { i, it -> assertEquals("$i", it.id) @@ -126,7 +127,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> assertFailsWith { - client.serverSentEventsSession("http://unknown_host") + client.serverSentEventsSession("http://unknown_host") }.apply { assertNotNull(cause) } @@ -141,7 +142,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> assertFailsWith { - client.serverSentEvents("$TEST_SERVER/sse/hello") { error("error") } + client.serverSentEvents("$TEST_SERVER/sse/hello") { error("error") } }.apply { assertTrue { cause is IllegalStateException } assertEquals("error", message) @@ -161,7 +162,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { val job: Job suspendCoroutine { cont -> job = launch { - client.serverSentEvents("$TEST_SERVER/sse/hello") { + client.serverSentEvents("$TEST_SERVER/sse/hello") { cont.resume(Unit) awaitCancellation() } @@ -258,7 +259,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } test { client -> - client.sse("$TEST_SERVER/sse/hello?delay=20") { + client.sse("$TEST_SERVER/sse/hello?delay=20") { val result = incoming.single() assertEquals("hello 0", result.event) } @@ -295,7 +296,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> assertFailsWith { - client.sse("$TEST_SERVER/sse/404") {} + client.sse("$TEST_SERVER/sse/404") {} }.apply { assertEquals(HttpStatusCode.NotFound, response?.status) assertEquals("Expected status code 200 but was 404", message) @@ -311,7 +312,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> assertFailsWith { - client.sse("$TEST_SERVER/sse/content-type-text-plain") {} + client.sse("$TEST_SERVER/sse/content-type-text-plain") {} }.apply { assertEquals(HttpStatusCode.OK, response?.status) assertEquals(ContentType.Text.Plain, response?.contentType()) @@ -327,7 +328,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } test { client -> - client.sse("$TEST_SERVER/sse/content_type_with_charset") { + client.sse("$TEST_SERVER/sse/content_type_with_charset") { assertEquals(ContentType.Text.EventStream.withCharset(Charsets.UTF_8), call.response.contentType()) incoming.single().apply { @@ -384,7 +385,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } test { client -> assertFailsWith { - client.sse({ + client.sse({ url("$TEST_SERVER/sse/echo") setBody(body) }) {} @@ -430,4 +431,134 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } } } + + + class Person(val name: String) + class Data(val value: String) + + @Test + fun testDeserializer() = clientTests { + config { + install(SSE) { + deserialize { + Person(it) + } + } + } + + test { client -> + val count = 10 + var size = 0 + client.sse({ + url("$TEST_SERVER/sse/person") + parameter("times", count) + }) { + incoming.collectIndexed { i, person -> + assertEquals("Name $i", person.data?.name) + assertEquals("$i", person.id) + size++ + } + } + assertEquals(count, size) + } + } + + @Test + fun testExceptionIfNoDeserializerProvided() = clientTests { + config { + install(SSE) + } + + test { client -> + assertFailsWith { + client.sse({ url("$TEST_SERVER/sse/person") }) { + incoming.single().apply { + assertEquals("Name 0", data?.name) + } + } + } + } + } + + @Test + fun testExceptionIfWrongDeserializerProvided() = clientTests { + config { + install(SSE) { + deserialize { + Data(it) + } + } + } + + test { client -> + assertFailsWith { + client.sse({ url("$TEST_SERVER/sse/person") }) { + incoming.single().apply { + assertEquals("Name 0", data?.name) + } + } + }.apply { + assertTrue { cause is ClassCastException } + } + } + } + + class Person1(val name: String) + class Person2(val middleName: String) + class Person3(val surname: String) + + @Test + fun testDifferentDeserializers() = clientTests { + config { + install(SSE) { + deserialize { + Person3(it) + } + } + } + + test { client -> + client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { Person1(it) }) { + incoming.single().apply { + assertEquals("Name 0", data?.name) + } + } + client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { Person2(it) }) { + incoming.single().apply { + assertEquals("Name 0", data?.middleName) + } + } + client.sse({ url("$TEST_SERVER/sse/person") }) { + incoming.single().apply { + assertEquals("Name 0", data?.surname) + } + } + } + } + + @Serializable + data class Customer(val id: Int, val firstName: String, val lastName: String) + + @Test + fun testJsonDeserializer() = clientTests { + config { + install(SSE) { + deserialize { + Json.decodeFromString(it) + } + } + } + + test { client -> + client.sse({ + url("$TEST_SERVER/sse/json") + }) { + incoming.single().apply { + assertEquals(1, data?.id) + assertEquals("Jet", data?.firstName) + assertEquals("Brains", data?.lastName) + } + } + } + } } diff --git a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt index ea7a0b810c9..dd134cacdc1 100644 --- a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt +++ b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt @@ -22,9 +22,15 @@ public class ServerSentEvent( public val retry: Long? = null, public val comments: String? = null ) { + @OptIn(InternalAPI::class) override fun toString(): String { + return toString { it.toString() } + } + + @InternalAPI + public fun toString(serializer: (T) -> String): String { return buildString { - appendField("data", data) + appendField("data", data?.let { serializer(it) }) appendField("event", event) appendField("id", id) appendField("retry", retry) From 2291b624c7154c40b5e1b12870ce47ad7ab7e029 Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Wed, 2 Oct 2024 00:11:07 +0200 Subject: [PATCH 03/14] Add server support --- .../ktor-server-sse/build.gradle.kts | 10 ++ .../server/sse/DefaultServerSSESession.kt | 11 ++- .../common/src/io/ktor/server/sse/Routing.kt | 56 +++++++++-- .../common/src/io/ktor/server/sse/SSE.kt | 16 +++- .../src/io/ktor/server/sse/SSEConfig.kt | 19 ++++ .../io/ktor/server/sse/SSEServerContent.kt | 9 +- .../{ServerSSESession.kt => SSESession.kt} | 10 +- .../ktor/server/sse/ServerSentEventsTest.kt | 95 ++++++++++++++++++- 8 files changed, 198 insertions(+), 28 deletions(-) create mode 100644 ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEConfig.kt rename ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/{ServerSSESession.kt => SSESession.kt} (83%) diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/build.gradle.kts b/ktor-server/ktor-server-plugins/ktor-server-sse/build.gradle.kts index 5148437ca11..f1ad9f6b9fc 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/build.gradle.kts +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/build.gradle.kts @@ -1,9 +1,19 @@ description = "Server-sent events (SSE) support" +plugins { + id("kotlinx-serialization") +} + kotlin.sourceSets { commonMain { dependencies { api(project(":ktor-shared:ktor-sse")) } } + commonTest { + dependencies { + api(project(":ktor-shared:ktor-serialization:ktor-serialization-kotlinx")) + api(project(":ktor-shared:ktor-serialization:ktor-serialization-kotlinx:ktor-serialization-kotlinx-json")) + } + } } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt index a0eeeb97ce7..916ff3bba33 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt @@ -10,14 +10,15 @@ import io.ktor.utils.io.* import kotlinx.coroutines.sync.* import kotlin.coroutines.* -internal class DefaultServerSSESession( +internal class DefaultServerSSESession( + private val serializer: (T) -> String, private val output: ByteWriteChannel, override val call: ApplicationCall, override val coroutineContext: CoroutineContext -) : ServerSSESession { +) : SSESession { private val mutex = Mutex() - override suspend fun send(event: ServerSentEvent) { + override suspend fun send(event: ServerSentEvent) { mutex.withLock { output.writeSSE(event) } @@ -30,8 +31,8 @@ internal class DefaultServerSSESession( } @OptIn(InternalAPI::class) - private suspend fun ByteWriteChannel.writeSSE(event: ServerSentEvent) { - writeStringUtf8(event.toString() + END_OF_LINE) + private suspend fun ByteWriteChannel.writeSSE(event: ServerSentEvent) { + writeStringUtf8(event.toString(serializer) + END_OF_LINE) flush() } } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt index 4ed05d3988a..f8edd146468 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt @@ -5,6 +5,7 @@ package io.ktor.server.sse import io.ktor.http.* +import io.ktor.server.application.* import io.ktor.server.response.* import io.ktor.server.routing.* @@ -14,14 +15,12 @@ import io.ktor.server.routing.* * * @param path URL path at which to handle SSE requests. * @param handler function that defines the behavior of the SSE session. It is invoked when a client connects to the SSE - * endpoint. Inside the handler, you can use the functions provided by [ServerSSESession] + * endpoint. Inside the handler, you can use the functions provided by [SSESession] * to send events to the connected clients. * - * @see ServerSSESession + * @see SSESession */ -public fun Route.sse(path: String, handler: suspend ServerSSESession.() -> Unit) { - plugin(SSE) - +public fun Route.sse(path: String, handler: suspend SSESession.() -> Unit) { route(path, HttpMethod.Get) { sse(handler) } @@ -32,12 +31,51 @@ public fun Route.sse(path: String, handler: suspend ServerSSESession.() -> Unit) * Requires [SSE] plugin to be installed. * * @param handler function that defines the behavior of the SSE session. It is invoked when a client connects to the SSE - * endpoint. Inside the handler, you can use the functions provided by [ServerSSESession] + * endpoint. Inside the handler, you can use the functions provided by [SSESession] + * to send events to the connected clients. + * + * @see SSESession + */ +public fun Route.sse(handler: suspend SSESession.() -> Unit) { + val sse = application.plugin(SSE) + + handle { + call.response.header(HttpHeaders.ContentType, ContentType.Text.EventStream.toString()) + call.response.header(HttpHeaders.CacheControl, "no-store") + call.response.header(HttpHeaders.Connection, "keep-alive") + call.response.header("X-Accel-Buffering", "no") + call.respond(SSEServerContent(call, sse.serialize, handler)) + } +} + +/** + * Adds a route to handle Server-Sent Events (SSE) at the specified [path] using the provided [handler]. + * Requires [SSE] plugin to be installed. + * + * @param path URL path at which to handle SSE requests. + * @param handler function that defines the behavior of the SSE session. It is invoked when a client connects to the SSE + * endpoint. Inside the handler, you can use the functions provided by [SSESession] + * to send events to the connected clients. + * + * @see SSESession + */ +public fun Route.sse(path: String, serialize: (T) -> String = { it.toString() }, handler: suspend SSESession.() -> Unit) { + route(path, HttpMethod.Get) { + sse(serialize, handler) + } +} + +/** + * Adds a route to handle Server-Sent Events (SSE) using the provided [handler]. + * Requires [SSE] plugin to be installed. + * + * @param handler function that defines the behavior of the SSE session. It is invoked when a client connects to the SSE + * endpoint. Inside the handler, you can use the functions provided by [SSESession] * to send events to the connected clients. * - * @see ServerSSESession + * @see SSESession */ -public fun Route.sse(handler: suspend ServerSSESession.() -> Unit) { +public fun Route.sse(serialize: (T) -> String = { it.toString() }, handler: suspend SSESession.() -> Unit) { plugin(SSE) handle { @@ -45,6 +83,6 @@ public fun Route.sse(handler: suspend ServerSSESession.() -> Unit) { call.response.header(HttpHeaders.CacheControl, "no-store") call.response.header(HttpHeaders.Connection, "keep-alive") call.response.header("X-Accel-Buffering", "no") - call.respond(SSEServerContent(call, handler)) + call.respond(SSEServerContent(call, serialize, handler)) } } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSE.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSE.kt index efbc9129de6..718d9ac0293 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSE.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSE.kt @@ -5,6 +5,7 @@ package io.ktor.server.sse import io.ktor.server.application.* +import io.ktor.util.* import io.ktor.util.logging.* internal val LOGGER = KtorSimpleLogger("io.ktor.server.plugins.sse.SSE") @@ -25,4 +26,17 @@ internal val LOGGER = KtorSimpleLogger("io.ktor.server.plugins.sse.SSE") * } * ``` */ -public val SSE: ApplicationPlugin = createApplicationPlugin("SSE") {} +public class SSE private constructor( + public val serialize: (Any) -> String, +) { + public companion object Plugin : BaseApplicationPlugin { + override val key: AttributeKey = AttributeKey("SSE") + + override fun install(pipeline: Application, configure: SSEConfig.() -> Unit): SSE { + val config = SSEConfig().also(configure) + with(config) { + return SSE(serialize) + } + } + } +} diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEConfig.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEConfig.kt new file mode 100644 index 00000000000..f874db092c0 --- /dev/null +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEConfig.kt @@ -0,0 +1,19 @@ +/* + * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package io.ktor.server.sse + +import io.ktor.utils.io.* + +@KtorDsl +public class SSEConfig { + internal var serialize: (Any) -> String = { it.toString() } + + /** + * Configures serialization logic for transforming data object into field `data` of `ServerSentEvent`. + */ + public fun serialize(serialize: (T) -> String) { + this.serialize = serialize as (Any) -> String + } +} diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt index 8dd94e6ddf4..1bd5427246b 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt @@ -23,19 +23,20 @@ import kotlinx.coroutines.* * @param call that is starting SSE session. * @param handle function that is started once SSE session created. */ -public class SSEServerContent( +public class SSEServerContent( public val call: ApplicationCall, - public val handle: suspend ServerSSESession.() -> Unit + private val serializer: (T) -> String, + public val handle: suspend SSESession.() -> Unit ) : OutgoingContent.WriteChannelContent() { override val contentType: ContentType = ContentType.Text.EventStream override suspend fun writeTo(channel: ByteWriteChannel) { LOGGER.trace("Starting sse session for ${call.request.uri}") - var session: ServerSSESession? = null + var session: SSESession? = null try { coroutineScope { - session = DefaultServerSSESession(channel, call, coroutineContext) + session = DefaultServerSSESession(serializer, channel, call, coroutineContext) session?.handle() } } finally { diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/ServerSSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt similarity index 83% rename from ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/ServerSSESession.kt rename to ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt index 2d281ee243d..be5bd75ce20 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/ServerSSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt @@ -10,11 +10,11 @@ import kotlinx.coroutines.* /** * Represents a server-side server-sent events session. - * An [ServerSSESession] allows the server to send [ServerSentEvent] to the client over a single HTTP connection. + * An [SSESession] allows the server to send [ServerSentEvent] to the client over a single HTTP connection. * * @see [SSE] */ -public interface ServerSSESession : CoroutineScope { +public interface SSESession : CoroutineScope { /** * Associated received [call] that originating this session. */ @@ -23,7 +23,7 @@ public interface ServerSSESession : CoroutineScope { /** * Sends a [ServerSentEvent] to the client. */ - public suspend fun send(event: ServerSentEvent) + public suspend fun send(event: ServerSentEvent) /** * Creates and sends a [ServerSentEvent] to the client. @@ -35,7 +35,7 @@ public interface ServerSSESession : CoroutineScope { * @param comments comment lines starting with a ':' character. */ public suspend fun send( - data: String? = null, + data: T? = null, event: String? = null, id: String? = null, retry: Long? = null, @@ -45,7 +45,7 @@ public interface ServerSSESession : CoroutineScope { } /** - * Closes the [ServerSSESession], terminating the connection with the client. + * Closes the [SSESession], terminating the connection with the client. * Once this method is called, the SSE session is closed and no further events can be sent. * You don't need to call this method as it is called automatically when all the send operations are completed. * diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt index 3f82c477c78..6981d5f2205 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt @@ -13,6 +13,8 @@ import io.ktor.server.testing.* import io.ktor.sse.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* +import kotlinx.serialization.* +import kotlinx.serialization.json.* import kotlin.test.* class ServerSentEventsTest { @@ -125,11 +127,11 @@ class ServerSentEventsTest { fun testNoDuplicateHeader() = testApplication { install(SSE) routing { - sse { } + sse { } } val client = createSseClient() - client.sse { + client.sse { call.response.headers.forEach { _, values -> assertEquals(1, values.size) } @@ -170,7 +172,7 @@ class ServerSentEventsTest { """.trimIndent() val actualMultilineData = StringBuilder() - client.sse("/multiline-data") { + client.sse("/multiline-data") { incoming.collect { actualMultilineData.append(it.toString()) } @@ -184,7 +186,7 @@ class ServerSentEventsTest { """.trimIndent() val actualOneEventData = StringBuilder() - client.sse("/one-event-data") { + client.sse("/one-event-data") { incoming.collect { actualOneEventData.append(it.toString()) } @@ -192,6 +194,91 @@ class ServerSentEventsTest { assertEquals(expectedOneEventData.lines(), actualOneEventData.toString().lines()) } + class Person(val age: Int) + + @Test + fun testSerializerInRoute() = testApplication { + install(SSE) + routing { + sse("/person", serialize = { "Age ${it.age}" }) { + repeat(10) { + send(Person(it)) + } + } + } + + val client = createSseClient() + + client.sse("/person") { + incoming.collectIndexed { i, person -> + assertEquals("Age $i", person.data) + } + } + } + + class Person1(val age: Int) + class Person2(val number: Int) + class Person3(val index: Int) + + @Test + fun testDifferentSerializers() = testApplication { + install(SSE) { + serialize { person: Person3 -> + "${person.index}" + } + } + routing { + sse("/person1", serialize = { println("1");"${it.age}" }) { + send(Person1(22)) + } + sse("/person2", serialize = { "${it.number}" }) { + send(Person2(123456)) + } + sse("/person3") { + send(Person3(0)) + } + } + + val client = createSseClient() + client.sse("/person1") { + incoming.single().apply { + assertEquals("22", data) + } + } + client.sse("/person2") { + incoming.single().apply { + assertEquals("123456", data) + } + } + client.sse("/person3") { + incoming.single().apply { + assertEquals("0", data) + } + } + } + + @Serializable + data class Customer(val id: Int, val firstName: String, val lastName: String) + + @Test + fun testJsonDeserializer() = testApplication { + install(SSE) { + serialize { + Json.encodeToString(it) + } + } + routing { + sse("/json") { + send(Customer(0, "Jet", "Brains")) + } + } + + assertEquals( + "data: {\"id\":0,\"firstName\":\"Jet\",\"lastName\":\"Brains\"}", + client.get("/json").bodyAsText().trim() + ) + } + private fun ApplicationTestBuilder.createSseClient(): HttpClient { val client = createClient { install(io.ktor.client.plugins.sse.SSE) From b33ccb5d70050ca131d2ca9205fdd541659286fd Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:11:49 +0200 Subject: [PATCH 04/14] Run apiDump --- .../ktor-client-core/api/ktor-client-core.api | 54 +++++++------ .../api/ktor-client-core.klib.api | 79 ++++++++++--------- .../tests/plugins/ServerSentEventsTest.kt | 1 - .../ktor-server-sse/api/ktor-server-sse.api | 33 ++++++-- .../api/ktor-server-sse.klib.api | 43 +++++++--- .../common/src/io/ktor/server/sse/Routing.kt | 8 +- .../ktor/server/sse/ServerSentEventsTest.kt | 2 +- ktor-shared/ktor-sse/api/ktor-sse.api | 7 +- ktor-shared/ktor-sse/api/ktor-sse.klib.api | 7 +- 9 files changed, 139 insertions(+), 95 deletions(-) diff --git a/ktor-client/ktor-client-core/api/ktor-client-core.api b/ktor-client/ktor-client-core/api/ktor-client-core.api index ef6f6b56e03..65bfb5f1212 100644 --- a/ktor-client/ktor-client-core/api/ktor-client-core.api +++ b/ktor-client/ktor-client-core/api/ktor-client-core.api @@ -763,30 +763,30 @@ public final class io/ktor/client/plugins/observer/ResponseObserverKt { public final class io/ktor/client/plugins/sse/BuildersKt { public static final fun SSE (Lio/ktor/client/HttpClientConfig;Lkotlin/jvm/functions/Function1;)V - public static final fun serverSentEvents-1wIb-0I (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEvents-1wIb-0I$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEvents-3bFjkrY (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEvents-3bFjkrY$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEvents-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEvents-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEventsSession-i8z2VEo (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEventsSession-i8z2VEo$default (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEventsSession-mY9Nd3A (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEventsSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEventsSession-xEWcMm4 (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEventsSession-xEWcMm4$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sse-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sse-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sse-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sse-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sse-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sse-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sseSession-i8z2VEo (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sseSession-i8z2VEo$default (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sseSession-mY9Nd3A (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sseSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sseSession-xEWcMm4 (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sseSession-xEWcMm4$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEvents-BqdlHlk (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEvents-BqdlHlk$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEvents-Mswn-_c (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEvents-Mswn-_c$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEvents-pTj2aPc (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEvents-pTj2aPc$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEventsSession-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEventsSession-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEventsSession-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEventsSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEventsSession-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEventsSession-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sse-BAHpl2s (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sse-BAHpl2s$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sse-Mswn-_c (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sse-Mswn-_c$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sse-Q9yt8Vw (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sse-Q9yt8Vw$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sseSession-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sseSession-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sseSession-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sseSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sseSession-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sseSession-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } public final class io/ktor/client/plugins/sse/ClientSSESession : io/ktor/client/plugins/sse/SSESession { @@ -797,7 +797,7 @@ public final class io/ktor/client/plugins/sse/ClientSSESession : io/ktor/client/ } public final class io/ktor/client/plugins/sse/DefaultClientSSESession : io/ktor/client/plugins/sse/SSESession { - public fun (Lio/ktor/client/plugins/sse/SSEClientContent;Lio/ktor/utils/io/ByteReadChannel;Lkotlin/coroutines/CoroutineContext;)V + public fun (Lio/ktor/client/plugins/sse/SSEClientContent;Lkotlin/jvm/functions/Function1;Lio/ktor/utils/io/ByteReadChannel;Lkotlin/coroutines/CoroutineContext;)V public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; public fun getIncoming ()Lkotlinx/coroutines/flow/Flow; } @@ -810,9 +810,10 @@ public final class io/ktor/client/plugins/sse/SSECapability : io/ktor/client/eng } public final class io/ktor/client/plugins/sse/SSEClientContent : io/ktor/http/content/OutgoingContent$ContentWrapper { - public synthetic fun (JZZLio/ktor/http/content/OutgoingContent;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Lkotlin/jvm/functions/Function1;JZZLio/ktor/http/content/OutgoingContent;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public fun copy (Lio/ktor/http/content/OutgoingContent;)Lio/ktor/client/plugins/sse/SSEClientContent; public synthetic fun copy (Lio/ktor/http/content/OutgoingContent;)Lio/ktor/http/content/OutgoingContent$ContentWrapper; + public final fun getDeserializer ()Lkotlin/jvm/functions/Function1; public fun getHeaders ()Lio/ktor/http/Headers; public final fun getReconnectionTime-UwyO8pc ()J public final fun getShowCommentEvents ()Z @@ -831,6 +832,7 @@ public final class io/ktor/client/plugins/sse/SSEClientException : java/lang/Ill public final class io/ktor/client/plugins/sse/SSEConfig { public fun ()V + public final fun deserialize (Lkotlin/jvm/functions/Function1;)V public final fun getReconnectionTime-UwyO8pc ()J public final fun setReconnectionTime-LRDsOJo (J)V public final fun showCommentEvents ()V diff --git a/ktor-client/ktor-client-core/api/ktor-client-core.klib.api b/ktor-client/ktor-client-core/api/ktor-client-core.klib.api index 653b926dd7b..15213562b47 100644 --- a/ktor-client/ktor-client-core/api/ktor-client-core.klib.api +++ b/ktor-client/ktor-client-core/api/ktor-client-core.klib.api @@ -35,6 +35,11 @@ abstract interface <#A: kotlin/Any?> io.ktor.client.plugins.api/ClientHook { // abstract fun install(io.ktor.client/HttpClient, #A) // io.ktor.client.plugins.api/ClientHook.install|install(io.ktor.client.HttpClient;1:0){}[0] } +abstract interface <#A: kotlin/Any?> io.ktor.client.plugins.sse/SSESession : kotlinx.coroutines/CoroutineScope { // io.ktor.client.plugins.sse/SSESession|null[0] + abstract val incoming // io.ktor.client.plugins.sse/SSESession.incoming|{}incoming[0] + abstract fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/SSESession.incoming.|(){}[0] +} + abstract interface <#A: out io.ktor.client.engine/HttpClientEngineConfig> io.ktor.client.engine/HttpClientEngineFactory { // io.ktor.client.engine/HttpClientEngineFactory|null[0] abstract fun create(kotlin/Function1<#A, kotlin/Unit> = ...): io.ktor.client.engine/HttpClientEngine // io.ktor.client.engine/HttpClientEngineFactory.create|create(kotlin.Function1<1:0,kotlin.Unit>){}[0] } @@ -77,11 +82,6 @@ abstract interface io.ktor.client.plugins.cookies/CookiesStorage : io.ktor.utils abstract suspend fun get(io.ktor.http/Url): kotlin.collections/List // io.ktor.client.plugins.cookies/CookiesStorage.get|get(io.ktor.http.Url){}[0] } -abstract interface io.ktor.client.plugins.sse/SSESession : kotlinx.coroutines/CoroutineScope { // io.ktor.client.plugins.sse/SSESession|null[0] - abstract val incoming // io.ktor.client.plugins.sse/SSESession.incoming|{}incoming[0] - abstract fun (): kotlinx.coroutines.flow/Flow // io.ktor.client.plugins.sse/SSESession.incoming.|(){}[0] -} - abstract interface io.ktor.client.plugins.websocket/ClientWebSocketSession : io.ktor.websocket/WebSocketSession { // io.ktor.client.plugins.websocket/ClientWebSocketSession|null[0] abstract val call // io.ktor.client.plugins.websocket/ClientWebSocketSession.call|{}call[0] abstract fun (): io.ktor.client.call/HttpClientCall // io.ktor.client.plugins.websocket/ClientWebSocketSession.call.|(){}[0] @@ -229,6 +229,26 @@ final class <#A: kotlin/Any> io.ktor.client.request.forms/FormPart { // io.ktor. final fun toString(): kotlin/String // io.ktor.client.request.forms/FormPart.toString|toString(){}[0] } +final class <#A: kotlin/Any?> io.ktor.client.plugins.sse/ClientSSESession : io.ktor.client.plugins.sse/SSESession<#A> { // io.ktor.client.plugins.sse/ClientSSESession|null[0] + constructor (io.ktor.client.call/HttpClientCall, io.ktor.client.plugins.sse/SSESession<#A>) // io.ktor.client.plugins.sse/ClientSSESession.|(io.ktor.client.call.HttpClientCall;io.ktor.client.plugins.sse.SSESession<1:0>){}[0] + + final val call // io.ktor.client.plugins.sse/ClientSSESession.call|{}call[0] + final fun (): io.ktor.client.call/HttpClientCall // io.ktor.client.plugins.sse/ClientSSESession.call.|(){}[0] + final val coroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext|{}coroutineContext[0] + final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext.|(){}[0] + final val incoming // io.ktor.client.plugins.sse/ClientSSESession.incoming|{}incoming[0] + final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/ClientSSESession.incoming.|(){}[0] +} + +final class <#A: kotlin/Any?> io.ktor.client.plugins.sse/DefaultClientSSESession : io.ktor.client.plugins.sse/SSESession<#A> { // io.ktor.client.plugins.sse/DefaultClientSSESession|null[0] + constructor (io.ktor.client.plugins.sse/SSEClientContent, kotlin/Function1, io.ktor.utils.io/ByteReadChannel, kotlin.coroutines/CoroutineContext) // io.ktor.client.plugins.sse/DefaultClientSSESession.|(io.ktor.client.plugins.sse.SSEClientContent;kotlin.Function1;io.ktor.utils.io.ByteReadChannel;kotlin.coroutines.CoroutineContext){}[0] + + final val coroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext|{}coroutineContext[0] + final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext.|(){}[0] + final val incoming // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming|{}incoming[0] + final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming.|(){}[0] +} + final class io.ktor.client.call/DoubleReceiveException : kotlin/IllegalStateException { // io.ktor.client.call/DoubleReceiveException|null[0] constructor (io.ktor.client.call/HttpClientCall) // io.ktor.client.call/DoubleReceiveException.|(io.ktor.client.call.HttpClientCall){}[0] @@ -426,29 +446,11 @@ final class io.ktor.client.plugins.observer/ResponseObserverConfig { // io.ktor. final fun onResponse(kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.observer/ResponseObserverConfig.onResponse|onResponse(kotlin.coroutines.SuspendFunction1){}[0] } -final class io.ktor.client.plugins.sse/ClientSSESession : io.ktor.client.plugins.sse/SSESession { // io.ktor.client.plugins.sse/ClientSSESession|null[0] - constructor (io.ktor.client.call/HttpClientCall, io.ktor.client.plugins.sse/SSESession) // io.ktor.client.plugins.sse/ClientSSESession.|(io.ktor.client.call.HttpClientCall;io.ktor.client.plugins.sse.SSESession){}[0] - - final val call // io.ktor.client.plugins.sse/ClientSSESession.call|{}call[0] - final fun (): io.ktor.client.call/HttpClientCall // io.ktor.client.plugins.sse/ClientSSESession.call.|(){}[0] - final val coroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext|{}coroutineContext[0] - final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext.|(){}[0] - final val incoming // io.ktor.client.plugins.sse/ClientSSESession.incoming|{}incoming[0] - final fun (): kotlinx.coroutines.flow/Flow // io.ktor.client.plugins.sse/ClientSSESession.incoming.|(){}[0] -} - -final class io.ktor.client.plugins.sse/DefaultClientSSESession : io.ktor.client.plugins.sse/SSESession { // io.ktor.client.plugins.sse/DefaultClientSSESession|null[0] - constructor (io.ktor.client.plugins.sse/SSEClientContent, io.ktor.utils.io/ByteReadChannel, kotlin.coroutines/CoroutineContext) // io.ktor.client.plugins.sse/DefaultClientSSESession.|(io.ktor.client.plugins.sse.SSEClientContent;io.ktor.utils.io.ByteReadChannel;kotlin.coroutines.CoroutineContext){}[0] - - final val coroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext|{}coroutineContext[0] - final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext.|(){}[0] - final val incoming // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming|{}incoming[0] - final fun (): kotlinx.coroutines.flow/Flow // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming.|(){}[0] -} - final class io.ktor.client.plugins.sse/SSEClientContent : io.ktor.http.content/OutgoingContent.ContentWrapper { // io.ktor.client.plugins.sse/SSEClientContent|null[0] - constructor (kotlin.time/Duration, kotlin/Boolean, kotlin/Boolean, io.ktor.http.content/OutgoingContent) // io.ktor.client.plugins.sse/SSEClientContent.|(kotlin.time.Duration;kotlin.Boolean;kotlin.Boolean;io.ktor.http.content.OutgoingContent){}[0] + constructor (kotlin/Function1, kotlin.time/Duration, kotlin/Boolean, kotlin/Boolean, io.ktor.http.content/OutgoingContent) // io.ktor.client.plugins.sse/SSEClientContent.|(kotlin.Function1;kotlin.time.Duration;kotlin.Boolean;kotlin.Boolean;io.ktor.http.content.OutgoingContent){}[0] + final val deserializer // io.ktor.client.plugins.sse/SSEClientContent.deserializer|{}deserializer[0] + final fun (): kotlin/Function1 // io.ktor.client.plugins.sse/SSEClientContent.deserializer.|(){}[0] final val headers // io.ktor.client.plugins.sse/SSEClientContent.headers|{}headers[0] final fun (): io.ktor.http/Headers // io.ktor.client.plugins.sse/SSEClientContent.headers.|(){}[0] final val reconnectionTime // io.ktor.client.plugins.sse/SSEClientContent.reconnectionTime|{}reconnectionTime[0] @@ -480,6 +482,7 @@ final class io.ktor.client.plugins.sse/SSEConfig { // io.ktor.client.plugins.sse final fun (): kotlin.time/Duration // io.ktor.client.plugins.sse/SSEConfig.reconnectionTime.|(){}[0] final fun (kotlin.time/Duration) // io.ktor.client.plugins.sse/SSEConfig.reconnectionTime.|(kotlin.time.Duration){}[0] + final fun deserialize(kotlin/Function1) // io.ktor.client.plugins.sse/SSEConfig.deserialize|deserialize(kotlin.Function1){}[0] final fun showCommentEvents() // io.ktor.client.plugins.sse/SSEConfig.showCommentEvents|showCommentEvents(){}[0] final fun showRetryEvents() // io.ktor.client.plugins.sse/SSEConfig.showRetryEvents|showRetryEvents(){}[0] } @@ -1402,18 +1405,6 @@ final suspend fun (io.ktor.client.statement/HttpResponse).io.ktor.client.stateme final suspend fun (io.ktor.client.statement/HttpResponse).io.ktor.client.statement/readRawBytes(): kotlin/ByteArray // io.ktor.client.statement/readRawBytes|readRawBytes@io.ktor.client.statement.HttpResponse(){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.cookies/cookies(io.ktor.http/Url): kotlin.collections/List // io.ktor.client.plugins.cookies/cookies|cookies@io.ktor.client.HttpClient(io.ktor.http.Url){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.cookies/cookies(kotlin/String): kotlin.collections/List // io.ktor.client.plugins.cookies/cookies|cookies@io.ktor.client.HttpClient(kotlin.String){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/Function1, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/Function1, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String, kotlin/Function1 = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/webSocket(io.ktor.http/HttpMethod = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/webSocket|webSocket@io.ktor.client.HttpClient(io.ktor.http.HttpMethod;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/webSocket(kotlin/Function1, kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/webSocket|webSocket@io.ktor.client.HttpClient(kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/webSocket(kotlin/String, kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/webSocket|webSocket@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] @@ -1428,6 +1419,18 @@ final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/w final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/wss(kotlin/String, kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/wss|wss@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.request.forms/prepareForm(kotlin/String, io.ktor.http/Parameters = ..., kotlin/Boolean = ..., kotlin/Function1 = ...): io.ktor.client.statement/HttpStatement // io.ktor.client.request.forms/prepareForm|prepareForm@io.ktor.client.HttpClient(kotlin.String;io.ktor.http.Parameters;kotlin.Boolean;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.request.forms/submitForm(kotlin/String, io.ktor.http/Parameters = ..., kotlin/Boolean = ..., kotlin/Function1 = ...): io.ktor.client.statement/HttpResponse // io.ktor.client.request.forms/submitForm|submitForm@io.ktor.client.HttpClient(kotlin.String;io.ktor.http.Parameters;kotlin.Boolean;kotlin.Function1){}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/Function1, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1?, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/Function1, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String, kotlin/Function1 = ..., kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] +final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] final suspend fun <#A: kotlin/Any?> (io.ktor.client.plugins.websocket/DefaultClientWebSocketSession).io.ktor.client.plugins.websocket/receiveDeserialized(io.ktor.util.reflect/TypeInfo): #A // io.ktor.client.plugins.websocket/receiveDeserialized|receiveDeserialized@io.ktor.client.plugins.websocket.DefaultClientWebSocketSession(io.ktor.util.reflect.TypeInfo){0§}[0] final suspend fun <#A: kotlin/Any?> (io.ktor.client.statement/HttpResponse).io.ktor.client.call/body(io.ktor.util.reflect/TypeInfo): #A // io.ktor.client.call/body|body@io.ktor.client.statement.HttpResponse(io.ktor.util.reflect.TypeInfo){0§}[0] final suspend fun io.ktor.client.engine/callContext(): kotlin.coroutines/CoroutineContext // io.ktor.client.engine/callContext|callContext(){}[0] diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt index c0ce667cd20..524c3ebd8da 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt @@ -432,7 +432,6 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } } - class Person(val name: String) class Data(val value: String) diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api index 1cc44ada840..665dd4b923f 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api @@ -1,14 +1,31 @@ public final class io/ktor/server/sse/RoutingKt { + public static final fun sse (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public static final fun sse (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)V + public static final fun sse (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public static final fun sse (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function2;)V + public static synthetic fun sse$default (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V + public static synthetic fun sse$default (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V } -public final class io/ktor/server/sse/SSEKt { - public static final fun getSSE ()Lio/ktor/server/application/ApplicationPlugin; +public final class io/ktor/server/sse/SSE { + public static final field Plugin Lio/ktor/server/sse/SSE$Plugin; + public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun getSerialize ()Lkotlin/jvm/functions/Function1; +} + +public final class io/ktor/server/sse/SSE$Plugin : io/ktor/server/application/BaseApplicationPlugin { + public fun getKey ()Lio/ktor/util/AttributeKey; + public fun install (Lio/ktor/server/application/Application;Lkotlin/jvm/functions/Function1;)Lio/ktor/server/sse/SSE; + public synthetic fun install (Lio/ktor/util/pipeline/Pipeline;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; +} + +public final class io/ktor/server/sse/SSEConfig { + public fun ()V + public final fun serialize (Lkotlin/jvm/functions/Function1;)V } public final class io/ktor/server/sse/SSEServerContent : io/ktor/http/content/OutgoingContent$WriteChannelContent { - public fun (Lio/ktor/server/application/ApplicationCall;Lkotlin/jvm/functions/Function2;)V + public fun (Lio/ktor/server/application/ApplicationCall;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public final fun getCall ()Lio/ktor/server/application/ApplicationCall; public fun getContentType ()Lio/ktor/http/ContentType; public final fun getHandle ()Lkotlin/jvm/functions/Function2; @@ -16,15 +33,15 @@ public final class io/ktor/server/sse/SSEServerContent : io/ktor/http/content/Ou public fun writeTo (Lio/ktor/utils/io/ByteWriteChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } -public abstract interface class io/ktor/server/sse/ServerSSESession : kotlinx/coroutines/CoroutineScope { +public abstract interface class io/ktor/server/sse/SSESession : kotlinx/coroutines/CoroutineScope { public abstract fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun getCall ()Lio/ktor/server/application/ApplicationCall; public abstract fun send (Lio/ktor/sse/ServerSentEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun send (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun send (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } -public final class io/ktor/server/sse/ServerSSESession$DefaultImpls { - public static fun send (Lio/ktor/server/sse/ServerSSESession;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun send$default (Lio/ktor/server/sse/ServerSSESession;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +public final class io/ktor/server/sse/SSESession$DefaultImpls { + public static fun send (Lio/ktor/server/sse/SSESession;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun send$default (Lio/ktor/server/sse/SSESession;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api index 341a6ddedb3..862e5edcd7b 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api @@ -6,31 +6,48 @@ // - Show declarations: true // Library unique name: -abstract interface io.ktor.server.sse/ServerSSESession : kotlinx.coroutines/CoroutineScope { // io.ktor.server.sse/ServerSSESession|null[0] - abstract val call // io.ktor.server.sse/ServerSSESession.call|{}call[0] - abstract fun (): io.ktor.server.application/ApplicationCall // io.ktor.server.sse/ServerSSESession.call.|(){}[0] +abstract interface <#A: kotlin/Any?> io.ktor.server.sse/SSESession : kotlinx.coroutines/CoroutineScope { // io.ktor.server.sse/SSESession|null[0] + abstract val call // io.ktor.server.sse/SSESession.call|{}call[0] + abstract fun (): io.ktor.server.application/ApplicationCall // io.ktor.server.sse/SSESession.call.|(){}[0] - abstract suspend fun close() // io.ktor.server.sse/ServerSSESession.close|close(){}[0] - abstract suspend fun send(io.ktor.sse/ServerSentEvent) // io.ktor.server.sse/ServerSSESession.send|send(io.ktor.sse.ServerSentEvent){}[0] - open suspend fun send(kotlin/String? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/ServerSSESession.send|send(kotlin.String?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] + abstract suspend fun close() // io.ktor.server.sse/SSESession.close|close(){}[0] + abstract suspend fun send(io.ktor.sse/ServerSentEvent<#A>) // io.ktor.server.sse/SSESession.send|send(io.ktor.sse.ServerSentEvent<1:0>){}[0] + open suspend fun send(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/SSESession.send|send(1:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] } -final class io.ktor.server.sse/SSEServerContent : io.ktor.http.content/OutgoingContent.WriteChannelContent { // io.ktor.server.sse/SSEServerContent|null[0] - constructor (io.ktor.server.application/ApplicationCall, kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/SSEServerContent.|(io.ktor.server.application.ApplicationCall;kotlin.coroutines.SuspendFunction1){}[0] +final class <#A: kotlin/Any?> io.ktor.server.sse/SSEServerContent : io.ktor.http.content/OutgoingContent.WriteChannelContent { // io.ktor.server.sse/SSEServerContent|null[0] + constructor (io.ktor.server.application/ApplicationCall, kotlin/Function1<#A, kotlin/String>, kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/SSEServerContent.|(io.ktor.server.application.ApplicationCall;kotlin.Function1<1:0,kotlin.String>;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){}[0] final val call // io.ktor.server.sse/SSEServerContent.call|{}call[0] final fun (): io.ktor.server.application/ApplicationCall // io.ktor.server.sse/SSEServerContent.call.|(){}[0] final val contentType // io.ktor.server.sse/SSEServerContent.contentType|{}contentType[0] final fun (): io.ktor.http/ContentType // io.ktor.server.sse/SSEServerContent.contentType.|(){}[0] final val handle // io.ktor.server.sse/SSEServerContent.handle|{}handle[0] - final fun (): kotlin.coroutines/SuspendFunction1 // io.ktor.server.sse/SSEServerContent.handle.|(){}[0] + final fun (): kotlin.coroutines/SuspendFunction1, kotlin/Unit> // io.ktor.server.sse/SSEServerContent.handle.|(){}[0] final fun toString(): kotlin/String // io.ktor.server.sse/SSEServerContent.toString|toString(){}[0] final suspend fun writeTo(io.ktor.utils.io/ByteWriteChannel) // io.ktor.server.sse/SSEServerContent.writeTo|writeTo(io.ktor.utils.io.ByteWriteChannel){}[0] } -final val io.ktor.server.sse/SSE // io.ktor.server.sse/SSE|{}SSE[0] - final fun (): io.ktor.server.application/ApplicationPlugin // io.ktor.server.sse/SSE.|(){}[0] +final class io.ktor.server.sse/SSE { // io.ktor.server.sse/SSE|null[0] + final val serialize // io.ktor.server.sse/SSE.serialize|{}serialize[0] + final fun (): kotlin/Function1 // io.ktor.server.sse/SSE.serialize.|(){}[0] -final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.coroutines.SuspendFunction1){}[0] -final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.coroutines.SuspendFunction1){}[0] + final object Plugin : io.ktor.server.application/BaseApplicationPlugin { // io.ktor.server.sse/SSE.Plugin|null[0] + final val key // io.ktor.server.sse/SSE.Plugin.key|{}key[0] + final fun (): io.ktor.util/AttributeKey // io.ktor.server.sse/SSE.Plugin.key.|(){}[0] + + final fun install(io.ktor.server.application/Application, kotlin/Function1): io.ktor.server.sse/SSE // io.ktor.server.sse/SSE.Plugin.install|install(io.ktor.server.application.Application;kotlin.Function1){}[0] + } +} + +final class io.ktor.server.sse/SSEConfig { // io.ktor.server.sse/SSEConfig|null[0] + constructor () // io.ktor.server.sse/SSEConfig.|(){}[0] + + final fun <#A1: kotlin/Any> serialize(kotlin/Function1<#A1, kotlin/String>) // io.ktor.server.sse/SSEConfig.serialize|serialize(kotlin.Function1<0:0,kotlin.String>){0§}[0] +} + +final fun <#A: kotlin/Any> (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final fun <#A: kotlin/Any> (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final fun <#A: kotlin/Any?> (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/Function1<#A, kotlin/String> = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.Function1<0:0,kotlin.String>;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final fun <#A: kotlin/Any?> (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin/Function1<#A, kotlin/String> = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.Function1<0:0,kotlin.String>;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt index f8edd146468..8669344b470 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt @@ -20,7 +20,7 @@ import io.ktor.server.routing.* * * @see SSESession */ -public fun Route.sse(path: String, handler: suspend SSESession.() -> Unit) { +public fun Route.sse(path: String, handler: suspend SSESession.() -> Unit) { route(path, HttpMethod.Get) { sse(handler) } @@ -59,7 +59,11 @@ public fun Route.sse(handler: suspend SSESession.() -> Unit) { * * @see SSESession */ -public fun Route.sse(path: String, serialize: (T) -> String = { it.toString() }, handler: suspend SSESession.() -> Unit) { +public fun Route.sse( + path: String, + serialize: (T) -> String = { it.toString() }, + handler: suspend SSESession.() -> Unit +) { route(path, HttpMethod.Get) { sse(serialize, handler) } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt index 6981d5f2205..b93d1043866 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt @@ -228,7 +228,7 @@ class ServerSentEventsTest { } } routing { - sse("/person1", serialize = { println("1");"${it.age}" }) { + sse("/person1", serialize = { println("1"); "${it.age}" }) { send(Person1(22)) } sse("/person2", serialize = { "${it.number}" }) { diff --git a/ktor-shared/ktor-sse/api/ktor-sse.api b/ktor-shared/ktor-sse/api/ktor-sse.api index 4d9751075f9..a41d3ee3ad8 100644 --- a/ktor-shared/ktor-sse/api/ktor-sse.api +++ b/ktor-shared/ktor-sse/api/ktor-sse.api @@ -1,13 +1,14 @@ public final class io/ktor/sse/ServerSentEvent { public fun ()V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun getComments ()Ljava/lang/String; - public final fun getData ()Ljava/lang/String; + public final fun getData ()Ljava/lang/Object; public final fun getEvent ()Ljava/lang/String; public final fun getId ()Ljava/lang/String; public final fun getRetry ()Ljava/lang/Long; public fun toString ()Ljava/lang/String; + public final fun toString (Lkotlin/jvm/functions/Function1;)Ljava/lang/String; } public final class io/ktor/sse/ServerSentEventKt { diff --git a/ktor-shared/ktor-sse/api/ktor-sse.klib.api b/ktor-shared/ktor-sse/api/ktor-sse.klib.api index 7c47153e249..5d8b34c7894 100644 --- a/ktor-shared/ktor-sse/api/ktor-sse.klib.api +++ b/ktor-shared/ktor-sse/api/ktor-sse.klib.api @@ -6,13 +6,13 @@ // - Show declarations: true // Library unique name: -final class io.ktor.sse/ServerSentEvent { // io.ktor.sse/ServerSentEvent|null[0] - constructor (kotlin/String? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.sse/ServerSentEvent.|(kotlin.String?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] +final class <#A: kotlin/Any?> io.ktor.sse/ServerSentEvent { // io.ktor.sse/ServerSentEvent|null[0] + constructor (#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.sse/ServerSentEvent.|(1:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] final val comments // io.ktor.sse/ServerSentEvent.comments|{}comments[0] final fun (): kotlin/String? // io.ktor.sse/ServerSentEvent.comments.|(){}[0] final val data // io.ktor.sse/ServerSentEvent.data|{}data[0] - final fun (): kotlin/String? // io.ktor.sse/ServerSentEvent.data.|(){}[0] + final fun (): #A? // io.ktor.sse/ServerSentEvent.data.|(){}[0] final val event // io.ktor.sse/ServerSentEvent.event|{}event[0] final fun (): kotlin/String? // io.ktor.sse/ServerSentEvent.event.|(){}[0] final val id // io.ktor.sse/ServerSentEvent.id|{}id[0] @@ -21,6 +21,7 @@ final class io.ktor.sse/ServerSentEvent { // io.ktor.sse/ServerSentEvent|null[0] final fun (): kotlin/Long? // io.ktor.sse/ServerSentEvent.retry.|(){}[0] final fun toString(): kotlin/String // io.ktor.sse/ServerSentEvent.toString|toString(){}[0] + final fun toString(kotlin/Function1<#A, kotlin/String>): kotlin/String // io.ktor.sse/ServerSentEvent.toString|toString(kotlin.Function1<1:0,kotlin.String>){}[0] } final const val io.ktor.sse/COLON // io.ktor.sse/COLON|{}COLON[0] From 3831ba9e2d3a1127fd19471baaa0d265f2167a6d Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Wed, 2 Oct 2024 16:50:23 +0200 Subject: [PATCH 05/14] Disable tests --- .../io/ktor/client/tests/plugins/ServerSentEventsTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt index 524c3ebd8da..f037a92e502 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt @@ -463,7 +463,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } @Test - fun testExceptionIfNoDeserializerProvided() = clientTests { + fun testExceptionIfNoDeserializerProvided() = clientTests(listOf("Curl", "Darwin", "DarwinLegacy", "Js")) { config { install(SSE) } @@ -480,7 +480,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } @Test - fun testExceptionIfWrongDeserializerProvided() = clientTests { + fun testExceptionIfWrongDeserializerProvided() = clientTests(listOf("Curl", "Darwin", "DarwinLegacy", "Js")) { config { install(SSE) { deserialize { @@ -507,7 +507,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { class Person3(val surname: String) @Test - fun testDifferentDeserializers() = clientTests { + fun testDifferentDeserializers() = clientTests(listOf("Js")) { config { install(SSE) { deserialize { @@ -539,7 +539,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { data class Customer(val id: Int, val firstName: String, val lastName: String) @Test - fun testJsonDeserializer() = clientTests { + fun testJsonDeserializer() = clientTests(listOf("Js")) { config { install(SSE) { deserialize { From 5d23f8932ca94432e1150a758ade3df51731e1cd Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Fri, 4 Oct 2024 17:31:07 +0200 Subject: [PATCH 06/14] Fix client side --- .../test/server/tests/ServerSentEvents.kt | 14 +- .../client/plugins/sse/ClientSSESession.kt | 51 ++- .../plugins/sse/DefaultClientSSESession.kt | 17 +- .../src/io/ktor/client/plugins/sse/SSE.kt | 15 +- .../client/plugins/sse/SSEClientContent.kt | 3 +- .../io/ktor/client/plugins/sse/SSEConfig.kt | 8 - .../io/ktor/client/plugins/sse/builders.kt | 374 ++++++++++++++---- .../src/io/ktor/client/request/HttpRequest.kt | 1 - .../ktor/client/engine/okhttp/OkHttpEngine.kt | 9 +- .../client/engine/okhttp/OkHttpSSESession.kt | 11 +- .../tests/plugins/SSESessionParserTest.kt | Bin 8368 -> 8056 bytes .../tests/plugins/ServerSentEventsTest.kt | 137 +++---- .../common/src/io/ktor/sse/ServerSentEvent.kt | 2 +- 13 files changed, 435 insertions(+), 207 deletions(-) diff --git a/buildSrc/src/main/kotlin/test/server/tests/ServerSentEvents.kt b/buildSrc/src/main/kotlin/test/server/tests/ServerSentEvents.kt index d5720ffc4cd..002a106bc99 100644 --- a/buildSrc/src/main/kotlin/test/server/tests/ServerSentEvents.kt +++ b/buildSrc/src/main/kotlin/test/server/tests/ServerSentEvents.kt @@ -103,13 +103,19 @@ internal fun Application.serverSentEvents() { } get("/json") { - val data = """{ "id": 1, - "firstName": "Jet", - "lastName": "Brains" + val customer = """ + { "id": 1, + "firstName": "Jet", + "lastName": "Brains" + }""".trimIndent() + val product = """ + { "name": "Milk", + "price": "100" }""".trimIndent() call.respondSseEvents( flow { - emit(SseEvent(data = data)) + emit(SseEvent(data = customer)) + emit(SseEvent(data = product)) } ) } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt index 0eaf2c92e4f..e552b3030c7 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt @@ -6,17 +6,52 @@ package io.ktor.client.plugins.sse import io.ktor.client.call.* import io.ktor.sse.* +import io.ktor.util.reflect.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.* /** * A Server-sent events session. */ -public interface SSESession : CoroutineScope { +public interface SSESession : CoroutineScope { /** * An incoming server-sent events flow. */ - public val incoming: Flow> + public val incoming: Flow> +} + +/** + * A Server-sent events session. + */ +public interface SSESessionWithDeserialization : SSESession { + /** + * Deserializer for transforming field `data` of `ServerSentEvent` into desired data object. + */ + public val deserializer: (TypeInfo) -> (String) -> Any +} + +/** + * Deserialize the provided [data] into an object of type [T] using the deserializer function + * defined in the [SSESessionWithDeserialization] interface. + * + * @param data The string data to deserialize. + * @return The deserialized object of type [T], or null if deserialization is not successful. + */ +public inline fun SSESessionWithDeserialization.deserialize(data: String?): T? { + return data?.let { + deserializer(typeInfo()).invoke(data) as? T + } +} + +/** + * Deserialize the provided [event] data into an object of type [T] using the deserializer function + * defined in the [SSESessionWithDeserialization] interface. + * + * @param event The Server-sent event containing data to deserialize. + * @return The deserialized object of type [T], or null if deserialization is not successful. + */ +public inline fun SSESessionWithDeserialization.deserialize(event: ServerSentEvent): T? { + return deserialize(event.data) } /** @@ -24,4 +59,14 @@ public interface SSESession : CoroutineScope { * * @property call associated with session. */ -public class ClientSSESession(public val call: HttpClientCall, delegate: SSESession) : SSESession by delegate +public class ClientSSESession(public val call: HttpClientCall, delegate: SSESession) : SSESession by delegate + +/** + * A client Server-sent events session with deserialization support. + * + * @property call associated with session. + */ +public class ClientSSESessionWithDeserialization( + public val call: HttpClientCall, + delegate: SSESessionWithDeserialization +) : SSESessionWithDeserialization by delegate diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt index e6979d0cca2..ddf6eaf4ccc 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt @@ -10,12 +10,11 @@ import kotlinx.coroutines.flow.* import kotlin.coroutines.* @OptIn(InternalAPI::class) -public class DefaultClientSSESession( +public class DefaultClientSSESession( content: SSEClientContent, - private val deserializer: (String) -> T, private var input: ByteReadChannel, override val coroutineContext: CoroutineContext, -) : SSESession { +) : SSESession { private var lastEventId: String? = null private var reconnectionTimeMillis = content.reconnectionTime.inWholeMilliseconds private val showCommentEvents = content.showCommentEvents @@ -32,10 +31,10 @@ public class DefaultClientSSESession( } } - override val incoming: Flow> + override val incoming: Flow> get() = _incoming - private suspend fun ByteReadChannel.parseEvent(): ServerSentEvent? { + private suspend fun ByteReadChannel.parseEvent(): ServerSentEvent? { val data = StringBuilder() val comments = StringBuilder() var eventType: String? = null @@ -56,7 +55,7 @@ public class DefaultClientSSESession( this@DefaultClientSSESession.lastEventId = lastEventId val event = ServerSentEvent( - if (wasData) deserializer(data.toText()) else null, + if (wasData) data.toText() else null, eventType, lastEventId, curRetry, @@ -106,13 +105,13 @@ public class DefaultClientSSESession( private fun StringBuilder.toText() = toString().removeSuffix(END_OF_LINE) - private fun ServerSentEvent.isEmpty() = + private fun ServerSentEvent.isEmpty() = data == null && id == null && event == null && retry == null && comments == null - private fun ServerSentEvent.isCommentsEvent() = + private fun ServerSentEvent.isCommentsEvent() = data == null && event == null && id == null && retry == null && comments != null - private fun ServerSentEvent.isRetryEvent() = + private fun ServerSentEvent.isRetryEvent() = data == null && event == null && id == null && comments == null && retry != null } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt index 5e56b929c06..921beccf499 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt @@ -14,6 +14,7 @@ import io.ktor.http.content.* import io.ktor.util.* import io.ktor.util.logging.* import io.ktor.util.pipeline.* +import io.ktor.util.reflect.* import io.ktor.utils.io.* internal val LOGGER = KtorSimpleLogger("io.ktor.client.plugins.sse.SSE") @@ -41,7 +42,6 @@ public val SSE: ClientPlugin = createClientPlugin( name = "SSE", createConfiguration = ::SSEConfig ) { - val deserializer = pluginConfig.deserialize val reconnectionTime = pluginConfig.reconnectionTime val showCommentEvents = pluginConfig.showCommentEvents val showRetryEvents = pluginConfig.showRetryEvents @@ -53,7 +53,6 @@ public val SSE: ClientPlugin = createClientPlugin( LOGGER.trace("Sending SSE request ${request.url}") request.setCapability(SSECapability, Unit) - val localDeserializer = getAttributeValue(request, deserializerAttr) val localReconnectionTime = getAttributeValue(request, reconnectionTimeAttr) val localShowCommentEvents = getAttributeValue(request, showCommentEventsAttr) val localShowRetryEvents = getAttributeValue(request, showRetryEventsAttr) @@ -61,7 +60,6 @@ public val SSE: ClientPlugin = createClientPlugin( request.attributes.put(ResponseAdapterAttributeKey, SSEClientResponseAdapter()) content.contentType?.let { request.contentType(it) } SSEClientContent( - localDeserializer ?: deserializer, localReconnectionTime ?: reconnectionTime, localShowCommentEvents ?: showCommentEvents, localShowRetryEvents ?: showRetryEvents, @@ -91,7 +89,7 @@ public val SSE: ClientPlugin = createClientPlugin( message = "Expected Content-Type ${ContentType.Text.EventStream} but was $contentType" ) } - if (session !is SSESession<*>) { + if (session !is SSESession) { throw SSEClientException( response, message = "Expected ${SSESession::class.simpleName} content but was $session" @@ -99,7 +97,14 @@ public val SSE: ClientPlugin = createClientPlugin( } LOGGER.trace("Receive SSE session from ${response.request.url}: $session") - proceedWith(HttpResponseContainer(info, ClientSSESession(context, session))) + + val deserializer = response.request.attributes.getOrNull(deserializerAttr) + val clientSSESession = deserializer?.let { + ClientSSESessionWithDeserialization(context, object : SSESessionWithDeserialization, SSESession by session { + override val deserializer: (TypeInfo) -> (String) -> Any = deserializer + }) + } ?: ClientSSESession(context, session) + proceedWith(HttpResponseContainer(info, clientSSESession)) } } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEClientContent.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEClientContent.kt index b43fc0cf78a..4cd67aa3a5f 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEClientContent.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEClientContent.kt @@ -11,7 +11,6 @@ import kotlin.time.* @InternalAPI public class SSEClientContent( - public val deserializer: (String) -> Any, public val reconnectionTime: Duration, public val showCommentEvents: Boolean, public val showRetryEvents: Boolean, @@ -28,6 +27,6 @@ public class SSEClientContent( override fun toString(): String = "SSEClientContent" override fun copy(delegate: OutgoingContent): SSEClientContent { - return SSEClientContent(deserializer, reconnectionTime, showCommentEvents, showRetryEvents, delegate) + return SSEClientContent(reconnectionTime, showCommentEvents, showRetryEvents, delegate) } } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEConfig.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEConfig.kt index 6bd1f27491b..b84098f4f8d 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEConfig.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSEConfig.kt @@ -13,7 +13,6 @@ import kotlin.time.Duration.Companion.milliseconds public class SSEConfig { internal var showCommentEvents = false internal var showRetryEvents = false - internal var deserialize: (String) -> Any = { s -> s } /** * The reconnection time. If the connection to the server is lost, @@ -36,11 +35,4 @@ public class SSEConfig { public fun showRetryEvents() { showRetryEvents = true } - - /** - * Configures deserialization logic for transforming field `data` of `ServerSentEvent` into desired data object. - */ - public fun deserialize(deserialize: (String) -> Any) { - this.deserialize = deserialize - } } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt index 253913d81bd..07ecc22b88b 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt @@ -10,6 +10,7 @@ import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.util.* +import io.ktor.util.reflect.* import kotlinx.coroutines.* import kotlin.time.* @@ -17,7 +18,7 @@ internal val sseRequestAttr = AttributeKey("SSERequestFlag") internal val reconnectionTimeAttr = AttributeKey("SSEReconnectionTime") internal val showCommentEventsAttr = AttributeKey("SSEShowCommentEvents") internal val showRetryEventsAttr = AttributeKey("SSEShowRetryEvents") -internal val deserializerAttr = AttributeKey<(String) -> Any>("SSEDeserializer") +internal val deserializerAttr = AttributeKey<(TypeInfo) -> (String) -> Any>("SSEDeserializer") /** * Installs the [SSE] plugin using the [config] as configuration. @@ -28,56 +29,31 @@ public fun HttpClientConfig<*>.SSE(config: SSEConfig.() -> Unit) { } } +// Builders for the `ClientSSESession` + /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEventsSession( - deserialize: ((String) -> T)? = null, +public suspend fun HttpClient.serverSentEventsSession( reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit -): ClientSSESession { - plugin(SSE) - - val sessionDeferred = CompletableDeferred>() - val statement = prepareRequest { - block() - addAttribute(sseRequestAttr, true) - addAttribute(deserializerAttr, deserialize) - addAttribute(reconnectionTimeAttr, reconnectionTime) - addAttribute(showCommentEventsAttr, showCommentEvents) - addAttribute(showRetryEventsAttr, showRetryEvents) - } - @Suppress("SuspendFunctionOnCoroutineScope") - launch { - try { - statement.body, Unit> { session -> - sessionDeferred.complete(session) - } - } catch (cause: CancellationException) { - sessionDeferred.cancel(cause) - } catch (cause: Throwable) { - sessionDeferred.completeExceptionally(mapToSSEException(response = null, cause)) - } - } - return sessionDeferred.await() -} +): ClientSSESession = processSession(reconnectionTime, showCommentEvents, showRetryEvents, block) {} /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEventsSession( +public suspend fun HttpClient.serverSentEventsSession( scheme: String? = null, host: String? = null, port: Int? = null, path: String? = null, - deserialize: ((String) -> T)?, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit = {} -): ClientSSESession = serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents) { +): ClientSSESession = serverSentEventsSession(reconnectionTime, showCommentEvents, showRetryEvents) { url(scheme, host, port, path) block() } @@ -85,14 +61,13 @@ public suspend fun HttpClient.serverSentEventsSession( /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEventsSession( +public suspend fun HttpClient.serverSentEventsSession( urlString: String, - deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit = {} -): ClientSSESession = serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents) { +): ClientSSESession = serverSentEventsSession(reconnectionTime, showCommentEvents, showRetryEvents) { url.takeFrom(urlString) block() } @@ -100,15 +75,14 @@ public suspend fun HttpClient.serverSentEventsSession( /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEvents( +public suspend fun HttpClient.serverSentEvents( request: HttpRequestBuilder.() -> Unit, - deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, - block: suspend ClientSSESession.() -> Unit + block: suspend ClientSSESession.() -> Unit ) { - val session = serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents, request) + val session = serverSentEventsSession(reconnectionTime, showCommentEvents, showRetryEvents, request) try { block(session) } catch (cause: CancellationException) { @@ -123,24 +97,22 @@ public suspend fun HttpClient.serverSentEvents( /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEvents( +public suspend fun HttpClient.serverSentEvents( scheme: String? = null, host: String? = null, port: Int? = null, path: String? = null, - deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, request: HttpRequestBuilder.() -> Unit = {}, - block: suspend ClientSSESession.() -> Unit + block: suspend ClientSSESession.() -> Unit ) { serverSentEvents( { url(scheme, host, port, path) request() }, - deserialize, reconnectionTime, showCommentEvents, showRetryEvents, @@ -151,21 +123,19 @@ public suspend fun HttpClient.serverSentEvents( /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.serverSentEvents( +public suspend fun HttpClient.serverSentEvents( urlString: String, - deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, request: HttpRequestBuilder.() -> Unit = {}, - block: suspend ClientSSESession.() -> Unit + block: suspend ClientSSESession.() -> Unit ) { serverSentEvents( { url.takeFrom(urlString) request() }, - deserialize, reconnectionTime, showCommentEvents, showRetryEvents, @@ -176,107 +146,341 @@ public suspend fun HttpClient.serverSentEvents( /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.sseSession( - deserialize: ((String) -> T)? = null, +public suspend fun HttpClient.sseSession( reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit -): ClientSSESession = - serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents, block) +): ClientSSESession = serverSentEventsSession(reconnectionTime, showCommentEvents, showRetryEvents, block) /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.sseSession( +public suspend fun HttpClient.sseSession( scheme: String? = null, host: String? = null, port: Int? = null, path: String? = null, - deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit = {} -): ClientSSESession = - serverSentEventsSession( - scheme, - host, - port, - path, - deserialize, - reconnectionTime, - showCommentEvents, - showRetryEvents, - block - ) +): ClientSSESession = + serverSentEventsSession(scheme, host, port, path, reconnectionTime, showCommentEvents, showRetryEvents, block) /** * Opens a [ClientSSESession]. */ -public suspend fun HttpClient.sseSession( +public suspend fun HttpClient.sseSession( urlString: String, - deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, block: HttpRequestBuilder.() -> Unit = {} -): ClientSSESession = - serverSentEventsSession(urlString, deserialize, reconnectionTime, showCommentEvents, showRetryEvents, block) +): ClientSSESession = serverSentEventsSession(urlString, reconnectionTime, showCommentEvents, showRetryEvents, block) /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.sse( +public suspend fun HttpClient.sse( request: HttpRequestBuilder.() -> Unit, - deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, - block: suspend ClientSSESession.() -> Unit -): Unit = serverSentEvents(request, deserialize, reconnectionTime, showCommentEvents, showRetryEvents, block) + block: suspend ClientSSESession.() -> Unit +): Unit = serverSentEvents(request, reconnectionTime, showCommentEvents, showRetryEvents, block) /** * Opens a [block] with [ClientSSESession]. */ -public suspend fun HttpClient.sse( +public suspend fun HttpClient.sse( scheme: String? = null, host: String? = null, port: Int? = null, path: String? = null, request: HttpRequestBuilder.() -> Unit = {}, - deserialize: ((String) -> T)? = null, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, - block: suspend ClientSSESession.() -> Unit + block: suspend ClientSSESession.() -> Unit ): Unit = + serverSentEvents(scheme, host, port, path, reconnectionTime, showCommentEvents, showRetryEvents, request, block) + +/** + * Opens a [block] with [ClientSSESession]. + */ +public suspend fun HttpClient.sse( + urlString: String, + request: HttpRequestBuilder.() -> Unit = {}, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: suspend ClientSSESession.() -> Unit +): Unit = serverSentEvents(urlString, reconnectionTime, showCommentEvents, showRetryEvents, request, block) + +// Builders for the `ClientSSESessionWithDeserialization` + +/** + * Opens a [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.serverSentEventsSession( + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: HttpRequestBuilder.() -> Unit +): ClientSSESessionWithDeserialization = processSession(reconnectionTime, showCommentEvents, showRetryEvents, block) { + addAttribute( + deserializerAttr, deserialize + ) +} + +/** + * Opens a [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.serverSentEventsSession( + scheme: String? = null, + host: String? = null, + port: Int? = null, + path: String? = null, + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: HttpRequestBuilder.() -> Unit = {} +): ClientSSESessionWithDeserialization = + serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents) { + url(scheme, host, port, path) + block() + } + +/** + * Opens a [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.serverSentEventsSession( + urlString: String, + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: HttpRequestBuilder.() -> Unit = {} +): ClientSSESessionWithDeserialization = + serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents) { + url.takeFrom(urlString) + block() + } + +/** + * Opens a [block] with [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.serverSentEvents( + request: HttpRequestBuilder.() -> Unit, + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: suspend ClientSSESessionWithDeserialization.() -> Unit +) { + val session = serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents, request) + try { + block(session) + } catch (cause: CancellationException) { + throw cause + } catch (cause: Throwable) { + throw mapToSSEException(session.call.response, cause) + } finally { + session.cancel() + } +} + +/** + * Opens a [block] with [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.serverSentEvents( + scheme: String? = null, + host: String? = null, + port: Int? = null, + path: String? = null, + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + request: HttpRequestBuilder.() -> Unit = {}, + block: suspend ClientSSESessionWithDeserialization.() -> Unit +) { serverSentEvents( - scheme, - host, - port, - path, + { + url(scheme, host, port, path) + request() + }, deserialize, reconnectionTime, showCommentEvents, showRetryEvents, - request, block ) +} /** - * Opens a [block] with [ClientSSESession]. + * Opens a [block] with [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.serverSentEvents( + urlString: String, + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + request: HttpRequestBuilder.() -> Unit = {}, + block: suspend ClientSSESessionWithDeserialization.() -> Unit +) { + serverSentEvents( + { + url.takeFrom(urlString) + request() + }, + deserialize, + reconnectionTime, + showCommentEvents, + showRetryEvents, + block + ) +} + +/** + * Opens a [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.sseSession( + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: HttpRequestBuilder.() -> Unit +): ClientSSESessionWithDeserialization = + serverSentEventsSession(deserialize, reconnectionTime, showCommentEvents, showRetryEvents, block) + +/** + * Opens a [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.sseSession( + scheme: String? = null, + host: String? = null, + port: Int? = null, + path: String? = null, + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: HttpRequestBuilder.() -> Unit = {} +): ClientSSESessionWithDeserialization = serverSentEventsSession( + scheme, + host, + port, + path, + deserialize, + reconnectionTime, + showCommentEvents, + showRetryEvents, + block +) + +/** + * Opens a [ClientSSESessionWithDeserialization]. */ -public suspend fun HttpClient.sse( +public suspend fun HttpClient.sseSession( urlString: String, + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: HttpRequestBuilder.() -> Unit = {} +): ClientSSESessionWithDeserialization = + serverSentEventsSession(urlString, deserialize, reconnectionTime, showCommentEvents, showRetryEvents, block) + +/** + * Opens a [block] with [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.sse( + request: HttpRequestBuilder.() -> Unit, + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: suspend ClientSSESessionWithDeserialization.() -> Unit +): Unit = serverSentEvents(request, deserialize, reconnectionTime, showCommentEvents, showRetryEvents, block) + +/** + * Opens a [block] with [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.sse( + scheme: String? = null, + host: String? = null, + port: Int? = null, + path: String? = null, request: HttpRequestBuilder.() -> Unit = {}, - deserialize: ((String) -> T)? = null, + deserialize: (TypeInfo) -> (String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, - block: suspend ClientSSESession.() -> Unit + block: suspend ClientSSESessionWithDeserialization.() -> Unit +): Unit = serverSentEvents( + scheme, + host, + port, + path, + deserialize, + reconnectionTime, + showCommentEvents, + showRetryEvents, + request, + block +) + +/** + * Opens a [block] with [ClientSSESessionWithDeserialization]. + */ +public suspend fun HttpClient.sse( + urlString: String, + request: HttpRequestBuilder.() -> Unit = {}, + deserialize: (TypeInfo) -> (String) -> Any, + reconnectionTime: Duration? = null, + showCommentEvents: Boolean? = null, + showRetryEvents: Boolean? = null, + block: suspend ClientSSESessionWithDeserialization.() -> Unit ): Unit = serverSentEvents(urlString, deserialize, reconnectionTime, showCommentEvents, showRetryEvents, request, block) +private suspend inline fun HttpClient.processSession( + reconnectionTime: Duration?, + showCommentEvents: Boolean?, + showRetryEvents: Boolean?, + block: HttpRequestBuilder.() -> Unit, + additionalAttributes: HttpRequestBuilder.() -> Unit +): T { + plugin(SSE) + + val sessionDeferred = CompletableDeferred() + val statement = prepareRequest { + block() + addAttribute(sseRequestAttr, true) + addAttribute(reconnectionTimeAttr, reconnectionTime) + addAttribute(showCommentEventsAttr, showCommentEvents) + addAttribute(showRetryEventsAttr, showRetryEvents) + additionalAttributes() + } + @Suppress("SuspendFunctionOnCoroutineScope") + launch { + try { + statement.body { session -> + sessionDeferred.complete(session) + } + } catch (cause: CancellationException) { + sessionDeferred.cancel(cause) + } catch (cause: Throwable) { + sessionDeferred.completeExceptionally(mapToSSEException(response = null, cause)) + } + } + return sessionDeferred.await() +} + private fun HttpRequestBuilder.addAttribute(attributeKey: AttributeKey, value: T?) { if (value != null) { attributes.put(attributeKey, value) diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/request/HttpRequest.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/request/HttpRequest.kt index ef2b1b11cc1..e4f283b97a3 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/request/HttpRequest.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/request/HttpRequest.kt @@ -342,7 +342,6 @@ public class SSEClientResponseAdapter : ResponseAdapter { outgoingContent as SSEClientContent DefaultClientSSESession( outgoingContent, - outgoingContent.deserializer, responseBody, callContext ) diff --git a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpEngine.kt b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpEngine.kt index 0342f346f1e..d4eba7134c9 100644 --- a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpEngine.kt +++ b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpEngine.kt @@ -66,7 +66,7 @@ public class OkHttpEngine(override val config: OkHttpConfig) : HttpClientEngineB return when { data.isUpgradeRequest() -> executeWebSocketRequest(requestEngine, engineRequest, callContext) - data.isSseRequest() -> executeServerSendEventsRequest(requestEngine, engineRequest, callContext, data) + data.isSseRequest() -> executeServerSendEventsRequest(requestEngine, engineRequest, callContext) else -> executeHttpRequest(requestEngine, engineRequest, callContext, data) } } @@ -96,16 +96,13 @@ public class OkHttpEngine(override val config: OkHttpConfig) : HttpClientEngineB private suspend fun executeServerSendEventsRequest( engine: OkHttpClient, engineRequest: Request, - callContext: CoroutineContext, - requestData: HttpRequestData + callContext: CoroutineContext ): HttpResponseData { val requestTime = GMTDate() - val deserializer = (requestData.body as SSEClientContent).deserializer val session = OkHttpSSESession( engine, engineRequest, - callContext, - deserializer + callContext ) val originResponse = session.originResponse.await() diff --git a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt index 1a88c86a2f3..3f232a2e30a 100644 --- a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt +++ b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt @@ -14,19 +14,18 @@ import okhttp3.* import okhttp3.sse.* import kotlin.coroutines.* -internal class OkHttpSSESession( +internal class OkHttpSSESession( engine: OkHttpClient, engineRequest: Request, override val coroutineContext: CoroutineContext, - private val deserializer: (String) -> T -) : SSESession, EventSourceListener() { +) : SSESession, EventSourceListener() { private val serverSentEventsSource = EventSources.createFactory(engine).newEventSource(engineRequest, this) internal val originResponse: CompletableDeferred = CompletableDeferred() - private val _incoming = Channel>(8) + private val _incoming = Channel>(8) - override val incoming: Flow> + override val incoming: Flow> get() = _incoming.receiveAsFlow() override fun onOpen(eventSource: EventSource, response: Response) { @@ -34,7 +33,7 @@ internal class OkHttpSSESession( } override fun onEvent(eventSource: EventSource, id: String?, type: String?, data: String) { - _incoming.trySendBlocking(ServerSentEvent(deserializer(data), type, id)) + _incoming.trySendBlocking(ServerSentEvent(data, type, id)) } override fun onFailure(eventSource: EventSource, t: Throwable?, response: Response?) { diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/SSESessionParserTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/SSESessionParserTest.kt index f758f484a3b61600afbc3cf753693559bf473638..ef91aecd2df98e5a0426cff5687aba5452e7a63a 100644 GIT binary patch delta 215 zcmdns_``0(gUQm2ZJUoWYKpUZ<`tBdO#USyKY2eF2ZvX2ex8D@g3{y)DSJkx%{h`h zjJztT6$PouC8;SO$;sQLCQR;=Gn%|dPGa&Q>59n_GCqu4lVl~8xfIXXv|A0ZNKT+4t&aY$-$$;9LHMJn`)Q;RYab26(EOEUBG z^s(xmXQliK@zc2&&*?33&kx(o0EA&QD2oE6UG>D7JwqwA08e(Nw7A+RQA;!#Me{g#Bb0sR=U3766Sx z=ITtIE_Iy;S!8mIbll`z8IH;SrB!$m3kq^76{@)ufMD`&X-RHF9fiykO*mgn$t$%) zNk<_qv#7YlFEKY2A*HC~RFs&RR}7TRNkou7D8$oX~&R diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt index f037a92e502..7e5c06fb061 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt @@ -15,7 +15,9 @@ import io.ktor.client.statement.* import io.ktor.client.tests.utils.* import io.ktor.http.* import io.ktor.http.content.* +import io.ktor.sse.* import io.ktor.test.dispatcher.* +import io.ktor.util.reflect.* import io.ktor.utils.io.* import io.ktor.utils.io.charsets.* import kotlinx.coroutines.* @@ -31,12 +33,12 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { fun testExceptionIfSseIsNotInstalled() = testSuspend { val client = HttpClient() assertFailsWith { - client.serverSentEventsSession {} + client.serverSentEventsSession {} }.apply { assertContains(message!!, SSE.key.name) } assertFailsWith { - client.serverSentEvents {} + client.serverSentEvents {} }.apply { assertContains(message!!, SSE.key.name) } @@ -64,7 +66,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } test { client -> - val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello") + val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello") session.incoming.single().apply { assertEquals("0", id) assertEquals("hello 0", event) @@ -86,7 +88,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> coroutineScope { launch { - val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello?times=100") + val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello?times=100") var size = 0 session.incoming.collectIndexed { i, it -> assertEquals("$i", it.id) @@ -101,7 +103,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { session.cancel() } launch { - val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello?times=50") + val session = client.serverSentEventsSession("$TEST_SERVER/sse/hello?times=50") var size = 0 session.incoming.collectIndexed { i, it -> assertEquals("$i", it.id) @@ -127,7 +129,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> assertFailsWith { - client.serverSentEventsSession("http://unknown_host") + client.serverSentEventsSession("http://unknown_host") }.apply { assertNotNull(cause) } @@ -142,7 +144,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> assertFailsWith { - client.serverSentEvents("$TEST_SERVER/sse/hello") { error("error") } + client.serverSentEvents("$TEST_SERVER/sse/hello") { error("error") } }.apply { assertTrue { cause is IllegalStateException } assertEquals("error", message) @@ -162,7 +164,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { val job: Job suspendCoroutine { cont -> job = launch { - client.serverSentEvents("$TEST_SERVER/sse/hello") { + client.serverSentEvents("$TEST_SERVER/sse/hello") { cont.resume(Unit) awaitCancellation() } @@ -259,7 +261,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } test { client -> - client.sse("$TEST_SERVER/sse/hello?delay=20") { + client.sse("$TEST_SERVER/sse/hello?delay=20") { val result = incoming.single() assertEquals("hello 0", result.event) } @@ -296,7 +298,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> assertFailsWith { - client.sse("$TEST_SERVER/sse/404") {} + client.sse("$TEST_SERVER/sse/404") {} }.apply { assertEquals(HttpStatusCode.NotFound, response?.status) assertEquals("Expected status code 200 but was 404", message) @@ -312,7 +314,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> assertFailsWith { - client.sse("$TEST_SERVER/sse/content-type-text-plain") {} + client.sse("$TEST_SERVER/sse/content-type-text-plain") {} }.apply { assertEquals(HttpStatusCode.OK, response?.status) assertEquals(ContentType.Text.Plain, response?.contentType()) @@ -328,7 +330,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } test { client -> - client.sse("$TEST_SERVER/sse/content_type_with_charset") { + client.sse("$TEST_SERVER/sse/content_type_with_charset") { assertEquals(ContentType.Text.EventStream.withCharset(Charsets.UTF_8), call.response.contentType()) incoming.single().apply { @@ -385,7 +387,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } test { client -> assertFailsWith { - client.sse({ + client.sse({ url("$TEST_SERVER/sse/echo") setBody(body) }) {} @@ -438,23 +440,23 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { @Test fun testDeserializer() = clientTests { config { - install(SSE) { - deserialize { - Person(it) - } - } + install(SSE) } test { client -> val count = 10 var size = 0 - client.sse({ - url("$TEST_SERVER/sse/person") - parameter("times", count) - }) { - incoming.collectIndexed { i, person -> - assertEquals("Name $i", person.data?.name) - assertEquals("$i", person.id) + client.sse( + { + url("$TEST_SERVER/sse/person") + parameter("times", count) + }, + deserialize = { { Person(it) } } + ) { + incoming.collectIndexed { i, event -> + val person = deserialize(event) + assertEquals("Name $i", person?.name) + assertEquals("$i", event.id) size++ } } @@ -463,15 +465,16 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } @Test - fun testExceptionIfNoDeserializerProvided() = clientTests(listOf("Curl", "Darwin", "DarwinLegacy", "Js")) { + fun testExceptionIfWrongDeserializerProvided() = clientTests { config { install(SSE) } test { client -> assertFailsWith { - client.sse({ url("$TEST_SERVER/sse/person") }) { + client.sse({ url("$TEST_SERVER/sse/person") }, { { Data(it) } }) { incoming.single().apply { + val data = deserialize(data) assertEquals("Name 0", data?.name) } } @@ -479,57 +482,24 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } } - @Test - fun testExceptionIfWrongDeserializerProvided() = clientTests(listOf("Curl", "Darwin", "DarwinLegacy", "Js")) { - config { - install(SSE) { - deserialize { - Data(it) - } - } - } - - test { client -> - assertFailsWith { - client.sse({ url("$TEST_SERVER/sse/person") }) { - incoming.single().apply { - assertEquals("Name 0", data?.name) - } - } - }.apply { - assertTrue { cause is ClassCastException } - } - } - } - class Person1(val name: String) class Person2(val middleName: String) - class Person3(val surname: String) @Test - fun testDifferentDeserializers() = clientTests(listOf("Js")) { + fun testDifferentDeserializers() = clientTests { config { - install(SSE) { - deserialize { - Person3(it) - } - } + install(SSE) } test { client -> - client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { Person1(it) }) { + client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { _ -> { str -> Person1(str) } }) { incoming.single().apply { - assertEquals("Name 0", data?.name) + assertEquals("Name 0", deserialize(data)?.name) } } - client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { Person2(it) }) { + client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { _ -> { str -> Person2(str) } }) { incoming.single().apply { - assertEquals("Name 0", data?.middleName) - } - } - client.sse({ url("$TEST_SERVER/sse/person") }) { - incoming.single().apply { - assertEquals("Name 0", data?.surname) + assertEquals("Name 0", deserialize(data)?.middleName) } } } @@ -538,24 +508,37 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { @Serializable data class Customer(val id: Int, val firstName: String, val lastName: String) + @Serializable + data class Product(val name: String, val price: Int) + @Test - fun testJsonDeserializer() = clientTests(listOf("Js")) { + fun testJsonDeserializer() = clientTests { config { - install(SSE) { - deserialize { - Json.decodeFromString(it) - } - } + install(SSE) } test { client -> - client.sse({ + client.sse({ url("$TEST_SERVER/sse/json") + }, deserialize = { typeInfo: TypeInfo -> + { jsonString: String -> + val serializer = Json.serializersModule.serializer(typeInfo.kotlinType!!) + Json.decodeFromString(serializer, jsonString) ?: Exception() + } }) { - incoming.single().apply { - assertEquals(1, data?.id) - assertEquals("Jet", data?.firstName) - assertEquals("Brains", data?.lastName) + var firstIsCustomer = true + incoming.collect { event: ServerSentEvent -> + if (firstIsCustomer) { + val customer = deserialize(event.data) + assertEquals(1, customer?.id) + assertEquals("Jet", customer?.firstName) + assertEquals("Brains", customer?.lastName) + firstIsCustomer = false + } else { + val product = deserialize(event.data) + assertEquals("Milk", product?.name) + assertEquals(100, product?.price) + } } } } diff --git a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt index dd134cacdc1..9cdcafe9b52 100644 --- a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt +++ b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt @@ -15,7 +15,7 @@ import io.ktor.utils.io.* * @property retry reconnection time, in milliseconds to wait before reconnecting. * @property comments comment lines starting with a ':' character. */ -public class ServerSentEvent( +public data class ServerSentEvent( public val data: T? = null, public val event: String? = null, public val id: String? = null, From 01e09fe69f433b4f454391483262fffc5e4eabbb Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Fri, 4 Oct 2024 18:35:51 +0200 Subject: [PATCH 07/14] Fix server side --- .../server/sse/DefaultServerSSESession.kt | 11 +- .../common/src/io/ktor/server/sse/Routing.kt | 40 +++---- .../common/src/io/ktor/server/sse/SSE.kt | 16 +-- .../src/io/ktor/server/sse/SSEConfig.kt | 19 --- .../io/ktor/server/sse/SSEServerContent.kt | 16 ++- .../src/io/ktor/server/sse/SSESession.kt | 50 +++++++- .../ktor/server/sse/ServerSentEventsTest.kt | 108 ++++++++++-------- 7 files changed, 146 insertions(+), 114 deletions(-) delete mode 100644 ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEConfig.kt diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt index 916ff3bba33..8fd1d2b06d9 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt @@ -10,15 +10,14 @@ import io.ktor.utils.io.* import kotlinx.coroutines.sync.* import kotlin.coroutines.* -internal class DefaultServerSSESession( - private val serializer: (T) -> String, +internal class DefaultServerSSESession( private val output: ByteWriteChannel, override val call: ApplicationCall, override val coroutineContext: CoroutineContext -) : SSESession { +) : SSESession { private val mutex = Mutex() - override suspend fun send(event: ServerSentEvent) { + override suspend fun send(event: ServerSentEvent) { mutex.withLock { output.writeSSE(event) } @@ -31,8 +30,8 @@ internal class DefaultServerSSESession( } @OptIn(InternalAPI::class) - private suspend fun ByteWriteChannel.writeSSE(event: ServerSentEvent) { - writeStringUtf8(event.toString(serializer) + END_OF_LINE) + private suspend fun ByteWriteChannel.writeSSE(event: ServerSentEvent) { + writeStringUtf8(event.toString() + END_OF_LINE) flush() } } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt index 8669344b470..a1387d55851 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt @@ -5,9 +5,9 @@ package io.ktor.server.sse import io.ktor.http.* -import io.ktor.server.application.* import io.ktor.server.response.* import io.ktor.server.routing.* +import io.ktor.util.reflect.* /** * Adds a route to handle Server-Sent Events (SSE) at the specified [path] using the provided [handler]. @@ -20,7 +20,7 @@ import io.ktor.server.routing.* * * @see SSESession */ -public fun Route.sse(path: String, handler: suspend SSESession.() -> Unit) { +public fun Route.sse(path: String, handler: suspend SSESession.() -> Unit) { route(path, HttpMethod.Get) { sse(handler) } @@ -36,17 +36,7 @@ public fun Route.sse(path: String, handler: suspend SSESession.() - * * @see SSESession */ -public fun Route.sse(handler: suspend SSESession.() -> Unit) { - val sse = application.plugin(SSE) - - handle { - call.response.header(HttpHeaders.ContentType, ContentType.Text.EventStream.toString()) - call.response.header(HttpHeaders.CacheControl, "no-store") - call.response.header(HttpHeaders.Connection, "keep-alive") - call.response.header("X-Accel-Buffering", "no") - call.respond(SSEServerContent(call, sse.serialize, handler)) - } -} +public fun Route.sse(handler: suspend SSESession.() -> Unit): Unit = processSSE(null, handler) /** * Adds a route to handle Server-Sent Events (SSE) at the specified [path] using the provided [handler]. @@ -54,15 +44,15 @@ public fun Route.sse(handler: suspend SSESession.() -> Unit) { * * @param path URL path at which to handle SSE requests. * @param handler function that defines the behavior of the SSE session. It is invoked when a client connects to the SSE - * endpoint. Inside the handler, you can use the functions provided by [SSESession] + * endpoint. Inside the handler, you can use the functions provided by [SSESessionWithDeserialization] * to send events to the connected clients. * - * @see SSESession + * @see SSESessionWithDeserialization */ -public fun Route.sse( +public fun Route.sse( path: String, - serialize: (T) -> String = { it.toString() }, - handler: suspend SSESession.() -> Unit + serialize: (TypeInfo) -> (Any) -> String = { { it.toString() } }, + handler: suspend SSESessionWithDeserialization.() -> Unit ) { route(path, HttpMethod.Get) { sse(serialize, handler) @@ -74,12 +64,20 @@ public fun Route.sse( * Requires [SSE] plugin to be installed. * * @param handler function that defines the behavior of the SSE session. It is invoked when a client connects to the SSE - * endpoint. Inside the handler, you can use the functions provided by [SSESession] + * endpoint. Inside the handler, you can use the functions provided by [SSESessionWithDeserialization] * to send events to the connected clients. * - * @see SSESession + * @see SSESessionWithDeserialization */ -public fun Route.sse(serialize: (T) -> String = { it.toString() }, handler: suspend SSESession.() -> Unit) { +public fun Route.sse( + serialize: (TypeInfo) -> (Any) -> String = { { it.toString() } }, + handler: suspend SSESessionWithDeserialization.() -> Unit +): Unit = processSSE(serialize, handler) + +private fun Route.processSSE( + serialize: ((TypeInfo) -> (Any) -> String)?, + handler: suspend SSESessionWithDeserialization.() -> Unit +) { plugin(SSE) handle { diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSE.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSE.kt index 718d9ac0293..efbc9129de6 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSE.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSE.kt @@ -5,7 +5,6 @@ package io.ktor.server.sse import io.ktor.server.application.* -import io.ktor.util.* import io.ktor.util.logging.* internal val LOGGER = KtorSimpleLogger("io.ktor.server.plugins.sse.SSE") @@ -26,17 +25,4 @@ internal val LOGGER = KtorSimpleLogger("io.ktor.server.plugins.sse.SSE") * } * ``` */ -public class SSE private constructor( - public val serialize: (Any) -> String, -) { - public companion object Plugin : BaseApplicationPlugin { - override val key: AttributeKey = AttributeKey("SSE") - - override fun install(pipeline: Application, configure: SSEConfig.() -> Unit): SSE { - val config = SSEConfig().also(configure) - with(config) { - return SSE(serialize) - } - } - } -} +public val SSE: ApplicationPlugin = createApplicationPlugin("SSE") {} diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEConfig.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEConfig.kt deleted file mode 100644 index f874db092c0..00000000000 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEConfig.kt +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. - */ - -package io.ktor.server.sse - -import io.ktor.utils.io.* - -@KtorDsl -public class SSEConfig { - internal var serialize: (Any) -> String = { it.toString() } - - /** - * Configures serialization logic for transforming data object into field `data` of `ServerSentEvent`. - */ - public fun serialize(serialize: (T) -> String) { - this.serialize = serialize as (Any) -> String - } -} diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt index 1bd5427246b..cb3d6c441d6 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt @@ -8,6 +8,7 @@ import io.ktor.http.* import io.ktor.http.content.* import io.ktor.server.application.* import io.ktor.server.request.* +import io.ktor.util.reflect.* import io.ktor.utils.io.* import kotlinx.coroutines.* @@ -23,20 +24,25 @@ import kotlinx.coroutines.* * @param call that is starting SSE session. * @param handle function that is started once SSE session created. */ -public class SSEServerContent( +public class SSEServerContent( public val call: ApplicationCall, - private val serializer: (T) -> String, - public val handle: suspend SSESession.() -> Unit + public val serialize: ((TypeInfo) -> (Any) -> String)?, + public val handle: suspend T.() -> Unit ) : OutgoingContent.WriteChannelContent() { override val contentType: ContentType = ContentType.Text.EventStream override suspend fun writeTo(channel: ByteWriteChannel) { LOGGER.trace("Starting sse session for ${call.request.uri}") - var session: SSESession? = null + var session: T? = null try { coroutineScope { - session = DefaultServerSSESession(serializer, channel, call, coroutineContext) + session = DefaultServerSSESession(channel, call, coroutineContext) as T + if (serialize != null) { + session = object : SSESessionWithDeserialization, SSESession by session as DefaultServerSSESession { + override val serializer: (TypeInfo) -> (Any) -> String = serialize + } as T + } session?.handle() } } finally { diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt index be5bd75ce20..2171d5c2cd6 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt @@ -6,6 +6,8 @@ package io.ktor.server.sse import io.ktor.server.application.* import io.ktor.sse.* +import io.ktor.util.reflect.* +import io.ktor.websocket.* import kotlinx.coroutines.* /** @@ -14,7 +16,7 @@ import kotlinx.coroutines.* * * @see [SSE] */ -public interface SSESession : CoroutineScope { +public interface SSESession : CoroutineScope { /** * Associated received [call] that originating this session. */ @@ -23,7 +25,7 @@ public interface SSESession : CoroutineScope { /** * Sends a [ServerSentEvent] to the client. */ - public suspend fun send(event: ServerSentEvent) + public suspend fun send(event: ServerSentEvent) /** * Creates and sends a [ServerSentEvent] to the client. @@ -35,7 +37,7 @@ public interface SSESession : CoroutineScope { * @param comments comment lines starting with a ':' character. */ public suspend fun send( - data: T? = null, + data: String? = null, event: String? = null, id: String? = null, retry: Long? = null, @@ -55,3 +57,45 @@ public interface SSESession : CoroutineScope { */ public suspend fun close() } + + +/** + * Represents a server-side server-sent events session with serialization support. + * An [SSESessionWithDeserialization] allows the server to send [ServerSentEvent] to the client over a single HTTP connection. + * + * @see [SSE] + */ +public interface SSESessionWithDeserialization : SSESession { + /** + * Serializer for transforming data object into field `data` of `ServerSentEvent`. + */ + public val serializer: (TypeInfo) -> (Any) -> String +} + +public suspend inline fun SSESessionWithDeserialization.sendSerialized(event: ServerSentEvent) { + send( + ServerSentEvent( + event.data?.let { + serializer(typeInfo()).invoke(it) + }, + event.event, + event.id, + event.retry, + event.comments + ) + ) +} + +public suspend inline fun SSESessionWithDeserialization.sendSerialized( + data: T? = null, + event: String? = null, + id: String? = null, + retry: Long? = null, + comments: String? = null +) { + sendSerialized(ServerSentEvent(data, event, id, retry, comments)) +} + +public suspend inline fun SSESessionWithDeserialization.sendSerialized(data: T) { + send(ServerSentEvent(serializer(typeInfo()).invoke(data))) +} diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt index b93d1043866..9e35b052704 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt @@ -127,11 +127,11 @@ class ServerSentEventsTest { fun testNoDuplicateHeader() = testApplication { install(SSE) routing { - sse { } + sse { } } val client = createSseClient() - client.sse { + client.sse { call.response.headers.forEach { _, values -> assertEquals(1, values.size) } @@ -172,7 +172,7 @@ class ServerSentEventsTest { """.trimIndent() val actualMultilineData = StringBuilder() - client.sse("/multiline-data") { + client.sse("/multiline-data") { incoming.collect { actualMultilineData.append(it.toString()) } @@ -186,7 +186,7 @@ class ServerSentEventsTest { """.trimIndent() val actualOneEventData = StringBuilder() - client.sse("/one-event-data") { + client.sse("/one-event-data") { incoming.collect { actualOneEventData.append(it.toString()) } @@ -194,15 +194,28 @@ class ServerSentEventsTest { assertEquals(expectedOneEventData.lines(), actualOneEventData.toString().lines()) } - class Person(val age: Int) + class Person1(val age: Int) + class Person2(val number: Int) @Test fun testSerializerInRoute() = testApplication { install(SSE) routing { - sse("/person", serialize = { "Age ${it.age}" }) { + sse("/person", serialize = { typeInfo -> + { data -> + when (typeInfo.type) { + Person1::class -> { + "Age ${(data as Person1).age}" + } + + else -> { + data.toString() + } + } + } + }) { repeat(10) { - send(Person(it)) + sendSerialized(Person1(it)) } } } @@ -216,43 +229,42 @@ class ServerSentEventsTest { } } - class Person1(val age: Int) - class Person2(val number: Int) - class Person3(val index: Int) - @Test fun testDifferentSerializers() = testApplication { - install(SSE) { - serialize { person: Person3 -> - "${person.index}" - } - } + install(SSE) routing { - sse("/person1", serialize = { println("1"); "${it.age}" }) { - send(Person1(22)) - } - sse("/person2", serialize = { "${it.number}" }) { - send(Person2(123456)) - } - sse("/person3") { - send(Person3(0)) + sse(serialize = { typeInfo -> + { data -> + when (typeInfo.type) { + Person1::class -> { + "Age ${(data as Person1).age}" + } + + Person2::class -> { + "Number ${(data as Person2).number}" + } + + else -> { + data.toString() + } + } + } + }) { + sendSerialized(Person1(22)) + sendSerialized(Person2(123456)) } } val client = createSseClient() - client.sse("/person1") { - incoming.single().apply { - assertEquals("22", data) - } - } - client.sse("/person2") { - incoming.single().apply { - assertEquals("123456", data) - } - } - client.sse("/person3") { - incoming.single().apply { - assertEquals("0", data) + client.sse { + var first = true + incoming.collect { + if (first) { + assertEquals("Age 22", it.data) + first = false + } else { + assertEquals("Number 123456", it.data) + } } } } @@ -260,21 +272,27 @@ class ServerSentEventsTest { @Serializable data class Customer(val id: Int, val firstName: String, val lastName: String) + @Serializable + data class Product(val id: Int, val prices: List) + @Test fun testJsonDeserializer() = testApplication { - install(SSE) { - serialize { - Json.encodeToString(it) - } - } + install(SSE) routing { - sse("/json") { - send(Customer(0, "Jet", "Brains")) + sse("/json", serialize = { typeInfo -> + { + val serializer = Json.serializersModule.serializer(typeInfo.kotlinType!!) + Json.encodeToString(serializer, it) + } + }) { + sendSerialized(Customer(0, "Jet", "Brains")) + sendSerialized(Product(0, listOf(100, 200))) } } assertEquals( - "data: {\"id\":0,\"firstName\":\"Jet\",\"lastName\":\"Brains\"}", + "data: {\"id\":0,\"firstName\":\"Jet\",\"lastName\":\"Brains\"}\r\n\r\n" + + "data: {\"id\":0,\"prices\":[100,200]}", client.get("/json").bodyAsText().trim() ) } From 4c451b217e5c18dc8ef3cc9e515162df26b921c0 Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:52:02 +0200 Subject: [PATCH 08/14] Fix codestyle and run apiDump --- .../ktor-client-core/api/ktor-client-core.api | 42 ++++++- .../api/ktor-client-core.klib.api | 111 +++++++++++------- .../src/io/ktor/client/plugins/sse/SSE.kt | 9 +- .../io/ktor/client/plugins/sse/builders.kt | 3 +- .../ktor-server-sse/api/ktor-server-sse.api | 32 +++-- .../api/ktor-server-sse.klib.api | 49 ++++---- .../src/io/ktor/server/sse/SSESession.kt | 1 - ktor-shared/ktor-sse/api/ktor-sse.api | 9 ++ ktor-shared/ktor-sse/api/ktor-sse.klib.api | 8 ++ 9 files changed, 169 insertions(+), 95 deletions(-) diff --git a/ktor-client/ktor-client-core/api/ktor-client-core.api b/ktor-client/ktor-client-core/api/ktor-client-core.api index 65bfb5f1212..dfda1c2ff3f 100644 --- a/ktor-client/ktor-client-core/api/ktor-client-core.api +++ b/ktor-client/ktor-client-core/api/ktor-client-core.api @@ -763,30 +763,54 @@ public final class io/ktor/client/plugins/observer/ResponseObserverKt { public final class io/ktor/client/plugins/sse/BuildersKt { public static final fun SSE (Lio/ktor/client/HttpClientConfig;Lkotlin/jvm/functions/Function1;)V + public static final fun serverSentEvents-1wIb-0I (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEvents-1wIb-0I$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEvents-3bFjkrY (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEvents-3bFjkrY$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEvents-BqdlHlk (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEvents-BqdlHlk$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEvents-Mswn-_c (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEvents-Mswn-_c$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEvents-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEvents-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEvents-pTj2aPc (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEvents-pTj2aPc$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEventsSession-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEventsSession-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEventsSession-i8z2VEo (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEventsSession-i8z2VEo$default (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEventsSession-mY9Nd3A (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun serverSentEventsSession-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEventsSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static synthetic fun serverSentEventsSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEventsSession-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEventsSession-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEventsSession-xEWcMm4 (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEventsSession-xEWcMm4$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sse-BAHpl2s (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sse-BAHpl2s$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sse-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun sse-Mswn-_c (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sse-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static synthetic fun sse-Mswn-_c$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sse-Q9yt8Vw (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sse-Q9yt8Vw$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sse-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sse-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sse-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sse-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sseSession-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sseSession-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sseSession-i8z2VEo (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sseSession-i8z2VEo$default (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sseSession-mY9Nd3A (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun sseSession-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sseSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static synthetic fun sseSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sseSession-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sseSession-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sseSession-xEWcMm4 (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sseSession-xEWcMm4$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } public final class io/ktor/client/plugins/sse/ClientSSESession : io/ktor/client/plugins/sse/SSESession { @@ -796,8 +820,16 @@ public final class io/ktor/client/plugins/sse/ClientSSESession : io/ktor/client/ public fun getIncoming ()Lkotlinx/coroutines/flow/Flow; } +public final class io/ktor/client/plugins/sse/ClientSSESessionWithDeserialization : io/ktor/client/plugins/sse/SSESessionWithDeserialization { + public fun (Lio/ktor/client/call/HttpClientCall;Lio/ktor/client/plugins/sse/SSESessionWithDeserialization;)V + public final fun getCall ()Lio/ktor/client/call/HttpClientCall; + public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; + public fun getDeserializer ()Lkotlin/jvm/functions/Function1; + public fun getIncoming ()Lkotlinx/coroutines/flow/Flow; +} + public final class io/ktor/client/plugins/sse/DefaultClientSSESession : io/ktor/client/plugins/sse/SSESession { - public fun (Lio/ktor/client/plugins/sse/SSEClientContent;Lkotlin/jvm/functions/Function1;Lio/ktor/utils/io/ByteReadChannel;Lkotlin/coroutines/CoroutineContext;)V + public fun (Lio/ktor/client/plugins/sse/SSEClientContent;Lio/ktor/utils/io/ByteReadChannel;Lkotlin/coroutines/CoroutineContext;)V public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; public fun getIncoming ()Lkotlinx/coroutines/flow/Flow; } @@ -810,10 +842,9 @@ public final class io/ktor/client/plugins/sse/SSECapability : io/ktor/client/eng } public final class io/ktor/client/plugins/sse/SSEClientContent : io/ktor/http/content/OutgoingContent$ContentWrapper { - public synthetic fun (Lkotlin/jvm/functions/Function1;JZZLio/ktor/http/content/OutgoingContent;Lkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (JZZLio/ktor/http/content/OutgoingContent;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public fun copy (Lio/ktor/http/content/OutgoingContent;)Lio/ktor/client/plugins/sse/SSEClientContent; public synthetic fun copy (Lio/ktor/http/content/OutgoingContent;)Lio/ktor/http/content/OutgoingContent$ContentWrapper; - public final fun getDeserializer ()Lkotlin/jvm/functions/Function1; public fun getHeaders ()Lio/ktor/http/Headers; public final fun getReconnectionTime-UwyO8pc ()J public final fun getShowCommentEvents ()Z @@ -832,7 +863,6 @@ public final class io/ktor/client/plugins/sse/SSEClientException : java/lang/Ill public final class io/ktor/client/plugins/sse/SSEConfig { public fun ()V - public final fun deserialize (Lkotlin/jvm/functions/Function1;)V public final fun getReconnectionTime-UwyO8pc ()J public final fun setReconnectionTime-LRDsOJo (J)V public final fun showCommentEvents ()V @@ -847,6 +877,10 @@ public abstract interface class io/ktor/client/plugins/sse/SSESession : kotlinx/ public abstract fun getIncoming ()Lkotlinx/coroutines/flow/Flow; } +public abstract interface class io/ktor/client/plugins/sse/SSESessionWithDeserialization : io/ktor/client/plugins/sse/SSESession { + public abstract fun getDeserializer ()Lkotlin/jvm/functions/Function1; +} + public final class io/ktor/client/plugins/websocket/BuildersKt { public static final fun WebSockets (Lio/ktor/client/HttpClientConfig;Lkotlin/jvm/functions/Function1;)V public static final fun webSocket (Lio/ktor/client/HttpClient;Lio/ktor/http/HttpMethod;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; diff --git a/ktor-client/ktor-client-core/api/ktor-client-core.klib.api b/ktor-client/ktor-client-core/api/ktor-client-core.klib.api index 15213562b47..d6e377276f5 100644 --- a/ktor-client/ktor-client-core/api/ktor-client-core.klib.api +++ b/ktor-client/ktor-client-core/api/ktor-client-core.klib.api @@ -35,11 +35,6 @@ abstract interface <#A: kotlin/Any?> io.ktor.client.plugins.api/ClientHook { // abstract fun install(io.ktor.client/HttpClient, #A) // io.ktor.client.plugins.api/ClientHook.install|install(io.ktor.client.HttpClient;1:0){}[0] } -abstract interface <#A: kotlin/Any?> io.ktor.client.plugins.sse/SSESession : kotlinx.coroutines/CoroutineScope { // io.ktor.client.plugins.sse/SSESession|null[0] - abstract val incoming // io.ktor.client.plugins.sse/SSESession.incoming|{}incoming[0] - abstract fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/SSESession.incoming.|(){}[0] -} - abstract interface <#A: out io.ktor.client.engine/HttpClientEngineConfig> io.ktor.client.engine/HttpClientEngineFactory { // io.ktor.client.engine/HttpClientEngineFactory|null[0] abstract fun create(kotlin/Function1<#A, kotlin/Unit> = ...): io.ktor.client.engine/HttpClientEngine // io.ktor.client.engine/HttpClientEngineFactory.create|create(kotlin.Function1<1:0,kotlin.Unit>){}[0] } @@ -82,6 +77,16 @@ abstract interface io.ktor.client.plugins.cookies/CookiesStorage : io.ktor.utils abstract suspend fun get(io.ktor.http/Url): kotlin.collections/List // io.ktor.client.plugins.cookies/CookiesStorage.get|get(io.ktor.http.Url){}[0] } +abstract interface io.ktor.client.plugins.sse/SSESession : kotlinx.coroutines/CoroutineScope { // io.ktor.client.plugins.sse/SSESession|null[0] + abstract val incoming // io.ktor.client.plugins.sse/SSESession.incoming|{}incoming[0] + abstract fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/SSESession.incoming.|(){}[0] +} + +abstract interface io.ktor.client.plugins.sse/SSESessionWithDeserialization : io.ktor.client.plugins.sse/SSESession { // io.ktor.client.plugins.sse/SSESessionWithDeserialization|null[0] + abstract val deserializer // io.ktor.client.plugins.sse/SSESessionWithDeserialization.deserializer|{}deserializer[0] + abstract fun (): kotlin/Function1> // io.ktor.client.plugins.sse/SSESessionWithDeserialization.deserializer.|(){}[0] +} + abstract interface io.ktor.client.plugins.websocket/ClientWebSocketSession : io.ktor.websocket/WebSocketSession { // io.ktor.client.plugins.websocket/ClientWebSocketSession|null[0] abstract val call // io.ktor.client.plugins.websocket/ClientWebSocketSession.call|{}call[0] abstract fun (): io.ktor.client.call/HttpClientCall // io.ktor.client.plugins.websocket/ClientWebSocketSession.call.|(){}[0] @@ -229,26 +234,6 @@ final class <#A: kotlin/Any> io.ktor.client.request.forms/FormPart { // io.ktor. final fun toString(): kotlin/String // io.ktor.client.request.forms/FormPart.toString|toString(){}[0] } -final class <#A: kotlin/Any?> io.ktor.client.plugins.sse/ClientSSESession : io.ktor.client.plugins.sse/SSESession<#A> { // io.ktor.client.plugins.sse/ClientSSESession|null[0] - constructor (io.ktor.client.call/HttpClientCall, io.ktor.client.plugins.sse/SSESession<#A>) // io.ktor.client.plugins.sse/ClientSSESession.|(io.ktor.client.call.HttpClientCall;io.ktor.client.plugins.sse.SSESession<1:0>){}[0] - - final val call // io.ktor.client.plugins.sse/ClientSSESession.call|{}call[0] - final fun (): io.ktor.client.call/HttpClientCall // io.ktor.client.plugins.sse/ClientSSESession.call.|(){}[0] - final val coroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext|{}coroutineContext[0] - final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext.|(){}[0] - final val incoming // io.ktor.client.plugins.sse/ClientSSESession.incoming|{}incoming[0] - final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/ClientSSESession.incoming.|(){}[0] -} - -final class <#A: kotlin/Any?> io.ktor.client.plugins.sse/DefaultClientSSESession : io.ktor.client.plugins.sse/SSESession<#A> { // io.ktor.client.plugins.sse/DefaultClientSSESession|null[0] - constructor (io.ktor.client.plugins.sse/SSEClientContent, kotlin/Function1, io.ktor.utils.io/ByteReadChannel, kotlin.coroutines/CoroutineContext) // io.ktor.client.plugins.sse/DefaultClientSSESession.|(io.ktor.client.plugins.sse.SSEClientContent;kotlin.Function1;io.ktor.utils.io.ByteReadChannel;kotlin.coroutines.CoroutineContext){}[0] - - final val coroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext|{}coroutineContext[0] - final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext.|(){}[0] - final val incoming // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming|{}incoming[0] - final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming.|(){}[0] -} - final class io.ktor.client.call/DoubleReceiveException : kotlin/IllegalStateException { // io.ktor.client.call/DoubleReceiveException|null[0] constructor (io.ktor.client.call/HttpClientCall) // io.ktor.client.call/DoubleReceiveException.|(io.ktor.client.call.HttpClientCall){}[0] @@ -446,11 +431,42 @@ final class io.ktor.client.plugins.observer/ResponseObserverConfig { // io.ktor. final fun onResponse(kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.observer/ResponseObserverConfig.onResponse|onResponse(kotlin.coroutines.SuspendFunction1){}[0] } +final class io.ktor.client.plugins.sse/ClientSSESession : io.ktor.client.plugins.sse/SSESession { // io.ktor.client.plugins.sse/ClientSSESession|null[0] + constructor (io.ktor.client.call/HttpClientCall, io.ktor.client.plugins.sse/SSESession) // io.ktor.client.plugins.sse/ClientSSESession.|(io.ktor.client.call.HttpClientCall;io.ktor.client.plugins.sse.SSESession){}[0] + + final val call // io.ktor.client.plugins.sse/ClientSSESession.call|{}call[0] + final fun (): io.ktor.client.call/HttpClientCall // io.ktor.client.plugins.sse/ClientSSESession.call.|(){}[0] + final val coroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext|{}coroutineContext[0] + final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext.|(){}[0] + final val incoming // io.ktor.client.plugins.sse/ClientSSESession.incoming|{}incoming[0] + final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/ClientSSESession.incoming.|(){}[0] +} + +final class io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization : io.ktor.client.plugins.sse/SSESessionWithDeserialization { // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization|null[0] + constructor (io.ktor.client.call/HttpClientCall, io.ktor.client.plugins.sse/SSESessionWithDeserialization) // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.|(io.ktor.client.call.HttpClientCall;io.ktor.client.plugins.sse.SSESessionWithDeserialization){}[0] + + final val call // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.call|{}call[0] + final fun (): io.ktor.client.call/HttpClientCall // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.call.|(){}[0] + final val coroutineContext // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.coroutineContext|{}coroutineContext[0] + final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.coroutineContext.|(){}[0] + final val deserializer // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.deserializer|{}deserializer[0] + final fun (): kotlin/Function1> // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.deserializer.|(){}[0] + final val incoming // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.incoming|{}incoming[0] + final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.incoming.|(){}[0] +} + +final class io.ktor.client.plugins.sse/DefaultClientSSESession : io.ktor.client.plugins.sse/SSESession { // io.ktor.client.plugins.sse/DefaultClientSSESession|null[0] + constructor (io.ktor.client.plugins.sse/SSEClientContent, io.ktor.utils.io/ByteReadChannel, kotlin.coroutines/CoroutineContext) // io.ktor.client.plugins.sse/DefaultClientSSESession.|(io.ktor.client.plugins.sse.SSEClientContent;io.ktor.utils.io.ByteReadChannel;kotlin.coroutines.CoroutineContext){}[0] + + final val coroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext|{}coroutineContext[0] + final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext.|(){}[0] + final val incoming // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming|{}incoming[0] + final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming.|(){}[0] +} + final class io.ktor.client.plugins.sse/SSEClientContent : io.ktor.http.content/OutgoingContent.ContentWrapper { // io.ktor.client.plugins.sse/SSEClientContent|null[0] - constructor (kotlin/Function1, kotlin.time/Duration, kotlin/Boolean, kotlin/Boolean, io.ktor.http.content/OutgoingContent) // io.ktor.client.plugins.sse/SSEClientContent.|(kotlin.Function1;kotlin.time.Duration;kotlin.Boolean;kotlin.Boolean;io.ktor.http.content.OutgoingContent){}[0] + constructor (kotlin.time/Duration, kotlin/Boolean, kotlin/Boolean, io.ktor.http.content/OutgoingContent) // io.ktor.client.plugins.sse/SSEClientContent.|(kotlin.time.Duration;kotlin.Boolean;kotlin.Boolean;io.ktor.http.content.OutgoingContent){}[0] - final val deserializer // io.ktor.client.plugins.sse/SSEClientContent.deserializer|{}deserializer[0] - final fun (): kotlin/Function1 // io.ktor.client.plugins.sse/SSEClientContent.deserializer.|(){}[0] final val headers // io.ktor.client.plugins.sse/SSEClientContent.headers|{}headers[0] final fun (): io.ktor.http/Headers // io.ktor.client.plugins.sse/SSEClientContent.headers.|(){}[0] final val reconnectionTime // io.ktor.client.plugins.sse/SSEClientContent.reconnectionTime|{}reconnectionTime[0] @@ -482,7 +498,6 @@ final class io.ktor.client.plugins.sse/SSEConfig { // io.ktor.client.plugins.sse final fun (): kotlin.time/Duration // io.ktor.client.plugins.sse/SSEConfig.reconnectionTime.|(){}[0] final fun (kotlin.time/Duration) // io.ktor.client.plugins.sse/SSEConfig.reconnectionTime.|(kotlin.time.Duration){}[0] - final fun deserialize(kotlin/Function1) // io.ktor.client.plugins.sse/SSEConfig.deserialize|deserialize(kotlin.Function1){}[0] final fun showCommentEvents() // io.ktor.client.plugins.sse/SSEConfig.showCommentEvents|showCommentEvents(){}[0] final fun showRetryEvents() // io.ktor.client.plugins.sse/SSEConfig.showRetryEvents|showRetryEvents(){}[0] } @@ -1390,6 +1405,8 @@ final fun io.ktor.client/HttpClient(io.ktor.client.engine/HttpClientEngine, kotl final fun io.ktor.client/HttpClient(kotlin/Function1, kotlin/Unit> = ...): io.ktor.client/HttpClient // io.ktor.client/HttpClient|HttpClient(kotlin.Function1,kotlin.Unit>){}[0] final inline fun (io.ktor.client.request.forms/FormBuilder).io.ktor.client.request.forms/append(kotlin/String, io.ktor.http/Headers = ..., kotlin/Long? = ..., crossinline kotlin/Function1) // io.ktor.client.request.forms/append|append@io.ktor.client.request.forms.FormBuilder(kotlin.String;io.ktor.http.Headers;kotlin.Long?;kotlin.Function1){}[0] final inline fun <#A: kotlin/Any?> io.ktor.client.plugins/unwrapRequestTimeoutException(kotlin/Function0<#A>): #A // io.ktor.client.plugins/unwrapRequestTimeoutException|unwrapRequestTimeoutException(kotlin.Function0<0:0>){0§}[0] +final inline fun <#A: reified kotlin/Any?> (io.ktor.client.plugins.sse/SSESessionWithDeserialization).io.ktor.client.plugins.sse/deserialize(io.ktor.sse/ServerSentEvent): #A? // io.ktor.client.plugins.sse/deserialize|deserialize@io.ktor.client.plugins.sse.SSESessionWithDeserialization(io.ktor.sse.ServerSentEvent){0§}[0] +final inline fun <#A: reified kotlin/Any?> (io.ktor.client.plugins.sse/SSESessionWithDeserialization).io.ktor.client.plugins.sse/deserialize(kotlin/String?): #A? // io.ktor.client.plugins.sse/deserialize|deserialize@io.ktor.client.plugins.sse.SSESessionWithDeserialization(kotlin.String?){0§}[0] final inline fun <#A: reified kotlin/Any?> (io.ktor.client.request/HttpRequestBuilder).io.ktor.client.request/setBody(#A) // io.ktor.client.request/setBody|setBody@io.ktor.client.request.HttpRequestBuilder(0:0){0§}[0] final suspend fun (io.ktor.client.call/HttpClientCall).io.ktor.client.call/save(): io.ktor.client.call/HttpClientCall // io.ktor.client.call/save|save@io.ktor.client.call.HttpClientCall(){}[0] final suspend fun (io.ktor.client.plugins.cache.storage/CacheStorage).io.ktor.client.plugins.cache.storage/store(io.ktor.client.statement/HttpResponse): io.ktor.client.plugins.cache.storage/CachedResponseData // io.ktor.client.plugins.cache.storage/store|store@io.ktor.client.plugins.cache.storage.CacheStorage(io.ktor.client.statement.HttpResponse){}[0] @@ -1405,6 +1422,30 @@ final suspend fun (io.ktor.client.statement/HttpResponse).io.ktor.client.stateme final suspend fun (io.ktor.client.statement/HttpResponse).io.ktor.client.statement/readRawBytes(): kotlin/ByteArray // io.ktor.client.statement/readRawBytes|readRawBytes@io.ktor.client.statement.HttpResponse(){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.cookies/cookies(io.ktor.http/Url): kotlin.collections/List // io.ktor.client.plugins.cookies/cookies|cookies@io.ktor.client.HttpClient(io.ktor.http.Url){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.cookies/cookies(kotlin/String): kotlin.collections/List // io.ktor.client.plugins.cookies/cookies|cookies@io.ktor.client.HttpClient(kotlin.String){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/Function1, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/Function1, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/Function1, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/Function1, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String, kotlin/Function1 = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String, kotlin/Function1 = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/webSocket(io.ktor.http/HttpMethod = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/webSocket|webSocket@io.ktor.client.HttpClient(io.ktor.http.HttpMethod;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/webSocket(kotlin/Function1, kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/webSocket|webSocket@io.ktor.client.HttpClient(kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/webSocket(kotlin/String, kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/webSocket|webSocket@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] @@ -1419,18 +1460,6 @@ final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/w final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/wss(kotlin/String, kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/wss|wss@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.request.forms/prepareForm(kotlin/String, io.ktor.http/Parameters = ..., kotlin/Boolean = ..., kotlin/Function1 = ...): io.ktor.client.statement/HttpStatement // io.ktor.client.request.forms/prepareForm|prepareForm@io.ktor.client.HttpClient(kotlin.String;io.ktor.http.Parameters;kotlin.Boolean;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.request.forms/submitForm(kotlin/String, io.ktor.http/Parameters = ..., kotlin/Boolean = ..., kotlin/Function1 = ...): io.ktor.client.statement/HttpResponse // io.ktor.client.request.forms/submitForm|submitForm@io.ktor.client.HttpClient(kotlin.String;io.ktor.http.Parameters;kotlin.Boolean;kotlin.Function1){}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/Function1, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1?, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/Function1, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String, kotlin/Function1 = ..., kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String, kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] -final suspend fun <#A: kotlin/Any> (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession<#A> // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){0§}[0] final suspend fun <#A: kotlin/Any?> (io.ktor.client.plugins.websocket/DefaultClientWebSocketSession).io.ktor.client.plugins.websocket/receiveDeserialized(io.ktor.util.reflect/TypeInfo): #A // io.ktor.client.plugins.websocket/receiveDeserialized|receiveDeserialized@io.ktor.client.plugins.websocket.DefaultClientWebSocketSession(io.ktor.util.reflect.TypeInfo){0§}[0] final suspend fun <#A: kotlin/Any?> (io.ktor.client.statement/HttpResponse).io.ktor.client.call/body(io.ktor.util.reflect/TypeInfo): #A // io.ktor.client.call/body|body@io.ktor.client.statement.HttpResponse(io.ktor.util.reflect.TypeInfo){0§}[0] final suspend fun io.ktor.client.engine/callContext(): kotlin.coroutines/CoroutineContext // io.ktor.client.engine/callContext|callContext(){}[0] diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt index 921beccf499..32ac113cdb7 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt @@ -100,9 +100,12 @@ public val SSE: ClientPlugin = createClientPlugin( val deserializer = response.request.attributes.getOrNull(deserializerAttr) val clientSSESession = deserializer?.let { - ClientSSESessionWithDeserialization(context, object : SSESessionWithDeserialization, SSESession by session { - override val deserializer: (TypeInfo) -> (String) -> Any = deserializer - }) + ClientSSESessionWithDeserialization( + context, + object : SSESessionWithDeserialization, SSESession by session { + override val deserializer: (TypeInfo) -> (String) -> Any = deserializer + } + ) } ?: ClientSSESession(context, session) proceedWith(HttpResponseContainer(info, clientSSESession)) } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt index 07ecc22b88b..d960637a379 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt @@ -231,7 +231,8 @@ public suspend fun HttpClient.serverSentEventsSession( block: HttpRequestBuilder.() -> Unit ): ClientSSESessionWithDeserialization = processSession(reconnectionTime, showCommentEvents, showRetryEvents, block) { addAttribute( - deserializerAttr, deserialize + deserializerAttr, + deserialize ) } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api index 665dd4b923f..265086f22dd 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api @@ -7,21 +7,8 @@ public final class io/ktor/server/sse/RoutingKt { public static synthetic fun sse$default (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V } -public final class io/ktor/server/sse/SSE { - public static final field Plugin Lio/ktor/server/sse/SSE$Plugin; - public synthetic fun (Lkotlin/jvm/functions/Function1;Lkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun getSerialize ()Lkotlin/jvm/functions/Function1; -} - -public final class io/ktor/server/sse/SSE$Plugin : io/ktor/server/application/BaseApplicationPlugin { - public fun getKey ()Lio/ktor/util/AttributeKey; - public fun install (Lio/ktor/server/application/Application;Lkotlin/jvm/functions/Function1;)Lio/ktor/server/sse/SSE; - public synthetic fun install (Lio/ktor/util/pipeline/Pipeline;Lkotlin/jvm/functions/Function1;)Ljava/lang/Object; -} - -public final class io/ktor/server/sse/SSEConfig { - public fun ()V - public final fun serialize (Lkotlin/jvm/functions/Function1;)V +public final class io/ktor/server/sse/SSEKt { + public static final fun getSSE ()Lio/ktor/server/application/ApplicationPlugin; } public final class io/ktor/server/sse/SSEServerContent : io/ktor/http/content/OutgoingContent$WriteChannelContent { @@ -29,6 +16,7 @@ public final class io/ktor/server/sse/SSEServerContent : io/ktor/http/content/Ou public final fun getCall ()Lio/ktor/server/application/ApplicationCall; public fun getContentType ()Lio/ktor/http/ContentType; public final fun getHandle ()Lkotlin/jvm/functions/Function2; + public final fun getSerialize ()Lkotlin/jvm/functions/Function1; public fun toString ()Ljava/lang/String; public fun writeTo (Lio/ktor/utils/io/ByteWriteChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -37,11 +25,19 @@ public abstract interface class io/ktor/server/sse/SSESession : kotlinx/coroutin public abstract fun close (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public abstract fun getCall ()Lio/ktor/server/application/ApplicationCall; public abstract fun send (Lio/ktor/sse/ServerSentEvent;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public abstract fun send (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun send (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } public final class io/ktor/server/sse/SSESession$DefaultImpls { - public static fun send (Lio/ktor/server/sse/SSESession;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun send$default (Lio/ktor/server/sse/SSESession;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static fun send (Lio/ktor/server/sse/SSESession;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun send$default (Lio/ktor/server/sse/SSESession;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; +} + +public abstract interface class io/ktor/server/sse/SSESessionWithDeserialization : io/ktor/server/sse/SSESession { + public abstract fun getSerializer ()Lkotlin/jvm/functions/Function1; +} + +public final class io/ktor/server/sse/SSESessionWithDeserialization$DefaultImpls { + public static fun send (Lio/ktor/server/sse/SSESessionWithDeserialization;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api index 862e5edcd7b..a959c82cf5a 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api @@ -6,48 +6,43 @@ // - Show declarations: true // Library unique name: -abstract interface <#A: kotlin/Any?> io.ktor.server.sse/SSESession : kotlinx.coroutines/CoroutineScope { // io.ktor.server.sse/SSESession|null[0] +abstract interface io.ktor.server.sse/SSESession : kotlinx.coroutines/CoroutineScope { // io.ktor.server.sse/SSESession|null[0] abstract val call // io.ktor.server.sse/SSESession.call|{}call[0] abstract fun (): io.ktor.server.application/ApplicationCall // io.ktor.server.sse/SSESession.call.|(){}[0] abstract suspend fun close() // io.ktor.server.sse/SSESession.close|close(){}[0] - abstract suspend fun send(io.ktor.sse/ServerSentEvent<#A>) // io.ktor.server.sse/SSESession.send|send(io.ktor.sse.ServerSentEvent<1:0>){}[0] - open suspend fun send(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/SSESession.send|send(1:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] + abstract suspend fun send(io.ktor.sse/ServerSentEvent) // io.ktor.server.sse/SSESession.send|send(io.ktor.sse.ServerSentEvent){}[0] + open suspend fun send(kotlin/String? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/SSESession.send|send(kotlin.String?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] } -final class <#A: kotlin/Any?> io.ktor.server.sse/SSEServerContent : io.ktor.http.content/OutgoingContent.WriteChannelContent { // io.ktor.server.sse/SSEServerContent|null[0] - constructor (io.ktor.server.application/ApplicationCall, kotlin/Function1<#A, kotlin/String>, kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/SSEServerContent.|(io.ktor.server.application.ApplicationCall;kotlin.Function1<1:0,kotlin.String>;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){}[0] +abstract interface io.ktor.server.sse/SSESessionWithDeserialization : io.ktor.server.sse/SSESession { // io.ktor.server.sse/SSESessionWithDeserialization|null[0] + abstract val serializer // io.ktor.server.sse/SSESessionWithDeserialization.serializer|{}serializer[0] + abstract fun (): kotlin/Function1> // io.ktor.server.sse/SSESessionWithDeserialization.serializer.|(){}[0] +} + +final class <#A: io.ktor.server.sse/SSESession> io.ktor.server.sse/SSEServerContent : io.ktor.http.content/OutgoingContent.WriteChannelContent { // io.ktor.server.sse/SSEServerContent|null[0] + constructor (io.ktor.server.application/ApplicationCall, kotlin/Function1>?, kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>) // io.ktor.server.sse/SSEServerContent.|(io.ktor.server.application.ApplicationCall;kotlin.Function1>?;kotlin.coroutines.SuspendFunction1<1:0,kotlin.Unit>){}[0] final val call // io.ktor.server.sse/SSEServerContent.call|{}call[0] final fun (): io.ktor.server.application/ApplicationCall // io.ktor.server.sse/SSEServerContent.call.|(){}[0] final val contentType // io.ktor.server.sse/SSEServerContent.contentType|{}contentType[0] final fun (): io.ktor.http/ContentType // io.ktor.server.sse/SSEServerContent.contentType.|(){}[0] final val handle // io.ktor.server.sse/SSEServerContent.handle|{}handle[0] - final fun (): kotlin.coroutines/SuspendFunction1, kotlin/Unit> // io.ktor.server.sse/SSEServerContent.handle.|(){}[0] + final fun (): kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit> // io.ktor.server.sse/SSEServerContent.handle.|(){}[0] + final val serialize // io.ktor.server.sse/SSEServerContent.serialize|{}serialize[0] + final fun (): kotlin/Function1>? // io.ktor.server.sse/SSEServerContent.serialize.|(){}[0] final fun toString(): kotlin/String // io.ktor.server.sse/SSEServerContent.toString|toString(){}[0] final suspend fun writeTo(io.ktor.utils.io/ByteWriteChannel) // io.ktor.server.sse/SSEServerContent.writeTo|writeTo(io.ktor.utils.io.ByteWriteChannel){}[0] } -final class io.ktor.server.sse/SSE { // io.ktor.server.sse/SSE|null[0] - final val serialize // io.ktor.server.sse/SSE.serialize|{}serialize[0] - final fun (): kotlin/Function1 // io.ktor.server.sse/SSE.serialize.|(){}[0] - - final object Plugin : io.ktor.server.application/BaseApplicationPlugin { // io.ktor.server.sse/SSE.Plugin|null[0] - final val key // io.ktor.server.sse/SSE.Plugin.key|{}key[0] - final fun (): io.ktor.util/AttributeKey // io.ktor.server.sse/SSE.Plugin.key.|(){}[0] - - final fun install(io.ktor.server.application/Application, kotlin/Function1): io.ktor.server.sse/SSE // io.ktor.server.sse/SSE.Plugin.install|install(io.ktor.server.application.Application;kotlin.Function1){}[0] - } -} - -final class io.ktor.server.sse/SSEConfig { // io.ktor.server.sse/SSEConfig|null[0] - constructor () // io.ktor.server.sse/SSEConfig.|(){}[0] - - final fun <#A1: kotlin/Any> serialize(kotlin/Function1<#A1, kotlin/String>) // io.ktor.server.sse/SSEConfig.serialize|serialize(kotlin.Function1<0:0,kotlin.String>){0§}[0] -} +final val io.ktor.server.sse/SSE // io.ktor.server.sse/SSE|{}SSE[0] + final fun (): io.ktor.server.application/ApplicationPlugin // io.ktor.server.sse/SSE.|(){}[0] -final fun <#A: kotlin/Any> (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] -final fun <#A: kotlin/Any> (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] -final fun <#A: kotlin/Any?> (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/Function1<#A, kotlin/String> = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.Function1<0:0,kotlin.String>;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] -final fun <#A: kotlin/Any?> (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin/Function1<#A, kotlin/String> = ..., kotlin.coroutines/SuspendFunction1, kotlin/Unit>) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.Function1<0:0,kotlin.String>;kotlin.coroutines.SuspendFunction1,kotlin.Unit>){0§}[0] +final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.coroutines.SuspendFunction1){}[0] +final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/Function1> = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.Function1>;kotlin.coroutines.SuspendFunction1){}[0] +final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.coroutines.SuspendFunction1){}[0] +final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin/Function1> = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.Function1>;kotlin.coroutines.SuspendFunction1){}[0] +final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithDeserialization).io.ktor.server.sse/sendSerialized(#A) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithDeserialization(0:0){0§}[0] +final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithDeserialization).io.ktor.server.sse/sendSerialized(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithDeserialization(0:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){0§}[0] +final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithDeserialization).io.ktor.server.sse/sendSerialized(io.ktor.sse/ServerSentEvent<#A>) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithDeserialization(io.ktor.sse.ServerSentEvent<0:0>){0§}[0] diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt index 2171d5c2cd6..85f12cb5288 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt @@ -58,7 +58,6 @@ public interface SSESession : CoroutineScope { public suspend fun close() } - /** * Represents a server-side server-sent events session with serialization support. * An [SSESessionWithDeserialization] allows the server to send [ServerSentEvent] to the client over a single HTTP connection. diff --git a/ktor-shared/ktor-sse/api/ktor-sse.api b/ktor-shared/ktor-sse/api/ktor-sse.api index a41d3ee3ad8..3cec40fe47c 100644 --- a/ktor-shared/ktor-sse/api/ktor-sse.api +++ b/ktor-shared/ktor-sse/api/ktor-sse.api @@ -2,11 +2,20 @@ public final class io/ktor/sse/ServerSentEvent { public fun ()V public fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)V public synthetic fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Object; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/Long; + public final fun component5 ()Ljava/lang/String; + public final fun copy (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)Lio/ktor/sse/ServerSentEvent; + public static synthetic fun copy$default (Lio/ktor/sse/ServerSentEvent;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILjava/lang/Object;)Lio/ktor/sse/ServerSentEvent; + public fun equals (Ljava/lang/Object;)Z public final fun getComments ()Ljava/lang/String; public final fun getData ()Ljava/lang/Object; public final fun getEvent ()Ljava/lang/String; public final fun getId ()Ljava/lang/String; public final fun getRetry ()Ljava/lang/Long; + public fun hashCode ()I public fun toString ()Ljava/lang/String; public final fun toString (Lkotlin/jvm/functions/Function1;)Ljava/lang/String; } diff --git a/ktor-shared/ktor-sse/api/ktor-sse.klib.api b/ktor-shared/ktor-sse/api/ktor-sse.klib.api index 5d8b34c7894..9d4118ddefd 100644 --- a/ktor-shared/ktor-sse/api/ktor-sse.klib.api +++ b/ktor-shared/ktor-sse/api/ktor-sse.klib.api @@ -20,6 +20,14 @@ final class <#A: kotlin/Any?> io.ktor.sse/ServerSentEvent { // io.ktor.sse/Serve final val retry // io.ktor.sse/ServerSentEvent.retry|{}retry[0] final fun (): kotlin/Long? // io.ktor.sse/ServerSentEvent.retry.|(){}[0] + final fun component1(): #A? // io.ktor.sse/ServerSentEvent.component1|component1(){}[0] + final fun component2(): kotlin/String? // io.ktor.sse/ServerSentEvent.component2|component2(){}[0] + final fun component3(): kotlin/String? // io.ktor.sse/ServerSentEvent.component3|component3(){}[0] + final fun component4(): kotlin/Long? // io.ktor.sse/ServerSentEvent.component4|component4(){}[0] + final fun component5(): kotlin/String? // io.ktor.sse/ServerSentEvent.component5|component5(){}[0] + final fun copy(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...): io.ktor.sse/ServerSentEvent<#A> // io.ktor.sse/ServerSentEvent.copy|copy(1:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // io.ktor.sse/ServerSentEvent.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // io.ktor.sse/ServerSentEvent.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.ktor.sse/ServerSentEvent.toString|toString(){}[0] final fun toString(kotlin/Function1<#A, kotlin/String>): kotlin/String // io.ktor.sse/ServerSentEvent.toString|toString(kotlin.Function1<1:0,kotlin.String>){}[0] } From 7603195b296394c53480463d20bb682654e40266 Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:32:51 +0200 Subject: [PATCH 09/14] Fix typo --- .../ktor-server-sse/api/ktor-server-sse.api | 8 ++++++++ .../ktor-server-sse/api/ktor-server-sse.klib.api | 16 ++++++++-------- .../common/src/io/ktor/server/sse/Routing.kt | 14 +++++++------- .../src/io/ktor/server/sse/SSEServerContent.kt | 2 +- .../common/src/io/ktor/server/sse/SSESession.kt | 10 +++++----- 5 files changed, 29 insertions(+), 21 deletions(-) diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api index 265086f22dd..892b5503528 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api @@ -41,3 +41,11 @@ public final class io/ktor/server/sse/SSESessionWithDeserialization$DefaultImpls public static fun send (Lio/ktor/server/sse/SSESessionWithDeserialization;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } +public abstract interface class io/ktor/server/sse/SSESessionWithSerialization : io/ktor/server/sse/SSESession { + public abstract fun getSerializer ()Lkotlin/jvm/functions/Function1; +} + +public final class io/ktor/server/sse/SSESessionWithSerialization$DefaultImpls { + public static fun send (Lio/ktor/server/sse/SSESessionWithSerialization;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api index a959c82cf5a..e2aa41cf994 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api @@ -15,9 +15,9 @@ abstract interface io.ktor.server.sse/SSESession : kotlinx.coroutines/CoroutineS open suspend fun send(kotlin/String? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/SSESession.send|send(kotlin.String?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] } -abstract interface io.ktor.server.sse/SSESessionWithDeserialization : io.ktor.server.sse/SSESession { // io.ktor.server.sse/SSESessionWithDeserialization|null[0] - abstract val serializer // io.ktor.server.sse/SSESessionWithDeserialization.serializer|{}serializer[0] - abstract fun (): kotlin/Function1> // io.ktor.server.sse/SSESessionWithDeserialization.serializer.|(){}[0] +abstract interface io.ktor.server.sse/SSESessionWithSerialization : io.ktor.server.sse/SSESession { // io.ktor.server.sse/SSESessionWithSerialization|null[0] + abstract val serializer // io.ktor.server.sse/SSESessionWithSerialization.serializer|{}serializer[0] + abstract fun (): kotlin/Function1> // io.ktor.server.sse/SSESessionWithSerialization.serializer.|(){}[0] } final class <#A: io.ktor.server.sse/SSESession> io.ktor.server.sse/SSEServerContent : io.ktor.http.content/OutgoingContent.WriteChannelContent { // io.ktor.server.sse/SSEServerContent|null[0] @@ -40,9 +40,9 @@ final val io.ktor.server.sse/SSE // io.ktor.server.sse/SSE|{}SSE[0] final fun (): io.ktor.server.application/ApplicationPlugin // io.ktor.server.sse/SSE.|(){}[0] final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.coroutines.SuspendFunction1){}[0] -final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/Function1> = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.Function1>;kotlin.coroutines.SuspendFunction1){}[0] +final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/Function1> = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.Function1>;kotlin.coroutines.SuspendFunction1){}[0] final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.coroutines.SuspendFunction1){}[0] -final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin/Function1> = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.Function1>;kotlin.coroutines.SuspendFunction1){}[0] -final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithDeserialization).io.ktor.server.sse/sendSerialized(#A) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithDeserialization(0:0){0§}[0] -final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithDeserialization).io.ktor.server.sse/sendSerialized(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithDeserialization(0:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){0§}[0] -final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithDeserialization).io.ktor.server.sse/sendSerialized(io.ktor.sse/ServerSentEvent<#A>) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithDeserialization(io.ktor.sse.ServerSentEvent<0:0>){0§}[0] +final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin/Function1> = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.Function1>;kotlin.coroutines.SuspendFunction1){}[0] +final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithSerialization).io.ktor.server.sse/sendSerialized(#A) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithSerialization(0:0){0§}[0] +final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithSerialization).io.ktor.server.sse/sendSerialized(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithSerialization(0:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){0§}[0] +final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithSerialization).io.ktor.server.sse/sendSerialized(io.ktor.sse/ServerSentEvent<#A>) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithSerialization(io.ktor.sse.ServerSentEvent<0:0>){0§}[0] diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt index a1387d55851..e503984164e 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt @@ -44,15 +44,15 @@ public fun Route.sse(handler: suspend SSESession.() -> Unit): Unit = processSSE( * * @param path URL path at which to handle SSE requests. * @param handler function that defines the behavior of the SSE session. It is invoked when a client connects to the SSE - * endpoint. Inside the handler, you can use the functions provided by [SSESessionWithDeserialization] + * endpoint. Inside the handler, you can use the functions provided by [SSESessionWithSerialization] * to send events to the connected clients. * - * @see SSESessionWithDeserialization + * @see SSESessionWithSerialization */ public fun Route.sse( path: String, serialize: (TypeInfo) -> (Any) -> String = { { it.toString() } }, - handler: suspend SSESessionWithDeserialization.() -> Unit + handler: suspend SSESessionWithSerialization.() -> Unit ) { route(path, HttpMethod.Get) { sse(serialize, handler) @@ -64,19 +64,19 @@ public fun Route.sse( * Requires [SSE] plugin to be installed. * * @param handler function that defines the behavior of the SSE session. It is invoked when a client connects to the SSE - * endpoint. Inside the handler, you can use the functions provided by [SSESessionWithDeserialization] + * endpoint. Inside the handler, you can use the functions provided by [SSESessionWithSerialization] * to send events to the connected clients. * - * @see SSESessionWithDeserialization + * @see SSESessionWithSerialization */ public fun Route.sse( serialize: (TypeInfo) -> (Any) -> String = { { it.toString() } }, - handler: suspend SSESessionWithDeserialization.() -> Unit + handler: suspend SSESessionWithSerialization.() -> Unit ): Unit = processSSE(serialize, handler) private fun Route.processSSE( serialize: ((TypeInfo) -> (Any) -> String)?, - handler: suspend SSESessionWithDeserialization.() -> Unit + handler: suspend SSESessionWithSerialization.() -> Unit ) { plugin(SSE) diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt index cb3d6c441d6..5abf0aa31f9 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt @@ -39,7 +39,7 @@ public class SSEServerContent( coroutineScope { session = DefaultServerSSESession(channel, call, coroutineContext) as T if (serialize != null) { - session = object : SSESessionWithDeserialization, SSESession by session as DefaultServerSSESession { + session = object : SSESessionWithSerialization, SSESession by session as DefaultServerSSESession { override val serializer: (TypeInfo) -> (Any) -> String = serialize } as T } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt index 85f12cb5288..9ba57f2f6bb 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt @@ -60,18 +60,18 @@ public interface SSESession : CoroutineScope { /** * Represents a server-side server-sent events session with serialization support. - * An [SSESessionWithDeserialization] allows the server to send [ServerSentEvent] to the client over a single HTTP connection. + * An [SSESessionWithSerialization] allows the server to send [ServerSentEvent] to the client over a single HTTP connection. * * @see [SSE] */ -public interface SSESessionWithDeserialization : SSESession { +public interface SSESessionWithSerialization : SSESession { /** * Serializer for transforming data object into field `data` of `ServerSentEvent`. */ public val serializer: (TypeInfo) -> (Any) -> String } -public suspend inline fun SSESessionWithDeserialization.sendSerialized(event: ServerSentEvent) { +public suspend inline fun SSESessionWithSerialization.sendSerialized(event: ServerSentEvent) { send( ServerSentEvent( event.data?.let { @@ -85,7 +85,7 @@ public suspend inline fun SSESessionWithDeserialization.sendSe ) } -public suspend inline fun SSESessionWithDeserialization.sendSerialized( +public suspend inline fun SSESessionWithSerialization.sendSerialized( data: T? = null, event: String? = null, id: String? = null, @@ -95,6 +95,6 @@ public suspend inline fun SSESessionWithDeserialization.sendSe sendSerialized(ServerSentEvent(data, event, id, retry, comments)) } -public suspend inline fun SSESessionWithDeserialization.sendSerialized(data: T) { +public suspend inline fun SSESessionWithSerialization.sendSerialized(data: T) { send(ServerSentEvent(serializer(typeInfo()).invoke(data))) } From 687439f06ac22f35c6ba4f641b72abefd58e8f08 Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Tue, 8 Oct 2024 13:57:29 +0200 Subject: [PATCH 10/14] Fix typo --- .../common/src/io/ktor/client/plugins/sse/SSE.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt index 32ac113cdb7..a08427cee73 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt @@ -32,7 +32,7 @@ public data object SSECapability : HttpClientEngineCapability * val client = HttpClient { * install(SSE) * } - * client.sse { + * client.sse { * val event = incoming.receive() * } * ``` From fee4ecb7339c3762640c018ca949a958f42a755a Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Tue, 8 Oct 2024 15:54:31 +0200 Subject: [PATCH 11/14] Split ServerSentEvent into ServerSentEvent and ParameterizedServerSentEvent --- .../client/plugins/sse/ClientSSESession.kt | 15 ++++-- .../plugins/sse/DefaultClientSSESession.kt | 10 ++-- .../src/io/ktor/client/plugins/sse/SSE.kt | 12 ++++- .../client/engine/okhttp/OkHttpSSESession.kt | 4 +- .../tests/plugins/SSESessionParserTest.kt | Bin 8056 -> 8040 bytes .../tests/plugins/ServerSentEventsTest.kt | 2 +- .../server/sse/DefaultServerSSESession.kt | 4 +- .../src/io/ktor/server/sse/SSESession.kt | 8 +-- .../common/src/io/ktor/sse/ServerSentEvent.kt | 49 +++++++++++++----- 9 files changed, 71 insertions(+), 33 deletions(-) diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt index e552b3030c7..e3080b01721 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt @@ -17,13 +17,18 @@ public interface SSESession : CoroutineScope { /** * An incoming server-sent events flow. */ - public val incoming: Flow> + public val incoming: Flow } /** * A Server-sent events session. */ -public interface SSESessionWithDeserialization : SSESession { +public interface SSESessionWithDeserialization : CoroutineScope { + /** + * An incoming server-sent events flow. + */ + public val incoming: Flow> + /** * Deserializer for transforming field `data` of `ServerSentEvent` into desired data object. */ @@ -50,9 +55,9 @@ public inline fun SSESessionWithDeserialization.deserialize(data: St * @param event The Server-sent event containing data to deserialize. * @return The deserialized object of type [T], or null if deserialization is not successful. */ -public inline fun SSESessionWithDeserialization.deserialize(event: ServerSentEvent): T? { - return deserialize(event.data) -} +public inline fun SSESessionWithDeserialization.deserialize( + event: ParameterizedServerSentEvent +): T? = deserialize(event.data) /** * A client Server-sent events session. diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt index ddf6eaf4ccc..6febafae7f2 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/DefaultClientSSESession.kt @@ -31,10 +31,10 @@ public class DefaultClientSSESession( } } - override val incoming: Flow> + override val incoming: Flow get() = _incoming - private suspend fun ByteReadChannel.parseEvent(): ServerSentEvent? { + private suspend fun ByteReadChannel.parseEvent(): ServerSentEvent? { val data = StringBuilder() val comments = StringBuilder() var eventType: String? = null @@ -105,13 +105,13 @@ public class DefaultClientSSESession( private fun StringBuilder.toText() = toString().removeSuffix(END_OF_LINE) - private fun ServerSentEvent.isEmpty() = + private fun ServerSentEvent.isEmpty() = data == null && id == null && event == null && retry == null && comments == null - private fun ServerSentEvent.isCommentsEvent() = + private fun ServerSentEvent.isCommentsEvent() = data == null && event == null && id == null && retry == null && comments != null - private fun ServerSentEvent.isRetryEvent() = + private fun ServerSentEvent.isRetryEvent() = data == null && event == null && id == null && comments == null && retry != null } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt index a08427cee73..0a43d9204f7 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt @@ -11,11 +11,14 @@ import io.ktor.client.request.* import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.http.content.* +import io.ktor.sse.* import io.ktor.util.* import io.ktor.util.logging.* import io.ktor.util.pipeline.* import io.ktor.util.reflect.* import io.ktor.utils.io.* +import kotlinx.coroutines.flow.* +import kotlin.coroutines.* internal val LOGGER = KtorSimpleLogger("io.ktor.client.plugins.sse.SSE") @@ -102,8 +105,15 @@ public val SSE: ClientPlugin = createClientPlugin( val clientSSESession = deserializer?.let { ClientSSESessionWithDeserialization( context, - object : SSESessionWithDeserialization, SSESession by session { + object : SSESessionWithDeserialization { + override val incoming: Flow> = + session.incoming.map { event: ServerSentEvent -> + ParameterizedServerSentEvent(event.data, event.event, event.id, event.retry, event.comments) + } + override val deserializer: (TypeInfo) -> (String) -> Any = deserializer + + override val coroutineContext: CoroutineContext = session.coroutineContext } ) } ?: ClientSSESession(context, session) diff --git a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt index 3f232a2e30a..f62eab234ee 100644 --- a/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt +++ b/ktor-client/ktor-client-okhttp/jvm/src/io/ktor/client/engine/okhttp/OkHttpSSESession.kt @@ -23,9 +23,9 @@ internal class OkHttpSSESession( internal val originResponse: CompletableDeferred = CompletableDeferred() - private val _incoming = Channel>(8) + private val _incoming = Channel(8) - override val incoming: Flow> + override val incoming: Flow get() = _incoming.receiveAsFlow() override fun onOpen(eventSource: EventSource, response: Response) { diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/SSESessionParserTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/SSESessionParserTest.kt index ef91aecd2df98e5a0426cff5687aba5452e7a63a..68d906b26c8dc691259c8b252084de55de370b30 100644 GIT binary patch delta 16 Ycmexi_rh+&W0}pbW#%$Yo*?%E08Lv6jQ{`u delta 34 jcmaE1_rq?(V;K&c;F6-uymY(Gk7edE!g-S$ -> + incoming.collect { event: ParameterizedServerSentEvent -> if (firstIsCustomer) { val customer = deserialize(event.data) assertEquals(1, customer?.id) diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt index 8fd1d2b06d9..44975a88506 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/DefaultServerSSESession.kt @@ -17,7 +17,7 @@ internal class DefaultServerSSESession( ) : SSESession { private val mutex = Mutex() - override suspend fun send(event: ServerSentEvent) { + override suspend fun send(event: ServerSentEvent) { mutex.withLock { output.writeSSE(event) } @@ -30,7 +30,7 @@ internal class DefaultServerSSESession( } @OptIn(InternalAPI::class) - private suspend fun ByteWriteChannel.writeSSE(event: ServerSentEvent) { + private suspend fun ByteWriteChannel.writeSSE(event: ServerSentEvent) { writeStringUtf8(event.toString() + END_OF_LINE) flush() } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt index 9ba57f2f6bb..39463659d2b 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt @@ -25,7 +25,7 @@ public interface SSESession : CoroutineScope { /** * Sends a [ServerSentEvent] to the client. */ - public suspend fun send(event: ServerSentEvent) + public suspend fun send(event: ServerSentEvent) /** * Creates and sends a [ServerSentEvent] to the client. @@ -71,7 +71,9 @@ public interface SSESessionWithSerialization : SSESession { public val serializer: (TypeInfo) -> (Any) -> String } -public suspend inline fun SSESessionWithSerialization.sendSerialized(event: ServerSentEvent) { +public suspend inline fun SSESessionWithSerialization.sendSerialized( + event: ParameterizedServerSentEvent +) { send( ServerSentEvent( event.data?.let { @@ -92,7 +94,7 @@ public suspend inline fun SSESessionWithSerialization.sendSeri retry: Long? = null, comments: String? = null ) { - sendSerialized(ServerSentEvent(data, event, id, retry, comments)) + sendSerialized(ParameterizedServerSentEvent(data, event, id, retry, comments)) } public suspend inline fun SSESessionWithSerialization.sendSerialized(data: T) { diff --git a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt index 9cdcafe9b52..65f7cd5ab50 100644 --- a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt +++ b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt @@ -14,28 +14,49 @@ import io.ktor.utils.io.* * @property id event ID. * @property retry reconnection time, in milliseconds to wait before reconnecting. * @property comments comment lines starting with a ':' character. + * + * @see ParameterizedServerSentEvent with parameterized parameter [data] */ -public data class ServerSentEvent( - public val data: T? = null, +public data class ServerSentEvent( + public val data: String? = null, public val event: String? = null, public val id: String? = null, public val retry: Long? = null, public val comments: String? = null ) { - @OptIn(InternalAPI::class) - override fun toString(): String { - return toString { it.toString() } - } + override fun toString(): String = eventToString(data, event, id, retry, comments) +} +/** + * Server-sent event with generic parameter [data]. + * + * @property data data field of the event. + * @property event string identifying the type of event. + * @property id event ID. + * @property retry reconnection time, in milliseconds to wait before reconnecting. + * @property comments comment lines starting with a ':' character. + * + * @see ServerSentEvent with default String parameter [data] + */ +public data class ParameterizedServerSentEvent( + public val data: T? = null, + public val event: String? = null, + public val id: String? = null, + public val retry: Long? = null, + public val comments: String? = null +) { @InternalAPI - public fun toString(serializer: (T) -> String): String { - return buildString { - appendField("data", data?.let { serializer(it) }) - appendField("event", event) - appendField("id", id) - appendField("retry", retry) - appendField("", comments) - } + public fun toString(serializer: (T) -> String): String = + eventToString(data?.let { serializer(it) }, event, id, retry, comments) +} + +private fun eventToString(data: String?, event: String?, id: String?, retry: Long?, comments: String?): String { + return buildString { + appendField("data", data) + appendField("event", event) + appendField("id", id) + appendField("retry", retry) + appendField("", comments) } } From f452e5c3435c550572c59f45747e3a1f43c50bea Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Tue, 8 Oct 2024 15:59:26 +0200 Subject: [PATCH 12/14] Change `deserialize` signature --- .../client/plugins/sse/ClientSSESession.kt | 4 +-- .../src/io/ktor/client/plugins/sse/SSE.kt | 2 +- .../io/ktor/client/plugins/sse/builders.kt | 26 +++++++++---------- .../tests/plugins/ServerSentEventsTest.kt | 17 +++++------- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt index e3080b01721..c29342cd381 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt @@ -32,7 +32,7 @@ public interface SSESessionWithDeserialization : CoroutineScope { /** * Deserializer for transforming field `data` of `ServerSentEvent` into desired data object. */ - public val deserializer: (TypeInfo) -> (String) -> Any + public val deserializer: (TypeInfo, String) -> Any } /** @@ -44,7 +44,7 @@ public interface SSESessionWithDeserialization : CoroutineScope { */ public inline fun SSESessionWithDeserialization.deserialize(data: String?): T? { return data?.let { - deserializer(typeInfo()).invoke(data) as? T + deserializer(typeInfo(), data) as? T } } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt index 0a43d9204f7..64b06e958a7 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt @@ -111,7 +111,7 @@ public val SSE: ClientPlugin = createClientPlugin( ParameterizedServerSentEvent(event.data, event.event, event.id, event.retry, event.comments) } - override val deserializer: (TypeInfo) -> (String) -> Any = deserializer + override val deserializer: (TypeInfo, String) -> Any = deserializer override val coroutineContext: CoroutineContext = session.coroutineContext } diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt index d960637a379..4cbf53cbb3c 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/builders.kt @@ -18,7 +18,7 @@ internal val sseRequestAttr = AttributeKey("SSERequestFlag") internal val reconnectionTimeAttr = AttributeKey("SSEReconnectionTime") internal val showCommentEventsAttr = AttributeKey("SSEShowCommentEvents") internal val showRetryEventsAttr = AttributeKey("SSEShowRetryEvents") -internal val deserializerAttr = AttributeKey<(TypeInfo) -> (String) -> Any>("SSEDeserializer") +internal val deserializerAttr = AttributeKey<(TypeInfo, String) -> Any>("SSEDeserializer") /** * Installs the [SSE] plugin using the [config] as configuration. @@ -224,7 +224,7 @@ public suspend fun HttpClient.sse( * Opens a [ClientSSESessionWithDeserialization]. */ public suspend fun HttpClient.serverSentEventsSession( - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -244,7 +244,7 @@ public suspend fun HttpClient.serverSentEventsSession( host: String? = null, port: Int? = null, path: String? = null, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -260,7 +260,7 @@ public suspend fun HttpClient.serverSentEventsSession( */ public suspend fun HttpClient.serverSentEventsSession( urlString: String, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -276,7 +276,7 @@ public suspend fun HttpClient.serverSentEventsSession( */ public suspend fun HttpClient.serverSentEvents( request: HttpRequestBuilder.() -> Unit, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -302,7 +302,7 @@ public suspend fun HttpClient.serverSentEvents( host: String? = null, port: Int? = null, path: String? = null, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -327,7 +327,7 @@ public suspend fun HttpClient.serverSentEvents( */ public suspend fun HttpClient.serverSentEvents( urlString: String, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -351,7 +351,7 @@ public suspend fun HttpClient.serverSentEvents( * Opens a [ClientSSESessionWithDeserialization]. */ public suspend fun HttpClient.sseSession( - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -367,7 +367,7 @@ public suspend fun HttpClient.sseSession( host: String? = null, port: Int? = null, path: String? = null, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -389,7 +389,7 @@ public suspend fun HttpClient.sseSession( */ public suspend fun HttpClient.sseSession( urlString: String, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -402,7 +402,7 @@ public suspend fun HttpClient.sseSession( */ public suspend fun HttpClient.sse( request: HttpRequestBuilder.() -> Unit, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -418,7 +418,7 @@ public suspend fun HttpClient.sse( port: Int? = null, path: String? = null, request: HttpRequestBuilder.() -> Unit = {}, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, @@ -442,7 +442,7 @@ public suspend fun HttpClient.sse( public suspend fun HttpClient.sse( urlString: String, request: HttpRequestBuilder.() -> Unit = {}, - deserialize: (TypeInfo) -> (String) -> Any, + deserialize: (TypeInfo, String) -> Any, reconnectionTime: Duration? = null, showCommentEvents: Boolean? = null, showRetryEvents: Boolean? = null, diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt index 56b2c180d7c..20a52ff693e 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt @@ -17,7 +17,6 @@ import io.ktor.http.* import io.ktor.http.content.* import io.ktor.sse.* import io.ktor.test.dispatcher.* -import io.ktor.util.reflect.* import io.ktor.utils.io.* import io.ktor.utils.io.charsets.* import kotlinx.coroutines.* @@ -451,7 +450,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { url("$TEST_SERVER/sse/person") parameter("times", count) }, - deserialize = { { Person(it) } } + deserialize = { _, it -> Person(it) } ) { incoming.collectIndexed { i, event -> val person = deserialize(event) @@ -472,7 +471,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> assertFailsWith { - client.sse({ url("$TEST_SERVER/sse/person") }, { { Data(it) } }) { + client.sse({ url("$TEST_SERVER/sse/person") }, { _, it -> Data(it) }) { incoming.single().apply { val data = deserialize(data) assertEquals("Name 0", data?.name) @@ -492,12 +491,12 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { } test { client -> - client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { _ -> { str -> Person1(str) } }) { + client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { _, str -> Person1(str) }) { incoming.single().apply { assertEquals("Name 0", deserialize(data)?.name) } } - client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { _ -> { str -> Person2(str) } }) { + client.sse({ url("$TEST_SERVER/sse/person") }, deserialize = { _, str -> Person2(str) }) { incoming.single().apply { assertEquals("Name 0", deserialize(data)?.middleName) } @@ -520,11 +519,9 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { test { client -> client.sse({ url("$TEST_SERVER/sse/json") - }, deserialize = { typeInfo: TypeInfo -> - { jsonString: String -> - val serializer = Json.serializersModule.serializer(typeInfo.kotlinType!!) - Json.decodeFromString(serializer, jsonString) ?: Exception() - } + }, deserialize = { typeInfo, jsonString -> + val serializer = Json.serializersModule.serializer(typeInfo.kotlinType!!) + Json.decodeFromString(serializer, jsonString) ?: Exception() }) { var firstIsCustomer = true incoming.collect { event: ParameterizedServerSentEvent -> From 937c39f0cb1588d46f06ce72af782d8941729230 Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Tue, 8 Oct 2024 16:23:29 +0200 Subject: [PATCH 13/14] Change `serialize` signature --- .../common/src/io/ktor/server/sse/Routing.kt | 6 +-- .../io/ktor/server/sse/SSEServerContent.kt | 4 +- .../src/io/ktor/server/sse/SSESession.kt | 6 +-- .../ktor/server/sse/ServerSentEventsTest.kt | 52 ++++++++----------- 4 files changed, 31 insertions(+), 37 deletions(-) diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt index e503984164e..c723db98f7f 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/Routing.kt @@ -51,7 +51,7 @@ public fun Route.sse(handler: suspend SSESession.() -> Unit): Unit = processSSE( */ public fun Route.sse( path: String, - serialize: (TypeInfo) -> (Any) -> String = { { it.toString() } }, + serialize: (TypeInfo, Any) -> String = { _, it -> it.toString() }, handler: suspend SSESessionWithSerialization.() -> Unit ) { route(path, HttpMethod.Get) { @@ -70,12 +70,12 @@ public fun Route.sse( * @see SSESessionWithSerialization */ public fun Route.sse( - serialize: (TypeInfo) -> (Any) -> String = { { it.toString() } }, + serialize: (TypeInfo, Any) -> String = { _, it -> it.toString() }, handler: suspend SSESessionWithSerialization.() -> Unit ): Unit = processSSE(serialize, handler) private fun Route.processSSE( - serialize: ((TypeInfo) -> (Any) -> String)?, + serialize: ((TypeInfo, Any) -> String)?, handler: suspend SSESessionWithSerialization.() -> Unit ) { plugin(SSE) diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt index 5abf0aa31f9..36b51f28b95 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSEServerContent.kt @@ -26,7 +26,7 @@ import kotlinx.coroutines.* */ public class SSEServerContent( public val call: ApplicationCall, - public val serialize: ((TypeInfo) -> (Any) -> String)?, + public val serialize: ((TypeInfo, Any) -> String)?, public val handle: suspend T.() -> Unit ) : OutgoingContent.WriteChannelContent() { override val contentType: ContentType = ContentType.Text.EventStream @@ -40,7 +40,7 @@ public class SSEServerContent( session = DefaultServerSSESession(channel, call, coroutineContext) as T if (serialize != null) { session = object : SSESessionWithSerialization, SSESession by session as DefaultServerSSESession { - override val serializer: (TypeInfo) -> (Any) -> String = serialize + override val serializer: (TypeInfo, Any) -> String = serialize } as T } session?.handle() diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt index 39463659d2b..b3f6847dd47 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt @@ -68,7 +68,7 @@ public interface SSESessionWithSerialization : SSESession { /** * Serializer for transforming data object into field `data` of `ServerSentEvent`. */ - public val serializer: (TypeInfo) -> (Any) -> String + public val serializer: (TypeInfo, Any) -> String } public suspend inline fun SSESessionWithSerialization.sendSerialized( @@ -77,7 +77,7 @@ public suspend inline fun SSESessionWithSerialization.sendSeri send( ServerSentEvent( event.data?.let { - serializer(typeInfo()).invoke(it) + serializer(typeInfo(), it) }, event.event, event.id, @@ -98,5 +98,5 @@ public suspend inline fun SSESessionWithSerialization.sendSeri } public suspend inline fun SSESessionWithSerialization.sendSerialized(data: T) { - send(ServerSentEvent(serializer(typeInfo()).invoke(data))) + send(ServerSentEvent(serializer(typeInfo(), data))) } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt index 9e35b052704..937d5e32cda 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt @@ -201,16 +201,14 @@ class ServerSentEventsTest { fun testSerializerInRoute() = testApplication { install(SSE) routing { - sse("/person", serialize = { typeInfo -> - { data -> - when (typeInfo.type) { - Person1::class -> { - "Age ${(data as Person1).age}" - } - - else -> { - data.toString() - } + sse("/person", serialize = { typeInfo, data -> + when (typeInfo.type) { + Person1::class -> { + "Age ${(data as Person1).age}" + } + + else -> { + data.toString() } } }) { @@ -233,20 +231,18 @@ class ServerSentEventsTest { fun testDifferentSerializers() = testApplication { install(SSE) routing { - sse(serialize = { typeInfo -> - { data -> - when (typeInfo.type) { - Person1::class -> { - "Age ${(data as Person1).age}" - } - - Person2::class -> { - "Number ${(data as Person2).number}" - } - - else -> { - data.toString() - } + sse(serialize = { typeInfo, data -> + when (typeInfo.type) { + Person1::class -> { + "Age ${(data as Person1).age}" + } + + Person2::class -> { + "Number ${(data as Person2).number}" + } + + else -> { + data.toString() } } }) { @@ -279,11 +275,9 @@ class ServerSentEventsTest { fun testJsonDeserializer() = testApplication { install(SSE) routing { - sse("/json", serialize = { typeInfo -> - { - val serializer = Json.serializersModule.serializer(typeInfo.kotlinType!!) - Json.encodeToString(serializer, it) - } + sse("/json", serialize = { typeInfo, it -> + val serializer = Json.serializersModule.serializer(typeInfo.kotlinType!!) + Json.encodeToString(serializer, it) }) { sendSerialized(Customer(0, "Jet", "Brains")) sendSerialized(Product(0, listOf(100, 200))) From 19f01fe63dbca875ed9fa10f6c00c341e8b157f8 Mon Sep 17 00:00:00 2001 From: Mariia Skripchenko <61115099+marychatte@users.noreply.github.com> Date: Thu, 10 Oct 2024 19:07:58 +0200 Subject: [PATCH 14/14] Change `ServerSentEvent` --- .../ktor-client-core/api/ktor-client-core.api | 55 ++++++++++--------- .../api/ktor-client-core.klib.api | 42 +++++++------- .../client/plugins/sse/ClientSSESession.kt | 4 +- .../src/io/ktor/client/plugins/sse/SSE.kt | 4 +- .../tests/plugins/ServerSentEventsTest.kt | 2 +- .../ktor-server-sse/api/ktor-server-sse.api | 22 +++----- .../api/ktor-server-sse.klib.api | 18 +++--- .../src/io/ktor/server/sse/SSESession.kt | 10 ++-- .../ktor/server/sse/ServerSentEventsTest.kt | 10 ++-- ktor-shared/ktor-sse/api/ktor-sse.api | 54 ++++++++++++++---- ktor-shared/ktor-sse/api/ktor-sse.klib.api | 50 +++++++++++++++-- .../common/src/io/ktor/sse/ServerSentEvent.kt | 48 +++++++++++----- 12 files changed, 200 insertions(+), 119 deletions(-) diff --git a/ktor-client/ktor-client-core/api/ktor-client-core.api b/ktor-client/ktor-client-core/api/ktor-client-core.api index dfda1c2ff3f..9c2d08860e8 100644 --- a/ktor-client/ktor-client-core/api/ktor-client-core.api +++ b/ktor-client/ktor-client-core/api/ktor-client-core.api @@ -767,48 +767,48 @@ public final class io/ktor/client/plugins/sse/BuildersKt { public static synthetic fun serverSentEvents-1wIb-0I$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEvents-3bFjkrY (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEvents-3bFjkrY$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEvents-BqdlHlk (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEvents-BqdlHlk$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEvents-Mswn-_c (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEvents-Mswn-_c$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEvents-BqdlHlk (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEvents-BqdlHlk$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEvents-Mswn-_c (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEvents-Mswn-_c$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEvents-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEvents-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEvents-pTj2aPc (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEvents-pTj2aPc$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEventsSession-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEventsSession-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEvents-pTj2aPc (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEvents-pTj2aPc$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEventsSession-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEventsSession-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEventsSession-i8z2VEo (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEventsSession-i8z2VEo$default (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEventsSession-mY9Nd3A (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun serverSentEventsSession-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun serverSentEventsSession-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEventsSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static synthetic fun serverSentEventsSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun serverSentEventsSession-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun serverSentEventsSession-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static synthetic fun serverSentEventsSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun serverSentEventsSession-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun serverSentEventsSession-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun serverSentEventsSession-xEWcMm4 (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun serverSentEventsSession-xEWcMm4$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sse-BAHpl2s (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sse-BAHpl2s$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sse-BAHpl2s (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sse-BAHpl2s$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sse-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun sse-Mswn-_c (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun sse-Mswn-_c (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sse-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static synthetic fun sse-Mswn-_c$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sse-Q9yt8Vw (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sse-Q9yt8Vw$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static synthetic fun sse-Mswn-_c$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sse-Q9yt8Vw (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sse-Q9yt8Vw$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sse-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sse-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sse-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sse-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sseSession-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sseSession-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sseSession-Mswn-_c (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sseSession-Mswn-_c$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sseSession-i8z2VEo (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sseSession-i8z2VEo$default (Lio/ktor/client/HttpClient;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sseSession-mY9Nd3A (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static final fun sseSession-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static final fun sseSession-mY9Nd3A (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sseSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static synthetic fun sseSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; - public static final fun sseSession-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; - public static synthetic fun sseSession-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static synthetic fun sseSession-mY9Nd3A$default (Lio/ktor/client/HttpClient;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; + public static final fun sseSession-tL6_L-A (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun sseSession-tL6_L-A$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public static final fun sseSession-xEWcMm4 (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun sseSession-xEWcMm4$default (Lio/ktor/client/HttpClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/String;Lkotlin/time/Duration;Ljava/lang/Boolean;Ljava/lang/Boolean;Lkotlin/jvm/functions/Function1;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } @@ -824,7 +824,7 @@ public final class io/ktor/client/plugins/sse/ClientSSESessionWithDeserializatio public fun (Lio/ktor/client/call/HttpClientCall;Lio/ktor/client/plugins/sse/SSESessionWithDeserialization;)V public final fun getCall ()Lio/ktor/client/call/HttpClientCall; public fun getCoroutineContext ()Lkotlin/coroutines/CoroutineContext; - public fun getDeserializer ()Lkotlin/jvm/functions/Function1; + public fun getDeserializer ()Lkotlin/jvm/functions/Function2; public fun getIncoming ()Lkotlinx/coroutines/flow/Flow; } @@ -877,8 +877,9 @@ public abstract interface class io/ktor/client/plugins/sse/SSESession : kotlinx/ public abstract fun getIncoming ()Lkotlinx/coroutines/flow/Flow; } -public abstract interface class io/ktor/client/plugins/sse/SSESessionWithDeserialization : io/ktor/client/plugins/sse/SSESession { - public abstract fun getDeserializer ()Lkotlin/jvm/functions/Function1; +public abstract interface class io/ktor/client/plugins/sse/SSESessionWithDeserialization : kotlinx/coroutines/CoroutineScope { + public abstract fun getDeserializer ()Lkotlin/jvm/functions/Function2; + public abstract fun getIncoming ()Lkotlinx/coroutines/flow/Flow; } public final class io/ktor/client/plugins/websocket/BuildersKt { diff --git a/ktor-client/ktor-client-core/api/ktor-client-core.klib.api b/ktor-client/ktor-client-core/api/ktor-client-core.klib.api index d6e377276f5..61823d6620e 100644 --- a/ktor-client/ktor-client-core/api/ktor-client-core.klib.api +++ b/ktor-client/ktor-client-core/api/ktor-client-core.klib.api @@ -79,12 +79,14 @@ abstract interface io.ktor.client.plugins.cookies/CookiesStorage : io.ktor.utils abstract interface io.ktor.client.plugins.sse/SSESession : kotlinx.coroutines/CoroutineScope { // io.ktor.client.plugins.sse/SSESession|null[0] abstract val incoming // io.ktor.client.plugins.sse/SSESession.incoming|{}incoming[0] - abstract fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/SSESession.incoming.|(){}[0] + abstract fun (): kotlinx.coroutines.flow/Flow // io.ktor.client.plugins.sse/SSESession.incoming.|(){}[0] } -abstract interface io.ktor.client.plugins.sse/SSESessionWithDeserialization : io.ktor.client.plugins.sse/SSESession { // io.ktor.client.plugins.sse/SSESessionWithDeserialization|null[0] +abstract interface io.ktor.client.plugins.sse/SSESessionWithDeserialization : kotlinx.coroutines/CoroutineScope { // io.ktor.client.plugins.sse/SSESessionWithDeserialization|null[0] abstract val deserializer // io.ktor.client.plugins.sse/SSESessionWithDeserialization.deserializer|{}deserializer[0] - abstract fun (): kotlin/Function1> // io.ktor.client.plugins.sse/SSESessionWithDeserialization.deserializer.|(){}[0] + abstract fun (): kotlin/Function2 // io.ktor.client.plugins.sse/SSESessionWithDeserialization.deserializer.|(){}[0] + abstract val incoming // io.ktor.client.plugins.sse/SSESessionWithDeserialization.incoming|{}incoming[0] + abstract fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/SSESessionWithDeserialization.incoming.|(){}[0] } abstract interface io.ktor.client.plugins.websocket/ClientWebSocketSession : io.ktor.websocket/WebSocketSession { // io.ktor.client.plugins.websocket/ClientWebSocketSession|null[0] @@ -439,7 +441,7 @@ final class io.ktor.client.plugins.sse/ClientSSESession : io.ktor.client.plugins final val coroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext|{}coroutineContext[0] final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/ClientSSESession.coroutineContext.|(){}[0] final val incoming // io.ktor.client.plugins.sse/ClientSSESession.incoming|{}incoming[0] - final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/ClientSSESession.incoming.|(){}[0] + final fun (): kotlinx.coroutines.flow/Flow // io.ktor.client.plugins.sse/ClientSSESession.incoming.|(){}[0] } final class io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization : io.ktor.client.plugins.sse/SSESessionWithDeserialization { // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization|null[0] @@ -450,9 +452,9 @@ final class io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization : io. final val coroutineContext // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.coroutineContext|{}coroutineContext[0] final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.coroutineContext.|(){}[0] final val deserializer // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.deserializer|{}deserializer[0] - final fun (): kotlin/Function1> // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.deserializer.|(){}[0] + final fun (): kotlin/Function2 // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.deserializer.|(){}[0] final val incoming // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.incoming|{}incoming[0] - final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.incoming.|(){}[0] + final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization.incoming.|(){}[0] } final class io.ktor.client.plugins.sse/DefaultClientSSESession : io.ktor.client.plugins.sse/SSESession { // io.ktor.client.plugins.sse/DefaultClientSSESession|null[0] @@ -461,7 +463,7 @@ final class io.ktor.client.plugins.sse/DefaultClientSSESession : io.ktor.client. final val coroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext|{}coroutineContext[0] final fun (): kotlin.coroutines/CoroutineContext // io.ktor.client.plugins.sse/DefaultClientSSESession.coroutineContext.|(){}[0] final val incoming // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming|{}incoming[0] - final fun (): kotlinx.coroutines.flow/Flow> // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming.|(){}[0] + final fun (): kotlinx.coroutines.flow/Flow // io.ktor.client.plugins.sse/DefaultClientSSESession.incoming.|(){}[0] } final class io.ktor.client.plugins.sse/SSEClientContent : io.ktor.http.content/OutgoingContent.ContentWrapper { // io.ktor.client.plugins.sse/SSEClientContent|null[0] @@ -1405,7 +1407,7 @@ final fun io.ktor.client/HttpClient(io.ktor.client.engine/HttpClientEngine, kotl final fun io.ktor.client/HttpClient(kotlin/Function1, kotlin/Unit> = ...): io.ktor.client/HttpClient // io.ktor.client/HttpClient|HttpClient(kotlin.Function1,kotlin.Unit>){}[0] final inline fun (io.ktor.client.request.forms/FormBuilder).io.ktor.client.request.forms/append(kotlin/String, io.ktor.http/Headers = ..., kotlin/Long? = ..., crossinline kotlin/Function1) // io.ktor.client.request.forms/append|append@io.ktor.client.request.forms.FormBuilder(kotlin.String;io.ktor.http.Headers;kotlin.Long?;kotlin.Function1){}[0] final inline fun <#A: kotlin/Any?> io.ktor.client.plugins/unwrapRequestTimeoutException(kotlin/Function0<#A>): #A // io.ktor.client.plugins/unwrapRequestTimeoutException|unwrapRequestTimeoutException(kotlin.Function0<0:0>){0§}[0] -final inline fun <#A: reified kotlin/Any?> (io.ktor.client.plugins.sse/SSESessionWithDeserialization).io.ktor.client.plugins.sse/deserialize(io.ktor.sse/ServerSentEvent): #A? // io.ktor.client.plugins.sse/deserialize|deserialize@io.ktor.client.plugins.sse.SSESessionWithDeserialization(io.ktor.sse.ServerSentEvent){0§}[0] +final inline fun <#A: reified kotlin/Any?> (io.ktor.client.plugins.sse/SSESessionWithDeserialization).io.ktor.client.plugins.sse/deserialize(io.ktor.sse/ServerSentEventParsed): #A? // io.ktor.client.plugins.sse/deserialize|deserialize@io.ktor.client.plugins.sse.SSESessionWithDeserialization(io.ktor.sse.ServerSentEventParsed){0§}[0] final inline fun <#A: reified kotlin/Any?> (io.ktor.client.plugins.sse/SSESessionWithDeserialization).io.ktor.client.plugins.sse/deserialize(kotlin/String?): #A? // io.ktor.client.plugins.sse/deserialize|deserialize@io.ktor.client.plugins.sse.SSESessionWithDeserialization(kotlin.String?){0§}[0] final inline fun <#A: reified kotlin/Any?> (io.ktor.client.request/HttpRequestBuilder).io.ktor.client.request/setBody(#A) // io.ktor.client.request/setBody|setBody@io.ktor.client.request.HttpRequestBuilder(0:0){0§}[0] final suspend fun (io.ktor.client.call/HttpClientCall).io.ktor.client.call/save(): io.ktor.client.call/HttpClientCall // io.ktor.client.call/save|save@io.ktor.client.call.HttpClientCall(){}[0] @@ -1423,29 +1425,29 @@ final suspend fun (io.ktor.client.statement/HttpResponse).io.ktor.client.stateme final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.cookies/cookies(io.ktor.http/Url): kotlin.collections/List // io.ktor.client.plugins.cookies/cookies|cookies@io.ktor.client.HttpClient(io.ktor.http.Url){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.cookies/cookies(kotlin/String): kotlin.collections/List // io.ktor.client.plugins.cookies/cookies|cookies@io.ktor.client.HttpClient(kotlin.String){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/Function1, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/Function1, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/Function1, kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String, kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEvents(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/serverSentEvents|serverSentEvents@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String, kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/serverSentEventsSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/serverSentEventsSession|serverSentEventsSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/Function1, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/Function1, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/Function1, kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.Function1;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String, kotlin/Function1 = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String, kotlin/Function1 = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String, kotlin/Function1 = ..., kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sse(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.sse/sse|sse@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String, kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String, kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESession // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] -final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1>, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1>;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] +final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.sse/sseSession(kotlin/String? = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function2, kotlin.time/Duration? = ..., kotlin/Boolean? = ..., kotlin/Boolean? = ..., kotlin/Function1 = ...): io.ktor.client.plugins.sse/ClientSSESessionWithDeserialization // io.ktor.client.plugins.sse/sseSession|sseSession@io.ktor.client.HttpClient(kotlin.String?;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function2;kotlin.time.Duration?;kotlin.Boolean?;kotlin.Boolean?;kotlin.Function1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/webSocket(io.ktor.http/HttpMethod = ..., kotlin/String? = ..., kotlin/Int? = ..., kotlin/String? = ..., kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/webSocket|webSocket@io.ktor.client.HttpClient(io.ktor.http.HttpMethod;kotlin.String?;kotlin.Int?;kotlin.String?;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/webSocket(kotlin/Function1, kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/webSocket|webSocket@io.ktor.client.HttpClient(kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] final suspend fun (io.ktor.client/HttpClient).io.ktor.client.plugins.websocket/webSocket(kotlin/String, kotlin/Function1 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.client.plugins.websocket/webSocket|webSocket@io.ktor.client.HttpClient(kotlin.String;kotlin.Function1;kotlin.coroutines.SuspendFunction1){}[0] diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt index c29342cd381..314ca086e62 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/ClientSSESession.kt @@ -27,7 +27,7 @@ public interface SSESessionWithDeserialization : CoroutineScope { /** * An incoming server-sent events flow. */ - public val incoming: Flow> + public val incoming: Flow> /** * Deserializer for transforming field `data` of `ServerSentEvent` into desired data object. @@ -56,7 +56,7 @@ public inline fun SSESessionWithDeserialization.deserialize(data: St * @return The deserialized object of type [T], or null if deserialization is not successful. */ public inline fun SSESessionWithDeserialization.deserialize( - event: ParameterizedServerSentEvent + event: ServerSentEventParsed ): T? = deserialize(event.data) /** diff --git a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt index 64b06e958a7..043cefc3155 100644 --- a/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt +++ b/ktor-client/ktor-client-core/common/src/io/ktor/client/plugins/sse/SSE.kt @@ -106,9 +106,9 @@ public val SSE: ClientPlugin = createClientPlugin( ClientSSESessionWithDeserialization( context, object : SSESessionWithDeserialization { - override val incoming: Flow> = + override val incoming: Flow> = session.incoming.map { event: ServerSentEvent -> - ParameterizedServerSentEvent(event.data, event.event, event.id, event.retry, event.comments) + ServerSentEventParsed(event.data, event.event, event.id, event.retry, event.comments) } override val deserializer: (TypeInfo, String) -> Any = deserializer diff --git a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt index 20a52ff693e..c66ae6cb175 100644 --- a/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt +++ b/ktor-client/ktor-client-tests/common/test/io/ktor/client/tests/plugins/ServerSentEventsTest.kt @@ -524,7 +524,7 @@ class ServerSentEventsTest : ClientLoader(timeoutSeconds = 120) { Json.decodeFromString(serializer, jsonString) ?: Exception() }) { var firstIsCustomer = true - incoming.collect { event: ParameterizedServerSentEvent -> + incoming.collect { event: ServerSentEventParsed -> if (firstIsCustomer) { val customer = deserialize(event.data) assertEquals(1, customer?.id) diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api index 892b5503528..9cb449dd6bf 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.api @@ -1,10 +1,10 @@ public final class io/ktor/server/sse/RoutingKt { - public static final fun sse (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V public static final fun sse (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function2;)V - public static final fun sse (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V + public static final fun sse (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)V public static final fun sse (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function2;)V - public static synthetic fun sse$default (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V - public static synthetic fun sse$default (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V + public static final fun sse (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)V + public static synthetic fun sse$default (Lio/ktor/server/routing/Route;Ljava/lang/String;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V + public static synthetic fun sse$default (Lio/ktor/server/routing/Route;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;ILjava/lang/Object;)V } public final class io/ktor/server/sse/SSEKt { @@ -12,11 +12,11 @@ public final class io/ktor/server/sse/SSEKt { } public final class io/ktor/server/sse/SSEServerContent : io/ktor/http/content/OutgoingContent$WriteChannelContent { - public fun (Lio/ktor/server/application/ApplicationCall;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V + public fun (Lio/ktor/server/application/ApplicationCall;Lkotlin/jvm/functions/Function2;Lkotlin/jvm/functions/Function2;)V public final fun getCall ()Lio/ktor/server/application/ApplicationCall; public fun getContentType ()Lio/ktor/http/ContentType; public final fun getHandle ()Lkotlin/jvm/functions/Function2; - public final fun getSerialize ()Lkotlin/jvm/functions/Function1; + public final fun getSerialize ()Lkotlin/jvm/functions/Function2; public fun toString ()Ljava/lang/String; public fun writeTo (Lio/ktor/utils/io/ByteWriteChannel;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; } @@ -33,16 +33,8 @@ public final class io/ktor/server/sse/SSESession$DefaultImpls { public static synthetic fun send$default (Lio/ktor/server/sse/SSESession;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; } -public abstract interface class io/ktor/server/sse/SSESessionWithDeserialization : io/ktor/server/sse/SSESession { - public abstract fun getSerializer ()Lkotlin/jvm/functions/Function1; -} - -public final class io/ktor/server/sse/SSESessionWithDeserialization$DefaultImpls { - public static fun send (Lio/ktor/server/sse/SSESessionWithDeserialization;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; -} - public abstract interface class io/ktor/server/sse/SSESessionWithSerialization : io/ktor/server/sse/SSESession { - public abstract fun getSerializer ()Lkotlin/jvm/functions/Function1; + public abstract fun getSerializer ()Lkotlin/jvm/functions/Function2; } public final class io/ktor/server/sse/SSESessionWithSerialization$DefaultImpls { diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api index e2aa41cf994..2a92d3d6e2a 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/api/ktor-server-sse.klib.api @@ -11,17 +11,17 @@ abstract interface io.ktor.server.sse/SSESession : kotlinx.coroutines/CoroutineS abstract fun (): io.ktor.server.application/ApplicationCall // io.ktor.server.sse/SSESession.call.|(){}[0] abstract suspend fun close() // io.ktor.server.sse/SSESession.close|close(){}[0] - abstract suspend fun send(io.ktor.sse/ServerSentEvent) // io.ktor.server.sse/SSESession.send|send(io.ktor.sse.ServerSentEvent){}[0] + abstract suspend fun send(io.ktor.sse/ServerSentEvent) // io.ktor.server.sse/SSESession.send|send(io.ktor.sse.ServerSentEvent){}[0] open suspend fun send(kotlin/String? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/SSESession.send|send(kotlin.String?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] } abstract interface io.ktor.server.sse/SSESessionWithSerialization : io.ktor.server.sse/SSESession { // io.ktor.server.sse/SSESessionWithSerialization|null[0] abstract val serializer // io.ktor.server.sse/SSESessionWithSerialization.serializer|{}serializer[0] - abstract fun (): kotlin/Function1> // io.ktor.server.sse/SSESessionWithSerialization.serializer.|(){}[0] + abstract fun (): kotlin/Function2 // io.ktor.server.sse/SSESessionWithSerialization.serializer.|(){}[0] } final class <#A: io.ktor.server.sse/SSESession> io.ktor.server.sse/SSEServerContent : io.ktor.http.content/OutgoingContent.WriteChannelContent { // io.ktor.server.sse/SSEServerContent|null[0] - constructor (io.ktor.server.application/ApplicationCall, kotlin/Function1>?, kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>) // io.ktor.server.sse/SSEServerContent.|(io.ktor.server.application.ApplicationCall;kotlin.Function1>?;kotlin.coroutines.SuspendFunction1<1:0,kotlin.Unit>){}[0] + constructor (io.ktor.server.application/ApplicationCall, kotlin/Function2?, kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit>) // io.ktor.server.sse/SSEServerContent.|(io.ktor.server.application.ApplicationCall;kotlin.Function2?;kotlin.coroutines.SuspendFunction1<1:0,kotlin.Unit>){}[0] final val call // io.ktor.server.sse/SSEServerContent.call|{}call[0] final fun (): io.ktor.server.application/ApplicationCall // io.ktor.server.sse/SSEServerContent.call.|(){}[0] @@ -30,7 +30,7 @@ final class <#A: io.ktor.server.sse/SSESession> io.ktor.server.sse/SSEServerCont final val handle // io.ktor.server.sse/SSEServerContent.handle|{}handle[0] final fun (): kotlin.coroutines/SuspendFunction1<#A, kotlin/Unit> // io.ktor.server.sse/SSEServerContent.handle.|(){}[0] final val serialize // io.ktor.server.sse/SSEServerContent.serialize|{}serialize[0] - final fun (): kotlin/Function1>? // io.ktor.server.sse/SSEServerContent.serialize.|(){}[0] + final fun (): kotlin/Function2? // io.ktor.server.sse/SSEServerContent.serialize.|(){}[0] final fun toString(): kotlin/String // io.ktor.server.sse/SSEServerContent.toString|toString(){}[0] final suspend fun writeTo(io.ktor.utils.io/ByteWriteChannel) // io.ktor.server.sse/SSEServerContent.writeTo|writeTo(io.ktor.utils.io.ByteWriteChannel){}[0] @@ -40,9 +40,9 @@ final val io.ktor.server.sse/SSE // io.ktor.server.sse/SSE|{}SSE[0] final fun (): io.ktor.server.application/ApplicationPlugin // io.ktor.server.sse/SSE.|(){}[0] final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.coroutines.SuspendFunction1){}[0] -final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/Function1> = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.Function1>;kotlin.coroutines.SuspendFunction1){}[0] +final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/Function2 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.Function2;kotlin.coroutines.SuspendFunction1){}[0] final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.coroutines.SuspendFunction1){}[0] -final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin/Function1> = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.Function1>;kotlin.coroutines.SuspendFunction1){}[0] -final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithSerialization).io.ktor.server.sse/sendSerialized(#A) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithSerialization(0:0){0§}[0] -final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithSerialization).io.ktor.server.sse/sendSerialized(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithSerialization(0:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){0§}[0] -final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithSerialization).io.ktor.server.sse/sendSerialized(io.ktor.sse/ServerSentEvent<#A>) // io.ktor.server.sse/sendSerialized|sendSerialized@io.ktor.server.sse.SSESessionWithSerialization(io.ktor.sse.ServerSentEvent<0:0>){0§}[0] +final fun (io.ktor.server.routing/Route).io.ktor.server.sse/sse(kotlin/String, kotlin/Function2 = ..., kotlin.coroutines/SuspendFunction1) // io.ktor.server.sse/sse|sse@io.ktor.server.routing.Route(kotlin.String;kotlin.Function2;kotlin.coroutines.SuspendFunction1){}[0] +final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithSerialization).io.ktor.server.sse/send(#A) // io.ktor.server.sse/send|send@io.ktor.server.sse.SSESessionWithSerialization(0:0){0§}[0] +final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithSerialization).io.ktor.server.sse/send(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.server.sse/send|send@io.ktor.server.sse.SSESessionWithSerialization(0:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){0§}[0] +final suspend inline fun <#A: reified kotlin/Any> (io.ktor.server.sse/SSESessionWithSerialization).io.ktor.server.sse/send(io.ktor.sse/ServerSentEventParsed<#A>) // io.ktor.server.sse/send|send@io.ktor.server.sse.SSESessionWithSerialization(io.ktor.sse.ServerSentEventParsed<0:0>){0§}[0] diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt index b3f6847dd47..a0c6573645a 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/src/io/ktor/server/sse/SSESession.kt @@ -71,9 +71,7 @@ public interface SSESessionWithSerialization : SSESession { public val serializer: (TypeInfo, Any) -> String } -public suspend inline fun SSESessionWithSerialization.sendSerialized( - event: ParameterizedServerSentEvent -) { +public suspend inline fun SSESessionWithSerialization.send(event: ServerSentEventParsed) { send( ServerSentEvent( event.data?.let { @@ -87,16 +85,16 @@ public suspend inline fun SSESessionWithSerialization.sendSeri ) } -public suspend inline fun SSESessionWithSerialization.sendSerialized( +public suspend inline fun SSESessionWithSerialization.send( data: T? = null, event: String? = null, id: String? = null, retry: Long? = null, comments: String? = null ) { - sendSerialized(ParameterizedServerSentEvent(data, event, id, retry, comments)) + send(ServerSentEventParsed(data, event, id, retry, comments)) } -public suspend inline fun SSESessionWithSerialization.sendSerialized(data: T) { +public suspend inline fun SSESessionWithSerialization.send(data: T) { send(ServerSentEvent(serializer(typeInfo(), data))) } diff --git a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt index 937d5e32cda..ff2a715ad69 100644 --- a/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt +++ b/ktor-server/ktor-server-plugins/ktor-server-sse/common/test/io/ktor/server/sse/ServerSentEventsTest.kt @@ -213,7 +213,7 @@ class ServerSentEventsTest { } }) { repeat(10) { - sendSerialized(Person1(it)) + send(Person1(it)) } } } @@ -246,8 +246,8 @@ class ServerSentEventsTest { } } }) { - sendSerialized(Person1(22)) - sendSerialized(Person2(123456)) + send(Person1(22)) + send(Person2(123456)) } } @@ -279,8 +279,8 @@ class ServerSentEventsTest { val serializer = Json.serializersModule.serializer(typeInfo.kotlinType!!) Json.encodeToString(serializer, it) }) { - sendSerialized(Customer(0, "Jet", "Brains")) - sendSerialized(Product(0, listOf(100, 200))) + send(Customer(0, "Jet", "Brains")) + send(Product(0, listOf(100, 200))) } } diff --git a/ktor-shared/ktor-sse/api/ktor-sse.api b/ktor-shared/ktor-sse/api/ktor-sse.api index 3cec40fe47c..91bd00b9a3a 100644 --- a/ktor-shared/ktor-sse/api/ktor-sse.api +++ b/ktor-shared/ktor-sse/api/ktor-sse.api @@ -1,23 +1,23 @@ -public final class io/ktor/sse/ServerSentEvent { +public final class io/ktor/sse/ServerSentEvent : io/ktor/sse/ServerSentEventMetadata { public fun ()V - public fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)V - public synthetic fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public final fun component1 ()Ljava/lang/Object; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/lang/String; public final fun component3 ()Ljava/lang/String; public final fun component4 ()Ljava/lang/Long; public final fun component5 ()Ljava/lang/String; - public final fun copy (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)Lio/ktor/sse/ServerSentEvent; - public static synthetic fun copy$default (Lio/ktor/sse/ServerSentEvent;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILjava/lang/Object;)Lio/ktor/sse/ServerSentEvent; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)Lio/ktor/sse/ServerSentEvent; + public static synthetic fun copy$default (Lio/ktor/sse/ServerSentEvent;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILjava/lang/Object;)Lio/ktor/sse/ServerSentEvent; public fun equals (Ljava/lang/Object;)Z - public final fun getComments ()Ljava/lang/String; - public final fun getData ()Ljava/lang/Object; - public final fun getEvent ()Ljava/lang/String; - public final fun getId ()Ljava/lang/String; - public final fun getRetry ()Ljava/lang/Long; + public fun getComments ()Ljava/lang/String; + public synthetic fun getData ()Ljava/lang/Object; + public fun getData ()Ljava/lang/String; + public fun getEvent ()Ljava/lang/String; + public fun getId ()Ljava/lang/String; + public fun getRetry ()Ljava/lang/Long; public fun hashCode ()I public fun toString ()Ljava/lang/String; - public final fun toString (Lkotlin/jvm/functions/Function1;)Ljava/lang/String; } public final class io/ktor/sse/ServerSentEventKt { @@ -27,3 +27,33 @@ public final class io/ktor/sse/ServerSentEventKt { public static final fun getEND_OF_LINE_VARIANTS ()Lkotlin/text/Regex; } +public abstract interface class io/ktor/sse/ServerSentEventMetadata { + public abstract fun getComments ()Ljava/lang/String; + public abstract fun getData ()Ljava/lang/Object; + public abstract fun getEvent ()Ljava/lang/String; + public abstract fun getId ()Ljava/lang/String; + public abstract fun getRetry ()Ljava/lang/Long; +} + +public final class io/ktor/sse/ServerSentEventParsed : io/ktor/sse/ServerSentEventMetadata { + public fun ()V + public fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)V + public synthetic fun (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/Object; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/String; + public final fun component4 ()Ljava/lang/Long; + public final fun component5 ()Ljava/lang/String; + public final fun copy (Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;)Lio/ktor/sse/ServerSentEventParsed; + public static synthetic fun copy$default (Lio/ktor/sse/ServerSentEventParsed;Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;ILjava/lang/Object;)Lio/ktor/sse/ServerSentEventParsed; + public fun equals (Ljava/lang/Object;)Z + public fun getComments ()Ljava/lang/String; + public fun getData ()Ljava/lang/Object; + public fun getEvent ()Ljava/lang/String; + public fun getId ()Ljava/lang/String; + public fun getRetry ()Ljava/lang/Long; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public final fun toString (Lkotlin/jvm/functions/Function1;)Ljava/lang/String; +} + diff --git a/ktor-shared/ktor-sse/api/ktor-sse.klib.api b/ktor-shared/ktor-sse/api/ktor-sse.klib.api index 9d4118ddefd..212855b6559 100644 --- a/ktor-shared/ktor-sse/api/ktor-sse.klib.api +++ b/ktor-shared/ktor-sse/api/ktor-sse.klib.api @@ -6,13 +6,52 @@ // - Show declarations: true // Library unique name: -final class <#A: kotlin/Any?> io.ktor.sse/ServerSentEvent { // io.ktor.sse/ServerSentEvent|null[0] - constructor (#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.sse/ServerSentEvent.|(1:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] +sealed interface <#A: kotlin/Any?> io.ktor.sse/ServerSentEventMetadata { // io.ktor.sse/ServerSentEventMetadata|null[0] + abstract val comments // io.ktor.sse/ServerSentEventMetadata.comments|{}comments[0] + abstract fun (): kotlin/String? // io.ktor.sse/ServerSentEventMetadata.comments.|(){}[0] + abstract val data // io.ktor.sse/ServerSentEventMetadata.data|{}data[0] + abstract fun (): #A? // io.ktor.sse/ServerSentEventMetadata.data.|(){}[0] + abstract val event // io.ktor.sse/ServerSentEventMetadata.event|{}event[0] + abstract fun (): kotlin/String? // io.ktor.sse/ServerSentEventMetadata.event.|(){}[0] + abstract val id // io.ktor.sse/ServerSentEventMetadata.id|{}id[0] + abstract fun (): kotlin/String? // io.ktor.sse/ServerSentEventMetadata.id.|(){}[0] + abstract val retry // io.ktor.sse/ServerSentEventMetadata.retry|{}retry[0] + abstract fun (): kotlin/Long? // io.ktor.sse/ServerSentEventMetadata.retry.|(){}[0] +} + +final class <#A: kotlin/Any?> io.ktor.sse/ServerSentEventParsed : io.ktor.sse/ServerSentEventMetadata<#A> { // io.ktor.sse/ServerSentEventParsed|null[0] + constructor (#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.sse/ServerSentEventParsed.|(1:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] + + final val comments // io.ktor.sse/ServerSentEventParsed.comments|{}comments[0] + final fun (): kotlin/String? // io.ktor.sse/ServerSentEventParsed.comments.|(){}[0] + final val data // io.ktor.sse/ServerSentEventParsed.data|{}data[0] + final fun (): #A? // io.ktor.sse/ServerSentEventParsed.data.|(){}[0] + final val event // io.ktor.sse/ServerSentEventParsed.event|{}event[0] + final fun (): kotlin/String? // io.ktor.sse/ServerSentEventParsed.event.|(){}[0] + final val id // io.ktor.sse/ServerSentEventParsed.id|{}id[0] + final fun (): kotlin/String? // io.ktor.sse/ServerSentEventParsed.id.|(){}[0] + final val retry // io.ktor.sse/ServerSentEventParsed.retry|{}retry[0] + final fun (): kotlin/Long? // io.ktor.sse/ServerSentEventParsed.retry.|(){}[0] + + final fun component1(): #A? // io.ktor.sse/ServerSentEventParsed.component1|component1(){}[0] + final fun component2(): kotlin/String? // io.ktor.sse/ServerSentEventParsed.component2|component2(){}[0] + final fun component3(): kotlin/String? // io.ktor.sse/ServerSentEventParsed.component3|component3(){}[0] + final fun component4(): kotlin/Long? // io.ktor.sse/ServerSentEventParsed.component4|component4(){}[0] + final fun component5(): kotlin/String? // io.ktor.sse/ServerSentEventParsed.component5|component5(){}[0] + final fun copy(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...): io.ktor.sse/ServerSentEventParsed<#A> // io.ktor.sse/ServerSentEventParsed.copy|copy(1:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] + final fun equals(kotlin/Any?): kotlin/Boolean // io.ktor.sse/ServerSentEventParsed.equals|equals(kotlin.Any?){}[0] + final fun hashCode(): kotlin/Int // io.ktor.sse/ServerSentEventParsed.hashCode|hashCode(){}[0] + final fun toString(): kotlin/String // io.ktor.sse/ServerSentEventParsed.toString|toString(){}[0] + final fun toString(kotlin/Function1<#A, kotlin/String>): kotlin/String // io.ktor.sse/ServerSentEventParsed.toString|toString(kotlin.Function1<1:0,kotlin.String>){}[0] +} + +final class io.ktor.sse/ServerSentEvent : io.ktor.sse/ServerSentEventMetadata { // io.ktor.sse/ServerSentEvent|null[0] + constructor (kotlin/String? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...) // io.ktor.sse/ServerSentEvent.|(kotlin.String?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] final val comments // io.ktor.sse/ServerSentEvent.comments|{}comments[0] final fun (): kotlin/String? // io.ktor.sse/ServerSentEvent.comments.|(){}[0] final val data // io.ktor.sse/ServerSentEvent.data|{}data[0] - final fun (): #A? // io.ktor.sse/ServerSentEvent.data.|(){}[0] + final fun (): kotlin/String? // io.ktor.sse/ServerSentEvent.data.|(){}[0] final val event // io.ktor.sse/ServerSentEvent.event|{}event[0] final fun (): kotlin/String? // io.ktor.sse/ServerSentEvent.event.|(){}[0] final val id // io.ktor.sse/ServerSentEvent.id|{}id[0] @@ -20,16 +59,15 @@ final class <#A: kotlin/Any?> io.ktor.sse/ServerSentEvent { // io.ktor.sse/Serve final val retry // io.ktor.sse/ServerSentEvent.retry|{}retry[0] final fun (): kotlin/Long? // io.ktor.sse/ServerSentEvent.retry.|(){}[0] - final fun component1(): #A? // io.ktor.sse/ServerSentEvent.component1|component1(){}[0] + final fun component1(): kotlin/String? // io.ktor.sse/ServerSentEvent.component1|component1(){}[0] final fun component2(): kotlin/String? // io.ktor.sse/ServerSentEvent.component2|component2(){}[0] final fun component3(): kotlin/String? // io.ktor.sse/ServerSentEvent.component3|component3(){}[0] final fun component4(): kotlin/Long? // io.ktor.sse/ServerSentEvent.component4|component4(){}[0] final fun component5(): kotlin/String? // io.ktor.sse/ServerSentEvent.component5|component5(){}[0] - final fun copy(#A? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...): io.ktor.sse/ServerSentEvent<#A> // io.ktor.sse/ServerSentEvent.copy|copy(1:0?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] + final fun copy(kotlin/String? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Long? = ..., kotlin/String? = ...): io.ktor.sse/ServerSentEvent // io.ktor.sse/ServerSentEvent.copy|copy(kotlin.String?;kotlin.String?;kotlin.String?;kotlin.Long?;kotlin.String?){}[0] final fun equals(kotlin/Any?): kotlin/Boolean // io.ktor.sse/ServerSentEvent.equals|equals(kotlin.Any?){}[0] final fun hashCode(): kotlin/Int // io.ktor.sse/ServerSentEvent.hashCode|hashCode(){}[0] final fun toString(): kotlin/String // io.ktor.sse/ServerSentEvent.toString|toString(){}[0] - final fun toString(kotlin/Function1<#A, kotlin/String>): kotlin/String // io.ktor.sse/ServerSentEvent.toString|toString(kotlin.Function1<1:0,kotlin.String>){}[0] } final const val io.ktor.sse/COLON // io.ktor.sse/COLON|{}COLON[0] diff --git a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt index 65f7cd5ab50..5f5bf3e1d25 100644 --- a/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt +++ b/ktor-shared/ktor-sse/common/src/io/ktor/sse/ServerSentEvent.kt @@ -6,6 +6,26 @@ package io.ktor.sse import io.ktor.utils.io.* +/** + * Server-sent event interface. + * + * @property data data field of the event. + * @property event string identifying the type of event. + * @property id event ID. + * @property retry reconnection time, in milliseconds to wait before reconnecting. + * @property comments comment lines starting with a ':' character. + * + * @see ServerSentEvent with default String parameter [data] + * @see ServerSentEventParsed with parameterized parameter [data] + */ +public sealed interface ServerSentEventMetadata { + public val data: T? + public val event: String? + public val id: String? + public val retry: Long? + public val comments: String? +} + /** * Server-sent event. * @@ -15,15 +35,15 @@ import io.ktor.utils.io.* * @property retry reconnection time, in milliseconds to wait before reconnecting. * @property comments comment lines starting with a ':' character. * - * @see ParameterizedServerSentEvent with parameterized parameter [data] + * @see ServerSentEventParsed with parameterized parameter [data] */ public data class ServerSentEvent( - public val data: String? = null, - public val event: String? = null, - public val id: String? = null, - public val retry: Long? = null, - public val comments: String? = null -) { + override val data: String? = null, + override val event: String? = null, + override val id: String? = null, + override val retry: Long? = null, + override val comments: String? = null +) : ServerSentEventMetadata { override fun toString(): String = eventToString(data, event, id, retry, comments) } @@ -38,13 +58,13 @@ public data class ServerSentEvent( * * @see ServerSentEvent with default String parameter [data] */ -public data class ParameterizedServerSentEvent( - public val data: T? = null, - public val event: String? = null, - public val id: String? = null, - public val retry: Long? = null, - public val comments: String? = null -) { +public data class ServerSentEventParsed( + override val data: T? = null, + override val event: String? = null, + override val id: String? = null, + override val retry: Long? = null, + override val comments: String? = null +) : ServerSentEventMetadata { @InternalAPI public fun toString(serializer: (T) -> String): String = eventToString(data?.let { serializer(it) }, event, id, retry, comments)