mirror of https://github.com/grpc/grpc-java.git
core: Free unused MessageProducer in RetriableStream
This prevents leaking message buffers. Fixes #9563
This commit is contained in:
parent
9de989bd64
commit
5a2c94bca1
|
|
@ -1099,6 +1099,7 @@ abstract class RetriableStream<ReqT> implements ClientStream {
|
||||||
checkState(
|
checkState(
|
||||||
savedState.winningSubstream != null, "Headers should be received prior to messages.");
|
savedState.winningSubstream != null, "Headers should be received prior to messages.");
|
||||||
if (savedState.winningSubstream != substream) {
|
if (savedState.winningSubstream != substream) {
|
||||||
|
GrpcUtil.closeQuietly(producer);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
listenerSerializeExecutor.execute(
|
listenerSerializeExecutor.execute(
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,8 @@ import io.grpc.internal.RetriableStream.Throttle;
|
||||||
import io.grpc.internal.StreamListener.MessageProducer;
|
import io.grpc.internal.StreamListener.MessageProducer;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
@ -998,6 +1000,27 @@ public class RetriableStreamTest {
|
||||||
verify(masterListener).messagesAvailable(messageProducer);
|
verify(masterListener).messagesAvailable(messageProducer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void inboundMessagesClosedOnCancel() throws Exception {
|
||||||
|
ClientStream mockStream1 = mock(ClientStream.class);
|
||||||
|
doReturn(mockStream1).when(retriableStreamRecorder).newSubstream(0);
|
||||||
|
|
||||||
|
retriableStream.start(masterListener);
|
||||||
|
retriableStream.request(1);
|
||||||
|
retriableStream.cancel(Status.CANCELLED.withDescription("on purpose"));
|
||||||
|
|
||||||
|
ArgumentCaptor<ClientStreamListener> sublistenerCaptor1 =
|
||||||
|
ArgumentCaptor.forClass(ClientStreamListener.class);
|
||||||
|
verify(mockStream1).start(sublistenerCaptor1.capture());
|
||||||
|
|
||||||
|
ClientStreamListener listener = sublistenerCaptor1.getValue();
|
||||||
|
listener.headersRead(new Metadata());
|
||||||
|
InputStream is = mock(InputStream.class);
|
||||||
|
listener.messagesAvailable(new FakeMessageProducer(is));
|
||||||
|
verify(masterListener, never()).messagesAvailable(any(MessageProducer.class));
|
||||||
|
verify(is).close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void notAdd0PrevRetryAttemptsToRespHeaders() {
|
public void notAdd0PrevRetryAttemptsToRespHeaders() {
|
||||||
ClientStream mockStream1 = mock(ClientStream.class);
|
ClientStream mockStream1 = mock(ClientStream.class);
|
||||||
|
|
@ -2786,4 +2809,22 @@ public class RetriableStreamTest {
|
||||||
|
|
||||||
Status prestart();
|
Status prestart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class FakeMessageProducer implements MessageProducer {
|
||||||
|
private final Iterator<InputStream> iterator;
|
||||||
|
|
||||||
|
public FakeMessageProducer(InputStream... iss) {
|
||||||
|
this.iterator = Arrays.asList(iss).iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public InputStream next() {
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
return iterator.next();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue