Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions docs/pages/kotlinx-rpc/topics/platforms.topic
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
-->

<!DOCTYPE topic
SYSTEM "https://resources.jetbrains.com/writerside/1.0/xhtml-entities.dtd">
<!--suppress WrsMissingSpaceChecker -->
Expand Down Expand Up @@ -68,6 +72,14 @@
<td><list><li>apple<list><li>ios<list><li>iosArm64</li><li>iosSimulatorArm64</li><li>iosX64</li></list></li><li>macos<list><li>macosArm64</li><li>macosX64</li></list></li><li>watchos<list><li>watchosArm32</li><li>watchosArm64</li><li>watchosDeviceArm64</li><li>watchosSimulatorArm64</li><li>watchosX64</li></list></li><li>tvos<list><li>tvosArm64</li><li>tvosSimulatorArm64</li><li>tvosX64</li></list></li></list></li><li>linux<list><li>linuxArm64</li><li>linuxX64</li></list></li><li>windows<list><li>mingwX64</li></list></li></list></td>
</tr>

<tr>
<td>protobuf-plugin</td>
<td>Jvm Only</td>
<td>-</td>
<td>-</td>
<td>-</td>
</tr>

<tr>
<td>utils</td>
<td>jvm</td>
Expand All @@ -76,6 +88,14 @@
<td><list><li>apple<list><li>ios<list><li>iosArm64</li><li>iosSimulatorArm64</li><li>iosX64</li></list></li><li>macos<list><li>macosArm64</li><li>macosX64</li></list></li><li>watchos<list><li>watchosArm32</li><li>watchosArm64</li><li>watchosDeviceArm64</li><li>watchosSimulatorArm64</li><li>watchosX64</li></list></li><li>tvos<list><li>tvosArm64</li><li>tvosSimulatorArm64</li><li>tvosX64</li></list></li></list></li><li>linux<list><li>linuxArm64</li><li>linuxX64</li></list></li><li>windows<list><li>mingwX64</li></list></li></list></td>
</tr>

<tr>
<td>grpc-core</td>
<td>jvm</td>
<td><list><li>browser</li><li>node</li></list></td>
<td><list><li>wasmJs<list><li>browser</li><li>d8</li><li>node</li></list></li></list></td>
<td><list><li>apple<list><li>ios<list><li>iosArm64</li><li>iosSimulatorArm64</li><li>iosX64</li></list></li><li>macos<list><li>macosArm64</li><li>macosX64</li></list></li><li>watchos<list><li>watchosArm32</li><li>watchosArm64</li><li>watchosDeviceArm64</li><li>watchosSimulatorArm64</li><li>watchosX64</li></list></li><li>tvos<list><li>tvosArm64</li><li>tvosSimulatorArm64</li><li>tvosX64</li></list></li></list></li><li>linux<list><li>linuxArm64</li><li>linuxX64</li></list></li><li>windows<list><li>mingwX64</li></list></li></list></td>
</tr>

<tr>
<td>krpc-client</td>
<td>jvm</td>
Expand Down
7 changes: 5 additions & 2 deletions grpc/grpc-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,20 @@ kotlin {
api(projects.core)
api(projects.utils)
api(libs.coroutines.core)

implementation(libs.atomicfu)
}
}

jvmMain {
dependencies {
api(libs.grpc.api)
api(libs.grpc.util)
api(libs.grpc.stub)
api(libs.grpc.protobuf)
api(libs.grpc.kotlin.stub)
implementation(libs.grpc.kotlin.stub) // causes problems to jpms if api
api(libs.protobuf.java.util)
api(libs.protobuf.kotlin)
implementation(libs.protobuf.kotlin)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import kotlinx.rpc.RpcClient
import kotlinx.rpc.grpc.descriptor.GrpcClientDelegate
import kotlinx.rpc.grpc.descriptor.GrpcServiceDescriptor
import kotlinx.rpc.internal.utils.map.RpcInternalConcurrentHashMap
import kotlin.time.Duration

/**
* GrpcClient manages gRPC communication by providing implementation for making asynchronous RPC calls.
Expand All @@ -19,6 +20,20 @@ import kotlinx.rpc.internal.utils.map.RpcInternalConcurrentHashMap
public class GrpcClient internal constructor(private val channel: ManagedChannel) : RpcClient {
private val stubs = RpcInternalConcurrentHashMap<Long, GrpcClientDelegate>()

public fun shutdown() {
stubs.clear()
channel.shutdown()
}

public fun shutdownNow() {
stubs.clear()
channel.shutdownNow()
}

public suspend fun awaitTermination(duration: Duration) {
channel.awaitTermination(duration)
}

override suspend fun <T> call(call: RpcCall): T {
return call.delegate().call(call)
}
Expand All @@ -39,11 +54,11 @@ public class GrpcClient internal constructor(private val channel: ManagedChannel
* Constructor function for the [GrpcClient] class.
*/
public fun GrpcClient(
name: String,
hostname: String,
port: Int,
configure: ManagedChannelBuilder<*>.() -> Unit = {},
): GrpcClient {
val channel = ManagedChannelBuilder(name, port).apply(configure).buildChannel()
val channel = ManagedChannelBuilder(hostname, port).apply(configure).buildChannel()
return GrpcClient(channel)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

package kotlinx.rpc.grpc

import kotlinx.atomicfu.atomic
import kotlinx.rpc.RpcServer
import kotlinx.rpc.descriptor.serviceDescriptorOf
import kotlinx.rpc.grpc.annotations.Grpc
Expand Down Expand Up @@ -65,9 +66,13 @@ public class GrpcServer internal constructor(
return grpc.delegate.definitionFor(service)
}

private val buildLock = atomic(false)

internal fun build() {
internalServer = Server(serverBuilder)
isBuilt = true
if (buildLock.compareAndSet(expect = false, update = true)) {
internalServer = Server(serverBuilder)
isBuilt = true
}
}

override val isShutdown: Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public interface ManagedChannel {
*/
public expect abstract class ManagedChannelBuilder<T : ManagedChannelBuilder<T>>

internal expect fun ManagedChannelBuilder(name: String, port: Int): ManagedChannelBuilder<*>
internal expect fun ManagedChannelBuilder(hostname: String, port: Int): ManagedChannelBuilder<*>
internal expect fun ManagedChannelBuilder(target: String): ManagedChannelBuilder<*>

internal expect fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal actual fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel {
error("JS target is not supported in gRPC")
}

internal actual fun ManagedChannelBuilder(name: String, port: Int): ManagedChannelBuilder<*> {
internal actual fun ManagedChannelBuilder(hostname: String, port: Int): ManagedChannelBuilder<*> {
error("JS target is not supported in gRPC")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ internal actual fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel {
return build().toKotlin()
}

internal actual fun ManagedChannelBuilder(name: String, port: Int): ManagedChannelBuilder<*> {
return io.grpc.ManagedChannelBuilder.forAddress(name, port)
internal actual fun ManagedChannelBuilder(hostname: String, port: Int): ManagedChannelBuilder<*> {
return io.grpc.ManagedChannelBuilder.forAddress(hostname, port)
}

internal actual fun ManagedChannelBuilder(target: String): ManagedChannelBuilder<*> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal actual fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel {
error("Native target is not supported in gRPC")
}

internal actual fun ManagedChannelBuilder(name: String, port: Int): ManagedChannelBuilder<*> {
internal actual fun ManagedChannelBuilder(hostname: String, port: Int): ManagedChannelBuilder<*> {
error("Native target is not supported in gRPC")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ internal actual fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel {
error("WasmJS target is not supported in gRPC")
}

internal actual fun ManagedChannelBuilder(name: String, port: Int): ManagedChannelBuilder<*> {
internal actual fun ManagedChannelBuilder(hostname: String, port: Int): ManagedChannelBuilder<*> {
error("WasmJS target is not supported in gRPC")
}

Expand Down
41 changes: 41 additions & 0 deletions grpc/grpc-ktor-server-test/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
alias(libs.plugins.conventions.jvm)
alias(libs.plugins.kotlinx.rpc)
alias(libs.plugins.protobuf)
}

dependencies {
// for the jar dependency
testImplementation(kotlin("test"))
testImplementation(projects.grpc.grpcCore)
testImplementation(projects.grpc.grpcKtorServer)

testImplementation(libs.grpc.kotlin.stub)
testImplementation(libs.grpc.netty)

testImplementation(libs.ktor.server.core)
testImplementation(libs.ktor.server.test.host)
testRuntimeOnly(libs.logback.classic)
}

rpc {
grpc {
enabled = true

val globalRootDir: String by extra

plugin {
locator {
path = "$globalRootDir/protobuf-plugin/build/libs/protobuf-plugin-$version-all.jar"
}
}

tasksMatching { it.isTest }.all {
dependsOn(project(":protobuf-plugin").tasks.jar)
}
}
}
5 changes: 5 additions & 0 deletions grpc/grpc-ktor-server-test/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
# Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
#

kotlinx.rpc.exclude.wasmWasi=true
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

package kotlinx.rpc.grpc.ktor.server.test

import io.ktor.server.testing.testApplication
import kotlinx.rpc.grpc.GrpcClient
import kotlin.test.Test
import kotlinx.rpc.grpc.ktor.server.grpc
import kotlinx.rpc.registerService
import kotlinx.rpc.withService
import kotlin.test.assertEquals
import kotlin.time.Duration.Companion.minutes

class KtorTestServiceImpl : KtorTestService {
override suspend fun sayHello(message: Hello): Hello {
return message
}
}

const val PORT = 8085

class TestServer {
@Test
fun testPlainRequests() = testApplication {
application {
grpc(PORT) {
registerService<KtorTestService> { KtorTestServiceImpl() }
}
}

startApplication()

val client = GrpcClient("localhost", PORT) {
usePlaintext()
}

val response = client.withService<KtorTestService>().sayHello(Hello { message = "Hello" })
assertEquals("Hello", response.message, "Wrong response message")

client.shutdown()
client.awaitTermination(1.minutes)
}
}
11 changes: 11 additions & 0 deletions grpc/grpc-ktor-server-test/src/test/proto/ktor-test-service.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
syntax = "proto3";

package kotlinx.rpc.grpc.ktor.server.test;

message Hello {
string message = 1;
}

service KtorTestService {
rpc sayHello(Hello) returns (Hello);
}
16 changes: 16 additions & 0 deletions grpc/grpc-ktor-server-test/src/test/resources/logback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!--
~ Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
-->

<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{YYYY-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="trace">
<appender-ref ref="STDOUT"/>
</root>
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="io.netty" level="TRACE"/>
</configuration>
19 changes: 19 additions & 0 deletions grpc/grpc-ktor-server/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
alias(libs.plugins.conventions.kmp)
alias(libs.plugins.kotlinx.rpc)
}

kotlin {
sourceSets {
commonMain {
dependencies {
api(projects.grpc.grpcCore)
implementation(libs.ktor.server.core)
}
}
}
}
5 changes: 5 additions & 0 deletions grpc/grpc-ktor-server/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#
# Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
#

kotlinx.rpc.exclude.wasmWasi=true
Loading
Loading