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
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,12 @@ internal class RpcIrContext(
if (isJvmTarget()) {
getIrClassSymbol("io.grpc", "MethodDescriptor")
} else {
getIrClassSymbol("kotlinx.rpc.grpc.internal", "MethodDescriptor")
getIrClassSymbol("kotlinx.rpc.grpc.descriptor", "MethodDescriptor")
}
}

val grpcPlatformMethodType by lazy {
getIrClassSymbol("kotlinx.rpc.grpc.internal", "MethodType")
getIrClassSymbol("kotlinx.rpc.grpc.descriptor", "MethodType")
}

val grpcPlatformMethodTypeUnary by lazy {
Expand Down Expand Up @@ -248,7 +248,7 @@ internal class RpcIrContext(
}

val methodDescriptor by lazy {
namedFunction("kotlinx.rpc.grpc.internal", "methodDescriptor")
namedFunction("kotlinx.rpc.grpc.descriptor", "methodDescriptor")
}

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

@file:OptIn(InternalRpcApi::class)

import kotlinx.rpc.internal.InternalRpcApi

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

kotlin {
compilerOptions {
freeCompilerArgs.add("-Xexpect-actual-classes")
}

sourceSets {
commonMain {
dependencies {
api(projects.grpc.grpcCore)
}
}

nativeMain {
dependencies {
implementation(libs.atomicfu)
}
}
}
}
16 changes: 16 additions & 0 deletions grpc/grpc-client/gradle.properties
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.
#
kotlinx.rpc.exclude.wasmWasi=true
kotlinx.rpc.exclude.js=true
kotlinx.rpc.exclude.wasmJs=true
kotlinx.rpc.exclude.mingwX64=true
kotlinx.rpc.exclude.tvosArm64=true
kotlinx.rpc.exclude.tvosSimulatorArm64=true
kotlinx.rpc.exclude.tvosX64=true
kotlinx.rpc.exclude.watchosArm32=true
kotlinx.rpc.exclude.watchosDeviceArm64=true
kotlinx.rpc.exclude.watchosX64=true
# TODO: Remove once we ant to activate WatchOS (these two targets are already prepared for activation)
kotlinx.rpc.exclude.watchosArm64=true
kotlinx.rpc.exclude.watchosSimulatorArm64=true
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
* 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
package kotlinx.rpc.grpc.client

import kotlinx.coroutines.flow.Flow
import kotlinx.rpc.grpc.internal.GrpcCallOptions
import kotlinx.rpc.grpc.internal.MethodDescriptor
import kotlinx.rpc.grpc.GrpcMetadata
import kotlinx.rpc.grpc.Status
import kotlinx.rpc.grpc.client.internal.GrpcCallOptions
import kotlinx.rpc.grpc.descriptor.MethodDescriptor

/**
* The scope of a single outgoing gRPC client call observed by a [ClientInterceptor].
Expand Down Expand Up @@ -78,7 +80,7 @@ public interface ClientCallScope<Request, Response> {
* Cancel the call locally, providing a human-readable [message] and an optional [cause].
* This method won't return and abort all further processing.
*
* We made cancel throw a [StatusException] instead of returning, so control flow is explicit and
* We made cancel throw a [kotlinx.rpc.grpc.StatusException] instead of returning, so control flow is explicit and
* race conditions between interceptors and the transport layer are avoided.
*/
public fun cancel(message: String, cause: Throwable? = null): Nothing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,29 @@
* 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
package kotlinx.rpc.grpc.client

import kotlinx.coroutines.flow.Flow
import kotlinx.rpc.RpcCall
import kotlinx.rpc.RpcClient
import kotlinx.rpc.grpc.GrpcMetadata
import kotlinx.rpc.grpc.client.internal.GrpcDefaultCallOptions
import kotlinx.rpc.grpc.client.internal.ManagedChannel
import kotlinx.rpc.grpc.client.internal.ManagedChannelBuilder
import kotlinx.rpc.grpc.client.internal.bidirectionalStreamingRpc
import kotlinx.rpc.grpc.client.internal.buildChannel
import kotlinx.rpc.grpc.client.internal.clientStreamingRpc
import kotlinx.rpc.grpc.client.internal.serverStreamingRpc
import kotlinx.rpc.grpc.client.internal.unaryRpc
import kotlinx.rpc.grpc.codec.EmptyMessageCodecResolver
import kotlinx.rpc.grpc.codec.MessageCodecResolver
import kotlinx.rpc.grpc.codec.ThrowingMessageCodecResolver
import kotlinx.rpc.grpc.codec.plus
import kotlinx.rpc.grpc.descriptor.GrpcServiceDelegate
import kotlinx.rpc.grpc.descriptor.GrpcServiceDescriptor
import kotlinx.rpc.grpc.internal.GrpcDefaultCallOptions
import kotlinx.rpc.grpc.internal.MethodDescriptor
import kotlinx.rpc.grpc.internal.MethodType
import kotlinx.rpc.grpc.internal.bidirectionalStreamingRpc
import kotlinx.rpc.grpc.internal.clientStreamingRpc
import kotlinx.rpc.grpc.internal.serverStreamingRpc
import kotlinx.rpc.grpc.internal.type
import kotlinx.rpc.grpc.internal.unaryRpc
import kotlinx.rpc.grpc.descriptor.MethodDescriptor
import kotlinx.rpc.grpc.descriptor.MethodType
import kotlinx.rpc.grpc.descriptor.methodType
import kotlinx.rpc.internal.utils.map.RpcInternalConcurrentHashMap
import kotlin.time.Duration

Expand All @@ -29,7 +33,7 @@ private typealias RequestClient = Any
/**
* GrpcClient manages gRPC communication by providing implementation for making asynchronous RPC calls.
*
* @field channel The [ManagedChannel] used to communicate with remote gRPC services.
* @field channel The [kotlinx.rpc.grpc.client.internal.ManagedChannel] used to communicate with remote gRPC services.
*/
public class GrpcClient internal constructor(
internal val channel: ManagedChannel,
Expand Down Expand Up @@ -57,7 +61,7 @@ public class GrpcClient internal constructor(
val callOptions = GrpcDefaultCallOptions
val trailers = GrpcMetadata()

return when (methodDescriptor.type) {
return when (methodDescriptor.methodType) {
MethodType.UNARY -> unaryRpc(
descriptor = methodDescriptor,
request = request,
Expand All @@ -72,15 +76,15 @@ public class GrpcClient internal constructor(
trailers = trailers,
)

else -> error("Wrong method type ${methodDescriptor.type}")
else -> error("Wrong method type ${methodDescriptor.methodType}")
}
}

override fun <T> callServerStreaming(call: RpcCall): Flow<T> = withGrpcCall(call) { methodDescriptor, request ->
val callOptions = GrpcDefaultCallOptions
val trailers = GrpcMetadata()

when (methodDescriptor.type) {
when (methodDescriptor.methodType) {
MethodType.SERVER_STREAMING -> serverStreamingRpc(
descriptor = methodDescriptor,
request = request,
Expand All @@ -95,7 +99,7 @@ public class GrpcClient internal constructor(
trailers = trailers,
)

else -> error("Wrong method type ${methodDescriptor.type}")
else -> error("Wrong method type ${methodDescriptor.methodType}")
}
}

Expand Down Expand Up @@ -187,6 +191,10 @@ private fun GrpcClient(
* Configuration class for a gRPC client, providing customization options
* for client behavior, including interceptors, credentials, codec resolution,
* and authority overrides.
*
* @see credentials
* @see overrideAuthority
* @see intercept
*/
public class GrpcClientConfiguration internal constructor() {
internal val interceptors: MutableList<ClientInterceptor> = mutableListOf()
Expand Down Expand Up @@ -222,6 +230,9 @@ public class GrpcClientConfiguration internal constructor() {
* credentials = plaintext() // for testing purposes only!
* }
* ```
*
* @see tls
* @see plaintext
*/
public var credentials: ClientCredentials? = null

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* 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.client

import kotlinx.rpc.internal.utils.InternalRpcApi

public expect abstract class ClientCredentials

public expect class InsecureClientCredentials : ClientCredentials

// we need a wrapper for InsecureChannelCredentials as our constructor would conflict with the private
// java constructor.
@InternalRpcApi
public expect fun createInsecureClientCredentials(): ClientCredentials

public expect class TlsClientCredentials : ClientCredentials

public fun TlsClientCredentials(configure: TlsClientCredentialsBuilder.() -> Unit = {}): ClientCredentials {
val builder = TlsClientCredentialsBuilder()
builder.configure()
return builder.build()
}

public interface TlsClientCredentialsBuilder {
public fun trustManager(rootCertsPem: String): TlsClientCredentialsBuilder
public fun keyManager(certChainPem: String, privateKeyPem: String): TlsClientCredentialsBuilder
}

internal expect fun TlsClientCredentialsBuilder(): TlsClientCredentialsBuilder

internal expect fun TlsClientCredentialsBuilder.build(): ClientCredentials
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* 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.internal
package kotlinx.rpc.grpc.client.internal

import kotlinx.rpc.grpc.GrpcMetadata
import kotlinx.rpc.grpc.Status
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* 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.internal
package kotlinx.rpc.grpc.client.internal

import kotlinx.rpc.internal.utils.InternalRpcApi

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
* 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.internal
package kotlinx.rpc.grpc.client.internal

import kotlinx.rpc.grpc.descriptor.MethodDescriptor
import kotlinx.rpc.internal.utils.InternalRpcApi

@InternalRpcApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@

@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")

package kotlinx.rpc.grpc
package kotlinx.rpc.grpc.client.internal

import kotlinx.rpc.grpc.internal.GrpcChannel
import kotlinx.rpc.grpc.client.ClientCredentials
import kotlinx.rpc.internal.utils.InternalRpcApi
import kotlin.time.Duration

/**
* Same as [ManagedChannel], but is platform-exposed.
*/
@InternalRpcApi
public expect abstract class ManagedChannelPlatform : GrpcChannel

/**
Expand All @@ -22,6 +24,7 @@ public expect abstract class ManagedChannelPlatform : GrpcChannel
*
* Provides lifecycle management.
*/
@InternalRpcApi
public interface ManagedChannel {
/**
* Returns whether the channel is shutdown.
Expand Down Expand Up @@ -67,19 +70,23 @@ public interface ManagedChannel {
/**
* Builder class for [ManagedChannel].
*/
@InternalRpcApi
public expect abstract class ManagedChannelBuilder<T : ManagedChannelBuilder<T>> {
public abstract fun overrideAuthority(authority: String): T
}

internal expect fun ManagedChannelBuilder(
@InternalRpcApi
public expect fun ManagedChannelBuilder(
hostname: String,
port: Int,
credentials: ClientCredentials? = null,
): ManagedChannelBuilder<*>

internal expect fun ManagedChannelBuilder(
@InternalRpcApi
public expect fun ManagedChannelBuilder(
target: String,
credentials: ClientCredentials? = null,
): ManagedChannelBuilder<*>

internal expect fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel
@InternalRpcApi
public expect fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel
Loading
Loading