Skip to content

Commit d7e8271

Browse files
authored
grpc: Add TLS credentials support (#471)
Subsystem gRPC Problem Description Previously all connections were plaintext and thus not encrypted. Solution This PR adds TLS encryption support for both client and server on JVM and Native. New TLS credentials can be created using TlsClientCredentials and TlsServerCredentials.
1 parent 565ea07 commit d7e8271

File tree

23 files changed

+830
-91
lines changed

23 files changed

+830
-91
lines changed

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/GrpcClient.kt

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,14 @@ import kotlinx.rpc.grpc.codec.ThrowingMessageCodecResolver
1313
import kotlinx.rpc.grpc.codec.plus
1414
import kotlinx.rpc.grpc.descriptor.GrpcServiceDelegate
1515
import kotlinx.rpc.grpc.descriptor.GrpcServiceDescriptor
16-
import kotlinx.rpc.grpc.internal.*
16+
import kotlinx.rpc.grpc.internal.GrpcDefaultCallOptions
17+
import kotlinx.rpc.grpc.internal.MethodDescriptor
18+
import kotlinx.rpc.grpc.internal.MethodType
19+
import kotlinx.rpc.grpc.internal.bidirectionalStreamingRpc
20+
import kotlinx.rpc.grpc.internal.clientStreamingRpc
21+
import kotlinx.rpc.grpc.internal.serverStreamingRpc
22+
import kotlinx.rpc.grpc.internal.type
23+
import kotlinx.rpc.grpc.internal.unaryRpc
1724
import kotlinx.rpc.internal.utils.map.RpcInternalConcurrentHashMap
1825
import kotlin.time.Duration
1926

@@ -124,10 +131,11 @@ public class GrpcClient internal constructor(
124131
public fun GrpcClient(
125132
hostname: String,
126133
port: Int,
134+
credentials: ClientCredentials? = null,
127135
messageCodecResolver: MessageCodecResolver = EmptyMessageCodecResolver,
128136
configure: ManagedChannelBuilder<*>.() -> Unit = {},
129137
): GrpcClient {
130-
val channel = ManagedChannelBuilder(hostname, port).apply(configure).buildChannel()
138+
val channel = ManagedChannelBuilder(hostname, port, credentials).apply(configure).buildChannel()
131139
return GrpcClient(channel, messageCodecResolver)
132140
}
133141

@@ -136,9 +144,10 @@ public fun GrpcClient(
136144
*/
137145
public fun GrpcClient(
138146
target: String,
147+
credentials: ClientCredentials? = null,
139148
messageCodecResolver: MessageCodecResolver = EmptyMessageCodecResolver,
140149
configure: ManagedChannelBuilder<*>.() -> Unit = {},
141150
): GrpcClient {
142-
val channel = ManagedChannelBuilder(target).apply(configure).buildChannel()
151+
val channel = ManagedChannelBuilder(target, credentials).apply(configure).buildChannel()
143152
return GrpcClient(channel, messageCodecResolver)
144153
}

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/GrpcServer.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import kotlinx.coroutines.Job
1010
import kotlinx.coroutines.SupervisorJob
1111
import kotlinx.coroutines.cancel
1212
import kotlinx.coroutines.flow.Flow
13-
import kotlinx.coroutines.job
1413
import kotlinx.rpc.RpcServer
1514
import kotlinx.rpc.descriptor.RpcCallable
1615
import kotlinx.rpc.descriptor.flowInvokator
@@ -49,6 +48,7 @@ private typealias ResponseServer = Any
4948
*/
5049
public class GrpcServer internal constructor(
5150
override val port: Int = 8080,
51+
credentials: ServerCredentials? = null,
5252
messageCodecResolver: MessageCodecResolver = EmptyMessageCodecResolver,
5353
parentContext: CoroutineContext = EmptyCoroutineContext,
5454
configure: ServerBuilder<*>.() -> Unit,
@@ -61,7 +61,7 @@ public class GrpcServer internal constructor(
6161
private var isBuilt = false
6262
private lateinit var internalServer: Server
6363

64-
private val serverBuilder: ServerBuilder<*> = ServerBuilder(port).apply(configure)
64+
private val serverBuilder: ServerBuilder<*> = ServerBuilder(port, credentials).apply(configure)
6565
private val registry: MutableHandlerRegistry by lazy {
6666
MutableHandlerRegistry().apply { serverBuilder.fallbackHandlerRegistry(this) }
6767
}
@@ -192,10 +192,12 @@ public class GrpcServer internal constructor(
192192
*/
193193
public fun GrpcServer(
194194
port: Int,
195+
credentials: ServerCredentials? = null,
195196
messageCodecResolver: MessageCodecResolver = EmptyMessageCodecResolver,
196197
parentContext: CoroutineContext = EmptyCoroutineContext,
197198
configure: ServerBuilder<*>.() -> Unit = {},
198199
builder: RpcServer.() -> Unit = {},
199200
): GrpcServer {
200-
return GrpcServer(port, messageCodecResolver, parentContext, configure).apply(builder).apply { build() }
201+
return GrpcServer(port, credentials, messageCodecResolver, parentContext, configure).apply(builder)
202+
.apply { build() }
201203
}

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/ManagedChannel.kt

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,19 @@ public interface ManagedChannel {
6969
*/
7070
public expect abstract class ManagedChannelBuilder<T : ManagedChannelBuilder<T>> {
7171
public fun usePlaintext(): T
72+
73+
public abstract fun overrideAuthority(authority: String): T
7274
}
7375

74-
internal expect fun ManagedChannelBuilder(hostname: String, port: Int): ManagedChannelBuilder<*>
75-
internal expect fun ManagedChannelBuilder(target: String): ManagedChannelBuilder<*>
76+
internal expect fun ManagedChannelBuilder(
77+
hostname: String,
78+
port: Int,
79+
credentials: ClientCredentials? = null,
80+
): ManagedChannelBuilder<*>
81+
82+
internal expect fun ManagedChannelBuilder(
83+
target: String,
84+
credentials: ClientCredentials? = null,
85+
): ManagedChannelBuilder<*>
7686

7787
internal expect fun ManagedChannelBuilder<*>.buildChannel(): ManagedChannel

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/Server.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public expect abstract class ServerBuilder<T : ServerBuilder<T>> {
3131
public abstract fun fallbackHandlerRegistry(registry: HandlerRegistry?): T
3232
}
3333

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

3636
/**
3737
* Server for listening for and dispatching incoming calls.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.rpc.grpc
6+
7+
public expect abstract class ClientCredentials
8+
public expect abstract class ServerCredentials
9+
10+
public expect class InsecureClientCredentials : ClientCredentials
11+
public expect class InsecureServerCredentials : ServerCredentials
12+
13+
public expect class TlsClientCredentials : ClientCredentials
14+
public expect class TlsServerCredentials : ServerCredentials
15+
16+
public fun TlsClientCredentials(configure: TlsClientCredentialsBuilder.() -> Unit = {}): ClientCredentials {
17+
val builder = TlsClientCredentialsBuilder()
18+
builder.configure()
19+
return builder.build()
20+
}
21+
22+
public fun TlsServerCredentials(
23+
certChain: String,
24+
privateKey: String,
25+
configure: TlsServerCredentialsBuilder.() -> Unit = {},
26+
): ServerCredentials {
27+
val builder = TlsServerCredentialsBuilder(certChain, privateKey)
28+
builder.configure()
29+
return builder.build()
30+
}
31+
32+
public interface TlsClientCredentialsBuilder {
33+
public fun trustManager(rootCertsPem: String): TlsClientCredentialsBuilder
34+
public fun keyManager(certChainPem: String, privateKeyPem: String): TlsClientCredentialsBuilder
35+
}
36+
37+
public enum class TlsClientAuth {
38+
/** Clients will not present any identity. */
39+
NONE,
40+
41+
/**
42+
* Clients are requested to present their identity, but clients without identities are
43+
* permitted.
44+
* Also, if the client certificate is provided but cannot be verified,
45+
* the client is permitted.
46+
*/
47+
OPTIONAL,
48+
49+
/**
50+
* Clients are requested to present their identity, and are required to provide a valid
51+
* identity.
52+
*/
53+
REQUIRE
54+
}
55+
56+
public interface TlsServerCredentialsBuilder {
57+
public fun trustManager(rootCertsPem: String): TlsServerCredentialsBuilder
58+
public fun clientAuth(clientAuth: TlsClientAuth): TlsServerCredentialsBuilder
59+
}
60+
61+
internal expect fun TlsClientCredentialsBuilder(): TlsClientCredentialsBuilder
62+
internal expect fun TlsServerCredentialsBuilder(
63+
certChain: String,
64+
privateKey: String,
65+
): TlsServerCredentialsBuilder
66+
67+
internal expect fun TlsClientCredentialsBuilder.build(): ClientCredentials
68+
internal expect fun TlsServerCredentialsBuilder.build(): ServerCredentials

grpc/grpc-core/src/commonMain/kotlin/kotlinx/rpc/grpc/internal/GrpcChannel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ public expect abstract class GrpcChannel {
1313
callOptions: GrpcCallOptions,
1414
): ClientCall<RequestT, ResponseT>
1515

16-
public abstract fun authority(): String
16+
public abstract fun authority(): String?
1717
}
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
/*
2+
* Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.rpc.grpc.test
6+
7+
// Certs are taken from grpc-java/testing/src/main/resources/certs
8+
9+
val CA_PEM = """
10+
-----BEGIN CERTIFICATE-----
11+
MIIDWjCCAkKgAwIBAgIUWrP0VvHcy+LP6UuYNtiL9gBhD5owDQYJKoZIhvcNAQEL
12+
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
13+
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIw
14+
MDMxNzE4NTk1MVoXDTMwMDMxNTE4NTk1MVowVjELMAkGA1UEBhMCQVUxEzARBgNV
15+
BAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
16+
ZDEPMA0GA1UEAwwGdGVzdGNhMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC
17+
AQEAsGL0oXflF0LzoM+Bh+qUU9yhqzw2w8OOX5mu/iNCyUOBrqaHi7mGHx73GD01
18+
diNzCzvlcQqdNIH6NQSL7DTpBjca66jYT9u73vZe2MDrr1nVbuLvfu9850cdxiUO
19+
Inv5xf8+sTHG0C+a+VAvMhsLiRjsq+lXKRJyk5zkbbsETybqpxoJ+K7CoSy3yc/k
20+
QIY3TipwEtwkKP4hzyo6KiGd/DPexie4nBUInN3bS1BUeNZ5zeaIC2eg3bkeeW7c
21+
qT55b+Yen6CxY0TEkzBK6AKt/WUialKMgT0wbTxRZO7kUCH3Sq6e/wXeFdJ+HvdV
22+
LPlAg5TnMaNpRdQih/8nRFpsdwIDAQABoyAwHjAMBgNVHRMEBTADAQH/MA4GA1Ud
23+
DwEB/wQEAwICBDANBgkqhkiG9w0BAQsFAAOCAQEAkTrKZjBrJXHps/HrjNCFPb5a
24+
THuGPCSsepe1wkKdSp1h4HGRpLoCgcLysCJ5hZhRpHkRihhef+rFHEe60UePQO3S
25+
CVTtdJB4CYWpcNyXOdqefrbJW5QNljxgi6Fhvs7JJkBqdXIkWXtFk2eRgOIP2Eo9
26+
/OHQHlYnwZFrk6sp4wPyR+A95S0toZBcyDVz7u+hOW0pGK3wviOe9lvRgj/H3Pwt
27+
bewb0l+MhRig0/DVHamyVxrDRbqInU1/GTNCwcZkXKYFWSf92U+kIcTth24Q1gcw
28+
eZiLl5FfrWokUNytFElXob0V0a5/kbhiLc3yWmvWqHTpqCALbVyF+rKJo2f5Kw==
29+
-----END CERTIFICATE-----
30+
""".trimIndent()
31+
32+
val SERVER_CERT_PEM = """
33+
-----BEGIN CERTIFICATE-----
34+
MIIDtDCCApygAwIBAgIUbJfTREJ6k6/+oInWhV1O1j3ZT0IwDQYJKoZIhvcNAQEL
35+
BQAwVjELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoM
36+
GEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEPMA0GA1UEAwwGdGVzdGNhMB4XDTIw
37+
MDMxODAzMTA0MloXDTMwMDMxNjAzMTA0MlowZTELMAkGA1UEBhMCVVMxETAPBgNV
38+
BAgMCElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRUwEwYDVQQKDAxFeGFtcGxl
39+
LCBDby4xGjAYBgNVBAMMESoudGVzdC5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0B
40+
AQEFAAOCAQ8AMIIBCgKCAQEA5xOONxJJ8b8Qauvob5/7dPYZfIcd+uhAWL2ZlTPz
41+
Qvu4oF0QI4iYgP5iGgry9zEtCM+YQS8UhiAlPlqa6ANxgiBSEyMHH/xE8lo/+caY
42+
GeACqy640Jpl/JocFGo3xd1L8DCawjlaj6eu7T7T/tpAV2qq13b5710eNRbCAfFe
43+
8yALiGQemx0IYhlZXNbIGWLBNhBhvVjJh7UvOqpADk4xtl8o5j0xgMIRg6WJGK6c
44+
6ffSIg4eP1XmovNYZ9LLEJG68tF0Q/yIN43B4dt1oq4jzSdCbG4F1EiykT2TmwPV
45+
YDi8tml6DfOCDGnit8svnMEmBv/fcPd31GSbXjF8M+KGGQIDAQABo2swaTAJBgNV
46+
HRMEAjAAMAsGA1UdDwQEAwIF4DBPBgNVHREESDBGghAqLnRlc3QuZ29vZ2xlLmZy
47+
ghh3YXRlcnpvb2kudGVzdC5nb29nbGUuYmWCEioudGVzdC55b3V0dWJlLmNvbYcE
48+
wKgBAzANBgkqhkiG9w0BAQsFAAOCAQEAS8hDQA8PSgipgAml7Q3/djwQ644ghWQv
49+
C2Kb+r30RCY1EyKNhnQnIIh/OUbBZvh0M0iYsy6xqXgfDhCB93AA6j0i5cS8fkhH
50+
Jl4RK0tSkGQ3YNY4NzXwQP/vmUgfkw8VBAZ4Y4GKxppdATjffIW+srbAmdDruIRM
51+
wPeikgOoRrXf0LA1fi4TqxARzeRwenQpayNfGHTvVF9aJkl8HoaMunTAdG5pIVcr
52+
9GKi/gEMpXUJbbVv3U5frX1Wo4CFo+rZWJ/LyCMeb0jciNLxSdMwj/E/ZuExlyeZ
53+
gc9ctPjSMvgSyXEKv6Vwobleeg88V2ZgzenziORoWj4KszG/lbQZvg==
54+
-----END CERTIFICATE-----
55+
""".trimIndent()
56+
57+
val SERVER_KEY_PEM = """
58+
-----BEGIN PRIVATE KEY-----
59+
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDnE443EknxvxBq
60+
6+hvn/t09hl8hx366EBYvZmVM/NC+7igXRAjiJiA/mIaCvL3MS0Iz5hBLxSGICU+
61+
WproA3GCIFITIwcf/ETyWj/5xpgZ4AKrLrjQmmX8mhwUajfF3UvwMJrCOVqPp67t
62+
PtP+2kBXaqrXdvnvXR41FsIB8V7zIAuIZB6bHQhiGVlc1sgZYsE2EGG9WMmHtS86
63+
qkAOTjG2XyjmPTGAwhGDpYkYrpzp99IiDh4/Veai81hn0ssQkbry0XRD/Ig3jcHh
64+
23WiriPNJ0JsbgXUSLKRPZObA9VgOLy2aXoN84IMaeK3yy+cwSYG/99w93fUZJte
65+
MXwz4oYZAgMBAAECggEBAIVn2Ncai+4xbH0OLWckabwgyJ4IM9rDc0LIU368O1kU
66+
koais8qP9dujAWgfoh3sGh/YGgKn96VnsZjKHlyMgF+r4TaDJn3k2rlAOWcurGlj
67+
1qaVlsV4HiEzp7pxiDmHhWvp4672Bb6iBG+bsjCUOEk/n9o9KhZzIBluRhtxCmw5
68+
nw4Do7z00PTvN81260uPWSc04IrytvZUiAIx/5qxD72bij2xJ8t/I9GI8g4FtoVB
69+
8pB6S/hJX1PZhh9VlU6Yk+TOfOVnbebG4W5138LkB835eqk3Zz0qsbc2euoi8Hxi
70+
y1VGwQEmMQ63jXz4c6g+X55ifvUK9Jpn5E8pq+pMd7ECgYEA93lYq+Cr54K4ey5t
71+
sWMa+ye5RqxjzgXj2Kqr55jb54VWG7wp2iGbg8FMlkQwzTJwebzDyCSatguEZLuB
72+
gRGroRnsUOy9vBvhKPOch9bfKIl6qOgzMJB267fBVWx5ybnRbWN/I7RvMQf3k+9y
73+
biCIVnxDLEEYyx7z85/5qxsXg/MCgYEA7wmWKtCTn032Hy9P8OL49T0X6Z8FlkDC
74+
Rk42ygrc/MUbugq9RGUxcCxoImOG9JXUpEtUe31YDm2j+/nbvrjl6/bP2qWs0V7l
75+
dTJl6dABP51pCw8+l4cWgBBX08Lkeen812AAFNrjmDCjX6rHjWHLJcpS18fnRRkP
76+
V1d/AHWX7MMCgYEA6Gsw2guhp0Zf2GCcaNK5DlQab8OL4Hwrpttzo4kuTlwtqNKp
77+
Q9H4al9qfF4Cr1TFya98+EVYf8yFRM3NLNjZpe3gwYf2EerlJj7VLcahw0KKzoN1
78+
QBENfwgPLRk5sDkx9VhSmcfl/diLroZdpAwtv3vo4nEoxeuGFbKTGx3Qkf0CgYEA
79+
xyR+dcb05Ygm3w4klHQTowQ10s1H80iaUcZBgQuR1ghEtDbUPZHsoR5t1xCB02ys
80+
DgAwLv1bChIvxvH/L6KM8ovZ2LekBX4AviWxoBxJnfz/EVau98B0b1auRN6eSC83
81+
FRuGldlSOW1z/nSh8ViizSYE5H5HX1qkXEippvFRE88CgYB3Bfu3YQY60ITWIShv
82+
nNkdcbTT9eoP9suaRJjw92Ln+7ZpALYlQMKUZmJ/5uBmLs4RFwUTQruLOPL4yLTH
83+
awADWUzs3IRr1fwn9E+zM8JVyKCnUEM3w4N5UZskGO2klashAd30hWO+knRv/y0r
84+
uGIYs9Ek7YXlXIRVrzMwcsrt1w==
85+
-----END PRIVATE KEY-----
86+
""".trimIndent()
87+
88+
val CLIENT_CERT_PEM = """
89+
-----BEGIN CERTIFICATE-----
90+
MIIDNzCCAh8CFGyX00RCepOv/qCJ1oVdTtY92U83MA0GCSqGSIb3DQEBCwUAMFYx
91+
CzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRl
92+
cm5ldCBXaWRnaXRzIFB0eSBMdGQxDzANBgNVBAMMBnRlc3RjYTAeFw0yMDAzMTgw
93+
MTA2MTBaFw0zMDAzMTYwMTA2MTBaMFoxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApT
94+
b21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEzAR
95+
BgNVBAMMCnRlc3RjbGllbnQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
96+
AQCyqYRp+DXVp72NFbQH8hdhTZLycZXOlJhmMsrJmrjn2p7pI/8mTZ/0FC+SGWBG
97+
ZV+ELiHrmCX5zfaILr9Iuw7Ghr3Vzoefi8r62rLupVPNi/qdqyjWk2dECHC9Z3+A
98+
g3KzKTyerXWjKcvyKVmM0ZxE0RXhDW/RoQbqZsU2GKg1B2rhUU8KN0gVmKn0rJHO
99+
xzRVSYeYLYp5Yn7KrtPJcKyo9aVuEr7dGANzpyF6lg/nYBWc+9SGwkoLdFvKvABY
100+
JMyrbNhHUQfv0fzaZ0P86dfTENrDxzALrzGnqcx3KTrwJjkZ/aSr1tyD0/tXvukR
101+
FiPxWBJhjHQ70GqTFQY19RbhAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAFXCewK8
102+
cWT+zWxXyGFnouFSBzTi0BMBJRrhsiNoiQxkqityJHWFExiQZie+7CA+EabXCQUB
103+
+JwMSWM29j3mSw10DTfmC3rhheQqGxy304BZyUpdpvI2dt3p/mcsE7O+p4sQrSep
104+
gijiDssKAfxTAmUM93N6+Q8yJK5immxlbeYfijoBvmkzyB/B+qNRPsx0n7aFGnfv
105+
oWfkW296iPhWLiwknpC3xB6oK3vRbK4Zj1OaGb0grK7VN8EyhBix2xVF61i4dzCK
106+
kMIpl7CUpw1Mb2z8q3F2bHBS7iF7g1Ccn5VGcO+aJ+6PWydaeqJ6VEBF0Nwv9woe
107+
mL5AluNRLaqjZvE=
108+
-----END CERTIFICATE-----
109+
""".trimIndent()
110+
111+
val CLIENT_KEY_PEM = """
112+
-----BEGIN PRIVATE KEY-----
113+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCyqYRp+DXVp72N
114+
FbQH8hdhTZLycZXOlJhmMsrJmrjn2p7pI/8mTZ/0FC+SGWBGZV+ELiHrmCX5zfaI
115+
Lr9Iuw7Ghr3Vzoefi8r62rLupVPNi/qdqyjWk2dECHC9Z3+Ag3KzKTyerXWjKcvy
116+
KVmM0ZxE0RXhDW/RoQbqZsU2GKg1B2rhUU8KN0gVmKn0rJHOxzRVSYeYLYp5Yn7K
117+
rtPJcKyo9aVuEr7dGANzpyF6lg/nYBWc+9SGwkoLdFvKvABYJMyrbNhHUQfv0fza
118+
Z0P86dfTENrDxzALrzGnqcx3KTrwJjkZ/aSr1tyD0/tXvukRFiPxWBJhjHQ70GqT
119+
FQY19RbhAgMBAAECggEAIL8JUhL4awyvpWhQ8xPgTSlWwbEn8BE0TacJnCILuhNM
120+
BRdf8LlRk/8PKQwVpVF3TFbYSMI+U6b4hMVssfv3HVQc/083dHq+3XOwUCVlUstR
121+
SAzTE2E5EDMr1stdh0SQhV4Nilfos9s5Uk1Z6IGSztoz1GgOErIc/mGPy/aA/hbr
122+
fRWHvTp35+MbCJSvZuOeevX2iLs0dNzqdk6DiOWIH/BVGirVPtO6ykrkuTj1FWiN
123+
hyZ3MBChShlNH2poNX46ntOc7nEus0qteOgxBK8lummFEtlehCA7hd/8xuvYlP0k
124+
7aN684LCRDajmAGpoZO57NSDYQhAFGZeUZ93SMFucQKBgQDe7GGkzZFEiv91u1q9
125+
lgMy1h5dZjIZKgQaOarPC6wCQMUdqCf6cSLsAPr4T8EDoWsnY7dSnrTZ6YCIFL1T
126+
idg8M3BQXipICCJkFORS76pKKZ0wMn3/NgkSepsmNct91WHr6okvx4tOaoRCtdzU
127+
g7jt4Mr3sfLCiZtqTQyySdMUEwKBgQDNK+ZFKL0XhkWZP+PGKjWG8LWpPiK3d78/
128+
wYBFXzSTGlkr6FvRmYtZeNwXWRYLB4UxZ9At4hbJVEdi/2dITOz/sehVDyCAjjs3
129+
gycsc3UJqiZbcw5XKhI5TWBuWxkKENdbMSayogVbp2aSYoRblH764//t0ACmbfTW
130+
KUQRQPB/uwKBgQC5QjjjfPL8w4cJkGoYpFKELO2PMR7xSrmeEc6hwlFwjeNCgjy3
131+
JM6g0y++rIj7O2qRkY0IXFxvvF3UuWedxTCu1xC/uYHp2ti506LsScB7YZoAM/YB
132+
4iYn9Tx6xLoYGP0H0iGwU2SyBlNkHT8oXU+SYP5MWtYkVbeS3/VtNWz1gQKBgQCA
133+
6Nk4kN0mH7YxEKRzSOfyzeDF4oV7kuB2FYUbkTL+TirC3K58JiYY5Egc31trOKFm
134+
Jlz1xz0b6DkmKWTiV3r9OPHKJ8P7IeJxAZWmZzCdDuwkv0i+WW+z0zsIe3JjEavN
135+
3zb6O7R0HtziksWoqMeTqZeO+wa9iw6vVKQw1wWEqwKBgFHfahFs0DZ5cUTpGpBt
136+
F/AQG7ukgipB6N6AkB9kDbgCs1FLgd199MQrEncug5hfpq8QerbyMatmA+GXoGMb
137+
7vztKEH85yzp4n02FNL6H7xL4VVILvyZHdolmiORJ4qT2hZnl8pEQ2TYuF4RlHUd
138+
nSwXX+2o0J/nF85fm4AwWKAc
139+
-----END PRIVATE KEY-----
140+
""".trimIndent()

grpc/grpc-core/src/commonTest/kotlin/kotlinx/rpc/grpc/test/proto/GrpcProtoTest.kt

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,30 +8,46 @@ import kotlinx.coroutines.sync.Mutex
88
import kotlinx.coroutines.sync.withLock
99
import kotlinx.coroutines.test.runTest
1010
import kotlinx.rpc.RpcServer
11+
import kotlinx.rpc.grpc.ClientCredentials
1112
import kotlinx.rpc.grpc.GrpcClient
1213
import kotlinx.rpc.grpc.GrpcServer
14+
import kotlinx.rpc.grpc.ServerCredentials
1315

1416
abstract class GrpcProtoTest {
1517
private val serverMutex = Mutex()
1618

1719
abstract fun RpcServer.registerServices()
1820

19-
protected fun runGrpcTest(test: suspend (GrpcClient) -> Unit) = runTest {
21+
protected fun runGrpcTest(
22+
serverCreds: ServerCredentials? = null,
23+
clientCreds: ClientCredentials? = null,
24+
overrideAuthority: String? = null,
25+
test: suspend (GrpcClient) -> Unit,
26+
) = runTest {
2027
serverMutex.withLock {
21-
val grpcClient = GrpcClient("localhost", PORT) {
22-
usePlaintext()
28+
val grpcClient = GrpcClient("localhost", PORT, credentials = clientCreds) {
29+
if (overrideAuthority != null) overrideAuthority(overrideAuthority)
30+
if (clientCreds == null) {
31+
usePlaintext()
32+
}
2333
}
2434

25-
val grpcServer = GrpcServer(PORT, builder = {
26-
registerServices()
27-
})
35+
val grpcServer = GrpcServer(
36+
PORT,
37+
credentials = serverCreds,
38+
builder = {
39+
registerServices()
40+
})
2841

2942
grpcServer.start()
30-
test(grpcClient)
31-
grpcServer.shutdown()
32-
grpcServer.awaitTermination()
33-
grpcClient.shutdown()
34-
grpcClient.awaitTermination()
43+
try {
44+
test(grpcClient)
45+
} finally {
46+
grpcServer.shutdown()
47+
grpcServer.awaitTermination()
48+
grpcClient.shutdown()
49+
grpcClient.awaitTermination()
50+
}
3551
}
3652
}
3753

0 commit comments

Comments
 (0)