diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java index 9a26f96d83..aa219e2658 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java @@ -236,31 +236,19 @@ public abstract class WebSocketClientHandshaker { */ public Future handshake(Channel channel) { requireNonNull(channel, "channel"); - return handshake(channel, channel.newPromise()); - } - - /** - * Begins the opening handshake - * - * @param channel - * Channel - * @param promise - * the {@link Promise} to be notified when the opening handshake is sent - */ - public final Future handshake(Channel channel, final Promise promise) { ChannelPipeline pipeline = channel.pipeline(); HttpResponseDecoder decoder = pipeline.get(HttpResponseDecoder.class); if (decoder == null) { HttpClientCodec codec = pipeline.get(HttpClientCodec.class); if (codec == null) { - promise.setFailure(new IllegalStateException("ChannelPipeline does not contain " + + return channel.newFailedFuture(new IllegalStateException("ChannelPipeline does not contain " + "an HttpResponseDecoder or HttpClientCodec")); - return promise; } } FullHttpRequest request = newHandshakeRequest(); + Promise promise = channel.newPromise(); channel.writeAndFlush(request).addListener(channel, (ch, future) -> { if (future.isSuccess()) { ChannelPipeline p = ch.pipeline(); @@ -385,29 +373,12 @@ public abstract class WebSocketClientHandshaker { * the {@link Future} which is notified once the handshake completes. */ public final Future processHandshake(final Channel channel, HttpResponse response) { - return processHandshake(channel, response, channel.newPromise()); - } - - /** - * Process the opening handshake initiated by {@link #handshake}}. - * - * @param channel - * Channel - * @param response - * HTTP response containing the closing handshake details - * @param promise - * the {@link Promise} to notify once the handshake completes. - * @return future - * the {@link Future} which is notified once the handshake completes. - */ - public final Future processHandshake(final Channel channel, HttpResponse response, - final Promise promise) { if (response instanceof FullHttpResponse) { try { finishHandshake(channel, (FullHttpResponse) response); - promise.setSuccess(null); + return channel.newSucceededFuture(); } catch (Throwable cause) { - promise.setFailure(cause); + return channel.newFailedFuture(cause); } } else { ChannelPipeline p = channel.pipeline(); @@ -415,10 +386,12 @@ public abstract class WebSocketClientHandshaker { if (ctx == null) { ctx = p.context(HttpClientCodec.class); if (ctx == null) { - return promise.setFailure(new IllegalStateException("ChannelPipeline does not contain " + + return channel.newFailedFuture(new IllegalStateException("ChannelPipeline does not contain " + "an HttpResponseDecoder or HttpClientCodec")); } } + + Promise promise = channel.newPromise(); // Add aggregator and ensure we feed the HttpResponse so it is aggregated. A limit of 8192 should be more // then enough for the websockets handshake payload. // @@ -459,8 +432,8 @@ public abstract class WebSocketClientHandshaker { } catch (Throwable cause) { promise.setFailure(cause); } + return promise; } - return promise; } /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java index a78bc9bb1c..0e316c310a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java @@ -169,7 +169,7 @@ public abstract class WebSocketServerHandshaker { * The {@link Future} which is notified once the opening handshake completes */ public Future handshake(Channel channel, FullHttpRequest req) { - return handshake(channel, req, null, channel.newPromise()); + return handshake(channel, req, null); } /** @@ -183,13 +183,10 @@ public abstract class WebSocketServerHandshaker { * HTTP Request * @param responseHeaders * Extra headers to add to the handshake response or {@code null} if no extra headers should be added - * @param promise - * the {@link Promise} to be notified when the opening handshake is done * @return future * the {@link Future} which is notified when the opening handshake is done */ - public final Future handshake(Channel channel, FullHttpRequest req, - HttpHeaders responseHeaders, final Promise promise) { + public final Future handshake(Channel channel, FullHttpRequest req, HttpHeaders responseHeaders) { if (logger.isDebugEnabled()) { logger.debug("{} WebSocket version {} server handshake", channel, version()); @@ -208,9 +205,8 @@ public abstract class WebSocketServerHandshaker { // this means the user use an HttpServerCodec ctx = p.context(HttpServerCodec.class); if (ctx == null) { - promise.setFailure( + return channel.newFailedFuture( new IllegalStateException("No HttpDecoder and no HttpServerCodec in the pipeline")); - return promise; } p.addBefore(ctx.name(), "wsencoder", newWebSocketEncoder()); p.addBefore(ctx.name(), "wsdecoder", newWebsocketDecoder()); @@ -221,16 +217,12 @@ public abstract class WebSocketServerHandshaker { encoderName = p.context(HttpResponseEncoder.class).name(); p.addBefore(encoderName, "wsencoder", newWebSocketEncoder()); } - channel.writeAndFlush(response).addListener(channel, (ch, future) -> { + return channel.writeAndFlush(response).addListener(channel, (ch, future) -> { if (future.isSuccess()) { ChannelPipeline p1 = ch.pipeline(); p1.remove(encoderName); - promise.setSuccess(null); - } else { - promise.setFailure(future.cause()); } }); - return promise; } /** @@ -245,7 +237,7 @@ public abstract class WebSocketServerHandshaker { * The {@link Future} which is notified once the opening handshake completes */ public Future handshake(Channel channel, HttpRequest req) { - return handshake(channel, req, null, channel.newPromise()); + return handshake(channel, req, null); } /** @@ -259,16 +251,14 @@ public abstract class WebSocketServerHandshaker { * HTTP Request * @param responseHeaders * Extra headers to add to the handshake response or {@code null} if no extra headers should be added - * @param promise - * the {@link Promise} to be notified when the opening handshake is done * @return future * the {@link Future} which is notified when the opening handshake is done */ public final Future handshake(final Channel channel, HttpRequest req, - final HttpHeaders responseHeaders, final Promise promise) { + final HttpHeaders responseHeaders) { if (req instanceof FullHttpRequest) { - return handshake(channel, (FullHttpRequest) req, responseHeaders, promise); + return handshake(channel, (FullHttpRequest) req, responseHeaders); } if (logger.isDebugEnabled()) { logger.debug("{} WebSocket version {} server handshake", channel, version()); @@ -279,11 +269,12 @@ public abstract class WebSocketServerHandshaker { // this means the user use an HttpServerCodec ctx = p.context(HttpServerCodec.class); if (ctx == null) { - promise.setFailure( + return channel.newFailedFuture( new IllegalStateException("No HttpDecoder and no HttpServerCodec in the pipeline")); - return promise; } } + + Promise promise = channel.newPromise(); // Add aggregator and ensure we feed the HttpRequest so it is aggregated. A limit o 8192 should be more then // enough for the websockets handshake payload. // @@ -295,7 +286,7 @@ public abstract class WebSocketServerHandshaker { protected void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { // Remove ourself and do the actual handshake ctx.pipeline().remove(this); - handshake(channel, msg, responseHeaders, promise); + handshake(channel, msg, responseHeaders); } @Override diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java index 9d6e6638b6..754f17ca54 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshakerTest.java @@ -100,7 +100,7 @@ public abstract class WebSocketServerHandshakerTest { "ws://example.com/chat"); request.headers().set("x-client-header", "value"); try { - serverHandshaker.handshake(null, request, null, null); + serverHandshaker.handshake(null, request, null); } catch (WebSocketServerHandshakeException exception) { assertNotNull(exception.getMessage()); assertEquals(request.headers(), exception.request().headers()); diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java index 9acf5ce841..4436bd872b 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/CompressorHttp2ConnectionEncoder.java @@ -143,12 +143,12 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE @Override public Future writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding, - final boolean endOfStream, Promise promise) { + final boolean endOfStream) { final Http2Stream stream = connection().stream(streamId); final EmbeddedChannel channel = stream == null ? null : (EmbeddedChannel) stream.getProperty(propertyKey); if (channel == null) { // The compressor may be null if no compatible encoding type was found in this stream's headers - return super.writeData(ctx, streamId, data, padding, endOfStream, promise); + return super.writeData(ctx, streamId, data, padding, endOfStream); } try { @@ -161,13 +161,13 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE buf = nextReadableBuf(channel); } return super.writeData(ctx, streamId, buf == null ? Unpooled.EMPTY_BUFFER : buf, padding, - true, promise); + true); } // END_STREAM is not set and the assumption is data is still forthcoming. - promise.setSuccess(null); - return promise; + return ctx.newSucceededFuture(); } + Promise promise = ctx.newPromise(); PromiseCombiner combiner = new PromiseCombiner(ctx.executor()); for (;;) { ByteBuf nextBuf = nextReadableBuf(channel); @@ -177,9 +177,8 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE compressedEndOfStream = nextBuf == null; } - Promise bufPromise = ctx.newPromise(); - combiner.add(bufPromise); - super.writeData(ctx, streamId, buf, padding, compressedEndOfStream, bufPromise); + Future future = super.writeData(ctx, streamId, buf, padding, compressedEndOfStream); + combiner.add(future); if (nextBuf == null) { break; } @@ -188,56 +187,54 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE buf = nextBuf; } combiner.finish(promise); + return promise; } catch (Throwable cause) { - promise.tryFailure(cause); + return ctx.newFailedFuture(cause); } finally { if (endOfStream) { cleanup(stream, channel); } } - return promise; } @Override public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream, Promise promise) { + boolean endStream) { try { // Determine if compression is required and sanitize the headers. EmbeddedChannel compressor = newCompressor(ctx, headers, endStream); // Write the headers and create the stream object. - Future future = super.writeHeaders(ctx, streamId, headers, padding, endStream, promise); + Future future = super.writeHeaders(ctx, streamId, headers, padding, endStream); // After the stream object has been created, then attach the compressor as a property for data compression. bindCompressorToStream(compressor, streamId); return future; } catch (Throwable e) { - promise.tryFailure(e); + return ctx.newFailedFuture(e); } - return promise; } @Override public Future 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, final Promise promise) { + final boolean endOfStream) { try { // Determine if compression is required and sanitize the headers. EmbeddedChannel compressor = newCompressor(ctx, headers, endOfStream); // Write the headers and create the stream object. Future future = super.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, - padding, endOfStream, promise); + padding, endOfStream); // After the stream object has been created, then attach the compressor as a property for data compression. bindCompressorToStream(compressor, streamId); return future; } catch (Throwable e) { - promise.tryFailure(e); + return ctx.newFailedFuture(e); } - return promise; } /** diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2FrameWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2FrameWriter.java index 71d3f28cd2..b8b0d3e938 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2FrameWriter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DecoratingHttp2FrameWriter.java @@ -17,7 +17,6 @@ package io.netty.handler.codec.http2; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; import io.netty.util.internal.UnstableApi; import static java.util.Objects.requireNonNull; @@ -35,73 +34,70 @@ public class DecoratingHttp2FrameWriter implements Http2FrameWriter { @Override public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, - boolean endStream, Promise promise) { - return delegate.writeData(ctx, streamId, data, padding, endStream, promise); + boolean endStream) { + return delegate.writeData(ctx, streamId, data, padding, endStream); } @Override public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream, Promise promise) { - return delegate.writeHeaders(ctx, streamId, headers, padding, endStream, promise); + boolean endStream) { + return delegate.writeHeaders(ctx, streamId, headers, padding, endStream); } @Override public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, int padding, - boolean endStream, Promise promise) { + boolean endStream) { return delegate - .writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream, promise); + .writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream); } @Override public Future writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, - boolean exclusive, Promise promise) { - return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise); + boolean exclusive) { + return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive); } @Override - public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode, - Promise promise) { - return delegate.writeRstStream(ctx, streamId, errorCode, promise); + public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) { + return delegate.writeRstStream(ctx, streamId, errorCode); } @Override - public Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings, Promise promise) { - return delegate.writeSettings(ctx, settings, promise); + public Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings) { + return delegate.writeSettings(ctx, settings); } @Override - public Future writeSettingsAck(ChannelHandlerContext ctx, Promise promise) { - return delegate.writeSettingsAck(ctx, promise); + public Future writeSettingsAck(ChannelHandlerContext ctx) { + return delegate.writeSettingsAck(ctx); } @Override - public Future writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise promise) { - return delegate.writePing(ctx, ack, data, promise); + public Future writePing(ChannelHandlerContext ctx, boolean ack, long data) { + return delegate.writePing(ctx, ack, data); } @Override public Future writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding, Promise promise) { - return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise); + Http2Headers headers, int padding) { + return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding); } @Override - public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData, - Promise promise) { - return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise); + public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) { + return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData); } @Override - public Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement, - Promise promise) { - return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise); + public Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) { + return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement); } @Override public Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload, Promise promise) { - return delegate.writeFrame(ctx, frameType, streamId, flags, payload, promise); + ByteBuf payload) { + return delegate.writeFrame(ctx, frameType, streamId, flags, payload); } @Override diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java index 8a54f5bfff..82d03cc93c 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2Connection.java @@ -20,7 +20,6 @@ import io.netty.handler.codec.http2.Http2Stream.State; import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectMap; import io.netty.util.collection.IntObjectMap.PrimitiveEntry; -import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.UnaryPromiseNotifier; import io.netty.util.internal.EmptyArrays; @@ -116,7 +115,7 @@ public class DefaultHttp2Connection implements Http2Connection { } @Override - public Future close(final Promise promise) { + public void close(final Promise promise) { requireNonNull(promise, "promise"); // Since we allow this method to be called multiple times, we must make sure that all the promises are notified // when all streams are removed and the close operation completes. @@ -131,7 +130,7 @@ public class DefaultHttp2Connection implements Http2Connection { } if (isStreamMapEmpty()) { promise.trySuccess(null); - return promise; + return; } Iterator> itr = streamMap.entries().iterator(); @@ -162,7 +161,6 @@ public class DefaultHttp2Connection implements Http2Connection { } } } - return closePromise; } @Override diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java index 3fb77e7fcd..34d3faeea8 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoder.java @@ -498,7 +498,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder { // Acknowledge receipt of the settings. We should do this before we process the settings to ensure our // remote peer applies these settings before any subsequent frames that we may send which depend upon // these new settings. See https://github.com/netty/netty/issues/6520. - encoder.writeSettingsAck(ctx, ctx.newPromise()); + encoder.writeSettingsAck(ctx); encoder.remoteSettings(settings); } else { @@ -512,7 +512,7 @@ public class DefaultHttp2ConnectionDecoder implements Http2ConnectionDecoder { public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception { if (autoAckPing) { // Send an ack back to the remote client. - encoder.writePing(ctx, true, data, ctx.newPromise()); + encoder.writePing(ctx, true, data); } listener.onPingRead(ctx, data); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java index dc02d63edb..63b9d837cc 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoder.java @@ -120,7 +120,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht @Override public Future writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding, - final boolean endOfStream, Promise promise) { + final boolean endOfStream) { final Http2Stream stream; try { stream = requireStream(streamId); @@ -136,9 +136,10 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht } } catch (Throwable e) { data.release(); - return promise.setFailure(e); + return ctx.newFailedFuture(e); } + Promise promise = ctx.newPromise(); // Hand control of the frame to the flow controller. flowController().addFlowControlled(stream, new FlowControlledData(stream, data, padding, endOfStream, promise, ctx.channel())); @@ -147,8 +148,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht @Override public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, - boolean endStream, Promise promise) { - return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream, promise); + boolean endStream) { + return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream); } private static boolean validateHeadersSentState(Http2Stream stream, Http2Headers headers, boolean isServer, @@ -163,10 +164,9 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht @Override public Future 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, - Promise promise) { + final boolean exclusive, final int padding, final boolean endOfStream) { return writeHeaders0(ctx, streamId, headers, true, streamDependency, - weight, exclusive, padding, endOfStream, promise); + weight, exclusive, padding, endOfStream); } /** @@ -177,19 +177,19 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht Http2Headers headers, final boolean hasPriority, int streamDependency, final short weight, boolean exclusive, final int padding, - boolean endOfStream, Promise promise) { + boolean endOfStream) { if (hasPriority) { return frameWriter.writeHeaders(ctx, streamId, headers, streamDependency, - weight, exclusive, padding, endOfStream, promise); + weight, exclusive, padding, endOfStream); } - return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream, promise); + return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream); } private Future 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, Promise promise) { + final boolean endOfStream) { try { Http2Stream stream = connection.stream(streamId); if (stream == null) { @@ -202,8 +202,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht stream = connection.local().createStream(streamId, /*endOfStream*/ false); } catch (Http2Exception cause) { if (connection.remote().mayHaveCreatedStream(streamId)) { - promise.tryFailure(new IllegalStateException("Stream no longer exists: " + streamId, cause)); - return promise; + return ctx.newFailedFuture( + new IllegalStateException("Stream no longer exists: " + streamId, cause)); } throw cause; } @@ -231,7 +231,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream); Future future = sendHeaders(frameWriter, ctx, streamId, headers, hasPriority, streamDependency, - weight, exclusive, padding, endOfStream, promise); + weight, exclusive, padding, endOfStream); // Writing headers may fail during the encode state if they violate HPACK limits. @@ -261,6 +261,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht return future; } else { + Promise promise = ctx.newPromise(); // Pass headers to the flow-controller so it can maintain their sequence relative to DATA frames. flowController.addFlowControlled(stream, new FlowControlledHeaders(stream, headers, hasPriority, streamDependency, @@ -269,27 +270,24 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht } } catch (Throwable t) { lifecycleManager.onError(ctx, true, t); - promise.tryFailure(t); - return promise; + return ctx.newFailedFuture(t); } } @Override public Future writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, - boolean exclusive, Promise promise) { - return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise); + boolean exclusive) { + return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive); } @Override - public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode, - Promise promise) { + public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) { // Delegate to the lifecycle manager for proper updating of connection state. - return lifecycleManager.resetStream(ctx, streamId, errorCode, promise); + return lifecycleManager.resetStream(ctx, streamId, errorCode); } @Override - public Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings, - Promise promise) { + public Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings) { outstandingLocalSettingsQueue.add(settings); try { Boolean pushEnabled = settings.pushEnabled(); @@ -297,27 +295,28 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht throw connectionError(PROTOCOL_ERROR, "Server sending SETTINGS frame with ENABLE_PUSH specified"); } } catch (Throwable e) { - return promise.setFailure(e); + return ctx.newFailedFuture(e); } - return frameWriter.writeSettings(ctx, settings, promise); + return frameWriter.writeSettings(ctx, settings); } @Override - public Future writeSettingsAck(ChannelHandlerContext ctx, Promise promise) { + public Future writeSettingsAck(ChannelHandlerContext ctx) { if (outstandingRemoteSettingsQueue == null) { - return frameWriter.writeSettingsAck(ctx, promise); + return frameWriter.writeSettingsAck(ctx); } Http2Settings settings = outstandingRemoteSettingsQueue.poll(); if (settings == null) { - return promise.setFailure(new Http2Exception(INTERNAL_ERROR, "attempted to write a SETTINGS ACK with no " + + return ctx.newFailedFuture(new Http2Exception(INTERNAL_ERROR, "attempted to write a SETTINGS ACK with no " + " pending SETTINGS")); } - SimpleChannelPromiseAggregator aggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor()); + SimpleChannelPromiseAggregator aggregator = + new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); // Acknowledge receipt of the settings. We should do this before we process the settings to ensure our // remote peer applies these settings before any subsequent frames that we may send which depend upon // these new settings. See https://github.com/netty/netty/issues/6520. - frameWriter.writeSettingsAck(ctx, aggregator.newPromise()); + frameWriter.writeSettingsAck(ctx).cascadeTo(aggregator.newPromise()); // We create a "new promise" to make sure that status from both the write and the application are taken into // account independently. @@ -333,13 +332,13 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht } @Override - public Future writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise promise) { - return frameWriter.writePing(ctx, ack, data, promise); + public Future writePing(ChannelHandlerContext ctx, boolean ack, long data) { + return frameWriter.writePing(ctx, ack, data); } @Override public Future writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding, Promise promise) { + Http2Headers headers, int padding) { try { if (connection.goAwayReceived()) { throw connectionError(PROTOCOL_ERROR, "Sending PUSH_PROMISE after GO_AWAY received."); @@ -349,8 +348,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht // Reserve the promised stream. connection.local().reservePushStream(promisedStreamId, stream); - Future future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, - promise); + Future future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding); // Writing headers may fail during the encode state if they violate HPACK limits. if (future.isSuccess() || !future.isDone()) { // This just sets internal stream state which is used elsewhere in the codec and doesn't @@ -368,28 +366,25 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht return future; } catch (Throwable t) { lifecycleManager.onError(ctx, true, t); - promise.tryFailure(t); - return promise; + return ctx.newFailedFuture(t); } } @Override - public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData, - Promise promise) { - return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData, promise); + public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData) { + return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData); } @Override - public Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement, - Promise promise) { - return promise.setFailure(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" + + public Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement) { + return ctx.newFailedFuture(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" + " objects to control window sizes")); } @Override public Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, - ByteBuf payload, Promise promise) { - return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload, promise); + ByteBuf payload) { + return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload); } @Override @@ -424,7 +419,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht @Override public void consumeReceivedSettings(Http2Settings settings) { if (outstandingRemoteSettingsQueue == null) { - outstandingRemoteSettingsQueue = new ArrayDeque(2); + outstandingRemoteSettingsQueue = new ArrayDeque<>(2); } outstandingRemoteSettingsQueue.add(settings); } @@ -508,7 +503,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht // Write the frame(s). frameWriter().writeData(ctx, stream.id(), toWrite, writablePadding, - endOfStream && size() == 0, writePromise); + endOfStream && size() == 0).cascadeTo(writePromise); } @Override @@ -580,7 +575,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht promise.addListener(this); Future f = sendHeaders(frameWriter, ctx, stream.id(), headers, hasPriority, streamDependency, - weight, exclusive, padding, endOfStream, promise); + weight, exclusive, padding, endOfStream); + f.cascadeTo(promise); // Writing headers may fail during the encode state if they violate HPACK limits. if (!f.isFailed()) { // "not failed" means either not done, or completed successfully. // This just sets internal stream state which is used elsewhere in the codec and doesn't @@ -621,7 +617,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht } @Override - public void operationComplete(Future future) throws Exception { + public void operationComplete(Future future) { if (future.isFailed()) { error(flowController().channelHandlerContext(), future.cause()); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java index 4e02365939..5672bc1543 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriter.java @@ -21,7 +21,6 @@ import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregato import io.netty.handler.codec.http2.Http2FrameWriter.Configuration; import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector; import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; import io.netty.util.internal.UnstableApi; import static io.netty.buffer.Unpooled.directBuffer; @@ -133,9 +132,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize @Override public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, - int padding, boolean endStream, Promise promise) { + int padding, boolean endStream) { final SimpleChannelPromiseAggregator promiseAggregator = - new SimpleChannelPromiseAggregator(promise, ctx.executor()); + new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); ByteBuf frameHeader = null; try { verifyStreamId(streamId, STREAM_ID); @@ -242,7 +241,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize // Use a try/finally here in case the data has been released before calling this method. This is not // necessary above because we internally allocate frameHeader. try { - if (data != null) { + if (data != null && + // Check if the data was released already. + data.refCnt() > 0) { data.release(); } } finally { @@ -256,22 +257,22 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize @Override public Future writeHeaders(ChannelHandlerContext ctx, int streamId, - Http2Headers headers, int padding, boolean endStream, Promise promise) { + Http2Headers headers, int padding, boolean endStream) { return writeHeadersInternal(ctx, streamId, headers, padding, endStream, - false, 0, (short) 0, false, promise); + false, 0, (short) 0, false); } @Override public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int streamDependency, short weight, boolean exclusive, - int padding, boolean endStream, Promise promise) { + int padding, boolean endStream) { return writeHeadersInternal(ctx, streamId, headers, padding, endStream, - true, streamDependency, weight, exclusive, promise); + true, streamDependency, weight, exclusive); } @Override public Future writePriority(ChannelHandlerContext ctx, int streamId, - int streamDependency, short weight, boolean exclusive, Promise promise) { + int streamDependency, short weight, boolean exclusive) { try { verifyStreamId(streamId, STREAM_ID); verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY); @@ -282,15 +283,14 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency); // Adjust the weight so that it fits into a single byte on the wire. buf.writeByte(weight - 1); - return ctx.write(buf).cascadeTo(promise); + return ctx.write(buf); } catch (Throwable t) { - return promise.setFailure(t); + return ctx.newFailedFuture(t); } } @Override - public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode, - Promise promise) { + public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) { try { verifyStreamId(streamId, STREAM_ID); verifyErrorCode(errorCode); @@ -298,15 +298,14 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize ByteBuf buf = ctx.alloc().buffer(RST_STREAM_FRAME_LENGTH); writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, RST_STREAM, new Http2Flags(), streamId); buf.writeInt((int) errorCode); - return ctx.write(buf).cascadeTo(promise); + return ctx.write(buf); } catch (Throwable t) { - return promise.setFailure(t); + return ctx.newFailedFuture(t); } } @Override - public Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings, - Promise promise) { + public Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings) { try { requireNonNull(settings, "settings"); int payloadLength = SETTING_ENTRY_LENGTH * settings.size(); @@ -316,39 +315,40 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize buf.writeChar(entry.key()); buf.writeInt(entry.value().intValue()); } - return ctx.write(buf).cascadeTo(promise); + return ctx.write(buf); } catch (Throwable t) { - return promise.setFailure(t); + return ctx.newFailedFuture(t); } } @Override - public Future writeSettingsAck(ChannelHandlerContext ctx, Promise promise) { + public Future writeSettingsAck(ChannelHandlerContext ctx) { try { ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH); writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0); - return ctx.write(buf).cascadeTo(promise); + return ctx.write(buf); } catch (Throwable t) { - return promise.setFailure(t); + return ctx.newFailedFuture(t); } } @Override - public Future writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise promise) { + public Future writePing(ChannelHandlerContext ctx, boolean ack, long data) { Http2Flags flags = ack ? new Http2Flags().ack(true) : new Http2Flags(); ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + PING_FRAME_PAYLOAD_LENGTH); // Assume nothing below will throw until buf is written. That way we don't have to take care of ownership // in the catch block. writeFrameHeaderInternal(buf, PING_FRAME_PAYLOAD_LENGTH, PING, flags, 0); buf.writeLong(data); - return ctx.write(buf).cascadeTo(promise); + return ctx.write(buf); } @Override public Future writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, - Http2Headers headers, int padding, Promise promise) { + Http2Headers headers, int padding) { ByteBuf headerBlock = null; - SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor()); + SimpleChannelPromiseAggregator promiseAggregator = + new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); try { verifyStreamId(streamId, STREAM_ID); verifyStreamId(promisedStreamId, "Promised Stream ID"); @@ -403,8 +403,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize @Override public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, - ByteBuf debugData, Promise promise) { - SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor()); + ByteBuf debugData) { + SimpleChannelPromiseAggregator promiseAggregator = + new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); try { verifyStreamOrConnectionId(lastStreamId, "Last Stream ID"); verifyErrorCode(errorCode); @@ -437,7 +438,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize @Override public Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId, - int windowSizeIncrement, Promise promise) { + int windowSizeIncrement) { try { verifyStreamOrConnectionId(streamId, STREAM_ID); verifyWindowSizeIncrement(windowSizeIncrement); @@ -445,16 +446,17 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize ByteBuf buf = ctx.alloc().buffer(WINDOW_UPDATE_FRAME_LENGTH); writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, WINDOW_UPDATE, new Http2Flags(), streamId); buf.writeInt(windowSizeIncrement); - return ctx.write(buf).cascadeTo(promise); + return ctx.write(buf); } catch (Throwable t) { - return promise.setFailure(t); + return ctx.newFailedFuture(t); } } @Override public Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, - Http2Flags flags, ByteBuf payload, Promise promise) { - SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor()); + Http2Flags flags, ByteBuf payload) { + SimpleChannelPromiseAggregator promiseAggregator = + new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); try { verifyStreamOrConnectionId(streamId, STREAM_ID); ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH); @@ -481,9 +483,10 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize private Future writeHeadersInternal(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, boolean endStream, - boolean hasPriority, int streamDependency, short weight, boolean exclusive, Promise promise) { + boolean hasPriority, int streamDependency, short weight, boolean exclusive) { ByteBuf headerBlock = null; - SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor()); + SimpleChannelPromiseAggregator promiseAggregator = + new SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor()); try { verifyStreamId(streamId, STREAM_ID); if (hasPriority) { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java index e6e7472ce9..5714de66e4 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowController.java @@ -483,7 +483,7 @@ public class DefaultHttp2LocalFlowController implements Http2LocalFlowController } // Send a window update for the stream/connection. - frameWriter.writeWindowUpdate(ctx, stream.id(), deltaWindowSize, ctx.newPromise()); + frameWriter.writeWindowUpdate(ctx, stream.id(), deltaWindowSize); } } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Connection.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Connection.java index 487ae76cb8..cc4e874b48 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Connection.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2Connection.java @@ -253,10 +253,10 @@ public interface Http2Connection { * all streams that exists (active or otherwise) will be closed and removed. *

Note if iterating active streams via {@link #forEachActiveStream(Http2StreamVisitor)} and an exception is * thrown it is necessary to call this method again to ensure the close completes. + * * @param promise Will be completed when all streams have been removed, and listeners have been notified. - * @return A future that will be completed when all streams have been removed, and listeners have been notified. */ - Future close(Promise promise); + void close(Promise promise); /** * Creates a new key that is unique within this {@link Http2Connection}. diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionEncoder.java index 2402801388..a81a1d34d8 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionEncoder.java @@ -61,9 +61,8 @@ public interface Http2ConnectionEncoder extends Http2FrameWriter { /** * Writes the given data to the internal {@link Http2FrameWriter} without performing any * state checks on the connection/stream. - * @return */ @Override Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, - Http2Flags flags, ByteBuf payload, Promise promise); + Http2Flags flags, ByteBuf payload); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java index 7149f72dbe..72f38331af 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ConnectionHandler.java @@ -356,7 +356,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http } // Both client and server must send their initial settings. - encoder.writeSettings(ctx, initialSettings, ctx.newPromise()) + encoder.writeSettings(ctx, initialSettings) .addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE); if (isClient) { @@ -450,7 +450,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http // a GO_AWAY has been sent we send a empty buffer just so we can wait to close until all other data has been // flushed to the OS. // https://github.com/netty/netty/issues/5307 - Future f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise()); + Future f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null); ctx.flush(); Promise promise = ctx.newPromise(); doGracefulShutdown(ctx, f, promise); @@ -626,7 +626,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http } Promise promise = ctx.newPromise(); - Future future = goAway(ctx, http2Ex, ctx.newPromise()); + Future future = goAway(ctx, http2Ex); if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) { doGracefulShutdown(ctx, future, promise); } else { @@ -663,7 +663,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http try { stream = encoder.connection().remote().createStream(streamId, true); } catch (Http2Exception e) { - resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise()); + resetUnknownStream(ctx, streamId, http2Ex.error().code()); return; } } @@ -680,10 +680,10 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http if (stream == null) { if (!outbound || connection().local().mayHaveCreatedStream(streamId)) { - resetUnknownStream(ctx, streamId, http2Ex.error().code(), ctx.newPromise()); + resetUnknownStream(ctx, streamId, http2Ex.error().code()); } } else { - resetStream(ctx, stream, http2Ex.error().code(), ctx.newPromise()); + resetStream(ctx, stream, http2Ex.error().code()); } } @@ -695,7 +695,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http * @param stream the Http2Stream on which the header was received */ protected void handleServerHeaderDecodeSizeError(ChannelHandlerContext ctx, Http2Stream stream) { - encoder().writeHeaders(ctx, stream.id(), HEADERS_TOO_LARGE_HEADERS, 0, true, ctx.newPromise()); + encoder().writeHeaders(ctx, stream.id(), HEADERS_TOO_LARGE_HEADERS, 0, true); } protected Http2FrameWriter frameWriter() { @@ -707,9 +707,8 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http * triggered by the first frame of a stream being invalid. That is, there was an error reading the frame before * we could create a new stream. */ - private Future resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode, - Promise promise) { - Future future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise); + private Future resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode) { + Future future = frameWriter().writeRstStream(ctx, streamId, errorCode); if (future.isDone()) { closeConnectionOnError(ctx, future); } else { @@ -719,21 +718,20 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http } @Override - public Future resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode, - Promise promise) { + public Future resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode) { final Http2Stream stream = connection().stream(streamId); if (stream == null) { - return resetUnknownStream(ctx, streamId, errorCode, promise); + return resetUnknownStream(ctx, streamId, errorCode); } - return resetStream(ctx, stream, errorCode, promise); + return resetStream(ctx, stream, errorCode); } private Future resetStream(final ChannelHandlerContext ctx, final Http2Stream stream, - long errorCode, Promise promise) { + long errorCode) { if (stream.isResetSent()) { // Don't write a RST_STREAM frame if we have already written one. - return promise.setSuccess(null); + return ctx.newSucceededFuture(); } // Synchronously set the resetSent flag to prevent any subsequent calls // from resulting in multiple reset frames being sent. @@ -747,9 +745,9 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http // https://tools.ietf.org/html/rfc7540#section-6.4. if (stream.state() == IDLE || connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) { - future = promise.setSuccess(null); + future = ctx.newSucceededFuture(); } else { - future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise); + future = frameWriter().writeRstStream(ctx, stream.id(), errorCode); } if (future.isDone()) { processRstStreamWriteResult(ctx, stream, future); @@ -762,24 +760,22 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http @Override public Future goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode, - final ByteBuf debugData, Promise promise) { + final ByteBuf debugData) { final Http2Connection connection = connection(); try { if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) { debugData.release(); - promise.trySuccess(null); - return promise; + return ctx.newSucceededFuture(); } } catch (Throwable cause) { debugData.release(); - promise.tryFailure(cause); - return promise; + return ctx.newFailedFuture(cause); } // Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and // result in an IllegalRefCountException. debugData.retain(); - Future future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise); + Future future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData); if (future.isDone()) { processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future); @@ -815,7 +811,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http * Close the remote endpoint with with a {@code GO_AWAY} frame. Does not flush * immediately, this is the responsibility of the caller. */ - private Future goAway(ChannelHandlerContext ctx, Http2Exception cause, Promise promise) { + private Future goAway(ChannelHandlerContext ctx, Http2Exception cause) { long errorCode = cause != null ? cause.error().code() : NO_ERROR.code(); int lastKnownStream; if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) { @@ -827,7 +823,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http } else { lastKnownStream = connection().remote().lastStreamCreated(); } - return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), promise); + return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause)); } @SuppressWarnings("unchecked") diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoder.java index c1eb3a6195..c68f2a74b2 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoder.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoder.java @@ -17,7 +17,6 @@ package io.netty.handler.codec.http2; import io.netty.channel.ChannelHandlerContext; import io.netty.util.concurrent.Future; import io.netty.util.concurrent.FutureListener; -import io.netty.util.concurrent.Promise; import io.netty.util.internal.ObjectUtil; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -49,38 +48,41 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod } @Override - public Future writeSettingsAck(ChannelHandlerContext ctx, Promise promise) { - Promise newPromise = handleOutstandingControlFrames(ctx, promise); - if (newPromise == null) { - return promise; + public Future writeSettingsAck(ChannelHandlerContext ctx) { + FutureListener listener = handleOutstandingControlFrames(ctx); + Future f = super.writeSettingsAck(ctx); + if (listener != null) { + f.addListener(listener); } - return super.writeSettingsAck(ctx, newPromise); + return f; } @Override - public Future writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise promise) { + public Future writePing(ChannelHandlerContext ctx, boolean ack, long data) { // Only apply the limit to ping acks. if (ack) { - Promise newPromise = handleOutstandingControlFrames(ctx, promise); - if (newPromise == null) { - return promise; + FutureListener listener = handleOutstandingControlFrames(ctx); + Future f = super.writePing(ctx, ack, data); + if (listener != null) { + f.addListener(listener); } - return super.writePing(ctx, ack, data, newPromise); + return f; } - return super.writePing(ctx, ack, data, promise); + return super.writePing(ctx, ack, data); } @Override public Future writeRstStream( - ChannelHandlerContext ctx, int streamId, long errorCode, Promise promise) { - Promise newPromise = handleOutstandingControlFrames(ctx, promise); - if (newPromise == null) { - return promise; + ChannelHandlerContext ctx, int streamId, long errorCode) { + FutureListener listener = handleOutstandingControlFrames(ctx); + Future f = super.writeRstStream(ctx, streamId, errorCode); + if (listener != null) { + f.addListener(listener); } - return super.writeRstStream(ctx, streamId, errorCode, newPromise); + return f; } - private Promise handleOutstandingControlFrames(ChannelHandlerContext ctx, Promise promise) { + private FutureListener handleOutstandingControlFrames(ChannelHandlerContext ctx) { if (!limitReached) { if (outstandingControlFrames == maxOutstandingControlFrames) { // Let's try to flush once as we may be able to flush some of the control frames. @@ -96,13 +98,14 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod // First notify the Http2LifecycleManager and then close the connection. lifecycleManager.onError(ctx, true, exception); ctx.close(); + return null; } outstandingControlFrames++; // We did not reach the limit yet, add the listener to decrement the number of outstanding control frames // once the promise was completed - promise.addListener(outstandingControlFramesListener); + return outstandingControlFramesListener; } - return promise; + return null; } } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataWriter.java index 6d369d2340..5da9a9ed6f 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataWriter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2DataWriter.java @@ -37,9 +37,8 @@ public interface Http2DataWriter { * A 256 byte padding is encoded as the pad length field with value 255 and 255 padding bytes * appended to the end of the frame. * @param endStream indicates if this is the last frame to be sent for the stream. - * @param promise the promise for the write. * @return the future for the write. */ Future writeData(ChannelHandlerContext ctx, int streamId, - ByteBuf data, int padding, boolean endStream, Promise promise); + ByteBuf data, int padding, boolean endStream); } diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java index 9436426a10..d59ffb00cf 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameCodec.java @@ -282,13 +282,12 @@ public class Http2FrameCodec extends Http2ConnectionHandler { */ @Override public Future write(ChannelHandlerContext ctx, Object msg) { - Promise promise = ctx.newPromise(); if (msg instanceof Http2DataFrame) { Http2DataFrame dataFrame = (Http2DataFrame) msg; - encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(), - dataFrame.padding(), dataFrame.isEndStream(), promise); + return encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(), + dataFrame.padding(), dataFrame.isEndStream()); } else if (msg instanceof Http2HeadersFrame) { - writeHeadersFrame(ctx, (Http2HeadersFrame) msg, promise); + return writeHeadersFrame(ctx, (Http2HeadersFrame) msg); } else if (msg instanceof Http2WindowUpdateFrame) { Http2WindowUpdateFrame frame = (Http2WindowUpdateFrame) msg; Http2FrameStream frameStream = frame.stream(); @@ -300,9 +299,9 @@ public class Http2FrameCodec extends Http2ConnectionHandler { } else { consumeBytes(frameStream.id(), frame.windowSizeIncrement()); } - promise.setSuccess(null); + return ctx.newSucceededFuture(); } catch (Throwable t) { - promise.setFailure(t); + return ctx.newFailedFuture(t); } } else if (msg instanceof Http2ResetFrame) { Http2ResetFrame rstFrame = (Http2ResetFrame) msg; @@ -310,41 +309,40 @@ public class Http2FrameCodec extends Http2ConnectionHandler { // Only ever send a reset frame if stream may have existed before as otherwise we may send a RST on a // stream in an invalid state and cause a connection error. if (connection().streamMayHaveExisted(id)) { - encoder().writeRstStream(ctx, rstFrame.stream().id(), rstFrame.errorCode(), promise); + return encoder().writeRstStream(ctx, rstFrame.stream().id(), rstFrame.errorCode()); } else { ReferenceCountUtil.release(rstFrame); - promise.setFailure(Http2Exception.streamError( + return ctx.newFailedFuture(Http2Exception.streamError( rstFrame.stream().id(), Http2Error.PROTOCOL_ERROR, "Stream never existed")); } } else if (msg instanceof Http2PingFrame) { Http2PingFrame frame = (Http2PingFrame) msg; - encoder().writePing(ctx, frame.ack(), frame.content(), promise); + return encoder().writePing(ctx, frame.ack(), frame.content()); } else if (msg instanceof Http2SettingsFrame) { - encoder().writeSettings(ctx, ((Http2SettingsFrame) msg).settings(), promise); + return encoder().writeSettings(ctx, ((Http2SettingsFrame) msg).settings()); } else if (msg instanceof Http2SettingsAckFrame) { // In the event of manual SETTINGS ACK, it is assumed the encoder will apply the earliest received but not // yet ACKed settings. - encoder().writeSettingsAck(ctx, promise); + return encoder().writeSettingsAck(ctx); } else if (msg instanceof Http2GoAwayFrame) { - writeGoAwayFrame(ctx, (Http2GoAwayFrame) msg, promise); + return writeGoAwayFrame(ctx, (Http2GoAwayFrame) msg); } else if (msg instanceof Http2PushPromiseFrame) { Http2PushPromiseFrame pushPromiseFrame = (Http2PushPromiseFrame) msg; - writePushPromise(ctx, pushPromiseFrame, promise); + return writePushPromise(ctx, pushPromiseFrame); } else if (msg instanceof Http2PriorityFrame) { Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg; - encoder().writePriority(ctx, priorityFrame.stream().id(), priorityFrame.streamDependency(), - priorityFrame.weight(), priorityFrame.exclusive(), promise); + return encoder().writePriority(ctx, priorityFrame.stream().id(), priorityFrame.streamDependency(), + priorityFrame.weight(), priorityFrame.exclusive()); } else if (msg instanceof Http2UnknownFrame) { Http2UnknownFrame unknownFrame = (Http2UnknownFrame) msg; - encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(), - unknownFrame.flags(), unknownFrame.content(), promise); + return encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(), + unknownFrame.flags(), unknownFrame.content()); } else if (!(msg instanceof Http2Frame)) { - ctx.write(msg).cascadeTo(promise); + return ctx.write(msg); } else { ReferenceCountUtil.release(msg); - promise.setFailure(new UnsupportedMessageTypeException(msg)); + return ctx.newFailedFuture(new UnsupportedMessageTypeException(msg)); } - return promise; } private void increaseInitialConnectionWindow(int deltaBytes) throws Http2Exception { @@ -366,10 +364,10 @@ public class Http2FrameCodec extends Http2ConnectionHandler { return connection().local().flowController().consumeBytes(stream, bytes); } - private void writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame, Promise promise) { + private Future writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame) { if (frame.lastStreamId() > -1) { frame.release(); - throw new IllegalArgumentException("Last stream id must not be set on GOAWAY frame"); + return ctx.newFailedFuture(new IllegalArgumentException("Last stream id must not be set on GOAWAY frame")); } int lastStreamCreated = connection().remote().lastStreamCreated(); @@ -378,66 +376,72 @@ public class Http2FrameCodec extends Http2ConnectionHandler { if (lastStreamId > Integer.MAX_VALUE) { lastStreamId = Integer.MAX_VALUE; } - goAway(ctx, (int) lastStreamId, frame.errorCode(), frame.content(), promise); + return goAway(ctx, (int) lastStreamId, frame.errorCode(), frame.content()); } - private void writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame, - final Promise promise) { + private Future writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame) { if (isStreamIdValid(headersFrame.stream().id())) { - encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(), - headersFrame.isEndStream(), promise); - } else if (initializeNewStream(ctx, (DefaultHttp2FrameStream) headersFrame.stream(), promise)) { - final int streamId = headersFrame.stream().id(); + return encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), + headersFrame.padding(), headersFrame.isEndStream()); + } else { + Future future = initializeNewStream(ctx, (DefaultHttp2FrameStream) headersFrame.stream()); + if (future == null) { + final int streamId = headersFrame.stream().id(); - encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(), - headersFrame.isEndStream(), promise); + future = encoder().writeHeaders(ctx, streamId, headersFrame.headers(), headersFrame.padding(), + headersFrame.isEndStream()); - if (!promise.isDone()) { - numBufferedStreams++; - // Clean up the stream being initialized if writing the headers fails and also - // decrement the number of buffered streams. - promise.addListener(channelFuture -> { - numBufferedStreams--; + if (!future.isDone()) { + numBufferedStreams++; + // Clean up the stream being initialized if writing the headers fails and also + // decrement the number of buffered streams. + future.addListener(channelFuture -> { + numBufferedStreams--; - handleHeaderFuture(channelFuture, streamId); - }); - } else { - handleHeaderFuture(promise, streamId); + handleHeaderFuture(channelFuture, streamId); + }); + } else { + handleHeaderFuture(future, streamId); + } } + return future; } } - private void writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame, - final Promise promise) { + private Future writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame) { if (isStreamIdValid(pushPromiseFrame.pushStream().id())) { - encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(), - pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise); - } else if (initializeNewStream(ctx, (DefaultHttp2FrameStream) pushPromiseFrame.pushStream(), promise)) { - final int streamId = pushPromiseFrame.stream().id(); - encoder().writePushPromise(ctx, streamId, pushPromiseFrame.pushStream().id(), - pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise); + return encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(), + pushPromiseFrame.http2Headers(), pushPromiseFrame.padding()); + } else { + Future future = initializeNewStream(ctx, (DefaultHttp2FrameStream) pushPromiseFrame.pushStream()); + if (future == null) { + final int streamId = pushPromiseFrame.stream().id(); + future = encoder().writePushPromise(ctx, streamId, pushPromiseFrame.pushStream().id(), + pushPromiseFrame.http2Headers(), pushPromiseFrame.padding()); - if (promise.isDone()) { - handleHeaderFuture(promise, streamId); - } else { - numBufferedStreams++; - // Clean up the stream being initialized if writing the headers fails and also - // decrement the number of buffered streams. - promise.addListener(f -> { - numBufferedStreams--; - handleHeaderFuture(f, streamId); - }); + if (future.isDone()) { + handleHeaderFuture(future, streamId); + } else { + numBufferedStreams++; + // Clean up the stream being initialized if writing the headers fails and also + // decrement the number of buffered streams. + future.addListener(f -> { + numBufferedStreams--; + handleHeaderFuture(f, streamId); + }); + } + return future; } + return future; } } - private boolean initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream, - Promise promise) { + private Future initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream) { final Http2Connection connection = connection(); final int streamId = connection.local().incrementAndGetNextStreamId(); if (streamId < 0) { - promise.setFailure(new Http2NoMoreStreamIdsException()); + Future f = ctx.newFailedFuture(new Http2NoMoreStreamIdsException()); // Simulate a GOAWAY being received due to stream exhaustion on this connection. We use the maximum // valid stream ID for the current peer. @@ -445,7 +449,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler { Integer.MAX_VALUE - 1, NO_ERROR.code(), writeAscii(ctx.alloc(), "Stream IDs exhausted on local stream creation"))); - return false; + return f; } http2FrameStream.id = streamId; @@ -458,7 +462,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler { // We should not re-use ids. assert old == null; - return true; + return null; } private void handleHeaderFuture(Future channelFuture, int streamId) { diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameWriter.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameWriter.java index eb890f1cf7..e708279e61 100644 --- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameWriter.java +++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2FrameWriter.java @@ -18,7 +18,6 @@ package io.netty.handler.codec.http2; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.util.concurrent.Future; -import io.netty.util.concurrent.Promise; import io.netty.util.internal.UnstableApi; import java.io.Closeable; @@ -54,7 +53,6 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable { * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and * 256 (inclusive). * @param endStream indicates if this is the last frame to be sent for the stream. - * @param promise the promise for the write. * @return the future for the write. * Section 10.5.1 states the following: *

@@ -65,7 +63,7 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * If this call has NOT modified the HPACK header state you are free to throw a stream error.
      */
     Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
-                        int padding, boolean endStream, Promise promise);
+                        int padding, boolean endStream);
 
     /**
      * Writes a HEADERS frame with priority specified to the remote endpoint.
@@ -80,7 +78,6 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and
      *                256 (inclusive).
      * @param endStream indicates if this is the last frame to be sent for the stream.
-     * @param promise the promise for the write.
      * @return the future for the write.
      * Section 10.5.1 states the following:
      * 
@@ -91,8 +88,7 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * If this call has NOT modified the HPACK header state you are free to throw a stream error.
      */
     Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
-                               int streamDependency, short weight, boolean exclusive, int padding, boolean endStream,
-                               Promise promise);
+                               int streamDependency, short weight, boolean exclusive, int padding, boolean endStream);
 
     /**
      * Writes a PRIORITY frame to the remote endpoint.
@@ -103,11 +99,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      *            depend on the connection.
      * @param weight the weight for this stream.
      * @param exclusive whether this stream should be the exclusive dependant of its parent.
-     * @param promise the promise for the write.
      * @return the future for the write.
      */
     Future writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency,
-            short weight, boolean exclusive, Promise promise);
+            short weight, boolean exclusive);
 
     /**
      * Writes a RST_STREAM frame to the remote endpoint.
@@ -115,31 +110,26 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * @param ctx the context to use for writing.
      * @param streamId the stream for which to send the frame.
      * @param errorCode the error code indicating the nature of the failure.
-     * @param promise the promise for the write.
      * @return the future for the write.
      */
-    Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
-            Promise promise);
+    Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode);
 
     /**
      * Writes a SETTINGS frame to the remote endpoint.
      *
      * @param ctx the context to use for writing.
      * @param settings the settings to be sent.
-     * @param promise the promise for the write.
      * @return the future for the write.
      */
-    Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
-            Promise promise);
+    Future writeSettings(ChannelHandlerContext ctx, Http2Settings settings);
 
     /**
      * Writes a SETTINGS acknowledgment to the remote endpoint.
      *
      * @param ctx the context to use for writing.
-     * @param promise the promise for the write.
      * @return the future for the write.
      */
-    Future writeSettingsAck(ChannelHandlerContext ctx, Promise promise);
+    Future writeSettingsAck(ChannelHandlerContext ctx);
 
     /**
      * Writes a PING frame to the remote endpoint.
@@ -148,11 +138,9 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * @param ack indicates whether this is an ack of a PING frame previously received from the
      *            remote endpoint.
      * @param data the payload of the frame.
-     * @param promise the promise for the write.
      * @return the future for the write.
      */
-    Future writePing(ChannelHandlerContext ctx, boolean ack, long data,
-            Promise promise);
+    Future writePing(ChannelHandlerContext ctx, boolean ack, long data);
 
     /**
      * Writes a PUSH_PROMISE frame to the remote endpoint.
@@ -163,7 +151,6 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * @param headers the headers to be sent.
      * @param padding additional bytes that should be added to obscure the true content size. Must be between 0 and
      *                256 (inclusive).
-     * @param promise the promise for the write.
      * @return the future for the write.
      * Section 10.5.1 states the following:
      * 
@@ -174,7 +161,7 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * If this call has NOT modified the HPACK header state you are free to throw a stream error.
      */
     Future writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
-                                   Http2Headers headers, int padding, Promise promise);
+                                   Http2Headers headers, int padding);
 
     /**
      * Writes a GO_AWAY frame to the remote endpoint.
@@ -183,11 +170,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * @param lastStreamId the last known stream of this endpoint.
      * @param errorCode the error code, if the connection was abnormally terminated.
      * @param debugData application-defined debug data. This will be released by this method.
-     * @param promise the promise for the write.
      * @return the future for the write.
      */
     Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
-            ByteBuf debugData, Promise promise);
+            ByteBuf debugData);
 
     /**
      * Writes a WINDOW_UPDATE frame to the remote endpoint.
@@ -196,11 +182,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * @param streamId the stream for which to send the frame.
      * @param windowSizeIncrement the number of bytes by which the local inbound flow control window
      *            is increasing.
-     * @param promise the promise for the write.
      * @return the future for the write.
      */
     Future writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
-            int windowSizeIncrement, Promise promise);
+            int windowSizeIncrement);
 
     /**
      * Generic write method for any HTTP/2 frame. This allows writing of non-standard frames.
@@ -210,11 +195,10 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
      * @param streamId the stream for which to send the frame.
      * @param flags the flags to write for this frame.
      * @param payload the payload to write for this frame. This will be released by this method.
-     * @param promise the promise for the write.
      * @return the future for the write.
      */
     Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
-            Http2Flags flags, ByteBuf payload, Promise promise);
+            Http2Flags flags, ByteBuf payload);
 
     /**
      * Get the configuration related elements for this {@link Http2FrameWriter}
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LifecycleManager.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LifecycleManager.java
index 3bab4932c1..60e91ddd24 100644
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LifecycleManager.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2LifecycleManager.java
@@ -59,13 +59,11 @@ public interface Http2LifecycleManager {
      * @param ctx The context used for communication and buffer allocation if necessary.
      * @param streamId The identifier of the stream to reset.
      * @param errorCode Justification as to why this stream is being reset. See {@link Http2Error}.
-     * @param promise Used to indicate the return status of this operation.
      * @return Will be considered successful when the connection and stream state has been updated, and a
      * {@code RST_STREAM} frame has been sent to the peer. If the stream state has already been updated and a
      * {@code RST_STREAM} frame has been sent then the return status may indicate success immediately.
      */
-    Future resetStream(ChannelHandlerContext ctx, int streamId, long errorCode,
-            Promise promise);
+    Future resetStream(ChannelHandlerContext ctx, int streamId, long errorCode);
 
     /**
      * Prevents the peer from creating streams and close the connection if {@code errorCode} is not
@@ -78,13 +76,12 @@ public interface Http2LifecycleManager {
      * @param lastStreamId The last stream that the local endpoint is claiming it will accept.
      * @param errorCode The rational as to why the connection is being closed. See {@link Http2Error}.
      * @param debugData For diagnostic purposes (carries no semantic value).
-     * @param promise Used to indicate the return status of this operation.
      * @return Will be considered successful when the connection and stream state has been updated, and a
      * {@code GO_AWAY} frame has been sent to the peer. If the stream state has already been updated and a
      * {@code GO_AWAY} frame has been sent then the return status may indicate success immediately.
      */
     Future goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
-            ByteBuf debugData, Promise promise);
+            ByteBuf debugData);
 
     /**
      * Processes the given error.
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2OutboundFrameLogger.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2OutboundFrameLogger.java
index ee8890f83d..3ba4be22b3 100644
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2OutboundFrameLogger.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/Http2OutboundFrameLogger.java
@@ -18,7 +18,6 @@ package io.netty.handler.codec.http2;
 import io.netty.buffer.ByteBuf;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.Promise;
 import io.netty.util.internal.UnstableApi;
 
 import static io.netty.handler.codec.http2.Http2FrameLogger.Direction.OUTBOUND;
@@ -40,93 +39,92 @@ public class Http2OutboundFrameLogger implements Http2FrameWriter {
 
     @Override
     public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
-                                  int padding, boolean endStream, Promise promise) {
+                                  int padding, boolean endStream) {
         logger.logData(OUTBOUND, ctx, streamId, data, padding, endStream);
-        return writer.writeData(ctx, streamId, data, padding, endStream, promise);
+        return writer.writeData(ctx, streamId, data, padding, endStream);
     }
 
     @Override
     public Future writeHeaders(ChannelHandlerContext ctx, int streamId,
-                                     Http2Headers headers, int padding, boolean endStream, Promise promise) {
+                                     Http2Headers headers, int padding, boolean endStream) {
         logger.logHeaders(OUTBOUND, ctx, streamId, headers, padding, endStream);
-        return writer.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
+        return writer.writeHeaders(ctx, streamId, headers, padding, endStream);
     }
 
     @Override
     public Future writeHeaders(ChannelHandlerContext ctx, int streamId,
                                      Http2Headers headers, int streamDependency, short weight, boolean exclusive,
-                                     int padding, boolean endStream, Promise promise) {
+                                     int padding, boolean endStream) {
         logger.logHeaders(OUTBOUND, ctx, streamId, headers, streamDependency, weight, exclusive,
                 padding, endStream);
         return writer.writeHeaders(ctx, streamId, headers, streamDependency, weight,
-                exclusive, padding, endStream, promise);
+                exclusive, padding, endStream);
     }
 
     @Override
     public Future writePriority(ChannelHandlerContext ctx, int streamId,
-                                      int streamDependency, short weight, boolean exclusive, Promise promise) {
+                                      int streamDependency, short weight, boolean exclusive) {
         logger.logPriority(OUTBOUND, ctx, streamId, streamDependency, weight, exclusive);
-        return writer.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
+        return writer.writePriority(ctx, streamId, streamDependency, weight, exclusive);
     }
 
     @Override
     public Future writeRstStream(ChannelHandlerContext ctx,
-                                       int streamId, long errorCode, Promise promise) {
+                                       int streamId, long errorCode) {
         logger.logRstStream(OUTBOUND, ctx, streamId, errorCode);
-        return writer.writeRstStream(ctx, streamId, errorCode, promise);
+        return writer.writeRstStream(ctx, streamId, errorCode);
     }
 
     @Override
     public Future writeSettings(ChannelHandlerContext ctx,
-                                      Http2Settings settings, Promise promise) {
+                                      Http2Settings settings) {
         logger.logSettings(OUTBOUND, ctx, settings);
-        return writer.writeSettings(ctx, settings, promise);
+        return writer.writeSettings(ctx, settings);
     }
 
     @Override
-    public Future writeSettingsAck(ChannelHandlerContext ctx, Promise promise) {
+    public Future writeSettingsAck(ChannelHandlerContext ctx) {
         logger.logSettingsAck(OUTBOUND, ctx);
-        return writer.writeSettingsAck(ctx, promise);
+        return writer.writeSettingsAck(ctx);
     }
 
     @Override
     public Future writePing(ChannelHandlerContext ctx, boolean ack,
-                                  long data, Promise promise) {
+                                  long data) {
         if (ack) {
             logger.logPingAck(OUTBOUND, ctx, data);
         } else {
             logger.logPing(OUTBOUND, ctx, data);
         }
-        return writer.writePing(ctx, ack, data, promise);
+        return writer.writePing(ctx, ack, data);
     }
 
     @Override
     public Future writePushPromise(ChannelHandlerContext ctx, int streamId,
-                                         int promisedStreamId, Http2Headers headers, int padding,
-                                         Promise promise) {
+                                         int promisedStreamId, Http2Headers headers, int padding) {
         logger.logPushPromise(OUTBOUND, ctx, streamId, promisedStreamId, headers, padding);
-        return writer.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise);
+        return writer.writePushPromise(ctx, streamId, promisedStreamId, headers, padding);
     }
 
     @Override
     public Future writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
-            ByteBuf debugData, Promise promise) {
+            ByteBuf debugData) {
         logger.logGoAway(OUTBOUND, ctx, lastStreamId, errorCode, debugData);
-        return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
+        return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData);
     }
 
     @Override
     public Future writeWindowUpdate(ChannelHandlerContext ctx,
-                                          int streamId, int windowSizeIncrement, Promise promise) {
+                                          int streamId, int windowSizeIncrement) {
         logger.logWindowsUpdate(OUTBOUND, ctx, streamId, windowSizeIncrement);
-        return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
+        return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement);
     }
 
     @Override
     public Future writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
-                                   Http2Flags flags, ByteBuf payload, Promise promise) {
+                                   Http2Flags flags, ByteBuf payload) {
         logger.logUnknownFrame(OUTBOUND, ctx, frameType, streamId, flags, payload);
-        return writer.writeFrame(ctx, frameType, streamId, flags, payload, promise);
+        return writer.writeFrame(ctx, frameType, streamId, flags, payload);
     }
 
     @Override
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandler.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandler.java
index 4ce80c31a3..002d57580a 100644
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandler.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/HttpToHttp2ConnectionHandler.java
@@ -106,7 +106,7 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
                 Http2Headers http2Headers = HttpConversionUtil.toHttp2Headers(httpMsg, validateHeaders);
                 endStream = msg instanceof FullHttpMessage && !((FullHttpMessage) msg).content().isReadable();
                 writeHeaders(ctx, encoder, currentStreamId, httpMsg.headers(), http2Headers,
-                        endStream, promiseAggregator);
+                        endStream).cascadeTo(promiseAggregator.newPromise());
             }
 
             if (!endStream && msg instanceof HttpContent) {
@@ -125,12 +125,14 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
                 // Write the data
                 final ByteBuf content = ((HttpContent) msg).content();
                 endStream = isLastContent && trailers.isEmpty();
-                encoder.writeData(ctx, currentStreamId, content, 0, endStream, promiseAggregator.newPromise());
+                encoder.writeData(ctx, currentStreamId, content, 0, endStream)
+                        .cascadeTo(promiseAggregator.newPromise());
                 release = false;
 
                 if (!trailers.isEmpty()) {
                     // Write trailing headers.
-                    writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true, promiseAggregator);
+                    writeHeaders(ctx, encoder, currentStreamId, trailers, http2Trailers, true)
+                            .cascadeTo(promiseAggregator.newPromise());
                 }
             }
         } catch (Throwable t) {
@@ -145,14 +147,13 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
         return promise;
     }
 
-    private static void writeHeaders(ChannelHandlerContext ctx, Http2ConnectionEncoder encoder, int streamId,
-                                     HttpHeaders headers, Http2Headers http2Headers, boolean endStream,
-                                     SimpleChannelPromiseAggregator promiseAggregator) {
+    private static Future writeHeaders(ChannelHandlerContext ctx, Http2ConnectionEncoder encoder, int streamId,
+                                     HttpHeaders headers, Http2Headers http2Headers, boolean endStream) {
         int dependencyId = headers.getInt(
                 HttpConversionUtil.ExtensionHeaderNames.STREAM_DEPENDENCY_ID.text(), 0);
         short weight = headers.getShort(
                 HttpConversionUtil.ExtensionHeaderNames.STREAM_WEIGHT.text(), Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT);
-        encoder.writeHeaders(ctx, streamId, http2Headers, dependencyId, weight, false,
-                0, endStream, promiseAggregator.newPromise());
+        return encoder.writeHeaders(ctx, streamId, http2Headers, dependencyId, weight, false,
+                0, endStream);
     }
 }
diff --git a/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java b/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java
index 9f61b90bb0..689d54aa5b 100644
--- a/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java
+++ b/codec-http2/src/main/java/io/netty/handler/codec/http2/StreamBufferingEncoder.java
@@ -154,40 +154,41 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
 
     @Override
     public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
-                                     int padding, boolean endStream, Promise promise) {
+                                     int padding, boolean endStream) {
         return writeHeaders(ctx, streamId, headers, 0, Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT,
-                false, padding, endStream, promise);
+                false, padding, endStream);
     }
 
     @Override
     public Future writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
                                      int streamDependency, short weight, boolean exclusive,
-                                     int padding, boolean endOfStream, Promise promise) {
+                                     int padding, boolean endOfStream) {
         if (closed) {
-            return promise.setFailure(new Http2ChannelClosedException());
+            return ctx.newFailedFuture(new Http2ChannelClosedException());
         }
         if (isExistingStream(streamId) || canCreateStream()) {
             return super.writeHeaders(ctx, streamId, headers, streamDependency, weight,
-                    exclusive, padding, endOfStream, promise);
+                    exclusive, padding, endOfStream);
         }
         if (goAwayDetail != null) {
-            return promise.setFailure(new Http2GoAwayException(goAwayDetail));
+            return ctx.newFailedFuture(new Http2GoAwayException(goAwayDetail));
         }
         PendingStream pendingStream = pendingStreams.get(streamId);
         if (pendingStream == null) {
             pendingStream = new PendingStream(ctx, streamId);
             pendingStreams.put(streamId, pendingStream);
         }
+        Promise promise = ctx.newPromise();
+
         pendingStream.frames.add(new HeadersFrame(headers, streamDependency, weight, exclusive,
                 padding, endOfStream, promise));
         return promise;
     }
 
     @Override
-    public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
-                                       Promise promise) {
+    public Future writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode) {
         if (isExistingStream(streamId)) {
-            return super.writeRstStream(ctx, streamId, errorCode, promise);
+            return super.writeRstStream(ctx, streamId, errorCode);
         }
         // Since the delegate doesn't know about any buffered streams we have to handle cancellation
         // of the promises and releasing of the ByteBufs here.
@@ -198,27 +199,27 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
             // about the stream anymore and thus there is not point in failing the promises and invoking
             // error handling routines.
             stream.close(null);
-            promise.setSuccess(null);
+            return ctx.newSucceededFuture();
         } else {
-            promise.setFailure(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
+            return ctx.newFailedFuture(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
         }
-        return promise;
     }
 
     @Override
     public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
-                                  int padding, boolean endOfStream, Promise promise) {
+                                  int padding, boolean endOfStream) {
         if (isExistingStream(streamId)) {
-            return super.writeData(ctx, streamId, data, padding, endOfStream, promise);
+            return super.writeData(ctx, streamId, data, padding, endOfStream);
         }
         PendingStream pendingStream = pendingStreams.get(streamId);
         if (pendingStream != null) {
+            Promise promise = ctx.newPromise();
             pendingStream.frames.add(new DataFrame(data, padding, endOfStream, promise));
+            return promise;
         } else {
             ReferenceCountUtil.safeRelease(data);
-            promise.setFailure(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
+            return ctx.newFailedFuture(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
         }
-        return promise;
     }
 
     @Override
@@ -352,7 +353,8 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
 
         @Override
         void send(ChannelHandlerContext ctx, int streamId) {
-            writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endOfStream, promise);
+            writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endOfStream)
+                    .cascadeTo(promise);
         }
     }
 
@@ -376,7 +378,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
 
         @Override
         void send(ChannelHandlerContext ctx, int streamId) {
-            writeData(ctx, streamId, data, padding, endOfStream, promise);
+            writeData(ctx, streamId, data, padding, endOfStream).cascadeTo(promise);
         }
     }
 }
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java
index 249241b243..e6de11691a 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DataCompressionHttp2Test.java
@@ -139,7 +139,7 @@ public class DataCompressionHttp2Test {
                 .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
 
         runInChannel(clientChannel, () -> {
-            clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, true, newPromiseClient());
+            clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, true);
             clientHandler.flush(ctxClient());
         });
         awaitServer();
@@ -157,8 +157,8 @@ public class DataCompressionHttp2Test {
                     .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
 
             runInChannel(clientChannel, () -> {
-                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
-                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
+                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
+                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
                 clientHandler.flush(ctxClient());
             });
             awaitServer();
@@ -178,8 +178,8 @@ public class DataCompressionHttp2Test {
                     .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
 
             runInChannel(clientChannel, () -> {
-                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
-                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
+                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
+                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
                 clientHandler.flush(ctxClient());
             });
             awaitServer();
@@ -201,9 +201,9 @@ public class DataCompressionHttp2Test {
                     .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.GZIP);
 
             runInChannel(clientChannel, () -> {
-                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
-                clientEncoder.writeData(ctxClient(), 3, data1.retain(), 0, false, newPromiseClient());
-                clientEncoder.writeData(ctxClient(), 3, data2.retain(), 0, true, newPromiseClient());
+                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
+                clientEncoder.writeData(ctxClient(), 3, data1.retain(), 0, false);
+                clientEncoder.writeData(ctxClient(), 3, data2.retain(), 0, true);
                 clientHandler.flush(ctxClient());
             });
             awaitServer();
@@ -224,8 +224,8 @@ public class DataCompressionHttp2Test {
                     .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR);
 
             runInChannel(clientChannel, () -> {
-                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
-                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
+                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
+                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
                 clientHandler.flush(ctxClient());
             });
             awaitServer();
@@ -245,8 +245,8 @@ public class DataCompressionHttp2Test {
                     .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.BR);
 
             runInChannel(clientChannel, () -> {
-                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
-                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
+                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
+                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
                 clientHandler.flush(ctxClient());
             });
             awaitServer();
@@ -266,8 +266,8 @@ public class DataCompressionHttp2Test {
                     .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD);
 
             runInChannel(clientChannel, () -> {
-                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
-                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
+                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
+                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
                 clientHandler.flush(ctxClient());
             });
             awaitServer();
@@ -287,8 +287,8 @@ public class DataCompressionHttp2Test {
                     .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.ZSTD);
 
             runInChannel(clientChannel, () -> {
-                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
-                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
+                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
+                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
                 clientHandler.flush(ctxClient());
             });
             awaitServer();
@@ -310,8 +310,8 @@ public class DataCompressionHttp2Test {
                     .set(HttpHeaderNames.CONTENT_ENCODING, HttpHeaderValues.DEFLATE);
 
             runInChannel(clientChannel, () -> {
-                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false, newPromiseClient());
-                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true, newPromiseClient());
+                clientEncoder.writeHeaders(ctxClient(), 3, headers, 0, false);
+                clientEncoder.writeData(ctxClient(), 3, data.retain(), 0, true);
                 clientHandler.flush(ctxClient());
             });
             awaitServer();
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java
index a336e265e8..f19f8d4acc 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionDecoderTest.java
@@ -79,7 +79,6 @@ public class DefaultHttp2ConnectionDecoderTest {
     private static final int STATE_RECV_TRAILERS = 1 << 1;
 
     private Http2ConnectionDecoder decoder;
-    private Promise promise;
 
     @Mock
     private Http2Connection connection;
@@ -130,7 +129,7 @@ public class DefaultHttp2ConnectionDecoderTest {
     public void setup() throws Exception {
         MockitoAnnotations.initMocks(this);
 
-        promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
+        Promise promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
 
         final AtomicInteger headersReceivedState = new AtomicInteger();
         when(channel.isActive()).thenReturn(true);
@@ -209,7 +208,7 @@ public class DefaultHttp2ConnectionDecoderTest {
         decode().onSettingsRead(ctx, new Http2Settings());
         verify(listener).onSettingsRead(eq(ctx), eq(new Http2Settings()));
         assertTrue(decoder.prefaceReceived());
-        verify(encoder).writeSettingsAck(eq(ctx), eq(promise));
+        verify(encoder).writeSettingsAck(eq(ctx));
 
         // Simulate receiving the SETTINGS ACK for the initial settings.
         decode().onSettingsAckRead(ctx);
@@ -793,7 +792,7 @@ public class DefaultHttp2ConnectionDecoderTest {
     @Test
     public void pingReadShouldReplyWithAck() throws Exception {
         decode().onPingRead(ctx, 0L);
-        verify(encoder).writePing(eq(ctx), eq(true), eq(0L), eq(promise));
+        verify(encoder).writePing(eq(ctx), eq(true), eq(0L));
         verify(listener, never()).onPingAckRead(eq(ctx), any(long.class));
     }
 
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java
index 2dd881e345..1667a92755 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionEncoderTest.java
@@ -26,7 +26,6 @@ import io.netty.channel.ChannelPipeline;
 import io.netty.channel.DefaultChannelConfig;
 import io.netty.handler.codec.http.HttpResponseStatus;
 import io.netty.handler.codec.http2.Http2RemoteFlowController.FlowControlled;
-import io.netty.util.concurrent.DefaultPromise;
 import io.netty.util.concurrent.Future;
 import io.netty.util.concurrent.ImmediateEventExecutor;
 import io.netty.util.concurrent.Promise;
@@ -126,25 +125,23 @@ public class DefaultHttp2ConnectionEncoderTest {
         when(channel.unsafe()).thenReturn(unsafe);
         ChannelConfig config = new DefaultChannelConfig(channel);
         when(channel.config()).thenReturn(config);
-        doAnswer(in -> newPromise()
-                .setFailure((Throwable) in.getArgument(0))).when(channel).newFailedFuture(any(Throwable.class));
 
         when(writer.configuration()).thenReturn(writerConfig);
         when(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy);
         when(frameSizePolicy.maxFrameSize()).thenReturn(64);
-        doAnswer((Answer>) in -> ((Promise) in.getArguments()[2])
-                .setSuccess(null)).when(writer).writeSettings(eq(ctx), any(Http2Settings.class), any(Promise.class));
+        doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null))
+                .when(writer).writeSettings(eq(ctx), any(Http2Settings.class));
         doAnswer((Answer>) in -> {
             ((ByteBuf) in.getArguments()[3]).release();
-            return ((Promise) in.getArguments()[4]).setSuccess(null);
-        }).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class), any(Promise.class));
+            return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
+        }).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class));
+
         writtenData = new ArrayList<>();
         writtenPadding = new ArrayList<>();
-        when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
-                any(Promise.class))).then((Answer>) in -> {
+        when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()))
+                .then((Answer>) in -> {
                     // Make sure we only receive stream closure on the last frame and that void promises
                     // are used for all writes except the last one.
-                    Promise promise = (Promise) in.getArguments()[5];
                     if (streamClosed) {
                         fail("Stream already closed");
                     } else {
@@ -156,39 +153,41 @@ public class DefaultHttp2ConnectionEncoderTest {
                     // Release the buffer just as DefaultHttp2FrameWriter does
                     data.release();
                     // Let the promise succeed to trigger listeners.
-                    return promise.setSuccess(null);
+                    return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
                 });
         when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(),
-                anyInt(), anyBoolean(), any(Promise.class)))
+                anyInt(), anyBoolean()))
                 .then((Answer>) invocationOnMock -> {
-                    Promise promise = invocationOnMock.getArgument(8);
                     if (streamClosed) {
                         fail("Stream already closed");
                     } else {
                         streamClosed = (Boolean) invocationOnMock.getArguments()[5];
                     }
-                    return promise.setSuccess(null);
+                    return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
                 });
         when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class),
-                anyInt(), anyBoolean(), any(Promise.class)))
+                anyInt(), anyBoolean()))
                 .then((Answer>) invocationOnMock -> {
-                    Promise promise = invocationOnMock.getArgument(5);
                     if (streamClosed) {
                         fail("Stream already closed");
                     } else {
                         streamClosed = invocationOnMock.getArgument(4);
                     }
-                    return promise.setSuccess(null);
+                    return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
                 });
         payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class);
         doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture());
         when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
         when(ctx.channel()).thenReturn(channel);
-        doAnswer((Answer>) in -> newPromise()).when(ctx).newPromise();
-        doAnswer((Answer>) in -> newSucceededFuture()).when(ctx).newSucceededFuture();
+        doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newPromise()).when(ctx).newPromise();
+        doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null))
+                .when(ctx).newSucceededFuture();
+        doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newFailedFuture(in.getArgument(0)))
+                .when(ctx).newFailedFuture(any(Throwable.class));
         when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden"));
         when(channel.alloc()).thenReturn(PooledByteBufAllocator.DEFAULT);
-
+        doAnswer((Answer>) in -> ImmediateEventExecutor.INSTANCE.newFailedFuture(in.getArgument(0)))
+                .when(channel).newFailedFuture(any(Throwable.class));
         // Use a server-side connection so we can test server push.
         connection = new DefaultHttp2Connection(true);
         connection.remote().flowController(remoteFlow);
@@ -210,8 +209,7 @@ public class DefaultHttp2ConnectionEncoderTest {
     private void dataWriteShouldSignalThatFrameWasConsumedOnError0(boolean endOfStream) throws Exception {
         createStream(STREAM_ID, false);
         final ByteBuf data = dummyData();
-        Promise p = newPromise();
-        encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream, p);
+        Future f = encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream);
 
         FlowControlled controlled = payloadCaptor.getValue();
         assertEquals(8, controlled.size());
@@ -224,21 +222,20 @@ public class DefaultHttp2ConnectionEncoderTest {
         assertEquals(0, controlled.size());
         assertEquals("abcd", writtenData.get(0));
         assertEquals(0, data.refCnt());
-        assertSame(error, p.cause());
+        assertSame(error, f.cause());
     }
 
     @Test
     public void dataWriteShouldSucceed() throws Exception {
         createStream(STREAM_ID, false);
         final ByteBuf data = dummyData();
-        Promise p = newPromise();
-        encoder.writeData(ctx, STREAM_ID, data, 0, true, p);
+        Future f = encoder.writeData(ctx, STREAM_ID, data, 0, true);
         assertEquals(8, payloadCaptor.getValue().size());
         payloadCaptor.getValue().write(ctx, 8);
         assertEquals(0, payloadCaptor.getValue().size());
         assertEquals("abcdefgh", writtenData.get(0));
         assertEquals(0, data.refCnt());
-        assertTrue(p.isSuccess());
+        assertTrue(f.isSuccess());
     }
 
     @Test
@@ -246,35 +243,33 @@ public class DefaultHttp2ConnectionEncoderTest {
         createStream(STREAM_ID, false);
         final ByteBuf data = dummyData().retain();
 
-        Promise promise1 = newPromise();
-        encoder.writeData(ctx, STREAM_ID, data, 0, true, promise1);
-        Promise promise2 = newPromise();
-        encoder.writeData(ctx, STREAM_ID, data, 0, true, promise2);
+        Future future1 = encoder.writeData(ctx, STREAM_ID, data, 0, true);
+        Future future2 = encoder.writeData(ctx, STREAM_ID, data, 0, true);
 
         // Now merge the two payloads.
         List capturedWrites = payloadCaptor.getAllValues();
         FlowControlled mergedPayload = capturedWrites.get(0);
         mergedPayload.merge(ctx, capturedWrites.get(1));
         assertEquals(16, mergedPayload.size());
-        assertFalse(promise1.isDone());
-        assertFalse(promise2.isDone());
+        assertFalse(future1.isDone());
+        assertFalse(future2.isDone());
 
         // Write the merged payloads and verify it was written correctly.
         mergedPayload.write(ctx, 16);
         assertEquals(0, mergedPayload.size());
         assertEquals("abcdefghabcdefgh", writtenData.get(0));
         assertEquals(0, data.refCnt());
-        assertTrue(promise1.isSuccess());
-        assertTrue(promise2.isSuccess());
+        assertTrue(future1.isSuccess());
+        assertTrue(future2.isSuccess());
     }
 
     @Test
     public void dataFramesDontMergeWithHeaders() throws Exception {
         createStream(STREAM_ID, false);
         final ByteBuf data = dummyData().retain();
-        encoder.writeData(ctx, STREAM_ID, data, 0, false, newPromise());
+        encoder.writeData(ctx, STREAM_ID, data, 0, false);
         when(remoteFlow.hasFlowControlled(any(Http2Stream.class))).thenReturn(true);
-        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, newPromise());
+        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
         List capturedWrites = payloadCaptor.getAllValues();
         assertFalse(capturedWrites.get(0).merge(ctx, capturedWrites.get(1)));
     }
@@ -289,8 +284,7 @@ public class DefaultHttp2ConnectionEncoderTest {
     private void assertSplitPaddingOnEmptyBuffer(ByteBuf data) throws Exception {
         createStream(STREAM_ID, false);
         when(frameSizePolicy.maxFrameSize()).thenReturn(5);
-        Promise p = newPromise();
-        encoder.writeData(ctx, STREAM_ID, data, 10, true, p);
+        Future f = encoder.writeData(ctx, STREAM_ID, data, 10, true);
         assertEquals(10, payloadCaptor.getValue().size());
         payloadCaptor.getValue().write(ctx, 10);
         // writer was called 2 times
@@ -298,18 +292,17 @@ public class DefaultHttp2ConnectionEncoderTest {
         assertEquals("", writtenData.get(0));
         assertEquals(10, (int) writtenPadding.get(0));
         assertEquals(0, data.refCnt());
-        assertTrue(p.isSuccess());
+        assertTrue(f.isSuccess());
     }
 
     @Test
     public void headersWriteForUnknownStreamShouldCreateStream() throws Exception {
         writeAllFlowControlledFrames();
         final int streamId = 6;
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
+        Future f = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
         verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
-                eq(false), eq(promise));
-        assertTrue(promise.isSuccess());
+                eq(false));
+        assertTrue(f.isSuccess());
     }
 
     @Test
@@ -318,46 +311,41 @@ public class DefaultHttp2ConnectionEncoderTest {
         Http2Stream parent = createStream(STREAM_ID, false);
         reservePushStream(PUSH_STREAM_ID, parent);
 
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
+        encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
         assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state());
         verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise));
+                eq(0), eq(false));
     }
 
     @Test
     public void trailersDoNotEndStreamThrows() {
         writeAllFlowControlledFrames();
         final int streamId = 6;
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
+        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
 
-        Promise promise2 = newPromise();
-        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
+        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
         assertTrue(future.isDone());
         assertFalse(future.isSuccess());
 
         verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise));
+                eq(0), eq(false));
     }
 
     @Test
     public void trailersDoNotEndStreamWithDataThrows() {
         writeAllFlowControlledFrames();
         final int streamId = 6;
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
+        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
 
         Http2Stream stream = connection.stream(streamId);
         when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
 
-        Promise promise2 = newPromise();
-        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
+        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
         assertTrue(future.isDone());
         assertFalse(future.isSuccess());
 
         verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise));
+                eq(0), eq(false));
     }
 
     @Test
@@ -373,20 +361,17 @@ public class DefaultHttp2ConnectionEncoderTest {
     private void tooManyHeadersThrows(boolean eos) {
         writeAllFlowControlledFrames();
         final int streamId = 6;
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
-        Promise promise2 = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
+        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
+        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true);
 
-        Promise promise3 = newPromise();
-        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
+        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
         assertTrue(future.isDone());
         assertFalse(future.isSuccess());
 
         verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise));
+                eq(0), eq(false));
         verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(true), eq(promise2));
+                eq(0), eq(true));
     }
 
     @Test
@@ -414,23 +399,21 @@ public class DefaultHttp2ConnectionEncoderTest {
         final int streamId = 6;
         Http2Headers infoHeaders = informationalHeaders();
         for (int i = 0; i < infoHeaderCount; ++i) {
-            encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false, newPromise());
+            encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false);
         }
-        Promise promise2 = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
+        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
 
-        Promise promise3 = newPromise();
-        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
+        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
         assertTrue(future.isDone());
         assertEquals(eos, future.isSuccess());
 
         verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
-                eq(0), eq(false), any(Promise.class));
+                eq(0), eq(false));
         verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise2));
+                eq(0), eq(false));
         if (eos) {
             verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                    eq(0), eq(true), eq(promise3));
+                    eq(0), eq(true));
         }
     }
 
@@ -453,24 +436,21 @@ public class DefaultHttp2ConnectionEncoderTest {
     private void tooManyHeadersWithDataThrows(boolean eos) {
         writeAllFlowControlledFrames();
         final int streamId = 6;
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
+        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
 
         Http2Stream stream = connection.stream(streamId);
         when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
 
-        Promise promise2 = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
+        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true);
 
-        Promise promise3 = newPromise();
-        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
+        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
         assertTrue(future.isDone());
         assertFalse(future.isSuccess());
 
         verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise));
+                eq(0), eq(false));
         verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(true), eq(promise2));
+                eq(0), eq(true));
     }
 
     @Test
@@ -498,27 +478,25 @@ public class DefaultHttp2ConnectionEncoderTest {
         final int streamId = 6;
         Http2Headers infoHeaders = informationalHeaders();
         for (int i = 0; i < infoHeaderCount; ++i) {
-            encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false, newPromise());
+            encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false);
         }
 
         Http2Stream stream = connection.stream(streamId);
         when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
 
-        Promise promise2 = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
+        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
 
-        Promise promise3 = newPromise();
-        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
+        Future future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos);
         assertTrue(future.isDone());
         assertEquals(eos, future.isSuccess());
 
         verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
-                eq(0), eq(false), any(Promise.class));
+                eq(0), eq(false));
         verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise2));
+                eq(0), eq(false));
         if (eos) {
             verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                    eq(0), eq(true), eq(promise3));
+                    eq(0), eq(true));
         }
     }
 
@@ -526,8 +504,7 @@ public class DefaultHttp2ConnectionEncoderTest {
     public void pushPromiseWriteAfterGoAwayReceivedShouldFail() throws Exception {
         createStream(STREAM_ID, false);
         goAwayReceived(0);
-        Future future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0,
-                newPromise());
+        Future future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0);
         assertTrue(future.isDone());
         assertFalse(future.isSuccess());
     }
@@ -535,42 +512,38 @@ public class DefaultHttp2ConnectionEncoderTest {
     @Test
     public void pushPromiseWriteShouldReserveStream() throws Exception {
         createStream(STREAM_ID, false);
-        Promise promise = newPromise();
-        encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, promise);
+        encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0);
         assertEquals(RESERVED_LOCAL, stream(PUSH_STREAM_ID).state());
         verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID),
-                eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(promise));
+                eq(EmptyHttp2Headers.INSTANCE), eq(0));
     }
 
     @Test
     public void priorityWriteAfterGoAwayShouldSucceed() throws Exception {
         createStream(STREAM_ID, false);
         goAwayReceived(Integer.MAX_VALUE);
-        Promise promise = newPromise();
-        encoder.writePriority(ctx, STREAM_ID, 0, (short) 255, true, promise);
-        verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true), eq(promise));
+        encoder.writePriority(ctx, STREAM_ID, 0, (short) 255, true);
+        verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true));
     }
 
     @Test
     public void priorityWriteShouldSetPriorityForStream() throws Exception {
-        Promise promise = newPromise();
         short weight = 255;
-        encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
+        encoder.writePriority(ctx, STREAM_ID, 0, weight, true);
 
         // Verify that this did NOT create a stream object.
         Http2Stream stream = stream(STREAM_ID);
         assertNull(stream);
 
-        verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true), eq(promise));
+        verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true));
     }
 
     @Test
     public void priorityWriteOnPreviouslyExistingStreamShouldSucceed() throws Exception {
         createStream(STREAM_ID, false).close();
-        Promise promise = newPromise();
         short weight = 255;
-        encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
-        verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true), eq(promise));
+        encoder.writePriority(ctx, STREAM_ID, 0, weight, true);
+        verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true));
     }
 
     @Test
@@ -579,53 +552,47 @@ public class DefaultHttp2ConnectionEncoderTest {
         createStream(STREAM_ID, false);
         createStream(parentStreamId, false).close();
 
-        Promise promise = newPromise();
         short weight = 255;
-        encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true, promise);
-        verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true), eq(promise));
+        encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true);
+        verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true));
     }
 
     @Test
     public void rstStreamWriteForUnknownStreamShouldIgnore() throws Exception {
-        Promise promise = newPromise();
-        encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code(), promise);
-        verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong(), eq(promise));
+        encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code());
+        verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong());
     }
 
     @Test
     public void rstStreamShouldCloseStream() throws Exception {
         // Create the stream and send headers.
         writeAllFlowControlledFrames();
-        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, newPromise());
+        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
 
         // Now verify that a stream reset is performed.
         stream(STREAM_ID);
-        Promise promise = newPromise();
-        encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
-        verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong(), eq(promise));
+        encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
+        verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong());
     }
 
     @Test
     public void pingWriteAfterGoAwayShouldSucceed() throws Exception {
-        Promise promise = newPromise();
         goAwayReceived(0);
-        encoder.writePing(ctx, false, 0L, promise);
-        verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
+        encoder.writePing(ctx, false, 0L);
+        verify(writer).writePing(eq(ctx), eq(false), eq(0L));
     }
 
     @Test
     public void pingWriteShouldSucceed() throws Exception {
-        Promise promise = newPromise();
-        encoder.writePing(ctx, false, 0L, promise);
-        verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
+        encoder.writePing(ctx, false, 0L);
+        verify(writer).writePing(eq(ctx), eq(false), eq(0L));
     }
 
     @Test
     public void settingsWriteAfterGoAwayShouldSucceed() throws Exception {
         goAwayReceived(0);
-        Promise promise = newPromise();
-        encoder.writeSettings(ctx, new Http2Settings(), promise);
-        verify(writer).writeSettings(eq(ctx), any(Http2Settings.class), eq(promise));
+        encoder.writeSettings(ctx, new Http2Settings());
+        verify(writer).writeSettings(eq(ctx), any(Http2Settings.class));
     }
 
     @Test
@@ -635,9 +602,8 @@ public class DefaultHttp2ConnectionEncoderTest {
         settings.maxConcurrentStreams(1000);
         settings.headerTableSize(2000);
 
-        Promise promise = newPromise();
-        encoder.writeSettings(ctx, settings, promise);
-        verify(writer).writeSettings(eq(ctx), eq(settings), eq(promise));
+        encoder.writeSettings(ctx, settings);
+        verify(writer).writeSettings(eq(ctx), eq(settings));
     }
 
     @Test
@@ -646,11 +612,10 @@ public class DefaultHttp2ConnectionEncoderTest {
 
         Http2Stream stream = createStream(STREAM_ID, false);
         ByteBuf data = dummyData();
-        Promise promise = newPromise();
-        encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true, promise);
-        assertTrue(promise.isSuccess());
+        Future f = encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true);
+        assertTrue(f.isSuccess());
         verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
-        verify(lifecycleManager).closeStreamLocal(stream, promise);
+        verify(lifecycleManager).closeStreamLocal(eq(stream), eq(f));
         assertEquals(data.toString(UTF_8), writtenData.get(0));
         data.release();
     }
@@ -659,11 +624,10 @@ public class DefaultHttp2ConnectionEncoderTest {
     public void headersWriteShouldHalfCloseStream() throws Exception {
         writeAllFlowControlledFrames();
         createStream(STREAM_ID, false);
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
+        Future f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
 
-        assertTrue(promise.isSuccess());
-        verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise));
+        assertTrue(f.isSuccess());
+        verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(f));
     }
 
     @Test
@@ -671,64 +635,54 @@ public class DefaultHttp2ConnectionEncoderTest {
         writeAllFlowControlledFrames();
         Http2Stream parent = createStream(STREAM_ID, false);
         Http2Stream stream = reservePushStream(PUSH_STREAM_ID, parent);
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
+        Future f = encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
         assertEquals(HALF_CLOSED_REMOTE, stream.state());
-        assertTrue(promise.isSuccess());
-        verify(lifecycleManager).closeStreamLocal(eq(stream), eq(promise));
+        assertTrue(f.isSuccess());
+        verify(lifecycleManager).closeStreamLocal(eq(stream), eq(f));
     }
 
     @Test
     public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception {
-        final Promise 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(true), eq(promise)))
-            .thenAnswer((Answer>) invocation -> {
-                promise.setFailure(ex);
-                return promise;
-            });
+        when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true)))
+            .thenReturn(ImmediateEventExecutor.INSTANCE.newFailedFuture(ex));
 
         writeAllFlowControlledFrames();
         Http2Stream stream = createStream(STREAM_ID, false);
-        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
+        Future f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
 
-        assertTrue(promise.isDone());
-        assertFalse(promise.isSuccess());
+        assertTrue(f.isDone());
+        assertFalse(f.isSuccess());
         assertFalse(stream.isHeadersSent());
         InOrder inOrder = inOrder(lifecycleManager);
         inOrder.verify(lifecycleManager).onError(eq(ctx), eq(true), eq(ex));
-        inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise));
+        inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(f));
     }
 
     @Test
     public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception {
-        final Promise 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(true), eq(promise)))
-            .thenAnswer((Answer>) invocation -> {
-                promise.setFailure(ex);
-                return promise;
-            });
+        when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true)))
+            .thenReturn(ImmediateEventExecutor.INSTANCE.newFailedFuture(ex));
 
         writeAllFlowControlledFrames();
-        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
+        Future f = encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true);
 
-        assertTrue(promise.isDone());
-        assertFalse(promise.isSuccess());
+        assertTrue(f.isDone());
+        assertFalse(f.isSuccess());
         assertFalse(stream(STREAM_ID).isHeadersSent());
         InOrder inOrder = inOrder(lifecycleManager);
         inOrder.verify(lifecycleManager).onError(eq(ctx), eq(true), eq(ex));
-        inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(promise));
+        inOrder.verify(lifecycleManager).closeStreamLocal(eq(stream(STREAM_ID)), eq(f));
     }
 
     @Test
     public void encoderDelegatesGoAwayToLifeCycleManager() {
-        Promise promise = newPromise();
-        encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null, promise);
+        encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null);
         verify(lifecycleManager).goAway(eq(ctx), eq(STREAM_ID), eq(Http2Error.INTERNAL_ERROR.code()),
-                eq((ByteBuf) null), eq(promise));
+                eq((ByteBuf) null));
         verifyNoMoreInteractions(writer);
     }
 
@@ -736,11 +690,10 @@ public class DefaultHttp2ConnectionEncoderTest {
     public void dataWriteToClosedStreamShouldFail() throws Exception {
         createStream(STREAM_ID, false).close();
         ByteBuf data = mock(ByteBuf.class);
-        Promise promise = newPromise();
-        encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
-        assertTrue(promise.isDone());
-        assertFalse(promise.isSuccess());
-        assertThat(promise.cause(), instanceOf(IllegalArgumentException.class));
+        Future f = encoder.writeData(ctx, STREAM_ID, data, 0, false);
+        assertTrue(f.isDone());
+        assertFalse(f.isSuccess());
+        assertThat(f.cause(), instanceOf(IllegalArgumentException.class));
         verify(data).release();
     }
 
@@ -748,11 +701,10 @@ public class DefaultHttp2ConnectionEncoderTest {
     public void dataWriteToHalfClosedLocalStreamShouldFail() throws Exception {
         createStream(STREAM_ID, true);
         ByteBuf data = mock(ByteBuf.class);
-        Promise promise = newPromise();
-        encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
-        assertTrue(promise.isDone());
-        assertFalse(promise.isSuccess());
-        assertThat(promise.cause(), instanceOf(IllegalStateException.class));
+        Future f = encoder.writeData(ctx, STREAM_ID, data, 0, false);
+        assertTrue(f.isDone());
+        assertFalse(f.isSuccess());
+        assertThat(f.cause(), instanceOf(IllegalStateException.class));
         verify(data).release();
     }
 
@@ -761,7 +713,7 @@ public class DefaultHttp2ConnectionEncoderTest {
         Http2Stream stream = createStream(STREAM_ID, false);
         connection.goAwaySent(0, 0, EMPTY_BUFFER);
         ByteBuf data = mock(ByteBuf.class);
-        encoder.writeData(ctx, STREAM_ID, data, 0, false, newPromise());
+        encoder.writeData(ctx, STREAM_ID, data, 0, false);
         verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
     }
 
@@ -770,10 +722,9 @@ public class DefaultHttp2ConnectionEncoderTest {
         writeAllFlowControlledFrames();
         createStream(STREAM_ID, false);
         goAwaySent(0);
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
+        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
         verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise));
+                eq(0), eq(false));
     }
 
     @Test
@@ -781,7 +732,7 @@ public class DefaultHttp2ConnectionEncoderTest {
         Http2Stream stream = createStream(STREAM_ID, false);
         goAwayReceived(STREAM_ID);
         ByteBuf data = mock(ByteBuf.class);
-        encoder.writeData(ctx, STREAM_ID, data, 0, false, newPromise());
+        encoder.writeData(ctx, STREAM_ID, data, 0, false);
         verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
     }
 
@@ -789,31 +740,28 @@ public class DefaultHttp2ConnectionEncoderTest {
     public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception {
         writeAllFlowControlledFrames();
         goAwayReceived(STREAM_ID);
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
+        encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false);
         verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise));
+                eq(0), eq(false));
     }
 
     @Test
     public void headersWithNoPriority() {
         writeAllFlowControlledFrames();
         final int streamId = 6;
-        Promise promise = newPromise();
-        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
+        encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false);
         verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
-                eq(0), eq(false), eq(promise));
+                eq(0), eq(false));
     }
 
     @Test
     public void headersWithPriority() {
         writeAllFlowControlledFrames();
         final int streamId = 6;
-        Promise promise = newPromise();
         encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 10, DEFAULT_PRIORITY_WEIGHT,
-                true, 1, false, promise);
+                true, 1, false);
         verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(10),
-                eq(DEFAULT_PRIORITY_WEIGHT), eq(true), eq(1), eq(false), eq(promise));
+                eq(DEFAULT_PRIORITY_WEIGHT), eq(true), eq(1), eq(false));
     }
 
     private void writeAllFlowControlledFrames() {
@@ -845,14 +793,6 @@ public class DefaultHttp2ConnectionEncoderTest {
         connection.goAwaySent(lastStreamId, 0, EMPTY_BUFFER);
     }
 
-    private static Promise newPromise() {
-        return new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
-    }
-
-    private static Future newSucceededFuture() {
-        return newPromise().setSuccess(null);
-    }
-
     private static ByteBuf dummyData() {
         // The buffer is purposely 8 bytes so it will even work for a ping frame.
         return wrappedBuffer("abcdefgh".getBytes(UTF_8));
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java
index c6e5122b81..126a2e9faf 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2ConnectionTest.java
@@ -22,8 +22,6 @@ import io.netty.channel.MultithreadEventLoopGroup;
 import io.netty.channel.local.LocalHandler;
 import io.netty.handler.codec.http2.Http2Connection.Endpoint;
 import io.netty.handler.codec.http2.Http2Stream.State;
-import io.netty.util.concurrent.Future;
-import io.netty.util.concurrent.FutureListener;
 import io.netty.util.concurrent.Promise;
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
@@ -157,10 +155,9 @@ public class DefaultHttp2ConnectionTest {
         final Promise promise = group.next().newPromise();
         final CountDownLatch latch = new CountDownLatch(client.numActiveStreams());
         client.forEachActiveStream(stream -> {
-            client.close(promise).addListener((FutureListener) future -> {
-                assertTrue(promise.isDone());
+            client.close(promise.addListener(future -> {
                 latch.countDown();
-            });
+            }));
             return true;
         });
         assertTrue(latch.await(5, TimeUnit.SECONDS));
@@ -186,10 +183,9 @@ public class DefaultHttp2ConnectionTest {
                 return true;
             });
         } catch (Http2Exception ignored) {
-            client.close(promise).addListener((FutureListener) future -> {
-                assertTrue(promise.isDone());
+            client.close(promise.addListener(future -> {
                 latch.countDown();
-            });
+            }));
         }
         assertTrue(latch.await(5, TimeUnit.SECONDS));
     }
@@ -636,10 +632,9 @@ public class DefaultHttp2ConnectionTest {
     private void testRemoveAllStreams() throws InterruptedException {
         final CountDownLatch latch = new CountDownLatch(1);
         final Promise promise = group.next().newPromise();
-        client.close(promise).addListener((FutureListener) future -> {
-            assertTrue(promise.isDone());
+        client.close(promise.addListener(future -> {
             latch.countDown();
-        });
+        }));
         assertTrue(latch.await(5, TimeUnit.SECONDS));
     }
 
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriterTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriterTest.java
index 61824dc483..259e1bc451 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriterTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2FrameWriterTest.java
@@ -20,10 +20,8 @@ import io.netty.buffer.UnpooledByteBufAllocator;
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.util.ReferenceCountUtil;
-import io.netty.util.concurrent.DefaultPromise;
 import io.netty.util.concurrent.Future;
 import io.netty.util.concurrent.ImmediateEventExecutor;
-import io.netty.util.concurrent.Promise;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -50,8 +48,6 @@ public class DefaultHttp2FrameWriterTest {
 
     private ByteBuf expectedOutbound;
 
-    private Promise promise;
-
     private Http2HeadersEncoder http2HeadersEncoder;
 
     @Mock
@@ -76,8 +72,6 @@ public class DefaultHttp2FrameWriterTest {
 
         expectedOutbound = Unpooled.EMPTY_BUFFER;
 
-        promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
-
         Answer answer = var1 -> {
             Object msg = var1.getArgument(0);
             if (msg instanceof ByteBuf) {
@@ -90,6 +84,7 @@ public class DefaultHttp2FrameWriterTest {
         when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
         when(ctx.channel()).thenReturn(channel);
         when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE);
+        when(ctx.newPromise()).thenReturn(ImmediateEventExecutor.INSTANCE.newPromise());
     }
 
     @AfterEach
@@ -105,7 +100,7 @@ public class DefaultHttp2FrameWriterTest {
         Http2Headers headers = new DefaultHttp2Headers()
                 .method("GET").path("/").authority("foo.com").scheme("https");
 
-        frameWriter.writeHeaders(ctx, streamId, headers, 0, true, promise);
+        frameWriter.writeHeaders(ctx, streamId, headers, 0, true);
 
         byte[] expectedPayload = headerPayload(streamId, headers);
         byte[] expectedFrameBytes = {
@@ -124,7 +119,7 @@ public class DefaultHttp2FrameWriterTest {
         Http2Headers headers = new DefaultHttp2Headers()
                 .method("GET").path("/").authority("foo.com").scheme("https");
 
-        frameWriter.writeHeaders(ctx, streamId, headers, 5, true, promise);
+        frameWriter.writeHeaders(ctx, streamId, headers, 5, true);
 
         byte[] expectedPayload = headerPayload(streamId, headers, (byte) 4);
         byte[] expectedFrameBytes = {
@@ -143,7 +138,7 @@ public class DefaultHttp2FrameWriterTest {
         Http2Headers headers = new DefaultHttp2Headers()
                 .method("GET").path("/").authority("foo.com").scheme("https");
 
-        frameWriter.writeHeaders(ctx, streamId, headers, 0, false, promise);
+        frameWriter.writeHeaders(ctx, streamId, headers, 0, false);
 
         byte[] expectedPayload = headerPayload(streamId, headers);
         byte[] expectedFrameBytes = {
@@ -170,7 +165,7 @@ public class DefaultHttp2FrameWriterTest {
         http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE);
         frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE);
         frameWriter.maxFrameSize(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
-        frameWriter.writeHeaders(ctx, streamId, headers, 0, true, promise);
+        frameWriter.writeHeaders(ctx, streamId, headers, 0, true);
 
         byte[] expectedPayload = headerPayload(streamId, headers);
 
@@ -211,7 +206,7 @@ public class DefaultHttp2FrameWriterTest {
         http2HeadersEncoder.configuration().maxHeaderListSize(Integer.MAX_VALUE);
         frameWriter.headersConfiguration().maxHeaderListSize(Integer.MAX_VALUE);
         frameWriter.maxFrameSize(Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
-        frameWriter.writeHeaders(ctx, streamId, headers, 5, true, promise);
+        frameWriter.writeHeaders(ctx, streamId, headers, 5, true);
 
         byte[] expectedPayload = buildLargeHeaderPayload(streamId, headers, (byte) 4,
                 Http2CodecUtil.MAX_FRAME_SIZE_LOWER_BOUND);
@@ -245,7 +240,7 @@ public class DefaultHttp2FrameWriterTest {
 
     @Test
     public void writeFrameZeroPayload() throws Exception {
-        frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), Unpooled.EMPTY_BUFFER, promise);
+        frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), Unpooled.EMPTY_BUFFER);
 
         byte[] expectedFrameBytes = {
                 (byte) 0x00, (byte) 0x00, (byte) 0x00, // payload length
@@ -264,7 +259,7 @@ public class DefaultHttp2FrameWriterTest {
 
         // will auto release after frameWriter.writeFrame succeed
         ByteBuf payloadByteBuf = Unpooled.wrappedBuffer(payload);
-        frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), payloadByteBuf, promise);
+        frameWriter.writeFrame(ctx, (byte) 0xf, 0, new Http2Flags(), payloadByteBuf);
 
         byte[] expectedFrameHeaderBytes = {
                 (byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length
@@ -279,7 +274,7 @@ public class DefaultHttp2FrameWriterTest {
     @Test
     public void writePriority() {
         frameWriter.writePriority(
-            ctx, /* streamId= */ 1, /* dependencyId= */ 2, /* weight= */ (short) 256, /* exclusive= */ true, promise);
+            ctx, /* streamId= */ 1, /* dependencyId= */ 2, /* weight= */ (short) 256, /* exclusive= */ true);
 
         expectedOutbound = Unpooled.copiedBuffer(new byte[] {
                 (byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5
@@ -295,7 +290,7 @@ public class DefaultHttp2FrameWriterTest {
     @Test
     public void writePriorityDefaults() {
         frameWriter.writePriority(
-            ctx, /* streamId= */ 1, /* dependencyId= */ 0, /* weight= */ (short) 16, /* exclusive= */ false, promise);
+            ctx, /* streamId= */ 1, /* dependencyId= */ 0, /* weight= */ (short) 16, /* exclusive= */ false);
 
         expectedOutbound = Unpooled.copiedBuffer(new byte[] {
                 (byte) 0x00, (byte) 0x00, (byte) 0x05, // payload length = 5
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowControllerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowControllerTest.java
index 397084d4d8..0573aab8fa 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowControllerTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/DefaultHttp2LocalFlowControllerTest.java
@@ -417,17 +417,16 @@ public class DefaultHttp2LocalFlowControllerTest {
     }
 
     private void verifyWindowUpdateSent(int streamId, int windowSizeIncrement) {
-        verify(frameWriter).writeWindowUpdate(eq(ctx), eq(streamId), eq(windowSizeIncrement), eq(promise));
+        verify(frameWriter).writeWindowUpdate(eq(ctx), eq(streamId), eq(windowSizeIncrement));
     }
 
     private void verifyWindowUpdateNotSent(int streamId) {
-        verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt(), eq(promise));
+        verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt());
     }
 
     @SuppressWarnings("unchecked")
     private void verifyWindowUpdateNotSent() {
-        verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
-                any(Promise.class));
+        verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt());
     }
 
     private int window(int streamId) {
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java
index de40d56b04..26885a8db2 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionHandlerTest.java
@@ -55,8 +55,10 @@ import static io.netty.handler.codec.http2.Http2Stream.State.IDLE;
 import static io.netty.util.CharsetUtil.US_ASCII;
 import static io.netty.util.CharsetUtil.UTF_8;
 import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.Mockito.any;
@@ -157,7 +159,7 @@ public class Http2ConnectionHandlerTest {
             buf.release();
             return future;
         }).when(frameWriter).writeGoAway(
-                any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class), any(Promise.class));
+                any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class));
         doAnswer((Answer>) invocation -> {
             Object o = invocation.getArguments()[0];
             if (o instanceof FutureListener) {
@@ -167,6 +169,7 @@ public class Http2ConnectionHandlerTest {
         }).when(future).addListener(any(FutureListener.class));
         when(future.cause()).thenReturn(fakeException);
         when(channel.isActive()).thenReturn(true);
+        when(future.isFailed()).thenReturn(true);
         when(channel.pipeline()).thenReturn(pipeline);
         when(connection.remote()).thenReturn(remote);
         when(remote.flowController()).thenReturn(remoteFlowController);
@@ -185,10 +188,13 @@ public class Http2ConnectionHandlerTest {
         when(connection.goAwaySent(anyInt(), anyLong(), any(ByteBuf.class))).thenReturn(true);
         when(stream.open(anyBoolean())).thenReturn(stream);
         when(encoder.writeSettings(any(ChannelHandlerContext.class),
-                any(Http2Settings.class), eq(promise))).thenReturn(future);
+                any(Http2Settings.class))).thenReturn(future);
         when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
         when(ctx.channel()).thenReturn(channel);
-        when(ctx.newSucceededFuture()).thenReturn(future);
+        when(ctx.newFailedFuture(any(Throwable.class)))
+                .thenAnswer(invocationOnMock ->
+                        DefaultPromise.newFailedPromise(executor, invocationOnMock.getArgument(0)));
+        when(ctx.newSucceededFuture()).thenReturn(DefaultPromise.newSuccessfulPromise(executor, null));
         when(ctx.newPromise()).thenReturn(promise);
         when(ctx.write(any())).thenReturn(future);
         when(ctx.executor()).thenReturn(executor);
@@ -254,7 +260,7 @@ public class Http2ConnectionHandlerTest {
         final Answer verifier = in -> {
             assertEquals(in.getArgument(0), evt);  // sanity check...
             verify(ctx).write(eq(connectionPrefaceBuf()));
-            verify(encoder).writeSettings(eq(ctx), any(Http2Settings.class), any(Promise.class));
+            verify(encoder).writeSettings(eq(ctx), any(Http2Settings.class));
             verified.set(true);
             return null;
         };
@@ -292,7 +298,7 @@ public class Http2ConnectionHandlerTest {
         handler.channelRead(ctx, copiedBuffer("BAD_PREFACE", UTF_8));
         ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class);
         verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class),
-                eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture(), eq(promise));
+                eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture());
         assertEquals(0, captor.getValue().refCnt());
     }
 
@@ -303,7 +309,7 @@ public class Http2ConnectionHandlerTest {
         handler.channelRead(ctx, copiedBuffer("GET /path HTTP/1.1", US_ASCII));
         ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class);
         verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(Integer.MAX_VALUE),
-                eq(PROTOCOL_ERROR.code()), captor.capture(), eq(promise));
+                eq(PROTOCOL_ERROR.code()), captor.capture());
         assertEquals(0, captor.getValue().refCnt());
         assertTrue(goAwayDebugCap.contains("/path"));
     }
@@ -319,7 +325,7 @@ public class Http2ConnectionHandlerTest {
         handler.channelRead(ctx, buf);
         ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class);
         verify(frameWriter, atLeastOnce()).writeGoAway(any(ChannelHandlerContext.class),
-                eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture(), eq(promise));
+                eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()), captor.capture());
         assertEquals(0, captor.getValue().refCnt());
     }
 
@@ -373,7 +379,7 @@ public class Http2ConnectionHandlerTest {
         handler.exceptionCaught(ctx, e);
         ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class);
         verify(frameWriter).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(PROTOCOL_ERROR.code()),
-                captor.capture(), eq(promise));
+                captor.capture());
         captor.getValue().release();
     }
 
@@ -389,16 +395,16 @@ public class Http2ConnectionHandlerTest {
         when(stream.isHeadersSent()).thenReturn(false);
         when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
         when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
-                eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
+                eq(PROTOCOL_ERROR.code()))).thenReturn(future);
 
         handler.exceptionCaught(ctx, e);
 
         ArgumentCaptor captor = ArgumentCaptor.forClass(Http2Headers.class);
         verify(encoder).writeHeaders(eq(ctx), eq(STREAM_ID),
-                captor.capture(), eq(padding), eq(true), eq(promise));
+                captor.capture(), eq(padding), eq(true));
         Http2Headers headers = captor.getValue();
         assertEquals(HttpResponseStatus.REQUEST_HEADER_FIELDS_TOO_LARGE.codeAsText(), headers.status());
-        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
+        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
     }
 
     @Test
@@ -413,13 +419,13 @@ public class Http2ConnectionHandlerTest {
         when(stream.isHeadersSent()).thenReturn(false);
         when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
         when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
-            eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
+            eq(PROTOCOL_ERROR.code()))).thenReturn(future);
 
         handler.exceptionCaught(ctx, e);
 
         verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
-            any(Http2Headers.class), eq(padding), eq(true), eq(promise));
-        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
+            any(Http2Headers.class), eq(padding), eq(true));
+        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
     }
 
     @Test
@@ -434,13 +440,13 @@ public class Http2ConnectionHandlerTest {
         when(stream.isHeadersSent()).thenReturn(false);
         when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
         when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
-                eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
+                eq(PROTOCOL_ERROR.code()))).thenReturn(future);
 
         handler.exceptionCaught(ctx, e);
 
         verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
-                any(Http2Headers.class), eq(padding), eq(true), eq(promise));
-        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
+                any(Http2Headers.class), eq(padding), eq(true));
+        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
     }
 
     @Test
@@ -448,7 +454,7 @@ public class Http2ConnectionHandlerTest {
         final CountDownLatch latch = new CountDownLatch(1);
         handler = new Http2ConnectionHandler(decoder, encoder, new Http2Settings()) {
             @Override
-            public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
+            public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
                 if (evt == Http2ConnectionPrefaceAndSettingsFrameWrittenEvent.INSTANCE) {
                     latch.countDown();
                 }
@@ -470,13 +476,13 @@ public class Http2ConnectionHandlerTest {
         when(stream.isHeadersSent()).thenReturn(true);
         when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
         when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
-            eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
+            eq(PROTOCOL_ERROR.code()))).thenReturn(future);
         handler.exceptionCaught(ctx, e);
 
         verify(encoder, never()).writeHeaders(eq(ctx), eq(STREAM_ID),
-            any(Http2Headers.class), eq(padding), eq(true), eq(promise));
+            any(Http2Headers.class), eq(padding), eq(true));
 
-        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
+        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
     }
 
     @Test
@@ -494,14 +500,14 @@ public class Http2ConnectionHandlerTest {
         when(stream.isHeadersSent()).thenReturn(false);
         when(remote.lastStreamCreated()).thenReturn(STREAM_ID);
         when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
-            eq(PROTOCOL_ERROR.code()), eq(promise))).thenReturn(future);
+            eq(PROTOCOL_ERROR.code()))).thenReturn(future);
         handler.exceptionCaught(ctx, e);
 
         verify(remote).createStream(STREAM_ID, true);
         verify(encoder).writeHeaders(eq(ctx), eq(STREAM_ID),
-            any(Http2Headers.class), eq(padding), eq(true), eq(promise));
+            any(Http2Headers.class), eq(padding), eq(true));
 
-        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
+        verify(frameWriter).writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code());
     }
 
     @Test
@@ -518,9 +524,9 @@ public class Http2ConnectionHandlerTest {
     public void writeRstOnNonExistantStreamShouldSucceed() throws Exception {
         handler = newHandler();
         when(frameWriter.writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID),
-                                        eq(STREAM_CLOSED.code()), eq(promise))).thenReturn(future);
-        handler.resetStream(ctx, NON_EXISTANT_STREAM_ID, STREAM_CLOSED.code(), promise);
-        verify(frameWriter).writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID), eq(STREAM_CLOSED.code()), eq(promise));
+                                        eq(STREAM_CLOSED.code()))).thenReturn(future);
+        handler.resetStream(ctx, NON_EXISTANT_STREAM_ID, STREAM_CLOSED.code());
+        verify(frameWriter).writeRstStream(eq(ctx), eq(NON_EXISTANT_STREAM_ID), eq(STREAM_CLOSED.code()));
     }
 
     @Test
@@ -528,21 +534,21 @@ public class Http2ConnectionHandlerTest {
         handler = newHandler();
         when(stream.id()).thenReturn(STREAM_ID);
         when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
-                anyLong(), any(Promise.class))).thenReturn(future);
+                anyLong())).thenReturn(future);
         when(stream.state()).thenReturn(CLOSED);
         when(stream.isHeadersSent()).thenReturn(true);
         // The stream is "closed" but is still known about by the connection (connection().stream(..)
         // will return the stream). We should still write a RST_STREAM frame in this scenario.
-        handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
-        verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
+        handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code());
+        verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong());
     }
 
     @Test
     public void writeRstOnIdleStreamShouldNotWriteButStillSucceed() throws Exception {
         handler = newHandler();
         when(stream.state()).thenReturn(IDLE);
-        handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
-        verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
+        handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code());
+        verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong());
         verify(stream).close();
     }
 
@@ -585,11 +591,10 @@ public class Http2ConnectionHandlerTest {
             return null;
         }).when(future).addListener(any(FutureListener.class));
         handler = newHandler();
-        handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
+        handler.goAway(ctx, STREAM_ID, errorCode, data);
 
         verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
-        verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data),
-                eq(promise));
+        verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data));
         verify(ctx).close();
         assertEquals(0, data.refCnt());
     }
@@ -600,13 +605,12 @@ public class Http2ConnectionHandlerTest {
         ByteBuf data = dummyData();
         long errorCode = Http2Error.INTERNAL_ERROR.code();
 
-        handler.goAway(ctx, STREAM_ID + 2, errorCode, data.retain(), promise);
-        verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data),
-                eq(promise));
+        handler.goAway(ctx, STREAM_ID + 2, errorCode, data.retain());
+        verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data));
         verify(connection).goAwaySent(eq(STREAM_ID + 2), eq(errorCode), eq(data));
         promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
-        handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
-        verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data), eq(promise));
+        handler.goAway(ctx, STREAM_ID, errorCode, data);
+        verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data));
         verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
         assertEquals(0, data.refCnt());
     }
@@ -617,20 +621,24 @@ public class Http2ConnectionHandlerTest {
         ByteBuf data = dummyData();
         long errorCode = Http2Error.INTERNAL_ERROR.code();
 
-        handler.goAway(ctx, STREAM_ID, errorCode, data.retain(), promise);
+        Future future = handler.goAway(ctx, STREAM_ID, errorCode, data.retain());
         verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
-        verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data), eq(promise));
+        verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data));
         // The frameWriter is only mocked, so it should not have interacted with the promise.
-        assertFalse(promise.isDone());
+        assertFalse(future.isDone());
 
         when(connection.goAwaySent()).thenReturn(true);
         when(remote.lastStreamKnownByPeer()).thenReturn(STREAM_ID);
+
+        Exception ex = new IllegalStateException();
         doAnswer((Answer) invocationOnMock -> {
-            throw new IllegalStateException();
+            throw ex;
         }).when(connection).goAwaySent(anyInt(), anyLong(), any(ByteBuf.class));
-        handler.goAway(ctx, STREAM_ID + 2, errorCode, data, promise);
-        assertTrue(promise.isDone());
-        assertFalse(promise.isSuccess());
+        Future future2 = handler.goAway(ctx, STREAM_ID + 2, errorCode, data);
+        assertTrue(future2.isDone());
+        assertFalse(future2.isSuccess());
+        assertSame(ex, future2.cause());
+
         assertEquals(0, data.refCnt());
         verifyNoMoreInteractions(frameWriter);
     }
@@ -657,9 +665,8 @@ public class Http2ConnectionHandlerTest {
         when(channel.isActive()).thenReturn(false);
         handler.channelInactive(ctx);
         verify(frameWriter, never()).writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(),
-                                                 any(ByteBuf.class), any(Promise.class));
-        verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong(),
-                                                    any(Promise.class));
+                                                 any(ByteBuf.class));
+        verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong());
     }
 
     @Test
@@ -729,22 +736,14 @@ public class Http2ConnectionHandlerTest {
             return stream;
         });
         when(stream.isResetSent()).then((Answer) invocationOnMock -> resetSent.get());
-        when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class)))
-                .then((Answer>) invocationOnMock -> {
-                    Promise promise = invocationOnMock.getArgument(3);
-                    return promise.setSuccess(null);
-                });
+        when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong()))
+                .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
 
-        Promise promise =
-                new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
-        final Promise promise2 =
-                new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
-        promise.addListener(future -> handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise2));
-
-        handler.resetStream(ctx, STREAM_ID, CANCEL.code(), promise);
-        verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
-        assertTrue(promise.isSuccess());
-        assertTrue(promise2.isSuccess());
+        Future f1 = handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code());
+        Future f2 = handler.resetStream(ctx, STREAM_ID, CANCEL.code());
+        verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong());
+        assertTrue(f1.isSuccess());
+        assertTrue(f2.isSuccess());
     }
 
     private static ByteBuf dummyData() {
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java
index ec0148b1e5..5b33a9e3dd 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ConnectionRoundtripTest.java
@@ -35,6 +35,7 @@ import io.netty.util.AsciiString;
 import io.netty.util.IllegalReferenceCountException;
 import io.netty.util.ReferenceCountUtil;
 import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.ImmediateEventExecutor;
 import io.netty.util.concurrent.Promise;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -152,8 +153,7 @@ public class Http2ConnectionRoundtripTest {
                     (Integer) invocationOnMock.getArgument(1),
                     (Http2Headers) invocationOnMock.getArgument(2),
                     0,
-                    false,
-                    ctx.newPromise());
+                    false);
             http2Server.flush(ctx);
             return null;
         }).when(serverListener).onHeadersRead(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
@@ -169,14 +169,14 @@ public class Http2ConnectionRoundtripTest {
         final short weight = 16;
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, false, newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, false);
             http2Client.flush(ctx());
-            http2Client.encoder().writeRstStream(ctx(), 3, Http2Error.INTERNAL_ERROR.code(), newPromise());
+            http2Client.encoder().writeRstStream(ctx(), 3, Http2Error.INTERNAL_ERROR.code());
             http2Client.flush(ctx());
         });
 
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, weight, false, 0, false, newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, weight, false, 0, false);
             http2Client.flush(ctx());
         });
 
@@ -191,8 +191,7 @@ public class Http2ConnectionRoundtripTest {
         final short weight = 16;
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, true,
-                    newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, weight, false, 0, true);
             http2Client.flush(ctx());
         });
 
@@ -250,18 +249,17 @@ public class Http2ConnectionRoundtripTest {
         runInChannel(serverConnectedChannel, () -> {
             http2Server.encoder().writeSettings(serverCtx(),
                     new Http2Settings().copyFrom(http2Server.decoder().localSettings())
-                            .maxHeaderListSize(100),
-                    serverNewPromise());
+                            .maxHeaderListSize(100));
             http2Server.flush(serverCtx());
         });
 
         assertTrue(serverSettingsAckLatch1.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
 
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, false, newPromise())
+            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, false)
                     .addListener(future -> clientHeadersWriteException.set(future.cause()));
             // It is expected that this write should fail locally and the remote peer will never see this.
-            http2Client.encoder().writeData(ctx(), 3, Unpooled.buffer(), 0, true, newPromise())
+            http2Client.encoder().writeData(ctx(), 3, Unpooled.buffer(), 0, true)
                 .addListener(future -> {
                     clientDataWriteException.set(future.cause());
                     clientDataWrite.countDown();
@@ -277,8 +275,7 @@ public class Http2ConnectionRoundtripTest {
         runInChannel(serverConnectedChannel, () -> {
             http2Server.encoder().writeSettings(serverCtx(),
                     new Http2Settings().copyFrom(http2Server.decoder().localSettings())
-                            .maxHeaderListSize(Http2CodecUtil.MAX_HEADER_LIST_SIZE),
-                    serverNewPromise());
+                            .maxHeaderListSize(Http2CodecUtil.MAX_HEADER_LIST_SIZE));
             http2Server.flush(serverCtx());
         });
 
@@ -286,8 +283,7 @@ public class Http2ConnectionRoundtripTest {
         assertTrue(serverSettingsAckLatch2.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
 
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true,
-                    newPromise()).addListener(future -> {
+            http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true).addListener(future -> {
                 clientHeadersWriteException2.set(future.cause());
                 clientHeadersLatch.countDown();
             });
@@ -344,8 +340,7 @@ public class Http2ConnectionRoundtripTest {
         runInChannel(serverConnectedChannel, () -> {
             http2Server.encoder().writeSettings(serverCtx(),
                     new Http2Settings().copyFrom(http2Server.decoder().localSettings())
-                            .initialWindowSize(0),
-                    serverNewPromise());
+                            .initialWindowSize(0));
             http2Server.flush(serverCtx());
         });
 
@@ -354,9 +349,8 @@ public class Http2ConnectionRoundtripTest {
         // The client should now attempt to send data, but the window size is 0 so it will be queued in the flow
         // controller.
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
-                    newPromise());
-            http2Client.encoder().writeData(ctx(), 3, Unpooled.wrappedBuffer(data), 0, true, newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
+            http2Client.encoder().writeData(ctx(), 3, Unpooled.wrappedBuffer(data), 0, true);
             http2Client.flush(ctx());
             clientWriteDataLatch.countDown();
         });
@@ -367,8 +361,7 @@ public class Http2ConnectionRoundtripTest {
         runInChannel(serverConnectedChannel, () -> {
             http2Server.encoder().writeSettings(serverCtx(),
                     new Http2Settings().copyFrom(http2Server.decoder().localSettings())
-                            .initialWindowSize(data.length),
-                    serverNewPromise());
+                            .initialWindowSize(data.length));
             http2Server.flush(serverCtx());
         });
 
@@ -391,9 +384,8 @@ public class Http2ConnectionRoundtripTest {
 
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writePriority(ctx(), 5, 3, (short) 14, false, newPromise());
-            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
-                    newPromise());
+            http2Client.encoder().writePriority(ctx(), 5, 3, (short) 14, false);
+            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
             http2Client.flush(ctx());
         });
 
@@ -420,10 +412,8 @@ public class Http2ConnectionRoundtripTest {
 
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, false,
-                    newPromise());
-            http2Client.encoder().frameWriter().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
-                    newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, false);
+            http2Client.encoder().frameWriter().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
             http2Client.flush(ctx());
         });
 
@@ -465,8 +455,8 @@ public class Http2ConnectionRoundtripTest {
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
             http2Client.encoder().writeHeaders(ctx(), streamId, headers, CONNECTION_STREAM_ID,
-                    DEFAULT_PRIORITY_WEIGHT, false, 0, false, newPromise());
-            http2Client.encoder().writeRstStream(ctx(), streamId, Http2Error.CANCEL.code(), newPromise());
+                    DEFAULT_PRIORITY_WEIGHT, false, 0, false);
+            http2Client.encoder().writeRstStream(ctx(), streamId, Http2Error.CANCEL.code());
             http2Client.flush(ctx());
         });
 
@@ -478,7 +468,7 @@ public class Http2ConnectionRoundtripTest {
 
         // Now have the server attempt to send a headers frame simulating some asynchronous work.
         runInChannel(serverConnectedChannel, () -> {
-            http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true, serverNewPromise())
+            http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true)
                     .addListener(future -> {
                         serverWriteHeadersCauseRef.set(future.cause());
                         serverWriteHeadersLatch.countDown();
@@ -510,8 +500,7 @@ public class Http2ConnectionRoundtripTest {
         // Create a single stream by sending a HEADERS frame to the server.
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
-                    newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
             http2Client.flush(ctx());
         });
 
@@ -547,8 +536,7 @@ public class Http2ConnectionRoundtripTest {
 
         // Create a single stream by sending a HEADERS frame to the server.
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
-                    newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
             http2Client.flush(ctx());
         });
 
@@ -592,41 +580,42 @@ public class Http2ConnectionRoundtripTest {
             throws Exception {
         bootstrapEnv(1, 1, 2, 1);
 
-        final Promise emptyDataPromise = newPromise();
+        Promise promise = ImmediateEventExecutor.INSTANCE.newPromise();
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
-                    newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false);
             ByteBuf emptyBuf = Unpooled.buffer();
             emptyBuf.release();
+            final Future future;
             switch (mode) {
                 case SINGLE_END_OF_STREAM:
-                    http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, true, emptyDataPromise);
+                    future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, true);
                     break;
                 case SECOND_END_OF_STREAM:
-                    http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false, emptyDataPromise);
-                    http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, true, newPromise());
+                    future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false);
+                    http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, true);
                     break;
                 case SINGLE_WITH_TRAILERS:
-                    http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false, emptyDataPromise);
+                    future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false);
                     http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0,
-                            (short) 16, false, 0, true, newPromise());
+                            (short) 16, false, 0, true);
                     break;
                 case SECOND_WITH_TRAILERS:
-                    http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false, emptyDataPromise);
-                    http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false, newPromise());
+                    future = http2Client.encoder().writeData(ctx(), 3, emptyBuf, 0, false);
+                    http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false);
                     http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0,
-                            (short) 16, false, 0, true, newPromise());
+                            (short) 16, false, 0, true);
                     break;
                 default:
                     throw new Error();
             }
             http2Client.flush(ctx());
+            future.cascadeTo(promise);
         });
 
         ExecutionException e = assertThrows(ExecutionException.class, new Executable() {
             @Override
             public void execute() throws Throwable {
-                emptyDataPromise.get();
+                promise.get();
             }
         });
         assertThat(e.getCause(), is(instanceOf(IllegalReferenceCountException.class)));
@@ -641,8 +630,7 @@ public class Http2ConnectionRoundtripTest {
         final Promise assertPromise = newPromise();
 
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
-                    newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false);
             clientChannel.pipeline().addFirst(new ChannelHandler() {
                 @Override
                 public Future write(ChannelHandlerContext ctx, Object msg) {
@@ -660,7 +648,8 @@ public class Http2ConnectionRoundtripTest {
             });
 
             http2Client.encoder().flowController().initialWindowSize(4);
-            http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false, dataPromise);
+            http2Client.encoder().writeData(ctx(), 3, randomBytes(8), 0, false)
+                    .cascadeTo(dataPromise);
             assertTrue(http2Client.encoder().flowController()
                     .hasFlowControlled(http2Client.connection().stream(3)));
 
@@ -697,8 +686,7 @@ public class Http2ConnectionRoundtripTest {
         // Create a single stream by sending a HEADERS frame to the server.
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
-            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false,
-                    newPromise());
+            http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0, false);
             http2Client.flush(ctx());
         });
 
@@ -733,7 +721,7 @@ public class Http2ConnectionRoundtripTest {
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
             http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
-                    true, newPromise());
+                    true);
             http2Client.flush(ctx());
         });
 
@@ -741,7 +729,7 @@ public class Http2ConnectionRoundtripTest {
 
         runInChannel(clientChannel, () -> {
             http2Client.encoder().writeHeaders(ctx(), MAX_VALUE + 1, headers, 0, (short) 16, false, 0,
-                    true, newPromise());
+                    true);
             http2Client.flush(ctx());
         });
 
@@ -768,7 +756,7 @@ public class Http2ConnectionRoundtripTest {
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
             http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
-                    false, newPromise());
+                    false);
             http2Client.flush(ctx());
         });
 
@@ -778,7 +766,7 @@ public class Http2ConnectionRoundtripTest {
         assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
 
         runInChannel(serverChannel, () -> {
-            http2Server.encoder().writeGoAway(serverCtx(), 3, NO_ERROR.code(), EMPTY_BUFFER, serverNewPromise());
+            http2Server.encoder().writeGoAway(serverCtx(), 3, NO_ERROR.code(), EMPTY_BUFFER);
             http2Server.flush(serverCtx());
         });
 
@@ -791,7 +779,7 @@ public class Http2ConnectionRoundtripTest {
         final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1);
         runInChannel(clientChannel, () -> {
             Future f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0,
-                    true, newPromise());
+                    true);
             clientWriteAfterGoAwayFutureRef.set(f);
             http2Client.flush(ctx());
             f.addListener(future -> clientWriteAfterGoAwayLatch.countDown());
@@ -839,9 +827,9 @@ public class Http2ConnectionRoundtripTest {
         final Http2Headers headers = dummyHeaders();
         runInChannel(clientChannel, () -> {
             http2Client.encoder().writeHeaders(ctx(), 1, headers, 0, (short) 16, false, 0,
-                    false, newPromise());
+                    false);
             http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
-                    false, newPromise());
+                    false);
             http2Client.flush(ctx());
         });
 
@@ -851,7 +839,7 @@ public class Http2ConnectionRoundtripTest {
         assertTrue(requestLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
 
         runInChannel(serverChannel, () -> {
-            http2Server.encoder().writeGoAway(serverCtx(), 1, NO_ERROR.code(), EMPTY_BUFFER, serverNewPromise());
+            http2Server.encoder().writeGoAway(serverCtx(), 1, NO_ERROR.code(), EMPTY_BUFFER);
             http2Server.flush(serverCtx());
         });
 
@@ -911,12 +899,12 @@ public class Http2ConnectionRoundtripTest {
             // Create the stream and send all of the data at once.
             runInChannel(clientChannel, () -> {
                 http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
-                        false, newPromise());
-                http2Client.encoder().writeData(ctx(), 3, data.retainedDuplicate(), 0, false, newPromise());
+                        false);
+                http2Client.encoder().writeData(ctx(), 3, data.retainedDuplicate(), 0, false);
 
                 // Write trailers.
                 http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, (short) 16, false, 0,
-                        true, newPromise());
+                        true);
                 http2Client.flush(ctx());
             });
 
@@ -989,14 +977,13 @@ public class Http2ConnectionRoundtripTest {
                 for (int streamId = 3; streamId < upperLimit; streamId += 2) {
                     // Send a bunch of data on each stream.
                     http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16,
-                            false, 0, false, newPromise());
-                    http2Client.encoder().writePing(ctx(), false, pingData,
-                            newPromise());
+                            false, 0, false);
+                    http2Client.encoder().writePing(ctx(), false, pingData);
                     http2Client.encoder().writeData(ctx(), streamId, data.retainedSlice(), 0,
-                                                    false, newPromise());
+                                                    false);
                     // Write trailers.
                     http2Client.encoder().writeHeaders(ctx(), streamId, headers, 0, (short) 16,
-                            false, 0, true, newPromise());
+                            false, 0, true);
                     http2Client.flush(ctx());
                 }
             });
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoderTest.java
index f543c099fe..256f22c2e4 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoderTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2ControlFrameLimitEncoderTest.java
@@ -33,7 +33,6 @@ import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 
 import java.util.ArrayDeque;
@@ -59,7 +58,6 @@ import static org.mockito.Mockito.when;
 /**
  * Tests for {@link Http2ControlFrameLimitEncoder}.
  */
-@SuppressWarnings("unchecked")
 public class Http2ControlFrameLimitEncoderTest {
 
     private Http2ControlFrameLimitEncoder encoder;
@@ -101,22 +99,22 @@ public class Http2ControlFrameLimitEncoderTest {
         when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
         when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
 
-        when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class)))
-                .thenAnswer((Answer>) invocationOnMock -> handlePromise(invocationOnMock, 3));
-        when(writer.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
-                .thenAnswer((Answer>) invocationOnMock -> handlePromise(invocationOnMock, 1));
-        when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong(), any(Promise.class)))
+        when(writer.writeRstStream(eq(ctx), anyInt(), anyLong()))
+                .thenAnswer((Answer>) invocationOnMock -> handlePromise());
+        when(writer.writeSettingsAck(any(ChannelHandlerContext.class)))
+                .thenAnswer((Answer>) invocationOnMock -> handlePromise());
+        when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong()))
                 .thenAnswer((Answer>) invocationOnMock -> {
-                    Promise promise = handlePromise(invocationOnMock, 3);
+                    Promise promise = handlePromise();
                     if (invocationOnMock.getArgument(1) == Boolean.FALSE) {
                         promise.trySuccess(null);
                     }
                     return promise;
                 });
-        when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
-                any(Promise.class))).thenAnswer((Answer>) invocationOnMock -> {
+        when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)))
+                .thenAnswer((Answer>) invocationOnMock -> {
                     ReferenceCountUtil.release(invocationOnMock.getArgument(3));
-                    Promise promise = invocationOnMock.getArgument(4);
+                    Promise promise =  ImmediateEventExecutor.INSTANCE.newPromise();
                     goAwayPromises.offer(promise);
                     return promise;
                 });
@@ -139,7 +137,12 @@ public class Http2ControlFrameLimitEncoderTest {
         when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
         when(executor.inEventLoop()).thenReturn(true);
         doAnswer((Answer) invocation -> newPromise()).when(ctx).newPromise();
+        doAnswer((Answer) invocation ->
+                ImmediateEventExecutor.INSTANCE.newFailedFuture(invocation.getArgument(0)))
+                .when(ctx).newFailedFuture(any(Throwable.class));
+
         when(ctx.executor()).thenReturn(executor);
+        when(ctx.close()).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
         when(channel.isActive()).thenReturn(false);
         when(channel.config()).thenReturn(config);
         when(channel.isWritable()).thenReturn(true);
@@ -152,12 +155,12 @@ public class Http2ControlFrameLimitEncoderTest {
         handler.handlerAdded(ctx);
     }
 
-    private Promise handlePromise(InvocationOnMock invocationOnMock, int promiseIdx) {
-        Promise promise = invocationOnMock.getArgument(promiseIdx);
+    private Promise handlePromise() {
+        Promise p =  ImmediateEventExecutor.INSTANCE.newPromise();
         if (++numWrites == 2) {
-            promise.setSuccess(null);
+            p.setSuccess(null);
         }
-        return promise;
+        return p;
     }
 
     @AfterEach
@@ -178,75 +181,75 @@ public class Http2ControlFrameLimitEncoderTest {
 
     @Test
     public void testLimitSettingsAck() {
-        assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
+        assertFalse(encoder.writeSettingsAck(ctx).isDone());
         // The second write is always marked as success by our mock, which means it will also not be queued and so
         // not count to the number of queued frames.
-        assertTrue(encoder.writeSettingsAck(ctx, newPromise()).isSuccess());
-        assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
+        assertTrue(encoder.writeSettingsAck(ctx).isSuccess());
+        assertFalse(encoder.writeSettingsAck(ctx).isDone());
 
         verifyFlushAndClose(0, false);
 
-        assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
-        assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
+        assertFalse(encoder.writeSettingsAck(ctx).isDone());
+        assertFalse(encoder.writeSettingsAck(ctx).isDone());
 
         verifyFlushAndClose(1, true);
     }
 
     @Test
     public void testLimitPingAck() {
-        assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
+        assertFalse(encoder.writePing(ctx, true, 8).isDone());
         // The second write is always marked as success by our mock, which means it will also not be queued and so
         // not count to the number of queued frames.
-        assertTrue(encoder.writePing(ctx, true, 8, newPromise()).isSuccess());
-        assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
+        assertTrue(encoder.writePing(ctx, true, 8).isSuccess());
+        assertFalse(encoder.writePing(ctx, true, 8).isDone());
 
         verifyFlushAndClose(0, false);
 
-        assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
-        assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isDone());
+        assertFalse(encoder.writePing(ctx, true, 8).isDone());
+        assertFalse(encoder.writePing(ctx, true, 8).isDone());
 
         verifyFlushAndClose(1, true);
     }
 
     @Test
     public void testNotLimitPing() {
-        assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
-        assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
-        assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
-        assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
+        assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
+        assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
+        assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
+        assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
 
         verifyFlushAndClose(0, false);
     }
 
     @Test
     public void testLimitRst() {
-        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
+        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
         // The second write is always marked as success by our mock, which means it will also not be queued and so
         // not count to the number of queued frames.
-        assertTrue(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isSuccess());
-        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
+        assertTrue(encoder.writeRstStream(ctx, 1, CANCEL.code()).isSuccess());
+        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
 
         verifyFlushAndClose(0, false);
 
-        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
-        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
+        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
+        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
 
         verifyFlushAndClose(1, true);
     }
 
     @Test
     public void testLimit() {
-        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
+        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
         // The second write is always marked as success by our mock, which means it will also not be queued and so
         // not count to the number of queued frames.
-        assertTrue(encoder.writePing(ctx, false, 8, newPromise()).isSuccess());
-        assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isSuccess());
+        assertTrue(encoder.writePing(ctx, false, 8).isSuccess());
+        assertFalse(encoder.writePing(ctx, true, 8).isSuccess());
 
         verifyFlushAndClose(0, false);
 
-        assertFalse(encoder.writeSettingsAck(ctx, newPromise()).isDone());
-        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code(), newPromise()).isDone());
-        assertFalse(encoder.writePing(ctx, true, 8, newPromise()).isSuccess());
+        assertFalse(encoder.writeSettingsAck(ctx).isDone());
+        assertFalse(encoder.writeRstStream(ctx, 1, CANCEL.code()).isDone());
+        assertFalse(encoder.writePing(ctx, true, 8).isSuccess());
 
         verifyFlushAndClose(1, true);
     }
@@ -256,7 +259,7 @@ public class Http2ControlFrameLimitEncoderTest {
         verify(ctx, times(invocations)).close();
         if (failed) {
             verify(writer, times(1)).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(ENHANCE_YOUR_CALM.code()),
-                    any(ByteBuf.class), any(Promise.class));
+                    any(ByteBuf.class));
         }
     }
 
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java
index f5b9f6e26d..ab996a09aa 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameCodecTest.java
@@ -58,7 +58,6 @@ import java.util.concurrent.atomic.AtomicReference;
 
 import static io.netty.handler.codec.http2.Http2CodecUtil.isStreamIdValid;
 import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
-import static io.netty.handler.codec.http2.Http2TestUtil.anyChannelPromise;
 import static io.netty.handler.codec.http2.Http2TestUtil.anyHttp2Settings;
 import static io.netty.handler.codec.http2.Http2TestUtil.assertEqualsAndRelease;
 import static io.netty.handler.codec.http2.Http2TestUtil.bb;
@@ -82,7 +81,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
 /**
  * Unit tests for {@link Http2FrameCodec}.
  */
-@SuppressWarnings("unchecked")
 public class Http2FrameCodecTest {
 
     // For verifying outbound frames
@@ -141,13 +139,13 @@ public class Http2FrameCodecTest {
         channel.pipeline().fireChannelActive();
 
         // Handshake
-        verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), anyHttp2Settings(), anyChannelPromise());
+        verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), anyHttp2Settings());
         verifyNoMoreInteractions(frameWriter);
         channel.writeInbound(Http2CodecUtil.connectionPrefaceBuf());
 
         frameInboundWriter.writeInboundSettings(initialRemoteSettings);
 
-        verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class), anyChannelPromise());
+        verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class));
 
         frameInboundWriter.writeInboundSettingsAck();
 
@@ -178,9 +176,9 @@ public class Http2FrameCodecTest {
         channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
         verify(frameWriter).writeHeaders(
                 any(ChannelHandlerContext.class), eq(1), eq(response),
-                eq(27), eq(true), anyChannelPromise());
+                eq(27), eq(true));
         verify(frameWriter, never()).writeRstStream(
-                any(ChannelHandlerContext.class), anyInt(), anyLong(), anyChannelPromise());
+                any(ChannelHandlerContext.class), anyInt(), anyLong());
 
         assertEquals(State.CLOSED, stream.state());
         event = inboundHandler.readInboundMessageOrUserEvent();
@@ -207,9 +205,9 @@ public class Http2FrameCodecTest {
         channel.writeOutbound(new DefaultHttp2HeadersFrame(response, true, 27).stream(stream2));
         verify(frameWriter).writeHeaders(
                 any(ChannelHandlerContext.class), eq(1), eq(response),
-                eq(27), eq(true), anyChannelPromise());
+                eq(27), eq(true));
         verify(frameWriter, never()).writeRstStream(
-                any(ChannelHandlerContext.class), anyInt(), anyLong(), anyChannelPromise());
+                any(ChannelHandlerContext.class), anyInt(), anyLong());
 
         assertEquals(State.CLOSED, stream.state());
         assertTrue(channel.isActive());
@@ -266,12 +264,12 @@ public class Http2FrameCodecTest {
 
         inboundHandler.writeOutbound(new DefaultHttp2HeadersFrame(response, false).stream(stream2));
         verify(frameWriter).writeHeaders(any(ChannelHandlerContext.class), eq(1), eq(response), eq(0),
-                eq(false), anyChannelPromise());
+                eq(false));
 
         channel.writeOutbound(new DefaultHttp2DataFrame(bb("world"), true, 27).stream(stream2));
         ArgumentCaptor outboundData = ArgumentCaptor.forClass(ByteBuf.class);
         verify(frameWriter).writeData(any(ChannelHandlerContext.class), eq(1), outboundData.capture(), eq(27),
-                                      eq(true), anyChannelPromise());
+                                      eq(true));
 
         ByteBuf bb = bb("world");
         assertEquals(bb, outboundData.getValue());
@@ -280,7 +278,7 @@ public class Http2FrameCodecTest {
         outboundData.getValue().release();
 
         verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class),
-                anyInt(), anyLong(), anyChannelPromise());
+                anyInt(), anyLong());
         assertTrue(channel.isActive());
     }
 
@@ -301,7 +299,7 @@ public class Http2FrameCodecTest {
         assertEquals(3, stream2.id());
 
         channel.writeOutbound(new DefaultHttp2ResetFrame(314 /* non-standard error */).stream(stream2));
-        verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eq(3), eq(314L), anyChannelPromise());
+        verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eq(3), eq(314L));
         assertEquals(State.CLOSED, stream.state());
         assertTrue(channel.isActive());
     }
@@ -343,7 +341,7 @@ public class Http2FrameCodecTest {
 
         channel.writeOutbound(goAwayFrame);
         verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(7),
-                eq(NO_ERROR.code()), eq(expected), anyChannelPromise());
+                eq(NO_ERROR.code()), eq(expected));
         assertEquals(State.OPEN, stream.state());
         assertTrue(channel.isActive());
         expected.release();
@@ -419,7 +417,7 @@ public class Http2FrameCodecTest {
         channel.writeOutbound(goAwayFrame);
         // When the last stream id computation overflows, the last stream id should just be set to 2^31 - 1.
         verify(frameWriter).writeGoAway(any(ChannelHandlerContext.class), eq(Integer.MAX_VALUE),
-                eq(NO_ERROR.code()), eq(debugData), anyChannelPromise());
+                eq(NO_ERROR.code()), eq(debugData));
         debugData.release();
         assertEquals(State.OPEN, stream.state());
         assertTrue(channel.isActive());
@@ -596,7 +594,7 @@ public class Http2FrameCodecTest {
         channel.write(unknownFrame);
 
         verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq(unknownFrame.frameType()),
-                eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer), any(Promise.class));
+                eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer));
     }
 
     @Test
@@ -604,7 +602,7 @@ public class Http2FrameCodecTest {
         Http2Settings settings = new Http2Settings();
         channel.write(new DefaultHttp2SettingsFrame(settings));
 
-        verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), same(settings), any(Promise.class));
+        verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), same(settings));
     }
 
     @Test
@@ -763,7 +761,7 @@ public class Http2FrameCodecTest {
         channel.writeAndFlush(new DefaultHttp2PingFrame(12345));
 
         verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(false),
-                eq(12345L), anyChannelPromise());
+                eq(12345L));
     }
 
     @Test
@@ -781,7 +779,7 @@ public class Http2FrameCodecTest {
         Http2Settings settings = new Http2Settings().maxConcurrentStreams(1);
         channel.writeAndFlush(new DefaultHttp2SettingsFrame(settings));
 
-        verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), eq(settings), anyChannelPromise());
+        verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), eq(settings));
     }
 
     @Test
@@ -837,21 +835,21 @@ public class Http2FrameCodecTest {
         Http2PingFrame frame = inboundHandler.readInbound();
         assertFalse(frame.ack());
         assertEquals(8, frame.content());
-        verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L), anyChannelPromise());
+        verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L));
     }
 
     @Test
     public void autoAckPingFalse() throws Exception {
         setUp(Http2FrameCodecBuilder.forServer().autoAckPingFrame(false), new Http2Settings());
         frameInboundWriter.writeInboundPing(false, 8);
-        verify(frameWriter, never()).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L), anyChannelPromise());
+        verify(frameWriter, never()).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L));
         Http2PingFrame frame = inboundHandler.readInbound();
         assertFalse(frame.ack());
         assertEquals(8, frame.content());
 
         // Now ack the frame manually.
         channel.writeAndFlush(new DefaultHttp2PingFrame(8, true));
-        verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L), anyChannelPromise());
+        verify(frameWriter).writePing(any(ChannelHandlerContext.class), eq(true), eq(8L));
     }
 
     @Test
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameInboundWriter.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameInboundWriter.java
index 4f9c0aa08d..090a9fd628 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameInboundWriter.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameInboundWriter.java
@@ -49,59 +49,59 @@ final class Http2FrameInboundWriter {
     }
 
     void writeInboundData(int streamId, ByteBuf data, int padding, boolean endStream) {
-        writer.writeData(ctx, streamId, data, padding, endStream, ctx.newPromise()).syncUninterruptibly();
+        writer.writeData(ctx, streamId, data, padding, endStream).syncUninterruptibly();
     }
 
     void writeInboundHeaders(int streamId, Http2Headers headers,
                          int padding, boolean endStream) {
-        writer.writeHeaders(ctx, streamId, headers, padding, endStream, ctx.newPromise()).syncUninterruptibly();
+        writer.writeHeaders(ctx, streamId, headers, padding, endStream).syncUninterruptibly();
     }
 
     void writeInboundHeaders(int streamId, Http2Headers headers,
                                int streamDependency, short weight, boolean exclusive, int padding, boolean endStream) {
         writer.writeHeaders(ctx, streamId, headers, streamDependency,
-                weight, exclusive, padding, endStream, ctx.newPromise()).syncUninterruptibly();
+                weight, exclusive, padding, endStream).syncUninterruptibly();
     }
 
     void writeInboundPriority(int streamId, int streamDependency,
                                 short weight, boolean exclusive) {
         writer.writePriority(ctx, streamId, streamDependency, weight,
-                exclusive, ctx.newPromise()).syncUninterruptibly();
+                exclusive).syncUninterruptibly();
     }
 
     void writeInboundRstStream(int streamId, long errorCode) {
-        writer.writeRstStream(ctx, streamId, errorCode, ctx.newPromise()).syncUninterruptibly();
+        writer.writeRstStream(ctx, streamId, errorCode).syncUninterruptibly();
     }
 
     void writeInboundSettings(Http2Settings settings) {
-        writer.writeSettings(ctx, settings, ctx.newPromise()).syncUninterruptibly();
+        writer.writeSettings(ctx, settings).syncUninterruptibly();
     }
 
     void writeInboundSettingsAck() {
-        writer.writeSettingsAck(ctx, ctx.newPromise()).syncUninterruptibly();
+        writer.writeSettingsAck(ctx).syncUninterruptibly();
     }
 
     void writeInboundPing(boolean ack, long data) {
-        writer.writePing(ctx, ack, data, ctx.newPromise()).syncUninterruptibly();
+        writer.writePing(ctx, ack, data).syncUninterruptibly();
     }
 
     void writePushPromise(int streamId, int promisedStreamId,
                                    Http2Headers headers, int padding) {
            writer.writePushPromise(ctx, streamId, promisedStreamId,
-                   headers, padding, ctx.newPromise()).syncUninterruptibly();
+                   headers, padding).syncUninterruptibly();
     }
 
     void writeInboundGoAway(int lastStreamId, long errorCode, ByteBuf debugData) {
-        writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, ctx.newPromise()).syncUninterruptibly();
+        writer.writeGoAway(ctx, lastStreamId, errorCode, debugData).syncUninterruptibly();
     }
 
     void writeInboundWindowUpdate(int streamId, int windowSizeIncrement) {
-        writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, ctx.newPromise()).syncUninterruptibly();
+        writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement).syncUninterruptibly();
     }
 
     void writeInboundFrame(byte frameType, int streamId,
                              Http2Flags flags, ByteBuf payload) {
-        writer.writeFrame(ctx, frameType, streamId, flags, payload, ctx.newPromise()).syncUninterruptibly();
+        writer.writeFrame(ctx, frameType, streamId, flags, payload).syncUninterruptibly();
     }
 
     private static final class WriteInboundChannelHandlerContext
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameRoundtripTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameRoundtripTest.java
index e4b062322a..dd1d24fad1 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameRoundtripTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2FrameRoundtripTest.java
@@ -59,7 +59,6 @@ import static org.mockito.Mockito.anyShort;
 import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.isA;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
@@ -134,7 +133,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void emptyDataShouldMatch() throws Exception {
         final ByteBuf data = EMPTY_BUFFER;
-        writer.writeData(ctx, STREAM_ID, data.slice(), 0, false, ctx.newPromise());
+        writer.writeData(ctx, STREAM_ID, data.slice(), 0, false);
         readFrames();
         verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(0), eq(false));
     }
@@ -142,7 +141,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void dataShouldMatch() throws Exception {
         final ByteBuf data = data(10);
-        writer.writeData(ctx, STREAM_ID, data.slice(), 1, false, ctx.newPromise());
+        writer.writeData(ctx, STREAM_ID, data.slice(), 1, false);
         readFrames();
         verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(1), eq(false));
     }
@@ -150,7 +149,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void dataWithPaddingShouldMatch() throws Exception {
         final ByteBuf data = data(10);
-        writer.writeData(ctx, STREAM_ID, data.slice(), MAX_PADDING, true, ctx.newPromise());
+        writer.writeData(ctx, STREAM_ID, data.slice(), MAX_PADDING, true);
         readFrames();
         verify(listener).onDataRead(eq(ctx), eq(STREAM_ID), eq(data), eq(MAX_PADDING), eq(true));
     }
@@ -163,7 +162,7 @@ public class Http2FrameRoundtripTest {
         final boolean endOfStream = true;
 
         writer.writeData(ctx, STREAM_ID, originalData.slice(), originalPadding,
-                endOfStream, ctx.newPromise());
+                endOfStream);
         readFrames();
 
         // Verify that at least one frame was sent with eos=false and exactly one with eos=true.
@@ -196,7 +195,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void emptyHeadersShouldMatch() throws Exception {
         final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
-        writer.writeHeaders(ctx, STREAM_ID, headers, 0, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, 0, true);
         readFrames();
         verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
     }
@@ -204,7 +203,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void emptyHeadersWithPaddingShouldMatch() throws Exception {
         final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
-        writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true);
         readFrames();
         verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true));
     }
@@ -212,7 +211,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void binaryHeadersWithoutPriorityShouldMatch() throws Exception {
         final Http2Headers headers = binaryHeaders();
-        writer.writeHeaders(ctx, STREAM_ID, headers, 0, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, 0, true);
         readFrames();
         verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
     }
@@ -220,7 +219,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void headersFrameWithoutPriorityShouldMatch() throws Exception {
         final Http2Headers headers = headers();
-        writer.writeHeaders(ctx, STREAM_ID, headers, 0, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, 0, true);
         readFrames();
         verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(0), eq(true));
     }
@@ -228,7 +227,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void headersFrameWithPriorityShouldMatch() throws Exception {
         final Http2Headers headers = headers();
-        writer.writeHeaders(ctx, STREAM_ID, headers, 4, (short) 255, true, 0, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, 4, (short) 255, true, 0, true);
         readFrames();
         verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(4), eq((short) 255),
                 eq(true), eq(0), eq(true));
@@ -237,7 +236,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void headersWithPaddingWithoutPriorityShouldMatch() throws Exception {
         final Http2Headers headers = headers();
-        writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, MAX_PADDING, true);
         readFrames();
         verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(MAX_PADDING), eq(true));
     }
@@ -245,7 +244,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void headersWithPaddingWithPriorityShouldMatch() throws Exception {
         final Http2Headers headers = headers();
-        writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 1, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 1, true);
         readFrames();
         verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true),
                 eq(1), eq(true));
@@ -254,7 +253,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void continuedHeadersShouldMatch() throws Exception {
         final Http2Headers headers = largeHeaders();
-        writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 0, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, 0, true);
         readFrames();
         verify(listener)
                 .onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true), eq(0), eq(true));
@@ -263,7 +262,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void continuedHeadersWithPaddingShouldMatch() throws Exception {
         final Http2Headers headers = largeHeaders();
-        writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true);
         readFrames();
         verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(headers), eq(2), eq((short) 3), eq(true),
                 eq(MAX_PADDING), eq(true));
@@ -275,7 +274,7 @@ public class Http2FrameRoundtripTest {
         final int maxListSize = 100;
         reader.configuration().headersConfiguration().maxHeaderListSize(maxListSize, maxListSize);
         final Http2Headers headers = headersOfSize(maxListSize + 1);
-        writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true, ctx.newPromise());
+        writer.writeHeaders(ctx, STREAM_ID, headers, 2, (short) 3, true, MAX_PADDING, true);
         assertThrows(Http2Exception.class, new Executable() {
             @Override
             public void execute() throws Throwable {
@@ -290,7 +289,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void emptyPushPromiseShouldMatch() throws Exception {
         final Http2Headers headers = EmptyHttp2Headers.INSTANCE;
-        writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0, ctx.newPromise());
+        writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0);
         readFrames();
         verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0));
     }
@@ -298,7 +297,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void pushPromiseFrameShouldMatch() throws Exception {
         final Http2Headers headers = headers();
-        writer.writePushPromise(ctx, STREAM_ID, 1, headers, 5, ctx.newPromise());
+        writer.writePushPromise(ctx, STREAM_ID, 1, headers, 5);
         readFrames();
         verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(1), eq(headers), eq(5));
     }
@@ -306,7 +305,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void pushPromiseWithPaddingShouldMatch() throws Exception {
         final Http2Headers headers = headers();
-        writer.writePushPromise(ctx, STREAM_ID, 2, headers, MAX_PADDING, ctx.newPromise());
+        writer.writePushPromise(ctx, STREAM_ID, 2, headers, MAX_PADDING);
         readFrames();
         verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(MAX_PADDING));
     }
@@ -314,7 +313,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void continuedPushPromiseShouldMatch() throws Exception {
         final Http2Headers headers = largeHeaders();
-        writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0, ctx.newPromise());
+        writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0);
         readFrames();
         verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0));
     }
@@ -322,7 +321,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void continuedPushPromiseWithPaddingShouldMatch() throws Exception {
         final Http2Headers headers = largeHeaders();
-        writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0xFF, ctx.newPromise());
+        writer.writePushPromise(ctx, STREAM_ID, 2, headers, 0xFF);
         readFrames();
         verify(listener).onPushPromiseRead(eq(ctx), eq(STREAM_ID), eq(2), eq(headers), eq(0xFF));
     }
@@ -332,7 +331,7 @@ public class Http2FrameRoundtripTest {
         final String text = "test";
         final ByteBuf data = buf(text.getBytes());
 
-        writer.writeGoAway(ctx, STREAM_ID, ERROR_CODE, data.slice(), ctx.newPromise());
+        writer.writeGoAway(ctx, STREAM_ID, ERROR_CODE, data.slice());
         readFrames();
 
         ArgumentCaptor captor = ArgumentCaptor.forClass(ByteBuf.class);
@@ -342,7 +341,7 @@ public class Http2FrameRoundtripTest {
 
     @Test
     public void pingFrameShouldMatch() throws Exception {
-        writer.writePing(ctx, false, 1234567, ctx.newPromise());
+        writer.writePing(ctx, false, 1234567);
         readFrames();
 
         ArgumentCaptor captor = ArgumentCaptor.forClass(long.class);
@@ -352,7 +351,7 @@ public class Http2FrameRoundtripTest {
 
     @Test
     public void pingAckFrameShouldMatch() throws Exception {
-        writer.writePing(ctx, true, 1234567, ctx.newPromise());
+        writer.writePing(ctx, true, 1234567);
         readFrames();
 
         ArgumentCaptor captor = ArgumentCaptor.forClass(long.class);
@@ -362,14 +361,14 @@ public class Http2FrameRoundtripTest {
 
     @Test
     public void priorityFrameShouldMatch() throws Exception {
-        writer.writePriority(ctx, STREAM_ID, 1, (short) 1, true, ctx.newPromise());
+        writer.writePriority(ctx, STREAM_ID, 1, (short) 1, true);
         readFrames();
         verify(listener).onPriorityRead(eq(ctx), eq(STREAM_ID), eq(1), eq((short) 1), eq(true));
     }
 
     @Test
     public void rstStreamFrameShouldMatch() throws Exception {
-        writer.writeRstStream(ctx, STREAM_ID, ERROR_CODE, ctx.newPromise());
+        writer.writeRstStream(ctx, STREAM_ID, ERROR_CODE);
         readFrames();
         verify(listener).onRstStreamRead(eq(ctx), eq(STREAM_ID), eq(ERROR_CODE));
     }
@@ -377,7 +376,7 @@ public class Http2FrameRoundtripTest {
     @Test
     public void emptySettingsFrameShouldMatch() throws Exception {
         final Http2Settings settings = new Http2Settings();
-        writer.writeSettings(ctx, settings, ctx.newPromise());
+        writer.writeSettings(ctx, settings);
         readFrames();
         verify(listener).onSettingsRead(eq(ctx), eq(settings));
     }
@@ -390,21 +389,21 @@ public class Http2FrameRoundtripTest {
         settings.initialWindowSize(123);
         settings.maxConcurrentStreams(456);
 
-        writer.writeSettings(ctx, settings, ctx.newPromise());
+        writer.writeSettings(ctx, settings);
         readFrames();
         verify(listener).onSettingsRead(eq(ctx), eq(settings));
     }
 
     @Test
     public void settingsAckShouldMatch() throws Exception {
-        writer.writeSettingsAck(ctx, ctx.newPromise());
+        writer.writeSettingsAck(ctx);
         readFrames();
         verify(listener).onSettingsAckRead(eq(ctx));
     }
 
     @Test
     public void windowUpdateFrameShouldMatch() throws Exception {
-        writer.writeWindowUpdate(ctx, STREAM_ID, WINDOW_UPDATE, ctx.newPromise());
+        writer.writeWindowUpdate(ctx, STREAM_ID, WINDOW_UPDATE);
         readFrames();
         verify(listener).onWindowUpdateRead(eq(ctx), eq(STREAM_ID), eq(WINDOW_UPDATE));
     }
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTest.java
index a9f81ffa42..0a5c1bfee7 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2MultiplexTest.java
@@ -28,6 +28,7 @@ import io.netty.handler.codec.http2.Http2Exception.StreamException;
 import io.netty.util.AsciiString;
 import io.netty.util.AttributeKey;
 import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.ImmediateEventExecutor;
 import io.netty.util.concurrent.Promise;
 import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.AfterEach;
@@ -49,7 +50,6 @@ import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 
-import static io.netty.handler.codec.http2.Http2TestUtil.anyChannelPromise;
 import static io.netty.handler.codec.http2.Http2TestUtil.anyHttp2Settings;
 import static io.netty.handler.codec.http2.Http2TestUtil.assertEqualsAndRelease;
 import static io.netty.handler.codec.http2.Http2TestUtil.bb;
@@ -110,7 +110,7 @@ public abstract class Http2MultiplexTest {
         Http2Settings settings = new Http2Settings().initialWindowSize(initialRemoteStreamWindow);
         frameInboundWriter.writeInboundSettings(settings);
 
-        verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class), anyChannelPromise());
+        verify(frameWriter).writeSettingsAck(any(ChannelHandlerContext.class));
 
         frameInboundWriter.writeInboundSettingsAck();
 
@@ -121,7 +121,7 @@ public abstract class Http2MultiplexTest {
 
         // Handshake
         verify(frameWriter).writeSettings(any(ChannelHandlerContext.class),
-                anyHttp2Settings(), anyChannelPromise());
+                anyHttp2Settings());
     }
 
     @AfterEach
@@ -151,7 +151,7 @@ public abstract class Http2MultiplexTest {
         assertTrue(childChannel.isActive());
 
         verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq((byte) 99), eqStreamId(childChannel),
-                any(Http2Flags.class), any(ByteBuf.class), any(Promise.class));
+                any(Http2Flags.class), any(ByteBuf.class));
     }
 
     private Http2StreamChannel newInboundStream(int streamId, boolean endStream, final ChannelHandler childHandler) {
@@ -552,14 +552,13 @@ public abstract class Http2MultiplexTest {
 
         childChannel.close();
         verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class),
-                eqStreamId(childChannel), eq(Http2Error.CANCEL.code()), anyChannelPromise());
+                eqStreamId(childChannel), eq(Http2Error.CANCEL.code()));
     }
 
     @Test
     public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() {
         when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
-                any(Http2Headers.class), anyInt(), anyBoolean(),
-                any(Promise.class))).thenAnswer(new Answer>() {
+                any(Http2Headers.class), anyInt(), anyBoolean())).thenAnswer(new Answer>() {
 
             private boolean headersWritten;
             @Override
@@ -568,9 +567,9 @@ public abstract class Http2MultiplexTest {
                 // refuses to allocate a new stream due to having received a GOAWAY.
                 if (!headersWritten) {
                     headersWritten = true;
-                    return ((Promise) invocationOnMock.getArgument(5)).setFailure(new Exception("boom"));
+                    return ImmediateEventExecutor.INSTANCE.newFailedFuture(new Exception("boom"));
                 }
-                return ((Promise) invocationOnMock.getArgument(5)).setSuccess(null);
+                return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
             }
         });
 
@@ -587,7 +586,7 @@ public abstract class Http2MultiplexTest {
         childChannel.close();
         // The channel was never active so we should not generate a RST frame.
         verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class),
-                eqStreamId(childChannel), anyLong(), anyChannelPromise());
+                eqStreamId(childChannel), anyLong());
 
         assertTrue(parentChannel.outboundMessages().isEmpty());
     }
@@ -603,7 +602,7 @@ public abstract class Http2MultiplexTest {
 
         // A RST_STREAM frame should NOT be emitted, as we received a RST_STREAM.
         verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(channel),
-                anyLong(), anyChannelPromise());
+                anyLong());
     }
 
     @Test
@@ -633,11 +632,9 @@ public abstract class Http2MultiplexTest {
 
         Http2Headers headers = new DefaultHttp2Headers();
         when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
-                eq(headers), anyInt(), anyBoolean(),
-                any(Promise.class))).thenAnswer(invocationOnMock -> {
-            return ((Promise) invocationOnMock.getArgument(5)).setFailure(
-                        new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed"));
-        });
+                eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock ->
+            ImmediateEventExecutor.INSTANCE.newFailedFuture(
+                        new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed")));
         final Future future = childChannel.writeAndFlush(
                 new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()));
 
@@ -680,7 +677,7 @@ public abstract class Http2MultiplexTest {
 
         // An active outbound stream should emit a RST_STREAM frame.
         verify(frameWriter).writeRstStream(any(ChannelHandlerContext.class), eqStreamId(childChannel),
-                anyLong(), anyChannelPromise());
+                anyLong());
 
         assertFalse(childChannel.isOpen());
         assertFalse(childChannel.isActive());
@@ -698,10 +695,8 @@ public abstract class Http2MultiplexTest {
 
         Http2Headers headers = new DefaultHttp2Headers();
         when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
-               eq(headers), anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(invocationOnMock -> {
-               return ((Promise) invocationOnMock.getArgument(5)).setFailure(
-                       new Http2NoMoreStreamIdsException());
-        });
+               eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock ->
+               ImmediateEventExecutor.INSTANCE.newFailedFuture(new Http2NoMoreStreamIdsException()));
 
         final Future future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
         parentChannel.flush();
@@ -782,9 +777,8 @@ public abstract class Http2MultiplexTest {
 
         Http2Headers headers = new DefaultHttp2Headers();
         when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
-                eq(headers), anyInt(), anyBoolean(),
-                any(Promise.class))).thenAnswer(invocationOnMock -> {
-            Promise promise = invocationOnMock.getArgument(5);
+                eq(headers), anyInt(), anyBoolean())).thenAnswer(invocationOnMock -> {
+            Promise promise = ImmediateEventExecutor.INSTANCE.newPromise();
             writePromises.offer(promise);
             return promise;
         });
@@ -1305,7 +1299,7 @@ public abstract class Http2MultiplexTest {
         assertTrue(flushSniffer.checkFlush());
 
         verify(frameWriter, never())
-                .writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(), anyChannelPromise());
+                .writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt());
         // only the first one was read because it was legacy auto-read behavior.
         verifyFramesMultiplexedToCorrectChannel(childChannel, inboundHandler, 1);
         assertFalse(flushSniffer.checkFlush());
@@ -1317,15 +1311,15 @@ public abstract class Http2MultiplexTest {
         // connection will collect the bytes and decide not to send a wire level frame until more are consumed.
         assertTrue(flushSniffer.checkFlush());
         verify(frameWriter, never())
-                .writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(), anyChannelPromise());
+                .writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt());
 
         // Call read one more time which should trigger the writing of the flow control update.
         childChannel.read();
         verify(frameWriter)
-                .writeWindowUpdate(any(ChannelHandlerContext.class), eq(0), eq(32 * 1024), anyChannelPromise());
+                .writeWindowUpdate(any(ChannelHandlerContext.class), eq(0), eq(32 * 1024));
         verify(frameWriter)
                 .writeWindowUpdate(any(ChannelHandlerContext.class), eq(childChannel.stream().id()),
-                        eq(32 * 1024), anyChannelPromise());
+                        eq(32 * 1024));
         assertTrue(flushSniffer.checkFlush());
     }
 
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java
index 582a1d7b25..3d5f5e171a 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/Http2TestUtil.java
@@ -22,10 +22,9 @@ import io.netty.channel.Channel;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.util.AsciiString;
 import io.netty.util.ReferenceCountUtil;
-import io.netty.util.concurrent.Future;
+import io.netty.util.concurrent.ImmediateEventExecutor;
 import io.netty.util.concurrent.Promise;
 import org.mockito.Mockito;
-import org.mockito.stubbing.Answer;
 
 import java.util.Random;
 import java.util.concurrent.ConcurrentLinkedQueue;
@@ -360,50 +359,45 @@ public final class Http2TestUtil {
         }).when(frameWriter).close();
 
         when(frameWriter.configuration()).thenReturn(configuration);
-        when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class),
-                any(Promise.class))).thenAnswer((Answer>) invocationOnMock ->
-                ((Promise) invocationOnMock.getArgument(2)).setSuccess(null));
+        when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class)))
+                .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
 
-        when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
-                .thenAnswer((Answer>) invocationOnMock ->
-                        ((Promise) invocationOnMock.getArgument(1)).setSuccess(null));
+        when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class)))
+                .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
 
+        when(frameWriter.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong()))
+                .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
         when(frameWriter.writeGoAway(any(ChannelHandlerContext.class), anyInt(),
-                anyLong(), any(ByteBuf.class), any(Promise.class))).thenAnswer(invocationOnMock -> {
+                anyLong(), any(ByteBuf.class))).thenAnswer(invocationOnMock -> {
                     buffers.offer((ByteBuf) invocationOnMock.getArgument(3));
-                    return ((Promise) invocationOnMock.getArgument(4)).setSuccess(null);
+                    return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
                 });
         when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
-                anyBoolean(), any(Promise.class))).thenAnswer((Answer>) invocationOnMock ->
-                ((Promise) invocationOnMock.getArgument(5)).setSuccess(null));
+                anyBoolean())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
 
         when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
-                any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
-                any(Promise.class))).thenAnswer((Answer>) invocationOnMock ->
-                ((Promise) invocationOnMock.getArgument(8)).setSuccess(null));
+                any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()))
+                .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
 
         when(frameWriter.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(),
-                anyBoolean(), any(Promise.class))).thenAnswer(invocationOnMock -> {
+                anyBoolean())).thenAnswer(invocationOnMock -> {
                     buffers.offer((ByteBuf) invocationOnMock.getArgument(2));
-                    return ((Promise) invocationOnMock.getArgument(5)).setSuccess(null);
+                    return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
                 });
 
         when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(),
-                anyLong(), any(Promise.class))).thenAnswer((Answer>) invocationOnMock ->
-                ((Promise) invocationOnMock.getArgument(3)).setSuccess(null));
+                anyLong())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
 
-        when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
-                any(Promise.class))).then((Answer>) invocationOnMock ->
-                ((Promise) invocationOnMock.getArgument(3)).setSuccess(null));
+        when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt()))
+                .thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
 
         when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class),
-                anyInt(), anyChannelPromise())).thenAnswer((Answer>) invocationOnMock ->
-                ((Promise) invocationOnMock.getArgument(5)).setSuccess(null));
+                anyInt())).thenReturn(ImmediateEventExecutor.INSTANCE.newSucceededFuture(null));
 
         when(frameWriter.writeFrame(any(ChannelHandlerContext.class), anyByte(), anyInt(), any(Http2Flags.class),
-                any(ByteBuf.class), anyChannelPromise())).thenAnswer(invocationOnMock -> {
+                any(ByteBuf.class))).thenAnswer(invocationOnMock -> {
                     buffers.offer((ByteBuf) invocationOnMock.getArgument(4));
-                    return ((Promise) invocationOnMock.getArgument(5)).setSuccess(null);
+                    return ImmediateEventExecutor.INSTANCE.newSucceededFuture(null);
                 });
         return frameWriter;
     }
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java
index d4a79d1f87..b5787221f7 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/InboundHttp2ToHttpAdapterTest.java
@@ -146,7 +146,7 @@ public class InboundHttp2ToHttpAdapterTest {
                     scheme(new AsciiString("https")).authority(new AsciiString("example.org"))
                     .path(new AsciiString("/some/path/resource2"));
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
                 clientChannel.flush();
             });
             awaitRequests();
@@ -179,7 +179,7 @@ public class InboundHttp2ToHttpAdapterTest {
                     .add(HttpHeaderNames.COOKIE, "c=d")
                     .add(HttpHeaderNames.COOKIE, "e=f");
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
                 clientChannel.flush();
             });
             awaitRequests();
@@ -211,7 +211,7 @@ public class InboundHttp2ToHttpAdapterTest {
                     .add(HttpHeaderNames.COOKIE, "a=b; c=d")
                     .add(HttpHeaderNames.COOKIE, "e=f");
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
                 clientChannel.flush();
             });
             awaitRequests();
@@ -235,7 +235,7 @@ public class InboundHttp2ToHttpAdapterTest {
                 .add(new AsciiString("çã".getBytes(CharsetUtil.UTF_8)),
                         new AsciiString("Ãã".getBytes(CharsetUtil.UTF_8)));
         runInChannel(clientChannel, () -> {
-            clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true, newPromiseClient());
+            clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, true);
             clientChannel.flush();
         });
         awaitResponses();
@@ -257,9 +257,8 @@ public class InboundHttp2ToHttpAdapterTest {
             final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
                     new AsciiString("/some/path/resource2"));
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
-                clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true,
-                                                  newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
+                clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true);
                 clientChannel.flush();
             });
             awaitRequests();
@@ -288,12 +287,12 @@ public class InboundHttp2ToHttpAdapterTest {
                     new AsciiString("/some/path/resource2"));
             final int midPoint = text.length() / 2;
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
                 clientHandler.encoder().writeData(
-                        ctxClient(), 3, content.retainedSlice(0, midPoint), 0, false, newPromiseClient());
+                        ctxClient(), 3, content.retainedSlice(0, midPoint), 0, false);
                 clientHandler.encoder().writeData(
                         ctxClient(), 3, content.retainedSlice(midPoint, text.length() - midPoint),
-                        0, true, newPromiseClient());
+                        0, true);
                 clientChannel.flush();
             });
             awaitRequests();
@@ -321,10 +320,10 @@ public class InboundHttp2ToHttpAdapterTest {
             final Http2Headers http2Headers = new DefaultHttp2Headers().method(new AsciiString("GET")).path(
                     new AsciiString("/some/path/resource2"));
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
-                clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false, newPromiseClient());
-                clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false, newPromiseClient());
-                clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, true, newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
+                clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false);
+                clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, false);
+                clientHandler.encoder().writeData(ctxClient(), 3, content.retain(), 0, true);
                 clientChannel.flush();
             });
             awaitRequests();
@@ -360,10 +359,9 @@ public class InboundHttp2ToHttpAdapterTest {
                     .set(new AsciiString("foo2"), new AsciiString("goo2"))
                     .add(new AsciiString("foo2"), new AsciiString("goo3"));
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
-                clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, false,
-                                                  newPromiseClient());
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers2, 0, true, newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
+                clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, false);
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers2, 0, true);
                 clientChannel.flush();
             });
             awaitRequests();
@@ -402,14 +400,12 @@ public class InboundHttp2ToHttpAdapterTest {
             final Http2Headers http2Headers2 = new DefaultHttp2Headers().method(new AsciiString("PUT")).path(
                     new AsciiString("/some/path/resource2"));
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
                 clientHandler.encoder().writeHeaders(ctxClient(), 5, http2Headers2, 3, (short) 123, true, 0,
-                        false, newPromiseClient());
+                        false);
                 clientChannel.flush(); // Headers are queued in the flow controller and so flush them.
-                clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true,
-                                                  newPromiseClient());
-                clientHandler.encoder().writeData(ctxClient(), 5, content2.retainedDuplicate(), 0, true,
-                                                  newPromiseClient());
+                clientHandler.encoder().writeData(ctxClient(), 3, content.retainedDuplicate(), 0, true);
+                clientHandler.encoder().writeData(ctxClient(), 5, content2.retainedDuplicate(), 0, true);
                 clientChannel.flush();
             });
             awaitRequests();
@@ -456,7 +452,7 @@ public class InboundHttp2ToHttpAdapterTest {
             final Http2Headers http2Headers3 = new DefaultHttp2Headers().method(new AsciiString("GET"))
                     .path(new AsciiString("/push/test"));
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers3, 0, true, newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers3, 0, true);
                 clientChannel.flush();
             });
             awaitRequests();
@@ -475,12 +471,10 @@ public class InboundHttp2ToHttpAdapterTest {
                     .scheme(new AsciiString("https"))
                     .authority(new AsciiString("example.org"));
             runInChannel(serverConnectedChannel, () -> {
-                serverHandler.encoder().writeHeaders(ctxServer(), 3, http2Headers, 0, false, newPromiseServer());
-                serverHandler.encoder().writePushPromise(ctxServer(), 3, 2, http2Headers2, 0, newPromiseServer());
-                serverHandler.encoder().writeData(ctxServer(), 3, content.retainedDuplicate(), 0, true,
-                                                  newPromiseServer());
-                serverHandler.encoder().writeData(ctxServer(), 5, content2.retainedDuplicate(), 0, true,
-                                                  newPromiseServer());
+                serverHandler.encoder().writeHeaders(ctxServer(), 3, http2Headers, 0, false);
+                serverHandler.encoder().writePushPromise(ctxServer(), 3, 2, http2Headers2, 0);
+                serverHandler.encoder().writeData(ctxServer(), 3, content.retainedDuplicate(), 0, true);
+                serverHandler.encoder().writeData(ctxServer(), 5, content2.retainedDuplicate(), 0, true);
                 serverConnectedChannel.flush();
             });
             awaitResponses();
@@ -518,7 +512,7 @@ public class InboundHttp2ToHttpAdapterTest {
 
         try {
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false, newPromiseClient());
+                clientHandler.encoder().writeHeaders(ctxClient(), 3, http2Headers, 0, false);
                 clientChannel.flush();
             });
 
@@ -528,8 +522,7 @@ public class InboundHttp2ToHttpAdapterTest {
             httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, 0);
             final Http2Headers http2HeadersResponse = new DefaultHttp2Headers().status(new AsciiString("100"));
             runInChannel(serverConnectedChannel, () -> {
-                serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse, 0, false,
-                                                     newPromiseServer());
+                serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse, 0, false);
                 serverConnectedChannel.flush();
             });
 
@@ -538,8 +531,7 @@ public class InboundHttp2ToHttpAdapterTest {
             httpHeaders.setInt(HttpHeaderNames.CONTENT_LENGTH, text.length());
             httpHeaders.remove(HttpHeaderNames.EXPECT);
             runInChannel(clientChannel, () -> {
-                clientHandler.encoder().writeData(ctxClient(), 3, payload.retainedDuplicate(), 0, true,
-                                                  newPromiseClient());
+                clientHandler.encoder().writeData(ctxClient(), 3, payload.retainedDuplicate(), 0, true);
                 clientChannel.flush();
             });
 
@@ -551,8 +543,7 @@ public class InboundHttp2ToHttpAdapterTest {
 
             final Http2Headers http2HeadersResponse2 = new DefaultHttp2Headers().status(new AsciiString("200"));
             runInChannel(serverConnectedChannel, () -> {
-                serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse2, 0, true,
-                                                     newPromiseServer());
+                serverHandler.encoder().writeHeaders(ctxServer(), 3, http2HeadersResponse2, 0, true);
                 serverConnectedChannel.flush();
             });
 
@@ -586,7 +577,7 @@ public class InboundHttp2ToHttpAdapterTest {
         boostrapEnv(1, 1, 2);
         final Http2Settings settings = new Http2Settings().pushEnabled(true);
         runInChannel(clientChannel, () -> {
-            clientHandler.encoder().writeSettings(ctxClient(), settings, newPromiseClient());
+            clientHandler.encoder().writeSettings(ctxClient(), settings);
             clientChannel.flush();
         });
         assertTrue(settingsLatch.await(5, SECONDS));
diff --git a/codec-http2/src/test/java/io/netty/handler/codec/http2/StreamBufferingEncoderTest.java b/codec-http2/src/test/java/io/netty/handler/codec/http2/StreamBufferingEncoderTest.java
index a15abaf4d8..9c1897761b 100644
--- a/codec-http2/src/test/java/io/netty/handler/codec/http2/StreamBufferingEncoderTest.java
+++ b/codec-http2/src/test/java/io/netty/handler/codec/http2/StreamBufferingEncoderTest.java
@@ -106,17 +106,16 @@ public class StreamBufferingEncoderTest {
         when(writer.configuration()).thenReturn(configuration);
         when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
         when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
-        when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
-                any(Promise.class))).thenAnswer(successAnswer());
-        when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class))).thenAnswer(
+        when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean()))
+                .thenAnswer(successAnswer());
+        when(writer.writeRstStream(eq(ctx), anyInt(), anyLong())).thenAnswer(
                 successAnswer());
-        when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
-                any(Promise.class)))
+        when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class)))
                 .thenAnswer(successAnswer());
         when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
-            anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(noopAnswer());
+            anyInt(), anyBoolean())).thenAnswer(noopAnswer());
         when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
-            anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Promise.class)))
+            anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean()))
             .thenAnswer(noopAnswer());
 
         connection = new DefaultHttp2Connection(false);
@@ -138,6 +137,13 @@ public class StreamBufferingEncoderTest {
         when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
         when(executor.inEventLoop()).thenReturn(true);
         doAnswer((Answer>) invocation -> newPromise()).when(ctx).newPromise();
+        doAnswer((Answer>) invocation -> ImmediateEventExecutor.INSTANCE.newSucceededFuture(null))
+                .when(ctx).newSucceededFuture();
+
+        doAnswer((Answer>) invocation ->
+                ImmediateEventExecutor.INSTANCE.newFailedFuture(invocation.getArgument(0)))
+                .when(ctx).newFailedFuture(any(Throwable.class));
+
         when(ctx.executor()).thenReturn(executor);
         when(channel.isActive()).thenReturn(false);
         when(channel.config()).thenReturn(config);
@@ -159,34 +165,34 @@ public class StreamBufferingEncoderTest {
 
     @Test
     public void multipleWritesToActiveStream() {
-        encoder.writeSettingsAck(ctx, newPromise());
-        encoderWriteHeaders(3, newPromise());
+        encoder.writeSettingsAck(ctx);
+        encoderWriteHeaders(3);
         assertEquals(0, encoder.numBufferedStreams());
         ByteBuf data = data();
         final int expectedBytes = data.readableBytes() * 3;
-        encoder.writeData(ctx, 3, data, 0, false, newPromise());
-        encoder.writeData(ctx, 3, data(), 0, false, newPromise());
-        encoder.writeData(ctx, 3, data(), 0, false, newPromise());
-        encoderWriteHeaders(3, newPromise());
+        encoder.writeData(ctx, 3, data, 0, false);
+        encoder.writeData(ctx, 3, data(), 0, false);
+        encoder.writeData(ctx, 3, data(), 0, false);
+        encoderWriteHeaders(3);
 
         writeVerifyWriteHeaders(times(1), 3);
         // Contiguous data writes are coalesced
         ArgumentCaptor bufCaptor = ArgumentCaptor.forClass(ByteBuf.class);
         verify(writer, times(1)).writeData(any(ChannelHandlerContext.class), eq(3),
-                bufCaptor.capture(), eq(0), eq(false), any(Promise.class));
+                bufCaptor.capture(), eq(0), eq(false));
         assertEquals(expectedBytes, bufCaptor.getValue().readableBytes());
     }
 
     @Test
     public void ensureCanCreateNextStreamWhenStreamCloses() {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(1);
 
-        encoderWriteHeaders(3, newPromise());
+        encoderWriteHeaders(3);
         assertEquals(0, encoder.numBufferedStreams());
 
         // This one gets buffered.
-        encoderWriteHeaders(5, newPromise());
+        encoderWriteHeaders(5);
         assertEquals(1, connection.numActiveStreams());
         assertEquals(1, encoder.numBufferedStreams());
 
@@ -206,45 +212,44 @@ public class StreamBufferingEncoderTest {
 
     @Test
     public void alternatingWritesToActiveAndBufferedStreams() {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(1);
 
-        encoderWriteHeaders(3, newPromise());
+        encoderWriteHeaders(3);
         assertEquals(0, encoder.numBufferedStreams());
 
-        encoderWriteHeaders(5, newPromise());
+        encoderWriteHeaders(5);
         assertEquals(1, connection.numActiveStreams());
         assertEquals(1, encoder.numBufferedStreams());
 
-        encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, false, newPromise());
+        encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, false);
         writeVerifyWriteHeaders(times(1), 3);
-        encoder.writeData(ctx, 5, EMPTY_BUFFER, 0, false, newPromise());
+        encoder.writeData(ctx, 5, EMPTY_BUFFER, 0, false);
         verify(writer, never())
-                .writeData(eq(ctx), eq(5), any(ByteBuf.class), eq(0), eq(false), eq(newPromise()));
+                .writeData(eq(ctx), eq(5), any(ByteBuf.class), eq(0), eq(false));
     }
 
     @Test
     public void bufferingNewStreamFailsAfterGoAwayReceived() throws Http2Exception {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(0);
         connection.goAwayReceived(1, 8, EMPTY_BUFFER);
 
-        Promise promise = newPromise();
-        encoderWriteHeaders(3, promise);
+        Future future = encoderWriteHeaders(3);
         assertEquals(0, encoder.numBufferedStreams());
-        assertTrue(promise.isDone());
-        assertFalse(promise.isSuccess());
+        assertTrue(future.isDone());
+        assertFalse(future.isSuccess());
     }
 
     @Test
     public void receivingGoAwayFailsBufferedStreams() throws Http2Exception {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(5);
 
         int streamId = 3;
         List> futures = new ArrayList<>();
         for (int i = 0; i < 9; i++) {
-            futures.add(encoderWriteHeaders(streamId, newPromise()));
+            futures.add(encoderWriteHeaders(streamId));
             streamId += 2;
         }
         assertEquals(5, connection.numActiveStreams());
@@ -266,11 +271,11 @@ public class StreamBufferingEncoderTest {
 
     @Test
     public void receivingGoAwayFailsNewStreamIfMaxConcurrentStreamsReached() throws Http2Exception {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(1);
-        encoderWriteHeaders(3, newPromise());
+        encoderWriteHeaders(3);
         connection.goAwayReceived(11, 8, EMPTY_BUFFER);
-        Future f = encoderWriteHeaders(5, newPromise());
+        Future f = encoderWriteHeaders(5);
 
         assertTrue(f.awaitUninterruptibly().cause() instanceof Http2GoAwayException);
         assertEquals(0, encoder.numBufferedStreams());
@@ -278,40 +283,41 @@ public class StreamBufferingEncoderTest {
 
     @Test
     public void sendingGoAwayShouldNotFailStreams() {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(1);
 
         when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
-              anyBoolean(), any(Promise.class))).thenAnswer(successAnswer());
+              anyBoolean())).thenAnswer(successAnswer());
         when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
-              anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(successAnswer());
+              anyShort(), anyBoolean(), anyInt(), anyBoolean())).thenAnswer(successAnswer());
 
-        Future f1 = encoderWriteHeaders(3, newPromise());
+        Future f1 = encoderWriteHeaders(3);
         assertEquals(0, encoder.numBufferedStreams());
-        Future f2 = encoderWriteHeaders(5, newPromise());
+        Future f2 = encoderWriteHeaders(5);
         assertEquals(1, encoder.numBufferedStreams());
-        Future f3 = encoderWriteHeaders(7, newPromise());
+        Future f3 = encoderWriteHeaders(7);
         assertEquals(2, encoder.numBufferedStreams());
 
         ByteBuf empty = Unpooled.buffer(0);
-        encoder.writeGoAway(ctx, 3, CANCEL.code(), empty, newPromise());
+        encoder.writeGoAway(ctx, 3, CANCEL.code(), empty);
 
         assertEquals(1, connection.numActiveStreams());
         assertEquals(2, encoder.numBufferedStreams());
-        assertFalse(f1.isDone());
+        // As the first stream did exists the first future should be done.
+        assertTrue(f1.isDone());
         assertFalse(f2.isDone());
         assertFalse(f3.isDone());
     }
 
     @Test
     public void endStreamDoesNotFailBufferedStream() {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(0);
 
-        encoderWriteHeaders(3, newPromise());
+        encoderWriteHeaders(3);
         assertEquals(1, encoder.numBufferedStreams());
 
-        encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, true, newPromise());
+        encoder.writeData(ctx, 3, EMPTY_BUFFER, 0, true);
 
         assertEquals(0, connection.numActiveStreams());
         assertEquals(1, encoder.numBufferedStreams());
@@ -319,7 +325,7 @@ public class StreamBufferingEncoderTest {
         // Simulate that we received a SETTINGS frame which
         // increased MAX_CONCURRENT_STREAMS to 1.
         setMaxConcurrentStreams(1);
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
 
         assertEquals(1, connection.numActiveStreams());
         assertEquals(0, encoder.numBufferedStreams());
@@ -328,61 +334,60 @@ public class StreamBufferingEncoderTest {
 
     @Test
     public void rstStreamClosesBufferedStream() {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(0);
 
-        encoderWriteHeaders(3, newPromise());
+        encoderWriteHeaders(3);
         assertEquals(1, encoder.numBufferedStreams());
 
-        Promise rstStreamPromise = newPromise();
-        encoder.writeRstStream(ctx, 3, CANCEL.code(), rstStreamPromise);
-        assertTrue(rstStreamPromise.isSuccess());
+        Future rstStreamFuture = encoder.writeRstStream(ctx, 3, CANCEL.code());
+        assertTrue(rstStreamFuture.isSuccess());
         assertEquals(0, encoder.numBufferedStreams());
     }
 
     @Test
     public void bufferUntilActiveStreamsAreReset() throws Exception {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(1);
 
-        encoderWriteHeaders(3, newPromise());
+        encoderWriteHeaders(3);
         assertEquals(0, encoder.numBufferedStreams());
-        encoderWriteHeaders(5, newPromise());
+        encoderWriteHeaders(5);
         assertEquals(1, encoder.numBufferedStreams());
-        encoderWriteHeaders(7, newPromise());
+        encoderWriteHeaders(7);
         assertEquals(2, encoder.numBufferedStreams());
 
         writeVerifyWriteHeaders(times(1), 3);
         writeVerifyWriteHeaders(never(), 5);
         writeVerifyWriteHeaders(never(), 7);
 
-        encoder.writeRstStream(ctx, 3, CANCEL.code(), newPromise());
+        encoder.writeRstStream(ctx, 3, CANCEL.code());
         connection.remote().flowController().writePendingBytes();
         writeVerifyWriteHeaders(times(1), 5);
         writeVerifyWriteHeaders(never(), 7);
         assertEquals(1, connection.numActiveStreams());
         assertEquals(1, encoder.numBufferedStreams());
 
-        encoder.writeRstStream(ctx, 5, CANCEL.code(), newPromise());
+        encoder.writeRstStream(ctx, 5, CANCEL.code());
         connection.remote().flowController().writePendingBytes();
         writeVerifyWriteHeaders(times(1), 7);
         assertEquals(1, connection.numActiveStreams());
         assertEquals(0, encoder.numBufferedStreams());
 
-        encoder.writeRstStream(ctx, 7, CANCEL.code(), newPromise());
+        encoder.writeRstStream(ctx, 7, CANCEL.code());
         assertEquals(0, connection.numActiveStreams());
         assertEquals(0, encoder.numBufferedStreams());
     }
 
     @Test
     public void bufferUntilMaxStreamsIncreased() {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(2);
 
-        encoderWriteHeaders(3, newPromise());
-        encoderWriteHeaders(5, newPromise());
-        encoderWriteHeaders(7, newPromise());
-        encoderWriteHeaders(9, newPromise());
+        encoderWriteHeaders(3);
+        encoderWriteHeaders(5);
+        encoderWriteHeaders(7);
+        encoderWriteHeaders(9);
         assertEquals(2, encoder.numBufferedStreams());
 
         writeVerifyWriteHeaders(times(1), 3);
@@ -393,13 +398,13 @@ public class StreamBufferingEncoderTest {
         // Simulate that we received a SETTINGS frame which
         // increased MAX_CONCURRENT_STREAMS to 5.
         setMaxConcurrentStreams(5);
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
 
         assertEquals(0, encoder.numBufferedStreams());
         writeVerifyWriteHeaders(times(1), 7);
         writeVerifyWriteHeaders(times(1), 9);
 
-        encoderWriteHeaders(11, newPromise());
+        encoderWriteHeaders(11);
 
         writeVerifyWriteHeaders(times(1), 11);
 
@@ -411,7 +416,7 @@ public class StreamBufferingEncoderTest {
         int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS;
         int numStreams = initialLimit * 2;
         for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) {
-            encoderWriteHeaders(nextStreamId, newPromise());
+            encoderWriteHeaders(nextStreamId);
             if (ix < initialLimit) {
                 writeVerifyWriteHeaders(times(1), nextStreamId);
             } else {
@@ -432,7 +437,7 @@ public class StreamBufferingEncoderTest {
         int initialLimit = SMALLEST_MAX_CONCURRENT_STREAMS;
         int numStreams = initialLimit * 2;
         for (int ix = 0, nextStreamId = 3; ix < numStreams; ++ix, nextStreamId += 2) {
-            encoderWriteHeaders(nextStreamId, newPromise());
+            encoderWriteHeaders(nextStreamId);
             if (ix < initialLimit) {
                 writeVerifyWriteHeaders(times(1), nextStreamId);
             } else {
@@ -452,13 +457,13 @@ public class StreamBufferingEncoderTest {
     public void exhaustedStreamsDoNotBuffer() throws Http2Exception {
         // Write the highest possible stream ID for the client.
         // This will cause the next stream ID to be negative.
-        encoderWriteHeaders(Integer.MAX_VALUE, newPromise());
+        encoderWriteHeaders(Integer.MAX_VALUE);
 
         // Disallow any further streams.
         setMaxConcurrentStreams(0);
 
         // Simulate numeric overflow for the next stream ID.
-        Future f = encoderWriteHeaders(-1, newPromise());
+        Future f = encoderWriteHeaders(-1);
 
         // Verify that the write fails.
         assertNotNull(f.awaitUninterruptibly().cause());
@@ -466,18 +471,17 @@ public class StreamBufferingEncoderTest {
 
     @Test
     public void closedBufferedStreamReleasesByteBuf() {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         setMaxConcurrentStreams(0);
         ByteBuf data = mock(ByteBuf.class);
-        Future f1 = encoderWriteHeaders(3, newPromise());
+        Future f1 = encoderWriteHeaders(3);
         assertEquals(1, encoder.numBufferedStreams());
-        Future f2 = encoder.writeData(ctx, 3, data, 0, false, newPromise());
+        Future f2 = encoder.writeData(ctx, 3, data, 0, false);
 
-        Promise rstPromise = mock(Promise.class);
-        encoder.writeRstStream(ctx, 3, CANCEL.code(), rstPromise);
+        Future rstFuture = encoder.writeRstStream(ctx, 3, CANCEL.code());
 
         assertEquals(0, encoder.numBufferedStreams());
-        verify(rstPromise).setSuccess(null);
+        assertTrue(rstFuture.isSuccess());
         assertTrue(f1.isSuccess());
         assertTrue(f2.isSuccess());
         verify(data).release();
@@ -485,12 +489,12 @@ public class StreamBufferingEncoderTest {
 
     @Test
     public void closeShouldCancelAllBufferedStreams() throws Http2Exception {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         connection.local().maxActiveStreams(0);
 
-        Future f1 = encoderWriteHeaders(3, newPromise());
-        Future f2 = encoderWriteHeaders(5, newPromise());
-        Future f3 = encoderWriteHeaders(7, newPromise());
+        Future f1 = encoderWriteHeaders(3);
+        Future f2 = encoderWriteHeaders(5);
+        Future f3 = encoderWriteHeaders(7);
 
         encoder.close();
         assertNotNull(f1.awaitUninterruptibly().cause());
@@ -500,10 +504,10 @@ public class StreamBufferingEncoderTest {
 
     @Test
     public void headersAfterCloseShouldImmediatelyFail() {
-        encoder.writeSettingsAck(ctx, newPromise());
+        encoder.writeSettingsAck(ctx);
         encoder.close();
 
-        Future f = encoderWriteHeaders(3, newPromise());
+        Future f = encoderWriteHeaders(3);
         assertNotNull(f.cause());
     }
 
@@ -517,12 +521,13 @@ public class StreamBufferingEncoderTest {
         }
     }
 
-    private Future encoderWriteHeaders(int streamId, Promise promise) {
-        encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT,
-                             false, 0, false, promise);
+    private Future encoderWriteHeaders(int streamId) {
+        Future future =
+                encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT,
+                             false, 0, false);
         try {
             encoder.flowController().writePendingBytes();
-            return promise;
+            return future;
         } catch (Http2Exception e) {
             throw new RuntimeException(e);
         }
@@ -531,7 +536,7 @@ public class StreamBufferingEncoderTest {
     private void writeVerifyWriteHeaders(VerificationMode mode, int streamId) {
         verify(writer, mode).writeHeaders(eq(ctx), eq(streamId), any(Http2Headers.class), eq(0),
                                           eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0),
-                                          eq(false), any(Promise.class));
+                                          eq(false));
     }
 
     private static Answer> successAnswer() {
diff --git a/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2Handler.java b/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2Handler.java
index df20d34f57..b63a70be82 100644
--- a/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2Handler.java
+++ b/example/src/main/java/io/netty/example/http2/helloworld/server/HelloWorldHttp2Handler.java
@@ -88,8 +88,8 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
     private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {
         // Send a frame for the response status
         Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
-        encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());
-        encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise());
+        encoder().writeHeaders(ctx, streamId, headers, 0, false);
+        encoder().writeData(ctx, streamId, payload, 0, true);
 
         // no need to call flush as channelReadComplete(...) will take care of it.
     }
diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
index 660f8a7887..485ea169c5 100644
--- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
+++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java
@@ -602,17 +602,8 @@ public class SslHandler extends ByteToMessageDecoder {
      * {@link ChannelHandlerContext#close()}
      */
     public Future closeOutbound() {
-        return closeOutbound(ctx.newPromise());
-    }
-
-    /**
-     * Sends an SSL {@code close_notify} message to the specified channel and
-     * destroys the underlying {@link SSLEngine}. This will not close the underlying
-     * {@link Channel}. If you want to also close the {@link Channel} use {@link Channel#close()} or
-     * {@link ChannelHandlerContext#close()}
-     */
-    public Future closeOutbound(final Promise promise) {
         final ChannelHandlerContext ctx = this.ctx;
+        Promise promise = ctx.newPromise();
         if (ctx.executor().inEventLoop()) {
             closeOutbound0(promise);
         } else {
diff --git a/microbench/src/main/java/io/netty/handler/codec/http2/Http2FrameWriterDataBenchmark.java b/microbench/src/main/java/io/netty/handler/codec/http2/Http2FrameWriterDataBenchmark.java
index f0ffd2ca65..3509cc54c7 100644
--- a/microbench/src/main/java/io/netty/handler/codec/http2/Http2FrameWriterDataBenchmark.java
+++ b/microbench/src/main/java/io/netty/handler/codec/http2/Http2FrameWriterDataBenchmark.java
@@ -101,14 +101,14 @@ public class Http2FrameWriterDataBenchmark extends AbstractMicrobenchmark {
     @Benchmark
     @BenchmarkMode(Mode.AverageTime)
     public void newWriter() {
-        writer.writeData(ctx, 3, payload.retain(), padding, true, ctx.newPromise());
+        writer.writeData(ctx, 3, payload.retain(), padding, true);
         ctx.flush();
     }
 
     @Benchmark
     @BenchmarkMode(Mode.AverageTime)
     public void oldWriter() {
-        oldWriter.writeData(ctx, 3, payload.retain(), padding, true, ctx.newPromise());
+        oldWriter.writeData(ctx, 3, payload.retain(), padding, true);
         ctx.flush();
     }
 
@@ -118,9 +118,9 @@ public class Http2FrameWriterDataBenchmark extends AbstractMicrobenchmark {
         private final int maxFrameSize = DEFAULT_MAX_FRAME_SIZE;
         @Override
         public Future writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
-                                      int padding, boolean endStream, Promise promise) {
+                                      int padding, boolean endStream) {
             final Http2CodecUtil.SimpleChannelPromiseAggregator promiseAggregator =
-                    new Http2CodecUtil.SimpleChannelPromiseAggregator(promise, ctx.executor());
+                    new Http2CodecUtil.SimpleChannelPromiseAggregator(ctx.newPromise(), ctx.executor());
             final DataFrameHeader header = new DataFrameHeader(ctx, streamId);
             boolean needToReleaseHeaders = true;
             boolean needToReleaseData = true;
diff --git a/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2Handler.java b/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2Handler.java
index 7af4caac0a..e056142b2c 100644
--- a/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2Handler.java
+++ b/testsuite-http2/src/main/java/io/netty/testsuite/http2/HelloWorldHttp2Handler.java
@@ -88,8 +88,8 @@ public final class HelloWorldHttp2Handler extends Http2ConnectionHandler impleme
     private void sendResponse(ChannelHandlerContext ctx, int streamId, ByteBuf payload) {
         // Send a frame for the response status
         Http2Headers headers = new DefaultHttp2Headers().status(OK.codeAsText());
-        encoder().writeHeaders(ctx, streamId, headers, 0, false, ctx.newPromise());
-        encoder().writeData(ctx, streamId, payload, 0, true, ctx.newPromise());
+        encoder().writeHeaders(ctx, streamId, headers, 0, false);
+        encoder().writeData(ctx, streamId, payload, 0, true);
 
         // no need to call flush as channelReadComplete(...) will take care of it.
     }