diff --git a/docs/pages/kotlinx-rpc/topics/transport.topic b/docs/pages/kotlinx-rpc/topics/transport.topic index dc593ab6..cf8d1691 100644 --- a/docs/pages/kotlinx-rpc/topics/transport.topic +++ b/docs/pages/kotlinx-rpc/topics/transport.topic @@ -34,12 +34,17 @@ } } - val rpcClient: RPCClient = + val rpcClient: KtorRPCClient = ktorClient.rpc("ws://localhost:4242/services") { // this: HttpRequestBuilder rpcConfig { // this: RPCConfigBuilder.Client waitForServices = false } } + + // access WebSocketSession that created the connection + rpcClient.webSocketSession + + // create RPC service val myService: MyService = rpcClient.withService<MyService>()

Note that in this example, only the latter defined RPCConfig will be used.

@@ -56,7 +61,7 @@ } routing { - rpc("/services") { // this RPCRoute + rpc("/services") { // this RPCRoute, inherits WebSocketSession rpcConfig { // this: RPCConfigBuilder.Server waitForServices = false } @@ -85,7 +90,7 @@ suspend fun processImage(url: Srting): ProcessedImage } -// ### CLIENT CODE ### + // ### CLIENT CODE ### val client = HttpClient { installRPC { @@ -99,7 +104,7 @@ service.processImage(url = "https://catsanddogs.com/cats/1") -// ### SERVER CODE ### + // ### SERVER CODE ### class ImageServiceImpl(override val coroutineContext: CoroutineContext) : ImageService { // user defined classes diff --git a/transport/transport-ktor/src/jvmTest/kotlin/kotlinx/rpc/transport/ktor/KtorTransportTest.kt b/transport/transport-ktor/src/jvmTest/kotlin/kotlinx/rpc/transport/ktor/KtorTransportTest.kt index 0dbe63f7..5f98910e 100644 --- a/transport/transport-ktor/src/jvmTest/kotlin/kotlinx/rpc/transport/ktor/KtorTransportTest.kt +++ b/transport/transport-ktor/src/jvmTest/kotlin/kotlinx/rpc/transport/ktor/KtorTransportTest.kt @@ -7,6 +7,7 @@ package kotlinx.rpc.transport.ktor import io.ktor.client.* +import io.ktor.client.plugins.websocket.* import io.ktor.server.application.* import io.ktor.server.engine.* import io.ktor.server.netty.* @@ -29,8 +30,12 @@ interface NewService : RPC { suspend fun echo(value: String): String } -class NewServiceImpl(override val coroutineContext: CoroutineContext) : NewService { +class NewServiceImpl( + override val coroutineContext: CoroutineContext, + private val call: ApplicationCall, +) : NewService { override suspend fun echo(value: String): String { + assertEquals("test-header", call.request.headers["TestHeader"]) return value } } @@ -52,22 +57,30 @@ class KtorTransportTest { waitForServices = true } - registerService { NewServiceImpl(it) } + registerService { NewServiceImpl(it, call) } } } }.start() val clientWithGlobalConfig = HttpClient { - installRPC { + install(WebSockets) { + maxFrameSize = Int.MAX_VALUE.toLong() - 42 + } + install(kotlinx.rpc.transport.ktor.client.RPC) { serialization { json() } } } - val serviceWithGlobalConfig = clientWithGlobalConfig - .rpc("ws://localhost:4242/rpc") - .withService() + val ktorRPCClient = clientWithGlobalConfig + .rpc("ws://localhost:4242/rpc") { + headers["TestHeader"] = "test-header" + } + + assertEquals(Int.MAX_VALUE.toLong() - 42, ktorRPCClient.webSocketSession.maxFrameSize) + + val serviceWithGlobalConfig = ktorRPCClient.withService() val firstActual = serviceWithGlobalConfig.echo("Hello, world!") @@ -81,6 +94,8 @@ class KtorTransportTest { } val serviceWithLocalConfig = clientWithNoConfig.rpc("ws://localhost:4242/rpc") { + headers["TestHeader"] = "test-header" + rpcConfig { serialization { json() diff --git a/transport/transport-ktor/transport-ktor-client/api/transport-ktor-client.api b/transport/transport-ktor/transport-ktor-client/api/transport-ktor-client.api index 64aad451..250fd91f 100644 --- a/transport/transport-ktor/transport-ktor-client/api/transport-ktor-client.api +++ b/transport/transport-ktor/transport-ktor-client/api/transport-ktor-client.api @@ -7,6 +7,10 @@ public final class kotlinx/rpc/transport/ktor/client/KtorClientDslKt { public static synthetic fun rpcConfig$default (Lio/ktor/client/request/HttpRequestBuilder;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V } +public abstract interface class kotlinx/rpc/transport/ktor/client/KtorRPCClient : kotlinx/rpc/RPCClient { + public abstract fun getWebSocketSession ()Lio/ktor/websocket/WebSocketSession; +} + public final class kotlinx/rpc/transport/ktor/client/RPCKt { public static final fun getRPC ()Lio/ktor/client/plugins/api/ClientPlugin; public static final fun installRPC (Lio/ktor/client/HttpClientConfig;Lkotlin/jvm/functions/Function1;)V diff --git a/transport/transport-ktor/transport-ktor-client/src/commonMain/kotlin/kotlinx/rpc/transport/ktor/client/KtorClientDsl.kt b/transport/transport-ktor/transport-ktor-client/src/commonMain/kotlin/kotlinx/rpc/transport/ktor/client/KtorClientDsl.kt index b7010373..f51cbc54 100644 --- a/transport/transport-ktor/transport-ktor-client/src/commonMain/kotlin/kotlinx/rpc/transport/ktor/client/KtorClientDsl.kt +++ b/transport/transport-ktor/transport-ktor-client/src/commonMain/kotlin/kotlinx/rpc/transport/ktor/client/KtorClientDsl.kt @@ -39,7 +39,7 @@ public fun HttpRequestBuilder.rpcConfig(configBuilder: RPCConfigBuilder.Client.( public suspend fun HttpClient.rpc( urlString: String, block: HttpRequestBuilder.() -> Unit = {}, -): RPCClient { +): KtorRPCClient { return rpc { url(urlString) block() @@ -55,7 +55,7 @@ public suspend fun HttpClient.rpc( */ public suspend fun HttpClient.rpc( block: HttpRequestBuilder.() -> Unit = {}, -): RPCClient { +): KtorRPCClient { pluginOrNull(WebSockets) ?: error("RPC for client requires $WebSockets plugin to be installed firstly") @@ -72,5 +72,5 @@ public suspend fun HttpClient.rpc( val rpcConfig = pluginConfigBuilder?.apply(requestConfigBuilder)?.build() ?: rpcClientConfig(requestConfigBuilder) - return KtorRPCClient(session, rpcConfig) + return KtorRPCClientImpl(session, rpcConfig) } diff --git a/transport/transport-ktor/transport-ktor-client/src/commonMain/kotlin/kotlinx/rpc/transport/ktor/client/KtorRPCClient.kt b/transport/transport-ktor/transport-ktor-client/src/commonMain/kotlin/kotlinx/rpc/transport/ktor/client/KtorRPCClient.kt index 9b6fe757..b8eb683c 100644 --- a/transport/transport-ktor/transport-ktor-client/src/commonMain/kotlin/kotlinx/rpc/transport/ktor/client/KtorRPCClient.kt +++ b/transport/transport-ktor/transport-ktor-client/src/commonMain/kotlin/kotlinx/rpc/transport/ktor/client/KtorRPCClient.kt @@ -5,11 +5,21 @@ package kotlinx.rpc.transport.ktor.client import io.ktor.websocket.* +import kotlinx.rpc.RPCClient import kotlinx.rpc.RPCConfig import kotlinx.rpc.client.KRPCClient import kotlinx.rpc.transport.ktor.KtorTransport -internal class KtorRPCClient( - webSocketSession: WebSocketSession, +/** + * [RPCClient] implementation for Ktor, containing [webSocketSession] object, + * that is used to maintain connection. + */ +public interface KtorRPCClient : RPCClient { + public val webSocketSession: WebSocketSession +} + +internal class KtorRPCClientImpl( + override val webSocketSession: WebSocketSession, config: RPCConfig.Client, -): KRPCClient(config, KtorTransport(webSocketSession)) +): KRPCClient(config, KtorTransport(webSocketSession)), KtorRPCClient +