DefaultHttp2ConnectionEncoder writeHeaders method always send an head frame with a priority (#9852)

Motivation:

The current implementation delegates to writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, boolean endStream, ChannelPromise promise) that will send an header frame with the priority flag set and the default priority values even if the user didnt want too.

Modifications:

- Change DefaultHttp2ConnectionEncoder to call the correct Http2FrameWriter method depending on if the user wants to use priorities or not
- Adjust tests

Result:

Fixes https://github.com/netty/netty/issues/9842
This commit is contained in:
Norman Maurer 2019-12-08 07:43:11 +01:00
parent bf0bd9aac9
commit 8e281dc54e
4 changed files with 120 additions and 63 deletions

View File

@ -148,7 +148,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
@Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
boolean endStream, ChannelPromise promise) {
return writeHeaders(ctx, streamId, headers, 0, DEFAULT_PRIORITY_WEIGHT, false, padding, endStream, promise);
return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream, promise);
}
private static boolean validateHeadersSentState(Http2Stream stream, Http2Headers headers, boolean isServer,
@ -164,6 +164,31 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
public ChannelFuture writeHeaders(final ChannelHandlerContext ctx, final int streamId,
final Http2Headers headers, final int streamDependency, final short weight,
final boolean exclusive, final int padding, final boolean endOfStream, ChannelPromise promise) {
return writeHeaders0(ctx, streamId, headers, true, streamDependency,
weight, exclusive, padding, endOfStream, promise);
}
/**
* Write headers via {@link Http2FrameWriter}. If {@code hasPriority} is {@code false} it will ignore the
* {@code streamDependency}, {@code weight} and {@code exclusive} parameters.
*/
private static ChannelFuture sendHeaders(Http2FrameWriter frameWriter, ChannelHandlerContext ctx, int streamId,
Http2Headers headers, final boolean hasPriority,
int streamDependency, final short weight,
boolean exclusive, final int padding,
boolean endOfStream, ChannelPromise promise) {
if (hasPriority) {
return frameWriter.writeHeaders(ctx, streamId, headers, streamDependency,
weight, exclusive, padding, endOfStream, promise);
}
return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream, promise);
}
private ChannelFuture writeHeaders0(final ChannelHandlerContext ctx, final int streamId,
final Http2Headers headers, final boolean hasPriority,
final int streamDependency, final short weight,
final boolean exclusive, final int padding,
final boolean endOfStream, ChannelPromise promise) {
try {
Http2Stream stream = connection.stream(streamId);
if (stream == null) {
@ -205,8 +230,9 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
promise = promise.unvoid();
boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream);
ChannelFuture future = frameWriter.writeHeaders(ctx, streamId, headers, streamDependency,
weight, exclusive, padding, endOfStream, promise);
ChannelFuture future = sendHeaders(frameWriter, ctx, streamId, headers, hasPriority, streamDependency,
weight, exclusive, padding, endOfStream, promise);
// Writing headers may fail during the encode state if they violate HPACK limits.
Throwable failureCause = future.cause();
if (failureCause == null) {
@ -236,8 +262,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
} else {
// Pass headers to the flow-controller so it can maintain their sequence relative to DATA frames.
flowController.addFlowControlled(stream,
new FlowControlledHeaders(stream, headers, streamDependency, weight, exclusive, padding,
true, promise));
new FlowControlledHeaders(stream, headers, hasPriority, streamDependency,
weight, exclusive, padding, true, promise));
return promise;
}
} catch (Throwable t) {
@ -516,14 +542,17 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
*/
private final class FlowControlledHeaders extends FlowControlledBase {
private final Http2Headers headers;
private final boolean hasPriorty;
private final int streamDependency;
private final short weight;
private final boolean exclusive;
FlowControlledHeaders(Http2Stream stream, Http2Headers headers, int streamDependency, short weight,
boolean exclusive, int padding, boolean endOfStream, ChannelPromise promise) {
FlowControlledHeaders(Http2Stream stream, Http2Headers headers, boolean hasPriority,
int streamDependency, short weight, boolean exclusive,
int padding, boolean endOfStream, ChannelPromise promise) {
super(stream, padding, endOfStream, promise.unvoid());
this.headers = headers;
this.hasPriorty = hasPriority;
this.streamDependency = streamDependency;
this.weight = weight;
this.exclusive = exclusive;
@ -549,8 +578,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
// closeStreamLocal().
promise.addListener(this);
ChannelFuture f = frameWriter.writeHeaders(ctx, stream.id(), headers, streamDependency, weight, exclusive,
padding, endOfStream, promise);
ChannelFuture f = sendHeaders(frameWriter, ctx, stream.id(), headers, hasPriorty, streamDependency,
weight, exclusive, padding, endOfStream, promise);
// Writing headers may fail during the encode state if they violate HPACK limits.
Throwable failureCause = f.cause();
if (failureCause == null) {

View File

@ -37,7 +37,6 @@ import org.mockito.ArgumentCaptor;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
@ -162,7 +161,7 @@ public class DefaultHttp2ConnectionEncoderTest {
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(),
anyInt(), anyBoolean(), any(ChannelPromise.class)))
.then((Answer<ChannelFuture>) invocationOnMock -> {
ChannelPromise promise = (ChannelPromise) invocationOnMock.getArguments()[8];
ChannelPromise promise = invocationOnMock.getArgument(8);
if (streamClosed) {
fail("Stream already closed");
} else {
@ -170,6 +169,17 @@ public class DefaultHttp2ConnectionEncoderTest {
}
return promise.setSuccess();
});
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class),
anyInt(), anyBoolean(), any(ChannelPromise.class)))
.then((Answer<ChannelFuture>) invocationOnMock -> {
ChannelPromise promise = invocationOnMock.getArgument(5);
if (streamClosed) {
fail("Stream already closed");
} else {
streamClosed = invocationOnMock.getArgument(4);
}
return promise.setSuccess();
});
payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class);
doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture());
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
@ -308,10 +318,10 @@ public class DefaultHttp2ConnectionEncoderTest {
@Test
public void writeHeadersUsingVoidPromise() throws Exception {
final Throwable cause = new RuntimeException("fake exception");
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(),
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), any(Http2Headers.class),
anyInt(), anyBoolean(), any(ChannelPromise.class)))
.then((Answer<ChannelFuture>) invocationOnMock -> {
ChannelPromise promise = invocationOnMock.getArgument(8);
ChannelPromise promise = invocationOnMock.getArgument(5);
assertFalse(promise.isVoid());
return promise.setFailure(cause);
});
@ -319,7 +329,7 @@ public class DefaultHttp2ConnectionEncoderTest {
// END_STREAM flag, so that a listener is added to the future.
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, newVoidPromise(channel));
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(),
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), any(Http2Headers.class),
anyInt(), anyBoolean(), any(ChannelPromise.class));
// When using a void promise, the error should be propagated via the channel pipeline.
verify(pipeline).fireExceptionCaught(cause);
@ -347,7 +357,7 @@ public class DefaultHttp2ConnectionEncoderTest {
ChannelPromise promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
eq(false), eq(promise));
assertTrue(promise.isSuccess());
}
@ -360,8 +370,8 @@ public class DefaultHttp2ConnectionEncoderTest {
ChannelPromise promise = newPromise();
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state());
verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise));
}
@Test
@ -376,8 +386,8 @@ public class DefaultHttp2ConnectionEncoderTest {
assertTrue(future.isDone());
assertFalse(future.isSuccess());
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise));
}
@Test
@ -395,8 +405,8 @@ public class DefaultHttp2ConnectionEncoderTest {
assertTrue(future.isDone());
assertFalse(future.isSuccess());
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise));
}
@Test
@ -422,10 +432,10 @@ public class DefaultHttp2ConnectionEncoderTest {
assertTrue(future.isDone());
assertFalse(future.isSuccess());
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise2));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(true), eq(promise2));
}
@Test
@ -463,13 +473,13 @@ public class DefaultHttp2ConnectionEncoderTest {
assertTrue(future.isDone());
assertEquals(eos, future.isSuccess());
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), any(ChannelPromise.class));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise2));
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
eq(0), eq(false), any(ChannelPromise.class));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise2));
if (eos) {
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise3));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(true), eq(promise3));
}
}
@ -506,10 +516,10 @@ public class DefaultHttp2ConnectionEncoderTest {
assertTrue(future.isDone());
assertFalse(future.isSuccess());
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise2));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(true), eq(promise2));
}
@Test
@ -551,13 +561,13 @@ public class DefaultHttp2ConnectionEncoderTest {
assertTrue(future.isDone());
assertEquals(eos, future.isSuccess());
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), any(ChannelPromise.class));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise2));
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
eq(0), eq(false), any(ChannelPromise.class));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise2));
if (eos) {
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise3));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(true), eq(promise3));
}
}
@ -722,8 +732,7 @@ public class DefaultHttp2ConnectionEncoderTest {
final ChannelPromise promise = newPromise();
final Throwable ex = new RuntimeException();
// Fake an encoding error, like HPACK's HeaderListSizeException
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise)))
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
.thenAnswer((Answer<ChannelFuture>) invocation -> {
promise.setFailure(ex);
return promise;
@ -746,8 +755,7 @@ public class DefaultHttp2ConnectionEncoderTest {
final ChannelPromise promise = newPromise();
final Throwable ex = new RuntimeException();
// Fake an encoding error, like HPACK's HeaderListSizeException
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true), eq(promise)))
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
.thenAnswer((Answer<ChannelFuture>) invocation -> {
promise.setFailure(ex);
return promise;
@ -813,8 +821,8 @@ public class DefaultHttp2ConnectionEncoderTest {
goAwaySent(0);
ChannelPromise promise = newPromise();
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise));
}
@Test
@ -832,8 +840,29 @@ public class DefaultHttp2ConnectionEncoderTest {
goAwayReceived(STREAM_ID);
ChannelPromise promise = newPromise();
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(false), eq(promise));
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise));
}
@Test
public void headersWithNoPriority() {
writeAllFlowControlledFrames();
final int streamId = 6;
ChannelPromise promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise));
}
@Test
public void headersWithPriority() {
writeAllFlowControlledFrames();
final int streamId = 6;
ChannelPromise promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 10, DEFAULT_PRIORITY_WEIGHT,
true, 1, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(10),
eq(DEFAULT_PRIORITY_WEIGHT), eq(true), eq(1), eq(false), eq(promise));
}
private void writeAllFlowControlledFrames() {

View File

@ -177,7 +177,7 @@ public class Http2FrameCodecTest {
channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
verify(frameWriter).writeHeaders(
eqFrameCodecCtx(), eq(1), eq(response), anyInt(), anyShort(), anyBoolean(),
eqFrameCodecCtx(), eq(1), eq(response),
eq(27), eq(true), anyChannelPromise());
verify(frameWriter, never()).writeRstStream(
eqFrameCodecCtx(), anyInt(), anyLong(), anyChannelPromise());
@ -206,7 +206,7 @@ public class Http2FrameCodecTest {
channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
verify(frameWriter).writeHeaders(
eqFrameCodecCtx(), eq(1), eq(response), anyInt(), anyShort(), anyBoolean(),
eqFrameCodecCtx(), eq(1), eq(response),
eq(27), eq(true), anyChannelPromise());
verify(frameWriter, never()).writeRstStream(
eqFrameCodecCtx(), anyInt(), anyLong(), anyChannelPromise());
@ -265,8 +265,8 @@ public class Http2FrameCodecTest {
assertNull(inboundHandler.readInbound());
channel.writeOutbound(new DefaultHttp2HeadersFrame(response, false).stream(stream2));
verify(frameWriter).writeHeaders(eqFrameCodecCtx(), eq(1), eq(response), anyInt(),
anyShort(), anyBoolean(), eq(0), eq(false), anyChannelPromise());
verify(frameWriter).writeHeaders(eqFrameCodecCtx(), eq(1), eq(response),
eq(0), eq(false), anyChannelPromise());
channel.writeOutbound(new DefaultHttp2DataFrame(bb("world"), true, 27).stream(stream2));
ArgumentCaptor<ByteBuf> outboundData = ArgumentCaptor.forClass(ByteBuf.class);

View File

@ -427,7 +427,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
@Test
public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() {
when(frameWriter.writeHeaders(eqCodecCtx(), anyInt(),
any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
any(Http2Headers.class), anyInt(), anyBoolean(),
any(ChannelPromise.class))).thenAnswer(new Answer<ChannelFuture>() {
private boolean headersWritten;
@ -437,9 +437,9 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
// refuses to allocate a new stream due to having received a GOAWAY.
if (!headersWritten) {
headersWritten = true;
return ((ChannelPromise) invocationOnMock.getArgument(8)).setFailure(new Exception("boom"));
return ((ChannelPromise) invocationOnMock.getArgument(5)).setFailure(new Exception("boom"));
}
return ((ChannelPromise) invocationOnMock.getArgument(8)).setSuccess();
return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess();
}
});
@ -497,9 +497,9 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
Http2Headers headers = new DefaultHttp2Headers();
when(frameWriter.writeHeaders(eqCodecCtx(), anyInt(),
eq(headers), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
eq(headers), anyInt(), anyBoolean(),
any(ChannelPromise.class))).thenAnswer(invocationOnMock -> {
return ((ChannelPromise) invocationOnMock.getArgument(8)).setFailure(
return ((ChannelPromise) invocationOnMock.getArgument(5)).setFailure(
new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed"));
});
ChannelFuture future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()));
@ -561,9 +561,8 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
Http2Headers headers = new DefaultHttp2Headers();
when(frameWriter.writeHeaders(eqCodecCtx(), anyInt(),
eq(headers), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
any(ChannelPromise.class))).thenAnswer(invocationOnMock -> {
return ((ChannelPromise) invocationOnMock.getArgument(8)).setFailure(
eq(headers), anyInt(), anyBoolean(), any(ChannelPromise.class))).thenAnswer(invocationOnMock -> {
return ((ChannelPromise) invocationOnMock.getArgument(5)).setFailure(
new Http2NoMoreStreamIdsException());
});
@ -644,9 +643,9 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
Http2Headers headers = new DefaultHttp2Headers();
when(frameWriter.writeHeaders(eqCodecCtx(), anyInt(),
eq(headers), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
eq(headers), anyInt(), anyBoolean(),
any(ChannelPromise.class))).thenAnswer(invocationOnMock -> {
ChannelPromise promise = invocationOnMock.getArgument(8);
ChannelPromise promise = invocationOnMock.getArgument(5);
writePromises.offer(promise);
return promise;
});