@@ -8,29 +8,118 @@ import kotlinx.coroutines.flow.Flow
88import kotlinx.rpc.grpc.internal.GrpcCallOptions
99import kotlinx.rpc.grpc.internal.MethodDescriptor
1010
11+ /* *
12+ * The scope of a single outgoing gRPC client call observed by a [ClientInterceptor].
13+ *
14+ * An interceptor receives this scope instance for every call and can:
15+ * - Inspect the RPC [method] being invoked.
16+ * - Read or populate [requestHeaders] before the request is sent.
17+ * - Read [callOptions] that affect transport-level behavior.
18+ * - Register callbacks with [onHeaders] and [onClose] to observe response metadata and final status.
19+ * - Cancel the call early via [cancel].
20+ * - Continue the call by calling [proceed] with a (possibly transformed) request [Flow].
21+ * - Transform the response by modifying the returned [Flow].
22+ *
23+ * ```kt
24+ * val interceptor = object : ClientInterceptor {
25+ * override fun <Request, Response> ClientCallScope<Request, Response>.intercept(
26+ * request: Flow<Request>
27+ * ): Flow<Response> {
28+ * // Example: add a header before proceeding
29+ * requestHeaders[MyKeys.Authorization] = token
30+ *
31+ * // Example: observe response metadata
32+ * onHeaders { headers -> /* inspect headers */ }
33+ * onClose { status, trailers -> /* log status/trailers */ }
34+ *
35+ * // IMPORTANT: proceed forwards the call to the next interceptor/transport.
36+ * // If you do not call proceed, no request will be sent and the call is short-circuited.
37+ * return proceed(request)
38+ * }
39+ * }
40+ * ```
41+ *
42+ * @param Request the request message type of the RPC.
43+ * @param Response the response message type of the RPC.
44+ */
1145public interface ClientCallScope <Request , Response > {
46+ /* * Descriptor of the RPC method (name, marshalling, type) being invoked. */
1247 public val method: MethodDescriptor <Request , Response >
48+
49+ /* *
50+ * Outgoing request headers for this call.
51+ *
52+ * Interceptors may read and mutate this metadata
53+ * before calling [proceed] so the headers are sent to the server. Headers added after
54+ * the call has already been proceeded may not be reflected on the wire.
55+ */
1356 public val requestHeaders: GrpcMetadata
57+
58+ /* *
59+ * Transport/engine options used for this call (deadlines, compression, etc.).
60+ * Modifying this object is only possible before the call is proceeded.
61+ */
1462 public val callOptions: GrpcCallOptions
63+
64+ /* *
65+ * Register a callback invoked when the initial response headers are received.
66+ * Typical gRPC semantics guarantee headers are delivered at most once per call
67+ * and before the first message is received.
68+ */
1569 public fun onHeaders (block : (responseHeaders: GrpcMetadata ) -> Unit )
16- public fun onClose (block : (closeStatus: Status , responseTrailers: GrpcMetadata ) -> Unit )
70+
71+ /* *
72+ * Register a callback invoked when the call completes, successfully or not.
73+ * The final `status` and trailing `responseTrailers` are provided.
74+ */
75+ public fun onClose (block : (status: Status , responseTrailers: GrpcMetadata ) -> Unit )
76+
77+ /* *
78+ * Cancel the call locally, providing a human-readable [message] and an optional [cause].
79+ * This method won't return and abort all further processing.
80+ */
1781 public fun cancel (message : String , cause : Throwable ? = null): Nothing
82+
83+ /* *
84+ * Continue the invocation by forwarding it to the next interceptor or to the underlying transport.
85+ *
86+ * This function is the heart of an interceptor:
87+ * - It must be called to actually perform the RPC. If you never call [proceed], the request is not sent
88+ * and the call is effectively short-circuited by the interceptor.
89+ * - You may transform the [request] flow before passing it to [proceed] (e.g., logging, retry orchestration,
90+ * compression, metrics). The returned [Flow] yields response messages and can also be transformed
91+ * before being returned to the caller.
92+ * - Call [proceed] at most once per intercepted call. Calling it multiple times or after cancellation
93+ * is not supported.
94+ */
1895 public fun proceed (request : Flow <Request >): Flow <Response >
1996}
2097
98+ /* *
99+ * Client-side interceptor for gRPC calls.
100+ *
101+ * Implementations can observe and modify client calls in a structured way. The primary entry point is the
102+ * [intercept] extension function on [ClientCallScope], which receives the inbound request [Flow] and must
103+ * call [ClientCallScope.proceed] to forward the call.
104+ *
105+ * Common use-cases include:
106+ * - Adding authentication or custom headers.
107+ * - Implementing logging/metrics.
108+ * - Observing headers/trailers and final status.
109+ * - Transforming request/response flows (e.g., mapping, buffering, throttling).
110+ */
21111public interface ClientInterceptor {
22-
23112 /* *
24- * Intercepts and transforms the flow of requests and responses in a client call.
25- * An interceptor can throw an exception at any time to cancel the call.
113+ * Intercept a client call.
26114 *
27- * The interceptor must ensure that it emits an expected number of values.
28- * E.g. if the intercepted method is a unary call, the interceptor's returned flow must emit exactly one value.
115+ * You can:
116+ * - Inspect [ClientCallScope.method] and [ClientCallScope.callOptions].
117+ * - Read or populate [ClientCallScope.requestHeaders].
118+ * - Register [ClientCallScope.onHeaders] and [ClientCallScope.onClose] callbacks.
119+ * - Transform the [request] flow or wrap the resulting response flow.
29120 *
30- * @param this The scope of the client call, providing context and methods for managing
31- * the call lifecycle and metadata.
32- * @param request A flow of requests to be sent to the server.
33- * @return A flow of responses received from the server.
121+ * IMPORTANT: [ClientCallScope.proceed] must eventually be called to actually execute the RPC and obtain
122+ * the response [Flow]. If [ClientCallScope.proceed] is omitted, the call will not reach the server.
34123 */
35124 public fun <Request , Response > ClientCallScope <Request , Response >.intercept (
36125 request : Flow <Request >,
0 commit comments