Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ 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.*
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.internal.utils.map.RpcInternalConcurrentHashMap
import kotlin.time.Duration

Expand Down Expand Up @@ -124,10 +131,11 @@ public class GrpcClient internal constructor(
public fun GrpcClient(
hostname: String,
port: Int,
credentials: ClientCredentials? = null,
messageCodecResolver: MessageCodecResolver = EmptyMessageCodecResolver,
configure: ManagedChannelBuilder<*>.() -> Unit = {},
): GrpcClient {
val channel = ManagedChannelBuilder(hostname, port).apply(configure).buildChannel()
val channel = ManagedChannelBuilder(hostname, port, credentials).apply(configure).buildChannel()
return GrpcClient(channel, messageCodecResolver)
}

Expand All @@ -136,9 +144,10 @@ public fun GrpcClient(
*/
public fun GrpcClient(
target: String,
credentials: ClientCredentials? = null,
messageCodecResolver: MessageCodecResolver = EmptyMessageCodecResolver,
configure: ManagedChannelBuilder<*>.() -> Unit = {},
): GrpcClient {
val channel = ManagedChannelBuilder(target).apply(configure).buildChannel()
val channel = ManagedChannelBuilder(target, credentials).apply(configure).buildChannel()
return GrpcClient(channel, messageCodecResolver)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.job
import kotlinx.rpc.RpcServer
import kotlinx.rpc.descriptor.RpcCallable
import kotlinx.rpc.descriptor.flowInvokator
Expand Down Expand Up @@ -49,6 +48,7 @@ private typealias ResponseServer = Any
*/
public class GrpcServer internal constructor(
override val port: Int = 8080,
credentials: ServerCredentials? = null,
messageCodecResolver: MessageCodecResolver = EmptyMessageCodecResolver,
parentContext: CoroutineContext = EmptyCoroutineContext,
configure: ServerBuilder<*>.() -> Unit,
Expand All @@ -61,7 +61,7 @@ public class GrpcServer internal constructor(
private var isBuilt = false
private lateinit var internalServer: Server

private val serverBuilder: ServerBuilder<*> = ServerBuilder(port).apply(configure)
private val serverBuilder: ServerBuilder<*> = ServerBuilder(port, credentials).apply(configure)
private val registry: MutableHandlerRegistry by lazy {
MutableHandlerRegistry().apply { serverBuilder.fallbackHandlerRegistry(this) }
}
Expand Down Expand Up @@ -192,10 +192,12 @@ public class GrpcServer internal constructor(
*/
public fun GrpcServer(
port: Int,
credentials: ServerCredentials? = null,
messageCodecResolver: MessageCodecResolver = EmptyMessageCodecResolver,
parentContext: CoroutineContext = EmptyCoroutineContext,
configure: ServerBuilder<*>.() -> Unit = {},
builder: RpcServer.() -> Unit = {},
): GrpcServer {
return GrpcServer(port, messageCodecResolver, parentContext, configure).apply(builder).apply { build() }
return GrpcServer(port, credentials, messageCodecResolver, parentContext, configure).apply(builder)
.apply { build() }
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,19 @@ public interface ManagedChannel {
*/
public expect abstract class ManagedChannelBuilder<T : ManagedChannelBuilder<T>> {
public fun usePlaintext(): T

public abstract fun overrideAuthority(authority: String): T
}

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

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

internal expect fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public expect abstract class ServerBuilder<T : ServerBuilder<T>> {
public abstract fun fallbackHandlerRegistry(registry: HandlerRegistry?): T
}

internal expect fun ServerBuilder(port: Int): ServerBuilder<*>
internal expect fun ServerBuilder(port: Int, credentials: ServerCredentials? = null): ServerBuilder<*>

/**
* Server for listening for and dispatching incoming calls.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* 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

public expect abstract class ClientCredentials
public expect abstract class ServerCredentials

public expect class InsecureClientCredentials : ClientCredentials
public expect class InsecureServerCredentials : ServerCredentials

public expect class TlsClientCredentials : ClientCredentials
public expect class TlsServerCredentials : ServerCredentials

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

public fun TlsServerCredentials(
certChain: String,
privateKey: String,
configure: TlsServerCredentialsBuilder.() -> Unit = {},
): ServerCredentials {
val builder = TlsServerCredentialsBuilder(certChain, privateKey)
builder.configure()
return builder.build()
}

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

public enum class TlsClientAuth {
/** Clients will not present any identity. */
NONE,

/**
* Clients are requested to present their identity, but clients without identities are
* permitted.
* Also, if the client certificate is provided but cannot be verified,
* the client is permitted.
*/
OPTIONAL,

/**
* Clients are requested to present their identity, and are required to provide a valid
* identity.
*/
REQUIRE
}

public interface TlsServerCredentialsBuilder {
public fun trustManager(rootCertsPem: String): TlsServerCredentialsBuilder
public fun clientAuth(clientAuth: TlsClientAuth): TlsServerCredentialsBuilder
}

internal expect fun TlsClientCredentialsBuilder(): TlsClientCredentialsBuilder
internal expect fun TlsServerCredentialsBuilder(
certChain: String,
privateKey: String,
): TlsServerCredentialsBuilder

internal expect fun TlsClientCredentialsBuilder.build(): ClientCredentials
internal expect fun TlsServerCredentialsBuilder.build(): ServerCredentials
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@ public expect abstract class GrpcChannel {
callOptions: GrpcCallOptions,
): ClientCall<RequestT, ResponseT>

public abstract fun authority(): String
public abstract fun authority(): String?
}
140 changes: 140 additions & 0 deletions grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/certs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
/*
* 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.test

// Certs are taken from grpc-java/testing/src/main/resources/certs

val CA_PEM = """
-----BEGIN CERTIFICATE-----
MIIDWjCCAkKgAwIBAgIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQEL
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIw
MDMxNzE4NTk1MVoXDTMwMDMxNTE4NTk1MVowVjELMAkGA1UEBhMCQVUxEzARBgNV
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
ZDEPMA0GA1UEAwwGdGVzdGNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
AQEAsGL0oXflF0LzoM+Bh+qUU9yhqzw2w8OOX5mu/iNCyUOBrqaHi7mGHx73GD01
diNzCzvlcQqdNIH6NQSL7DTpBjca66jYT9u73vZe2MDrr1nVbuLvfu9850cdxiUO
Inv5xf8+sTHG0C+a+VAvMhsLiRjsq+lXKRJyk5zkbbsETybqpxoJ+K7CoSy3yc/k
QIY3TipwEtwkKP4hzyo6KiGd/DPexie4nBUInN3bS1BUeNZ5zeaIC2eg3bkeeW7c
qT55b+Yen6CxY0TEkzBK6AKt/WUialKMgT0wbTxRZO7kUCH3Sq6e/wXeFdJ+HvdV
LPlAg5TnMaNpRdQih/8nRFpsdwIDAQABoyAwHjAMBgNVHRMEBTADAQH/MA4GA1Ud
DwEB/wQEAwICBDANBgkqhkiG9w0BAQsFAAOCAQEAkTrKZjBrJXHps/HrjNCFPb5a
THuGPCSsepe1wkKdSp1h4HGRpLoCgcLysCJ5hZhRpHkRihhef+rFHEe60UePQO3S
CVTtdJB4CYWpcNyXOdqefrbJW5QNljxgi6Fhvs7JJkBqdXIkWXtFk2eRgOIP2Eo9
/OHQHlYnwZFrk6sp4wPyR+A95S0toZBcyDVz7u+hOW0pGK3wviOe9lvRgj/H3Pwt
bewb0l+MhRig0/DVHamyVxrDRbqInU1/GTNCwcZkXKYFWSf92U+kIcTth24Q1gcw
eZiLl5FfrWokUNytFElXob0V0a5/kbhiLc3yWmvWqHTpqCALbVyF+rKJo2f5Kw==
-----END CERTIFICATE-----
""".trimIndent()

val SERVER_CERT_PEM = """
-----BEGIN CERTIFICATE-----
MIIDtDCCApygAwIBAgIUbJfTREJ6k6/+oInWhV1O1j3ZT0IwDQYJKoZIhvcNAQEL
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIw
MDMxODAzMTA0MloXDTMwMDMxNjAzMTA0MlowZTELMAkGA1UEBhMCVVMxETAPBgNV
BAgMCElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRUwEwYDVQQKDAxFeGFtcGxl
LCBDby4xGjAYBgNVBAMMESoudGVzdC5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA5xOONxJJ8b8Qauvob5/7dPYZfIcd+uhAWL2ZlTPz
Qvu4oF0QI4iYgP5iGgry9zEtCM+YQS8UhiAlPlqa6ANxgiBSEyMHH/xE8lo/+caY
GeACqy640Jpl/JocFGo3xd1L8DCawjlaj6eu7T7T/tpAV2qq13b5710eNRbCAfFe
8yALiGQemx0IYhlZXNbIGWLBNhBhvVjJh7UvOqpADk4xtl8o5j0xgMIRg6WJGK6c
6ffSIg4eP1XmovNYZ9LLEJG68tF0Q/yIN43B4dt1oq4jzSdCbG4F1EiykT2TmwPV
YDi8tml6DfOCDGnit8svnMEmBv/fcPd31GSbXjF8M+KGGQIDAQABo2swaTAJBgNV
HRMEAjAAMAsGA1UdDwQEAwIF4DBPBgNVHREESDBGghAqLnRlc3QuZ29vZ2xlLmZy
ghh3YXRlcnpvb2kudGVzdC5nb29nbGUuYmWCEioudGVzdC55b3V0dWJlLmNvbYcE
wKgBAzANBgkqhkiG9w0BAQsFAAOCAQEAS8hDQA8PSgipgAml7Q3/djwQ644ghWQv
C2Kb+r30RCY1EyKNhnQnIIh/OUbBZvh0M0iYsy6xqXgfDhCB93AA6j0i5cS8fkhH
Jl4RK0tSkGQ3YNY4NzXwQP/vmUgfkw8VBAZ4Y4GKxppdATjffIW+srbAmdDruIRM
wPeikgOoRrXf0LA1fi4TqxARzeRwenQpayNfGHTvVF9aJkl8HoaMunTAdG5pIVcr
9GKi/gEMpXUJbbVv3U5frX1Wo4CFo+rZWJ/LyCMeb0jciNLxSdMwj/E/ZuExlyeZ
gc9ctPjSMvgSyXEKv6Vwobleeg88V2ZgzenziORoWj4KszG/lbQZvg==
-----END CERTIFICATE-----
""".trimIndent()

val SERVER_KEY_PEM = """
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDnE443EknxvxBq
6+hvn/t09hl8hx366EBYvZmVM/NC+7igXRAjiJiA/mIaCvL3MS0Iz5hBLxSGICU+
WproA3GCIFITIwcf/ETyWj/5xpgZ4AKrLrjQmmX8mhwUajfF3UvwMJrCOVqPp67t
PtP+2kBXaqrXdvnvXR41FsIB8V7zIAuIZB6bHQhiGVlc1sgZYsE2EGG9WMmHtS86
qkAOTjG2XyjmPTGAwhGDpYkYrpzp99IiDh4/Veai81hn0ssQkbry0XRD/Ig3jcHh
23WiriPNJ0JsbgXUSLKRPZObA9VgOLy2aXoN84IMaeK3yy+cwSYG/99w93fUZJte
MXwz4oYZAgMBAAECggEBAIVn2Ncai+4xbH0OLWckabwgyJ4IM9rDc0LIU368O1kU
koais8qP9dujAWgfoh3sGh/YGgKn96VnsZjKHlyMgF+r4TaDJn3k2rlAOWcurGlj
1qaVlsV4HiEzp7pxiDmHhWvp4672Bb6iBG+bsjCUOEk/n9o9KhZzIBluRhtxCmw5
nw4Do7z00PTvN81260uPWSc04IrytvZUiAIx/5qxD72bij2xJ8t/I9GI8g4FtoVB
8pB6S/hJX1PZhh9VlU6Yk+TOfOVnbebG4W5138LkB835eqk3Zz0qsbc2euoi8Hxi
y1VGwQEmMQ63jXz4c6g+X55ifvUK9Jpn5E8pq+pMd7ECgYEA93lYq+Cr54K4ey5t
sWMa+ye5RqxjzgXj2Kqr55jb54VWG7wp2iGbg8FMlkQwzTJwebzDyCSatguEZLuB
gRGroRnsUOy9vBvhKPOch9bfKIl6qOgzMJB267fBVWx5ybnRbWN/I7RvMQf3k+9y
biCIVnxDLEEYyx7z85/5qxsXg/MCgYEA7wmWKtCTn032Hy9P8OL49T0X6Z8FlkDC
Rk42ygrc/MUbugq9RGUxcCxoImOG9JXUpEtUe31YDm2j+/nbvrjl6/bP2qWs0V7l
dTJl6dABP51pCw8+l4cWgBBX08Lkeen812AAFNrjmDCjX6rHjWHLJcpS18fnRRkP
V1d/AHWX7MMCgYEA6Gsw2guhp0Zf2GCcaNK5DlQab8OL4Hwrpttzo4kuTlwtqNKp
Q9H4al9qfF4Cr1TFya98+EVYf8yFRM3NLNjZpe3gwYf2EerlJj7VLcahw0KKzoN1
QBENfwgPLRk5sDkx9VhSmcfl/diLroZdpAwtv3vo4nEoxeuGFbKTGx3Qkf0CgYEA
xyR+dcb05Ygm3w4klHQTowQ10s1H80iaUcZBgQuR1ghEtDbUPZHsoR5t1xCB02ys
DgAwLv1bChIvxvH/L6KM8ovZ2LekBX4AviWxoBxJnfz/EVau98B0b1auRN6eSC83
FRuGldlSOW1z/nSh8ViizSYE5H5HX1qkXEippvFRE88CgYB3Bfu3YQY60ITWIShv
nNkdcbTT9eoP9suaRJjw92Ln+7ZpALYlQMKUZmJ/5uBmLs4RFwUTQruLOPL4yLTH
awADWUzs3IRr1fwn9E+zM8JVyKCnUEM3w4N5UZskGO2klashAd30hWO+knRv/y0r
uGIYs9Ek7YXlXIRVrzMwcsrt1w==
-----END PRIVATE KEY-----
""".trimIndent()

val CLIENT_CERT_PEM = """
-----BEGIN CERTIFICATE-----
MIIDNzCCAh8CFGyX00RCepOv/qCJ1oVdTtY92U83MA0GCSqGSIb3DQEBCwUAMFYx
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
cm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMMBnRlc3RjYTAeFw0yMDAzMTgw
MTA2MTBaFw0zMDAzMTYwMTA2MTBaMFoxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApT
b21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEzAR
BgNVBAMMCnRlc3RjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
AQCyqYRp+DXVp72NFbQH8hdhTZLycZXOlJhmMsrJmrjn2p7pI/8mTZ/0FC+SGWBG
ZV+ELiHrmCX5zfaILr9Iuw7Ghr3Vzoefi8r62rLupVPNi/qdqyjWk2dECHC9Z3+A
g3KzKTyerXWjKcvyKVmM0ZxE0RXhDW/RoQbqZsU2GKg1B2rhUU8KN0gVmKn0rJHO
xzRVSYeYLYp5Yn7KrtPJcKyo9aVuEr7dGANzpyF6lg/nYBWc+9SGwkoLdFvKvABY
JMyrbNhHUQfv0fzaZ0P86dfTENrDxzALrzGnqcx3KTrwJjkZ/aSr1tyD0/tXvukR
FiPxWBJhjHQ70GqTFQY19RbhAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFXCewK8
cWT+zWxXyGFnouFSBzTi0BMBJRrhsiNoiQxkqityJHWFExiQZie+7CA+EabXCQUB
+JwMSWM29j3mSw10DTfmC3rhheQqGxy304BZyUpdpvI2dt3p/mcsE7O+p4sQrSep
gijiDssKAfxTAmUM93N6+Q8yJK5immxlbeYfijoBvmkzyB/B+qNRPsx0n7aFGnfv
oWfkW296iPhWLiwknpC3xB6oK3vRbK4Zj1OaGb0grK7VN8EyhBix2xVF61i4dzCK
kMIpl7CUpw1Mb2z8q3F2bHBS7iF7g1Ccn5VGcO+aJ+6PWydaeqJ6VEBF0Nwv9woe
mL5AluNRLaqjZvE=
-----END CERTIFICATE-----
""".trimIndent()

val CLIENT_KEY_PEM = """
-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyqYRp+DXVp72N
FbQH8hdhTZLycZXOlJhmMsrJmrjn2p7pI/8mTZ/0FC+SGWBGZV+ELiHrmCX5zfaI
Lr9Iuw7Ghr3Vzoefi8r62rLupVPNi/qdqyjWk2dECHC9Z3+Ag3KzKTyerXWjKcvy
KVmM0ZxE0RXhDW/RoQbqZsU2GKg1B2rhUU8KN0gVmKn0rJHOxzRVSYeYLYp5Yn7K
rtPJcKyo9aVuEr7dGANzpyF6lg/nYBWc+9SGwkoLdFvKvABYJMyrbNhHUQfv0fza
Z0P86dfTENrDxzALrzGnqcx3KTrwJjkZ/aSr1tyD0/tXvukRFiPxWBJhjHQ70GqT
FQY19RbhAgMBAAECggEAIL8JUhL4awyvpWhQ8xPgTSlWwbEn8BE0TacJnCILuhNM
BRdf8LlRk/8PKQwVpVF3TFbYSMI+U6b4hMVssfv3HVQc/083dHq+3XOwUCVlUstR
SAzTE2E5EDMr1stdh0SQhV4Nilfos9s5Uk1Z6IGSztoz1GgOErIc/mGPy/aA/hbr
fRWHvTp35+MbCJSvZuOeevX2iLs0dNzqdk6DiOWIH/BVGirVPtO6ykrkuTj1FWiN
hyZ3MBChShlNH2poNX46ntOc7nEus0qteOgxBK8lummFEtlehCA7hd/8xuvYlP0k
7aN684LCRDajmAGpoZO57NSDYQhAFGZeUZ93SMFucQKBgQDe7GGkzZFEiv91u1q9
lgMy1h5dZjIZKgQaOarPC6wCQMUdqCf6cSLsAPr4T8EDoWsnY7dSnrTZ6YCIFL1T
idg8M3BQXipICCJkFORS76pKKZ0wMn3/NgkSepsmNct91WHr6okvx4tOaoRCtdzU
g7jt4Mr3sfLCiZtqTQyySdMUEwKBgQDNK+ZFKL0XhkWZP+PGKjWG8LWpPiK3d78/
wYBFXzSTGlkr6FvRmYtZeNwXWRYLB4UxZ9At4hbJVEdi/2dITOz/sehVDyCAjjs3
gycsc3UJqiZbcw5XKhI5TWBuWxkKENdbMSayogVbp2aSYoRblH764//t0ACmbfTW
KUQRQPB/uwKBgQC5QjjjfPL8w4cJkGoYpFKELO2PMR7xSrmeEc6hwlFwjeNCgjy3
JM6g0y++rIj7O2qRkY0IXFxvvF3UuWedxTCu1xC/uYHp2ti506LsScB7YZoAM/YB
4iYn9Tx6xLoYGP0H0iGwU2SyBlNkHT8oXU+SYP5MWtYkVbeS3/VtNWz1gQKBgQCA
6Nk4kN0mH7YxEKRzSOfyzeDF4oV7kuB2FYUbkTL+TirC3K58JiYY5Egc31trOKFm
Jlz1xz0b6DkmKWTiV3r9OPHKJ8P7IeJxAZWmZzCdDuwkv0i+WW+z0zsIe3JjEavN
3zb6O7R0HtziksWoqMeTqZeO+wa9iw6vVKQw1wWEqwKBgFHfahFs0DZ5cUTpGpBt
F/AQG7ukgipB6N6AkB9kDbgCs1FLgd199MQrEncug5hfpq8QerbyMatmA+GXoGMb
7vztKEH85yzp4n02FNL6H7xL4VVILvyZHdolmiORJ4qT2hZnl8pEQ2TYuF4RlHUd
nSwXX+2o0J/nF85fm4AwWKAc
-----END PRIVATE KEY-----
""".trimIndent()
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,46 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.test.runTest
import kotlinx.rpc.RpcServer
import kotlinx.rpc.grpc.ClientCredentials
import kotlinx.rpc.grpc.GrpcClient
import kotlinx.rpc.grpc.GrpcServer
import kotlinx.rpc.grpc.ServerCredentials

abstract class GrpcProtoTest {
private val serverMutex = Mutex()

abstract fun RpcServer.registerServices()

protected fun runGrpcTest(test: suspend (GrpcClient) -> Unit) = runTest {
protected fun runGrpcTest(
serverCreds: ServerCredentials? = null,
clientCreds: ClientCredentials? = null,
overrideAuthority: String? = null,
test: suspend (GrpcClient) -> Unit,
) = runTest {
serverMutex.withLock {
val grpcClient = GrpcClient("localhost", PORT) {
usePlaintext()
val grpcClient = GrpcClient("localhost", PORT, credentials = clientCreds) {
if (overrideAuthority != null) overrideAuthority(overrideAuthority)
if (clientCreds == null) {
usePlaintext()
}
}

val grpcServer = GrpcServer(PORT, builder = {
registerServices()
})
val grpcServer = GrpcServer(
PORT,
credentials = serverCreds,
builder = {
registerServices()
})

grpcServer.start()
test(grpcClient)
grpcServer.shutdown()
grpcServer.awaitTermination()
grpcClient.shutdown()
grpcClient.awaitTermination()
try {
test(grpcClient)
} finally {
grpcServer.shutdown()
grpcServer.awaitTermination()
grpcClient.shutdown()
grpcClient.awaitTermination()
}
}
}

Expand Down
Loading
Loading