mirror of https://github.com/grpc/grpc-java.git
netty: fix StreamBufferingEncoder GOAWAY bug
Fix a bug in StreamBufferingEncoder: when client receives GOWAY while there are pending streams due to MAX_CONCURRENT_STREAMS, we see the following error: io.netty.handler.codec.http2.Http2Exception$StreamException: Maximum active streams violated for this endpoint.
This commit is contained in:
parent
b4fe07d22d
commit
49f9380fc9
|
|
@ -569,20 +569,22 @@ class NettyClientHandler extends AbstractNettyHandler {
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (connection().goAwayReceived()
|
if (connection().goAwayReceived()) {
|
||||||
&& streamId > connection().local().lastStreamKnownByPeer()) {
|
if (streamId > connection().local().lastStreamKnownByPeer()
|
||||||
// This should only be reachable during onGoAwayReceived, as otherwise
|
|| connection().local().numActiveStreams() == connection().local().maxActiveStreams()) {
|
||||||
// getShutdownThrowable() != null
|
// This should only be reachable during onGoAwayReceived, as otherwise
|
||||||
command.stream().setNonExistent();
|
// getShutdownThrowable() != null
|
||||||
Status s = abruptGoAwayStatus;
|
command.stream().setNonExistent();
|
||||||
if (s == null) {
|
Status s = abruptGoAwayStatus;
|
||||||
// Should be impossible, but handle psuedo-gracefully
|
if (s == null) {
|
||||||
s = Status.INTERNAL.withDescription(
|
// Should be impossible, but handle psuedo-gracefully
|
||||||
"Failed due to abrupt GOAWAY, but can't find GOAWAY details");
|
s = Status.INTERNAL.withDescription(
|
||||||
|
"Failed due to abrupt GOAWAY, but can't find GOAWAY details");
|
||||||
|
}
|
||||||
|
command.stream().transportReportStatus(s, RpcProgress.REFUSED, true, new Metadata());
|
||||||
|
promise.setFailure(s.asRuntimeException());
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
command.stream().transportReportStatus(s, RpcProgress.REFUSED, true, new Metadata());
|
|
||||||
promise.setFailure(s.asRuntimeException());
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NettyClientStream.TransportState stream = command.stream();
|
NettyClientStream.TransportState stream = command.stream();
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,36 @@ public class NettyClientHandlerTest extends NettyHandlerTestBase<NettyClientHand
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void receivedGoAway_shouldFailBufferedStreamsExceedingMaxConcurrentStreams()
|
||||||
|
throws Exception {
|
||||||
|
NettyClientStream.TransportState streamTransportState1 = new TransportStateImpl(
|
||||||
|
handler(),
|
||||||
|
channel().eventLoop(),
|
||||||
|
DEFAULT_MAX_MESSAGE_SIZE,
|
||||||
|
transportTracer);
|
||||||
|
streamTransportState1.setListener(mock(ClientStreamListener.class));
|
||||||
|
NettyClientStream.TransportState streamTransportState2 = new TransportStateImpl(
|
||||||
|
handler(),
|
||||||
|
channel().eventLoop(),
|
||||||
|
DEFAULT_MAX_MESSAGE_SIZE,
|
||||||
|
transportTracer);
|
||||||
|
streamTransportState2.setListener(mock(ClientStreamListener.class));
|
||||||
|
receiveMaxConcurrentStreams(1);
|
||||||
|
ChannelFuture future1 = writeQueue().enqueue(
|
||||||
|
newCreateStreamCommand(grpcHeaders, streamTransportState1), true);
|
||||||
|
ChannelFuture future2 = writeQueue().enqueue(
|
||||||
|
newCreateStreamCommand(grpcHeaders, streamTransportState2), true);
|
||||||
|
|
||||||
|
// GOAWAY
|
||||||
|
channelRead(goAwayFrame(Integer.MAX_VALUE));
|
||||||
|
assertTrue(future1.isSuccess());
|
||||||
|
assertTrue(future2.isDone());
|
||||||
|
assertThat(Status.fromThrowable(future2.cause()).getCode()).isEqualTo(Status.Code.UNAVAILABLE);
|
||||||
|
assertThat(future2.cause().getMessage()).contains(
|
||||||
|
"Abrupt GOAWAY closed unsent stream. HTTP/2 error code: NO_ERROR");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void receivedResetWithRefuseCode() throws Exception {
|
public void receivedResetWithRefuseCode() throws Exception {
|
||||||
ChannelFuture future = enqueue(newCreateStreamCommand(grpcHeaders, streamTransportState));
|
ChannelFuture future = enqueue(newCreateStreamCommand(grpcHeaders, streamTransportState));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue