@@ -19,11 +19,115 @@ import com.squareup.wire.GrpcMethod
1919import com.squareup.wire.GrpcServerStreamingCall
2020import com.squareup.wire.GrpcStreamingCall
2121import com.squareup.wire.MessageSource
22+ import com.squareup.wire.WireGrpcClient
23+ import java.util.concurrent.TimeUnit
2224import kotlinx.coroutines.CoroutineScope
25+ import kotlinx.coroutines.channels.Channel
2326import kotlinx.coroutines.channels.ReceiveChannel
27+ import okio.ForwardingTimeout
2428import okio.Timeout
2529
30+ /* *
31+ * A [GrpcServerStreamingCall] that sends a single non-duplex request and reads a streaming
32+ * response. Using a non-duplex request body ensures the complete request (including END_STREAM) is
33+ * sent to the server before responses are read, avoiding delays on servers that wait for the
34+ * client's half-close before starting to stream responses.
35+ */
2636internal class RealGrpcServerStreamingCall <S : Any , R : Any >(
37+ private val grpcClient : WireGrpcClient ,
38+ override val method : GrpcMethod <S , R >,
39+ ) : GrpcServerStreamingCall<S, R> {
40+
41+ private var call: okhttp3.Call ? = null
42+ private var canceled = false
43+
44+ override val timeout: Timeout = ForwardingTimeout (Timeout ())
45+
46+ init {
47+ timeout.clearTimeout()
48+ timeout.clearDeadline()
49+ }
50+
51+ override var requestMetadata: Map <String , String > = mapOf ()
52+
53+ override var responseMetadata: Map <String , String >? = null
54+ internal set
55+
56+ override fun cancel () {
57+ canceled = true
58+ call?.cancel()
59+ }
60+
61+ override fun isCanceled (): Boolean = canceled || call?.isCanceled() == true
62+
63+ override fun isExecuted (): Boolean = call?.isExecuted() ? : false
64+
65+ override fun clone (): GrpcServerStreamingCall <S , R > {
66+ val result = RealGrpcServerStreamingCall (grpcClient, method)
67+ val oldTimeout = this .timeout
68+ result.timeout.also { newTimeout ->
69+ newTimeout.timeout(oldTimeout.timeoutNanos(), TimeUnit .NANOSECONDS )
70+ if (oldTimeout.hasDeadline()) {
71+ newTimeout.deadlineNanoTime(oldTimeout.deadlineNanoTime())
72+ } else {
73+ newTimeout.clearDeadline()
74+ }
75+ }
76+ result.requestMetadata + = this .requestMetadata
77+ return result
78+ }
79+
80+ override suspend fun executeIn (scope : CoroutineScope , request : S ): ReceiveChannel <R > {
81+ val responseChannel = Channel <R >(1 )
82+ val call = initCall(request)
83+
84+ responseChannel.invokeOnClose {
85+ if (responseChannel.isClosedForReceive) {
86+ call.cancel()
87+ }
88+ }
89+
90+ call.enqueue(
91+ responseChannel.readFromResponseBodyCallback(
92+ onResponseMetadata = { this .responseMetadata = it },
93+ responseAdapter = method.responseAdapter,
94+ ),
95+ )
96+
97+ return responseChannel
98+ }
99+
100+ override fun executeBlocking (request : S ): MessageSource <R > {
101+ val call = initCall(request)
102+ val messageSource = BlockingMessageSource (
103+ onResponseMetadata = { this .responseMetadata = it },
104+ responseAdapter = method.responseAdapter,
105+ call = call,
106+ )
107+ call.enqueue(messageSource.readFromResponseBodyCallback())
108+ return messageSource
109+ }
110+
111+ private fun initCall (request : S ): okhttp3.Call {
112+ check(this .call == null ) { " already executed" }
113+ val requestBody = newRequestBody(
114+ minMessageToCompress = grpcClient.minMessageToCompress,
115+ requestAdapter = method.requestAdapter,
116+ onlyMessage = request,
117+ )
118+ val result = grpcClient.newCall(method, requestMetadata, requestBody, timeout)
119+ this .call = result
120+ if (canceled) result.cancel()
121+ (timeout as ForwardingTimeout ).setDelegate(result.timeout())
122+ return result
123+ }
124+ }
125+
126+ /* *
127+ * Wraps a [GrpcStreamingCall] as a [GrpcServerStreamingCall]. Used for test doubles created via
128+ * [com.squareup.wire.GrpcServerStreamingCall] factory functions in GrpcCalls.
129+ */
130+ internal class GrpcStreamingCallServerStreamingAdapter <S : Any , R : Any >(
27131 private val callDelegate : GrpcStreamingCall <S , R >,
28132 override val method : GrpcMethod <S , R >,
29133) : GrpcServerStreamingCall<S, R> {
@@ -46,7 +150,7 @@ internal class RealGrpcServerStreamingCall<S : Any, R : Any>(
46150
47151 override fun isExecuted () = callDelegate.isExecuted()
48152
49- override fun clone () = RealGrpcServerStreamingCall (callDelegate.clone(), method)
153+ override fun clone () = GrpcStreamingCallServerStreamingAdapter (callDelegate.clone(), method)
50154
51155 override suspend fun executeIn (scope : CoroutineScope , request : S ): ReceiveChannel <R > {
52156 val (sendChannel, receiveChannel) = callDelegate.executeIn(scope)
@@ -65,5 +169,5 @@ internal class RealGrpcServerStreamingCall<S : Any, R : Any>(
65169 }
66170}
67171
68- internal fun <S : Any , R : Any >GrpcStreamingCall <S , R >.asGrpcServerStreamingCall () =
69- RealGrpcServerStreamingCall (this , method)
172+ internal fun <S : Any , R : Any > GrpcStreamingCall <S , R >.asGrpcServerStreamingCall () =
173+ GrpcStreamingCallServerStreamingAdapter (this , method)
0 commit comments