3434import io .grpc .CompressorRegistry ;
3535import io .grpc .Context ;
3636import io .grpc .DecompressorRegistry ;
37+ import io .grpc .Detachable ;
3738import io .grpc .InternalDecompressorRegistry ;
3839import io .grpc .InternalStatus ;
3940import io .grpc .Metadata ;
4546import io .perfmark .PerfMark ;
4647import io .perfmark .Tag ;
4748import io .perfmark .TaskCloseable ;
49+ import java .io .ByteArrayInputStream ;
50+ import java .io .ByteArrayOutputStream ;
51+ import java .io .IOException ;
4852import java .io .InputStream ;
4953import java .util .logging .Level ;
5054import java .util .logging .Logger ;
@@ -288,6 +292,7 @@ static final class ServerStreamListenerImpl<ReqT> implements ServerStreamListene
288292 private final ServerCallImpl <ReqT , ?> call ;
289293 private final ServerCall .Listener <ReqT > listener ;
290294 private final Context .CancellableContext context ;
295+ private InputStream delayedMessage ;
291296
292297 public ServerStreamListenerImpl (
293298 ServerCallImpl <ReqT , ?> call , ServerCall .Listener <ReqT > listener ,
@@ -320,6 +325,20 @@ public void messagesAvailable(MessageProducer producer) {
320325 }
321326 }
322327
328+ private static InputStream bufferMessage (InputStream is ) throws IOException {
329+ if (is instanceof Detachable ) {
330+ return ((Detachable ) is ).detach ();
331+ }
332+ // Fallback: copy to byte array
333+ ByteArrayOutputStream baos = new ByteArrayOutputStream ();
334+ byte [] buffer = new byte [4096 ];
335+ int bytesRead ;
336+ while ((bytesRead = is .read (buffer )) != -1 ) {
337+ baos .write (buffer , 0 , bytesRead );
338+ }
339+ return new ByteArrayInputStream (baos .toByteArray ());
340+ }
341+
323342 @ SuppressWarnings ("Finally" ) // The code avoids suppressing the exception thrown from try
324343 private void messagesAvailableInternal (final MessageProducer producer ) {
325344 if (call .cancelled ) {
@@ -330,13 +349,32 @@ private void messagesAvailableInternal(final MessageProducer producer) {
330349 InputStream message ;
331350 try {
332351 while ((message = producer .next ()) != null ) {
333- try {
334- listener .onMessage (call .method .parseRequest (message ));
335- } catch (Throwable t ) {
336- GrpcUtil .closeQuietly (message );
337- throw t ;
352+ if (call .method .getType ().clientSendsOneMessage ()) {
353+ if (delayedMessage != null ) {
354+ GrpcUtil .closeQuietly (message );
355+ call .close (
356+ Status .INTERNAL .withDescription ("Too many requests" ),
357+ new Metadata ());
358+ GrpcUtil .closeQuietly (delayedMessage );
359+ delayedMessage = null ;
360+ return ;
361+ }
362+ try {
363+ delayedMessage = bufferMessage (message );
364+ } catch (Throwable t ) {
365+ GrpcUtil .closeQuietly (message );
366+ throw t ;
367+ }
368+ message .close ();
369+ } else {
370+ try {
371+ listener .onMessage (call .method .parseRequest (message ));
372+ } catch (Throwable t ) {
373+ GrpcUtil .closeQuietly (message );
374+ throw t ;
375+ }
376+ message .close ();
338377 }
339- message .close ();
340378 }
341379 } catch (Throwable t ) {
342380 GrpcUtil .closeQuietly (producer );
@@ -353,6 +391,23 @@ public void halfClosed() {
353391 return ;
354392 }
355393
394+ if (delayedMessage != null ) {
395+ InputStream message = delayedMessage ;
396+ delayedMessage = null ;
397+ try {
398+ listener .onMessage (call .method .parseRequest (message ));
399+ } catch (Throwable t ) {
400+ GrpcUtil .closeQuietly (message );
401+ Throwables .throwIfUnchecked (t );
402+ throw new RuntimeException (t );
403+ }
404+ try {
405+ message .close ();
406+ } catch (IOException e ) {
407+ throw new RuntimeException (e );
408+ }
409+ }
410+
356411 listener .onHalfClose ();
357412 }
358413 }
@@ -366,6 +421,10 @@ public void closed(Status status) {
366421 }
367422
368423 private void closedInternal (Status status ) {
424+ if (delayedMessage != null ) {
425+ GrpcUtil .closeQuietly (delayedMessage );
426+ delayedMessage = null ;
427+ }
369428 Throwable cancelCause = null ;
370429 try {
371430 if (status .isOk ()) {
0 commit comments