mirror of https://github.com/grpc/grpc-java.git
Properly removing buffered streams after receiving goAway
This commit is contained in:
parent
693a7d2846
commit
64df428e36
|
|
@ -92,6 +92,13 @@ class BufferingHttp2ConnectionEncoder extends DecoratingHttp2ConnectionEncoder {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates the number of streams that are currently buffered, awaiting creation.
|
||||||
|
*/
|
||||||
|
public int numBufferedStreams() {
|
||||||
|
return pendingStreams.size();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int padding, boolean endStream, ChannelPromise promise) {
|
int padding, boolean endStream, ChannelPromise promise) {
|
||||||
|
|
@ -199,6 +206,7 @@ class BufferingHttp2ConnectionEncoder extends DecoratingHttp2ConnectionEncoder {
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
PendingStream stream = iter.next();
|
PendingStream stream = iter.next();
|
||||||
if (stream.streamId > lastStreamId) {
|
if (stream.streamId > lastStreamId) {
|
||||||
|
iter.remove();
|
||||||
stream.close(e);
|
stream.close(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,6 @@ import io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder;
|
||||||
import io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder;
|
import io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder;
|
||||||
import io.netty.handler.codec.http2.DefaultHttp2Headers;
|
import io.netty.handler.codec.http2.DefaultHttp2Headers;
|
||||||
import io.netty.handler.codec.http2.Http2Connection;
|
import io.netty.handler.codec.http2.Http2Connection;
|
||||||
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
|
|
||||||
import io.netty.handler.codec.http2.Http2ConnectionHandler;
|
import io.netty.handler.codec.http2.Http2ConnectionHandler;
|
||||||
import io.netty.handler.codec.http2.Http2FrameListener;
|
import io.netty.handler.codec.http2.Http2FrameListener;
|
||||||
import io.netty.handler.codec.http2.Http2FrameReader;
|
import io.netty.handler.codec.http2.Http2FrameReader;
|
||||||
|
|
@ -66,6 +65,7 @@ import io.netty.handler.codec.http2.Http2FrameSizePolicy;
|
||||||
import io.netty.handler.codec.http2.Http2FrameWriter;
|
import io.netty.handler.codec.http2.Http2FrameWriter;
|
||||||
import io.netty.handler.codec.http2.Http2Headers;
|
import io.netty.handler.codec.http2.Http2Headers;
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
@ -82,7 +82,7 @@ import org.mockito.verification.VerificationMode;
|
||||||
@RunWith(JUnit4.class)
|
@RunWith(JUnit4.class)
|
||||||
public class BufferingHttp2ConnectionEncoderTest {
|
public class BufferingHttp2ConnectionEncoderTest {
|
||||||
|
|
||||||
private Http2ConnectionEncoder encoder;
|
private BufferingHttp2ConnectionEncoder encoder;
|
||||||
|
|
||||||
private Http2Connection connection;
|
private Http2Connection connection;
|
||||||
|
|
||||||
|
|
@ -133,6 +133,7 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
@Test
|
@Test
|
||||||
public void multipleWritesToActiveStream() {
|
public void multipleWritesToActiveStream() {
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
encoder.writeData(ctx, 3, data(), 0, false, promise);
|
encoder.writeData(ctx, 3, data(), 0, false, promise);
|
||||||
encoder.writeData(ctx, 3, data(), 0, false, promise);
|
encoder.writeData(ctx, 3, data(), 0, false, promise);
|
||||||
encoder.writeData(ctx, 3, data(), 0, false, promise);
|
encoder.writeData(ctx, 3, data(), 0, false, promise);
|
||||||
|
|
@ -148,9 +149,12 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
connection.local().maxActiveStreams(1);
|
connection.local().maxActiveStreams(1);
|
||||||
|
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
|
|
||||||
// This one gets buffered.
|
// This one gets buffered.
|
||||||
encoderWriteHeaders(5, promise);
|
encoderWriteHeaders(5, promise);
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
// Now prevent us from creating another stream.
|
// Now prevent us from creating another stream.
|
||||||
connection.local().maxActiveStreams(0);
|
connection.local().maxActiveStreams(0);
|
||||||
|
|
@ -163,6 +167,7 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
writeVerifyWriteHeaders(times(1), 3, promise);
|
writeVerifyWriteHeaders(times(1), 3, promise);
|
||||||
writeVerifyWriteHeaders(never(), 5, promise);
|
writeVerifyWriteHeaders(never(), 5, promise);
|
||||||
assertEquals(0, connection.numActiveStreams());
|
assertEquals(0, connection.numActiveStreams());
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -170,8 +175,11 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
connection.local().maxActiveStreams(1);
|
connection.local().maxActiveStreams(1);
|
||||||
|
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
|
|
||||||
encoderWriteHeaders(5, promise);
|
encoderWriteHeaders(5, promise);
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
encoder.writeData(ctx, 3, Unpooled.buffer(0), 0, false, promise);
|
encoder.writeData(ctx, 3, Unpooled.buffer(0), 0, false, promise);
|
||||||
writeVerifyWriteHeaders(times(1), 3, promise);
|
writeVerifyWriteHeaders(times(1), 3, promise);
|
||||||
|
|
@ -187,6 +195,7 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
|
|
||||||
promise = mock(ChannelPromise.class);
|
promise = mock(ChannelPromise.class);
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
verify(promise).setFailure(any(Throwable.class));
|
verify(promise).setFailure(any(Throwable.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -199,12 +208,14 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
encoderWriteHeaders(streamId, promise);
|
encoderWriteHeaders(streamId, promise);
|
||||||
streamId += 2;
|
streamId += 2;
|
||||||
}
|
}
|
||||||
|
assertEquals(4, encoder.numBufferedStreams());
|
||||||
|
|
||||||
connection.goAwayReceived(11, 8, null);
|
connection.goAwayReceived(11, 8, null);
|
||||||
|
|
||||||
assertEquals(5, connection.numActiveStreams());
|
assertEquals(5, connection.numActiveStreams());
|
||||||
// The 4 buffered streams must have been failed.
|
// The 4 buffered streams must have been failed.
|
||||||
verify(promise, times(4)).setFailure(any(Throwable.class));
|
verify(promise, times(4)).setFailure(any(Throwable.class));
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -212,13 +223,17 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
connection.local().maxActiveStreams(1);
|
connection.local().maxActiveStreams(1);
|
||||||
|
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
encoderWriteHeaders(5, promise);
|
encoderWriteHeaders(5, promise);
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
encoderWriteHeaders(7, promise);
|
encoderWriteHeaders(7, promise);
|
||||||
|
assertEquals(2, encoder.numBufferedStreams());
|
||||||
|
|
||||||
ByteBuf empty = Unpooled.buffer(0);
|
ByteBuf empty = Unpooled.buffer(0);
|
||||||
encoder.writeGoAway(ctx, 3, CANCEL.code(), empty, promise);
|
encoder.writeGoAway(ctx, 3, CANCEL.code(), empty, promise);
|
||||||
|
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
|
assertEquals(2, encoder.numBufferedStreams());
|
||||||
verify(promise, never()).setFailure(any(GoAwayClosedStreamException.class));
|
verify(promise, never()).setFailure(any(GoAwayClosedStreamException.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -227,11 +242,13 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
connection.local().maxActiveStreams(0);
|
connection.local().maxActiveStreams(0);
|
||||||
|
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
ByteBuf empty = Unpooled.buffer(0);
|
ByteBuf empty = Unpooled.buffer(0);
|
||||||
encoder.writeData(ctx, 3, empty, 0, true, promise);
|
encoder.writeData(ctx, 3, empty, 0, true, promise);
|
||||||
|
|
||||||
assertEquals(0, connection.numActiveStreams());
|
assertEquals(0, connection.numActiveStreams());
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
// Simulate that we received a SETTINGS frame which
|
// Simulate that we received a SETTINGS frame which
|
||||||
// increased MAX_CONCURRENT_STREAMS to 1.
|
// increased MAX_CONCURRENT_STREAMS to 1.
|
||||||
|
|
@ -239,6 +256,7 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
encoder.writeSettingsAck(ctx, promise);
|
encoder.writeSettingsAck(ctx, promise);
|
||||||
|
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
assertEquals(HALF_CLOSED_LOCAL, connection.stream(3).state());
|
assertEquals(HALF_CLOSED_LOCAL, connection.stream(3).state());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -247,12 +265,14 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
connection.local().maxActiveStreams(0);
|
connection.local().maxActiveStreams(0);
|
||||||
|
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
verify(promise, never()).setSuccess();
|
verify(promise, never()).setSuccess();
|
||||||
ChannelPromise rstStreamPromise = mock(ChannelPromise.class);
|
ChannelPromise rstStreamPromise = mock(ChannelPromise.class);
|
||||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstStreamPromise);
|
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstStreamPromise);
|
||||||
verify(promise).setSuccess();
|
verify(promise).setSuccess();
|
||||||
verify(rstStreamPromise).setSuccess();
|
verify(rstStreamPromise).setSuccess();
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -260,8 +280,11 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
connection.local().maxActiveStreams(1);
|
connection.local().maxActiveStreams(1);
|
||||||
|
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
encoderWriteHeaders(5, promise);
|
encoderWriteHeaders(5, promise);
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
encoderWriteHeaders(7, promise);
|
encoderWriteHeaders(7, promise);
|
||||||
|
assertEquals(2, encoder.numBufferedStreams());
|
||||||
|
|
||||||
writeVerifyWriteHeaders(times(1), 3, promise);
|
writeVerifyWriteHeaders(times(1), 3, promise);
|
||||||
writeVerifyWriteHeaders(never(), 5, promise);
|
writeVerifyWriteHeaders(never(), 5, promise);
|
||||||
|
|
@ -269,10 +292,13 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
|
|
||||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), promise);
|
encoder.writeRstStream(ctx, 3, CANCEL.code(), promise);
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
encoder.writeRstStream(ctx, 5, CANCEL.code(), promise);
|
encoder.writeRstStream(ctx, 5, CANCEL.code(), promise);
|
||||||
assertEquals(1, connection.numActiveStreams());
|
assertEquals(1, connection.numActiveStreams());
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
encoder.writeRstStream(ctx, 7, CANCEL.code(), promise);
|
encoder.writeRstStream(ctx, 7, CANCEL.code(), promise);
|
||||||
assertEquals(0, connection.numActiveStreams());
|
assertEquals(0, connection.numActiveStreams());
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -283,6 +309,7 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
encoderWriteHeaders(5, promise);
|
encoderWriteHeaders(5, promise);
|
||||||
encoderWriteHeaders(7, promise);
|
encoderWriteHeaders(7, promise);
|
||||||
encoderWriteHeaders(9, promise);
|
encoderWriteHeaders(9, promise);
|
||||||
|
assertEquals(2, encoder.numBufferedStreams());
|
||||||
|
|
||||||
writeVerifyWriteHeaders(times(1), 3, promise);
|
writeVerifyWriteHeaders(times(1), 3, promise);
|
||||||
writeVerifyWriteHeaders(times(1), 5, promise);
|
writeVerifyWriteHeaders(times(1), 5, promise);
|
||||||
|
|
@ -294,6 +321,7 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
connection.local().maxActiveStreams(5);
|
connection.local().maxActiveStreams(5);
|
||||||
encoder.writeSettingsAck(ctx, promise);
|
encoder.writeSettingsAck(ctx, promise);
|
||||||
|
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
writeVerifyWriteHeaders(times(1), 7, promise);
|
writeVerifyWriteHeaders(times(1), 7, promise);
|
||||||
writeVerifyWriteHeaders(times(1), 9, promise);
|
writeVerifyWriteHeaders(times(1), 9, promise);
|
||||||
|
|
||||||
|
|
@ -309,11 +337,13 @@ public class BufferingHttp2ConnectionEncoderTest {
|
||||||
connection.local().maxActiveStreams(0);
|
connection.local().maxActiveStreams(0);
|
||||||
ByteBuf data = mock(ByteBuf.class);
|
ByteBuf data = mock(ByteBuf.class);
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
encoder.writeData(ctx, 3, data, 0, false, promise);
|
encoder.writeData(ctx, 3, data, 0, false, promise);
|
||||||
|
|
||||||
ChannelPromise rstPromise = mock(ChannelPromise.class);
|
ChannelPromise rstPromise = mock(ChannelPromise.class);
|
||||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstPromise);
|
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstPromise);
|
||||||
|
|
||||||
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
verify(rstPromise).setSuccess();
|
verify(rstPromise).setSuccess();
|
||||||
verify(promise, times(2)).setSuccess();
|
verify(promise, times(2)).setSuccess();
|
||||||
verify(data).release();
|
verify(data).release();
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue