Clean up Future/Promises API (#11575)

Motivation:
The generics for the existing futures, promises, and listeners are too complicated.
This complication comes from the existence of `ChannelPromise` and `ChannelFuture`, which forces listeners to care about the particular _type_ of future being listened on.

Modification:
* Add a `FutureContextListener` which can take a context object as an additional argument. This allows our listeners to have the channel piped through to them, so they don't need to rely on the `ChannelFuture.channel()` method.
* Make the `FutureListener`, along with the `FutureContextListener` sibling, the default listener API, retiring the `GenericFutureListener` since we no longer need to abstract over the type of the future.
* Change all uses of `ChannelPromise` to `Promise<Void>`.
* Change all uses of `ChannelFuture` to `Future<Void>`.
* Change all uses of `GenericFutureListener` to either `FutureListener` or `FutureContextListener` as needed.
* Remove `ChannelFutureListener` and `GenericFutureListener`.
* Introduce a `ChannelFutureListeners` enum to house the constants that previously lived in `ChannelFutureListener`. These constants now implement `FutureContextListener` and take the `Channel` as a context.
* Remove `ChannelPromise` and `ChannelFuture` — all usages now rely on the plain `Future` and `Promise` APIs.
* Add static factory methods to `DefaultPromise` that allow us to create promises that are initialised as successful or failed.
* Remove `CompleteFuture`, `SucceededFuture`, `FailedFuture`, `CompleteChannelFuture`, `SucceededChannelFuture`, and `FailedChannelFuture`.
* Remove `ChannelPromiseNotifier`.

Result:
Cleaner generics and more straight forward code.
This commit is contained in:
Chris Vest 2021-08-20 09:55:16 +02:00 committed by GitHub
parent afd812c3a4
commit 7971a252a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
304 changed files with 3547 additions and 5233 deletions

View File

@ -16,19 +16,20 @@
package io.netty.handler.codec.haproxy; package io.netty.handler.codec.haproxy;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.ProtocolDetectionResult; import io.netty.handler.codec.ProtocolDetectionResult;
import io.netty.handler.codec.ProtocolDetectionState; import io.netty.handler.codec.ProtocolDetectionState;
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily; import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily;
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.TransportProtocol; import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.TransportProtocol;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.List; import java.util.List;
import static io.netty.buffer.Unpooled.*; import static io.netty.buffer.Unpooled.buffer;
import static io.netty.buffer.Unpooled.copiedBuffer;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
@ -224,7 +225,7 @@ public class HAProxyMessageDecoderTest {
@Test @Test
public void testCloseOnInvalid() { public void testCloseOnInvalid() {
ChannelFuture closeFuture = ch.closeFuture(); Future<Void> closeFuture = ch.closeFuture();
String header = "GET / HTTP/1.1\r\n"; String header = "GET / HTTP/1.1\r\n";
try { try {
ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII)); ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII));

View File

@ -17,24 +17,24 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.util.Attribute; import io.netty.util.Attribute;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.internal.ObjectUtil; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.Objects;
abstract class DelegatingChannelHandlerContext implements ChannelHandlerContext { abstract class DelegatingChannelHandlerContext implements ChannelHandlerContext {
private final ChannelHandlerContext ctx; private final ChannelHandlerContext ctx;
DelegatingChannelHandlerContext(ChannelHandlerContext ctx) { DelegatingChannelHandlerContext(ChannelHandlerContext ctx) {
this.ctx = ObjectUtil.checkNotNull(ctx, "ctx"); this.ctx = Objects.requireNonNull(ctx, "ctx");
} }
@Override @Override
@ -150,108 +150,108 @@ abstract class DelegatingChannelHandlerContext implements ChannelHandlerContext
} }
@Override @Override
public ChannelFuture bind(SocketAddress localAddress) { public Future<Void> bind(SocketAddress localAddress) {
return ctx.bind(localAddress); return ctx.bind(localAddress);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress) { public Future<Void> connect(SocketAddress remoteAddress) {
return ctx.connect(remoteAddress); return ctx.connect(remoteAddress);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return ctx.connect(remoteAddress, localAddress); return ctx.connect(remoteAddress, localAddress);
} }
@Override @Override
public ChannelFuture disconnect() { public Future<Void> disconnect() {
return ctx.disconnect(); return ctx.disconnect();
} }
@Override @Override
public ChannelFuture close() { public Future<Void> close() {
return ctx.close(); return ctx.close();
} }
@Override @Override
public ChannelFuture deregister() { public Future<Void> deregister() {
return ctx.deregister(); return ctx.deregister();
} }
@Override @Override
public ChannelFuture register() { public Future<Void> register() {
return ctx.register(); return ctx.register();
} }
@Override @Override
public ChannelFuture register(ChannelPromise promise) { public Future<Void> register(Promise<Void> promise) {
return ctx.register(promise); return ctx.register(promise);
} }
@Override @Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return ctx.bind(localAddress, promise); return ctx.bind(localAddress, promise);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, promise); return ctx.connect(remoteAddress, promise);
} }
@Override @Override
public ChannelFuture connect( public Future<Void> connect(
SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, localAddress, promise); return ctx.connect(remoteAddress, localAddress, promise);
} }
@Override @Override
public ChannelFuture disconnect(ChannelPromise promise) { public Future<Void> disconnect(Promise<Void> promise) {
return ctx.disconnect(promise); return ctx.disconnect(promise);
} }
@Override @Override
public ChannelFuture close(ChannelPromise promise) { public Future<Void> close(Promise<Void> promise) {
return ctx.close(promise); return ctx.close(promise);
} }
@Override @Override
public ChannelFuture deregister(ChannelPromise promise) { public Future<Void> deregister(Promise<Void> promise) {
return ctx.deregister(promise); return ctx.deregister(promise);
} }
@Override @Override
public ChannelFuture write(Object msg) { public Future<Void> write(Object msg) {
return ctx.write(msg); return ctx.write(msg);
} }
@Override @Override
public ChannelFuture write(Object msg, ChannelPromise promise) { public Future<Void> write(Object msg, Promise<Void> promise) {
return ctx.write(msg, promise); return ctx.write(msg, promise);
} }
@Override @Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return ctx.writeAndFlush(msg, promise); return ctx.writeAndFlush(msg, promise);
} }
@Override @Override
public ChannelFuture writeAndFlush(Object msg) { public Future<Void> writeAndFlush(Object msg) {
return ctx.writeAndFlush(msg); return ctx.writeAndFlush(msg);
} }
@Override @Override
public ChannelPromise newPromise() { public Promise<Void> newPromise() {
return ctx.newPromise(); return ctx.newPromise();
} }
@Override @Override
public ChannelFuture newSucceededFuture() { public Future<Void> newSucceededFuture() {
return ctx.newSucceededFuture(); return ctx.newSucceededFuture();
} }
@Override @Override
public ChannelFuture newFailedFuture(Throwable cause) { public Future<Void> newFailedFuture(Throwable cause) {
return ctx.newFailedFuture(cause); return ctx.newFailedFuture(cause);
} }
} }

View File

@ -36,7 +36,7 @@ import io.netty.handler.stream.ChunkedInput;
* *
* HttpContentChunkedInput httpChunkWriter = new HttpChunkedInput( * HttpContentChunkedInput httpChunkWriter = new HttpChunkedInput(
* new ChunkedFile(&quot;/tmp/myfile.txt&quot;)); * new ChunkedFile(&quot;/tmp/myfile.txt&quot;));
* ChannelFuture sendFileFuture = ctx.write(httpChunkWriter); * Future&lt;Void&gt; sendFileFuture = ctx.write(httpChunkWriter);
* } * }
* </pre> * </pre>
*/ */

View File

@ -15,8 +15,8 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.concurrent.Promise;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.Collection; import java.util.Collection;
@ -121,33 +121,33 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator {
} }
@Override @Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) { public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
ctx.bind(localAddress, promise); ctx.bind(localAddress, promise);
} }
@Override @Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) { Promise<Void> promise) {
ctx.connect(remoteAddress, localAddress, promise); ctx.connect(remoteAddress, localAddress, promise);
} }
@Override @Override
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) { public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.disconnect(promise); ctx.disconnect(promise);
} }
@Override @Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) { public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.close(promise); ctx.close(promise);
} }
@Override @Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) { public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.register(promise); ctx.register(promise);
} }
@Override @Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) { public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.deregister(promise); ctx.deregister(promise);
} }
@ -157,7 +157,7 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator {
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (!(msg instanceof HttpRequest)) { if (!(msg instanceof HttpRequest)) {
ctx.write(msg, promise); ctx.write(msg, promise);
return; return;

View File

@ -17,14 +17,13 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.MessageAggregator; import io.netty.handler.codec.MessageAggregator;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -250,15 +249,15 @@ public class HttpObjectAggregator
// If keep-alive is off and 'Expect: 100-continue' is missing, no need to leave the connection open. // If keep-alive is off and 'Expect: 100-continue' is missing, no need to leave the connection open.
if (oversized instanceof FullHttpMessage || if (oversized instanceof FullHttpMessage ||
!HttpUtil.is100ContinueExpected(oversized) && !HttpUtil.isKeepAlive(oversized)) { !HttpUtil.is100ContinueExpected(oversized) && !HttpUtil.isKeepAlive(oversized)) {
ChannelFuture future = ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate()); Future<Void> future = ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate());
future.addListener((ChannelFutureListener) future1 -> { future.addListener(future1 -> {
if (!future1.isSuccess()) { if (!future1.isSuccess()) {
logger.debug("Failed to send a 413 Request Entity Too Large.", future1.cause()); logger.debug("Failed to send a 413 Request Entity Too Large.", future1.cause());
} }
ctx.close(); ctx.close();
}); });
} else { } else {
ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener((ChannelFutureListener) future -> { ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener(future -> {
if (!future.isSuccess()) { if (!future.isSuccess()) {
logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause()); logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
ctx.close(); ctx.close();

View File

@ -108,7 +108,7 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
// Bypass the encoder in case of an empty buffer, so that the following idiom works: // Bypass the encoder in case of an empty buffer, so that the following idiom works:
// //
// ch.write(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); // ch.write(Unpooled.EMPTY_BUFFER).addListener(ch, ChannelFutureListeners.CLOSE);
// //
// See https://github.com/netty/netty/issues/2983 for more information. // See https://github.com/netty/netty/issues/2983 for more information.
if (msg instanceof ByteBufConvertible) { if (msg instanceof ByteBufConvertible) {

View File

@ -16,7 +16,7 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
@ -84,11 +84,11 @@ public class HttpServerExpectContinueHandler implements ChannelHandler {
// the expectation failed so we refuse the request. // the expectation failed so we refuse the request.
HttpResponse rejection = rejectResponse(req); HttpResponse rejection = rejectResponse(req);
ReferenceCountUtil.release(msg); ReferenceCountUtil.release(msg);
ctx.writeAndFlush(rejection).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); ctx.writeAndFlush(rejection).addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
return; return;
} }
ctx.writeAndFlush(accept).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); ctx.writeAndFlush(accept).addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
req.headers().remove(HttpHeaderNames.EXPECT); req.headers().remove(HttpHeaderNames.EXPECT);
} }
} }

View File

@ -15,13 +15,16 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.util.concurrent.Promise;
import static io.netty.handler.codec.http.HttpUtil.*; import static io.netty.handler.codec.http.HttpUtil.isContentLengthSet;
import static io.netty.handler.codec.http.HttpUtil.isKeepAlive;
import static io.netty.handler.codec.http.HttpUtil.isTransferEncodingChunked;
import static io.netty.handler.codec.http.HttpUtil.setKeepAlive;
/** /**
* HttpServerKeepAliveHandler helps close persistent connections when appropriate. * HttpServerKeepAliveHandler helps close persistent connections when appropriate.
@ -65,7 +68,7 @@ public class HttpServerKeepAliveHandler implements ChannelHandler {
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
// modify message on way out to add headers if needed // modify message on way out to add headers if needed
if (msg instanceof HttpResponse) { if (msg instanceof HttpResponse) {
final HttpResponse response = (HttpResponse) msg; final HttpResponse response = (HttpResponse) msg;
@ -82,7 +85,7 @@ public class HttpServerKeepAliveHandler implements ChannelHandler {
} }
} }
if (msg instanceof LastHttpContent && !shouldKeepAlive()) { if (msg instanceof LastHttpContent && !shouldKeepAlive()) {
promise.addListener(ChannelFutureListener.CLOSE); promise.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
} }
ctx.write(msg, promise); ctx.write(msg, promise);
} }

View File

@ -15,11 +15,11 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted; import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -360,7 +360,7 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
// of the write future and receiving data before the pipeline is // of the write future and receiving data before the pipeline is
// restructured. // restructured.
try { try {
final ChannelFuture writeComplete = ctx.writeAndFlush(upgradeResponse); Future<Void> writeComplete = ctx.writeAndFlush(upgradeResponse);
// Perform the upgrade to the new protocol. // Perform the upgrade to the new protocol.
sourceCodec.upgradeFrom(ctx); sourceCodec.upgradeFrom(ctx);
upgradeCodec.upgradeTo(ctx, request); upgradeCodec.upgradeTo(ctx, request);
@ -375,7 +375,7 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
// Add the listener last to avoid firing upgrade logic after // Add the listener last to avoid firing upgrade logic after
// the channel is already closed since the listener may fire // the channel is already closed since the listener may fire
// immediately if the write failed eagerly. // immediately if the write failed eagerly.
writeComplete.addListener(ChannelFutureListener.CLOSE_ON_FAILURE); writeComplete.addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
} finally { } finally {
// Release the event if the upgrade event wasn't fired. // Release the event if the upgrade event wasn't fired.
event.release(); event.release();

View File

@ -15,11 +15,9 @@
*/ */
package io.netty.handler.codec.http.cors; package io.netty.handler.codec.http.cors;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpHeaderValues;
@ -27,6 +25,8 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -69,7 +69,7 @@ public class CorsHandler implements ChannelHandler {
* config matches a certain origin, the first in the List will be used. * config matches a certain origin, the first in the List will be used.
* *
* @param configList List of {@link CorsConfig} * @param configList List of {@link CorsConfig}
* @param isShortCircuit Same as {@link CorsConfig#shortCircuit} but applicable to all supplied configs. * @param isShortCircuit Same as {@link CorsConfig#isShortCurcuit()} but applicable to all supplied configs.
*/ */
public CorsHandler(final List<CorsConfig> configList, boolean isShortCircuit) { public CorsHandler(final List<CorsConfig> configList, boolean isShortCircuit) {
checkNonEmpty(configList, "configList"); checkNonEmpty(configList, "configList");
@ -215,7 +215,7 @@ public class CorsHandler implements ChannelHandler {
} }
@Override @Override
public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) { public void write(final ChannelHandlerContext ctx, final Object msg, Promise<Void> promise) {
if (config != null && config.isCorsSupportEnabled() && msg instanceof HttpResponse) { if (config != null && config.isCorsSupportEnabled() && msg instanceof HttpResponse) {
final HttpResponse response = (HttpResponse) msg; final HttpResponse response = (HttpResponse) msg;
if (setOrigin(response)) { if (setOrigin(response)) {
@ -243,9 +243,9 @@ public class CorsHandler implements ChannelHandler {
HttpUtil.setKeepAlive(response, keepAlive); HttpUtil.setKeepAlive(response, keepAlive);
final ChannelFuture future = ctx.writeAndFlush(response); Future<Void> future = ctx.writeAndFlush(response);
if (!keepAlive) { if (!keepAlive) {
future.addListener(ChannelFutureListener.CLOSE); future.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
} }
} }
} }

View File

@ -17,7 +17,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.CorruptedFrameException; import io.netty.handler.codec.CorruptedFrameException;
@ -92,7 +92,7 @@ public class Utf8FrameValidator implements ChannelHandler {
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause instanceof CorruptedFrameException && ctx.channel().isOpen()) { if (cause instanceof CorruptedFrameException && ctx.channel().isOpen()) {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
} }
ctx.fireExceptionCaught(cause); ctx.fireExceptionCaught(cause);
} }

View File

@ -55,7 +55,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
@ -448,7 +448,7 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder
} }
closeMessage = new CloseWebSocketFrame(closeStatus, reasonText); closeMessage = new CloseWebSocketFrame(closeStatus, reasonText);
} }
ctx.writeAndFlush(closeMessage).addListener(ChannelFutureListener.CLOSE); ctx.writeAndFlush(closeMessage).addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
} }
throw ex; throw ex;
} }

View File

@ -15,16 +15,11 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import static java.util.Objects.requireNonNull;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundInvoker; import io.netty.channel.ChannelOutboundInvoker;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -39,14 +34,17 @@ import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpScheme; import io.netty.handler.codec.http.HttpScheme;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.net.URI; import java.net.URI;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import static java.util.Objects.requireNonNull;
/** /**
* Base class for web socket client handshake implementations * Base class for web socket client handshake implementations
*/ */
@ -236,7 +234,7 @@ public abstract class WebSocketClientHandshaker {
* @param channel * @param channel
* Channel * Channel
*/ */
public ChannelFuture handshake(Channel channel) { public Future<Void> handshake(Channel channel) {
requireNonNull(channel, "channel"); requireNonNull(channel, "channel");
return handshake(channel, channel.newPromise()); return handshake(channel, channel.newPromise());
} }
@ -247,9 +245,9 @@ public abstract class WebSocketClientHandshaker {
* @param channel * @param channel
* Channel * Channel
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the opening handshake is sent * the {@link Promise} to be notified when the opening handshake is sent
*/ */
public final ChannelFuture handshake(Channel channel, final ChannelPromise promise) { public final Future<Void> handshake(Channel channel, final Promise<Void> promise) {
ChannelPipeline pipeline = channel.pipeline(); ChannelPipeline pipeline = channel.pipeline();
HttpResponseDecoder decoder = pipeline.get(HttpResponseDecoder.class); HttpResponseDecoder decoder = pipeline.get(HttpResponseDecoder.class);
if (decoder == null) { if (decoder == null) {
@ -263,9 +261,9 @@ public abstract class WebSocketClientHandshaker {
FullHttpRequest request = newHandshakeRequest(); FullHttpRequest request = newHandshakeRequest();
channel.writeAndFlush(request).addListener((ChannelFutureListener) future -> { channel.writeAndFlush(request).addListener(channel, (ch, future) -> {
if (future.isSuccess()) { if (future.isSuccess()) {
ChannelPipeline p = future.channel().pipeline(); ChannelPipeline p = ch.pipeline();
ChannelHandlerContext ctx = p.context(HttpRequestEncoder.class); ChannelHandlerContext ctx = p.context(HttpRequestEncoder.class);
if (ctx == null) { if (ctx == null) {
ctx = p.context(HttpClientCodec.class); ctx = p.context(HttpClientCodec.class);
@ -277,7 +275,7 @@ public abstract class WebSocketClientHandshaker {
} }
p.addAfter(ctx.name(), "ws-encoder", newWebSocketEncoder()); p.addAfter(ctx.name(), "ws-encoder", newWebSocketEncoder());
promise.setSuccess(); promise.setSuccess(null);
} else { } else {
promise.setFailure(future.cause()); promise.setFailure(future.cause());
} }
@ -384,9 +382,9 @@ public abstract class WebSocketClientHandshaker {
* @param response * @param response
* HTTP response containing the closing handshake details * HTTP response containing the closing handshake details
* @return future * @return future
* the {@link ChannelFuture} which is notified once the handshake completes. * the {@link Future} which is notified once the handshake completes.
*/ */
public final ChannelFuture processHandshake(final Channel channel, HttpResponse response) { public final Future<Void> processHandshake(final Channel channel, HttpResponse response) {
return processHandshake(channel, response, channel.newPromise()); return processHandshake(channel, response, channel.newPromise());
} }
@ -398,16 +396,16 @@ public abstract class WebSocketClientHandshaker {
* @param response * @param response
* HTTP response containing the closing handshake details * HTTP response containing the closing handshake details
* @param promise * @param promise
* the {@link ChannelPromise} to notify once the handshake completes. * the {@link Promise} to notify once the handshake completes.
* @return future * @return future
* the {@link ChannelFuture} which is notified once the handshake completes. * the {@link Future} which is notified once the handshake completes.
*/ */
public final ChannelFuture processHandshake(final Channel channel, HttpResponse response, public final Future<Void> processHandshake(final Channel channel, HttpResponse response,
final ChannelPromise promise) { final Promise<Void> promise) {
if (response instanceof FullHttpResponse) { if (response instanceof FullHttpResponse) {
try { try {
finishHandshake(channel, (FullHttpResponse) response); finishHandshake(channel, (FullHttpResponse) response);
promise.setSuccess(); promise.setSuccess(null);
} catch (Throwable cause) { } catch (Throwable cause) {
promise.setFailure(cause); promise.setFailure(cause);
} }
@ -434,7 +432,7 @@ public abstract class WebSocketClientHandshaker {
ctx.pipeline().remove(this); ctx.pipeline().remove(this);
try { try {
finishHandshake(channel, msg); finishHandshake(channel, msg);
promise.setSuccess(); promise.setSuccess(null);
} catch (Throwable cause) { } catch (Throwable cause) {
promise.setFailure(cause); promise.setFailure(cause);
} }
@ -491,7 +489,7 @@ public abstract class WebSocketClientHandshaker {
* @param frame * @param frame
* Closing Frame that was received * Closing Frame that was received
*/ */
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame) { public Future<Void> close(Channel channel, CloseWebSocketFrame frame) {
requireNonNull(channel, "channel"); requireNonNull(channel, "channel");
return close(channel, frame, channel.newPromise()); return close(channel, frame, channel.newPromise());
} }
@ -500,16 +498,16 @@ public abstract class WebSocketClientHandshaker {
* Performs the closing handshake * Performs the closing handshake
* *
* When called from within a {@link ChannelHandler} you most likely want to use * When called from within a {@link ChannelHandler} you most likely want to use
* {@link #close(ChannelHandlerContext, CloseWebSocketFrame, ChannelPromise)}. * {@link #close(ChannelHandlerContext, CloseWebSocketFrame, Promise)}.
* *
* @param channel * @param channel
* Channel * Channel
* @param frame * @param frame
* Closing Frame that was received * Closing Frame that was received
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the closing handshake is done * the {@link Promise} to be notified when the closing handshake is done
*/ */
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame, ChannelPromise promise) { public Future<Void> close(Channel channel, CloseWebSocketFrame frame, Promise<Void> promise) {
requireNonNull(channel, "channel"); requireNonNull(channel, "channel");
return close0(channel, channel, frame, promise); return close0(channel, channel, frame, promise);
} }
@ -522,7 +520,7 @@ public abstract class WebSocketClientHandshaker {
* @param frame * @param frame
* Closing Frame that was received * Closing Frame that was received
*/ */
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) { public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
requireNonNull(ctx, "ctx"); requireNonNull(ctx, "ctx");
return close(ctx, frame, ctx.newPromise()); return close(ctx, frame, ctx.newPromise());
} }
@ -535,15 +533,15 @@ public abstract class WebSocketClientHandshaker {
* @param frame * @param frame
* Closing Frame that was received * Closing Frame that was received
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the closing handshake is done * the {@link Promise} to be notified when the closing handshake is done
*/ */
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, ChannelPromise promise) { public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, Promise<Void> promise) {
requireNonNull(ctx, "ctx"); requireNonNull(ctx, "ctx");
return close0(ctx, ctx.channel(), frame, promise); return close0(ctx, ctx.channel(), frame, promise);
} }
private ChannelFuture close0(final ChannelOutboundInvoker invoker, final Channel channel, private Future<Void> close0(final ChannelOutboundInvoker invoker, final Channel channel,
CloseWebSocketFrame frame, ChannelPromise promise) { CloseWebSocketFrame frame, Promise<Void> promise) {
invoker.writeAndFlush(frame, promise); invoker.writeAndFlush(frame, promise);
final long forceCloseTimeoutMillis = this.forceCloseTimeoutMillis; final long forceCloseTimeoutMillis = this.forceCloseTimeoutMillis;
final WebSocketClientHandshaker handshaker = this; final WebSocketClientHandshaker handshaker = this;

View File

@ -15,19 +15,16 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent; import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.Promise;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static io.netty.util.internal.ObjectUtil.*; import static io.netty.util.internal.ObjectUtil.checkPositive;
class WebSocketClientProtocolHandshakeHandler implements ChannelHandler { class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
@ -36,7 +33,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
private final WebSocketClientHandshaker handshaker; private final WebSocketClientHandshaker handshaker;
private final long handshakeTimeoutMillis; private final long handshakeTimeoutMillis;
private ChannelHandlerContext ctx; private ChannelHandlerContext ctx;
private ChannelPromise handshakePromise; private Promise<Void> handshakePromise;
WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker) { WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker) {
this(handshaker, DEFAULT_HANDSHAKE_TIMEOUT_MS); this(handshaker, DEFAULT_HANDSHAKE_TIMEOUT_MS);
@ -56,7 +53,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
@Override @Override
public void channelActive(final ChannelHandlerContext ctx) throws Exception { public void channelActive(final ChannelHandlerContext ctx) throws Exception {
ctx.fireChannelActive(); ctx.fireChannelActive();
handshaker.handshake(ctx.channel()).addListener((ChannelFutureListener) future -> { handshaker.handshake(ctx.channel()).addListener(future -> {
if (!future.isSuccess()) { if (!future.isSuccess()) {
handshakePromise.tryFailure(future.cause()); handshakePromise.tryFailure(future.cause());
ctx.fireExceptionCaught(future.cause()); ctx.fireExceptionCaught(future.cause());
@ -89,7 +86,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
try { try {
if (!handshaker.isHandshakeComplete()) { if (!handshaker.isHandshakeComplete()) {
handshaker.finishHandshake(ctx.channel(), response); handshaker.finishHandshake(ctx.channel(), response);
handshakePromise.trySuccess(); handshakePromise.trySuccess(null);
ctx.fireUserEventTriggered( ctx.fireUserEventTriggered(
WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE); WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE);
ctx.pipeline().remove(this); ctx.pipeline().remove(this);
@ -102,7 +99,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
} }
private void applyHandshakeTimeout() { private void applyHandshakeTimeout() {
final ChannelPromise localHandshakePromise = handshakePromise; final Promise<Void> localHandshakePromise = handshakePromise;
if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) { if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) {
return; return;
} }
@ -120,7 +117,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
// Cancel the handshake timeout when handshake is finished. // Cancel the handshake timeout when handshake is finished.
localHandshakePromise.addListener((FutureListener<Void>) f -> timeoutFuture.cancel(false)); localHandshakePromise.addListener(f -> timeoutFuture.cancel(false));
} }
/** /**
@ -128,7 +125,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
* *
* @return current handshake future * @return current handshake future
*/ */
ChannelFuture getHandshakeFuture() { Future<Void> getHandshakeFuture() {
return handshakePromise; return handshakePromise;
} }
} }

View File

@ -17,9 +17,9 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier; import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.concurrent.ScheduledFuture; import io.netty.util.concurrent.ScheduledFuture;
@ -31,7 +31,7 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
private final boolean dropPongFrames; private final boolean dropPongFrames;
private final WebSocketCloseStatus closeStatus; private final WebSocketCloseStatus closeStatus;
private final long forceCloseTimeoutMillis; private final long forceCloseTimeoutMillis;
private ChannelPromise closeSent; private Promise<Void> closeSent;
/** /**
* Creates a new {@link WebSocketProtocolHandler} that will <i>drop</i> {@link PongWebSocketFrame}s. * Creates a new {@link WebSocketProtocolHandler} that will <i>drop</i> {@link PongWebSocketFrame}s.
@ -82,7 +82,7 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
} }
@Override @Override
public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) { public void close(final ChannelHandlerContext ctx, final Promise<Void> promise) {
if (closeStatus == null || !ctx.channel().isActive()) { if (closeStatus == null || !ctx.channel().isActive()) {
ctx.close(promise); ctx.close(promise);
} else { } else {
@ -96,7 +96,7 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
} }
@Override @Override
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (closeSent != null) { if (closeSent != null) {
ReferenceCountUtil.release(msg); ReferenceCountUtil.release(msg);
promise.setFailure(new ClosedChannelException()); promise.setFailure(new ClosedChannelException());
@ -108,7 +108,7 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
} }
} }
void closeSent(ChannelPromise promise) { void closeSent(Promise<Void> promise) {
closeSent = promise; closeSent = promise;
} }

View File

@ -15,16 +15,12 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import static java.util.Objects.requireNonNull;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundInvoker; import io.netty.channel.ChannelOutboundInvoker;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -36,6 +32,8 @@ import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.EmptyArrays;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -46,6 +44,8 @@ import java.util.LinkedHashSet;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import static java.util.Objects.requireNonNull;
/** /**
* Base class for server side web socket opening and closing handshakes * Base class for server side web socket opening and closing handshakes
*/ */
@ -114,7 +114,7 @@ public abstract class WebSocketServerHandshaker {
} else { } else {
this.subprotocols = EmptyArrays.EMPTY_STRINGS; this.subprotocols = EmptyArrays.EMPTY_STRINGS;
} }
this.decoderConfig = Objects.requireNonNull(decoderConfig, "decoderConfig"); this.decoderConfig = requireNonNull(decoderConfig, "decoderConfig");
} }
/** /**
@ -167,9 +167,9 @@ public abstract class WebSocketServerHandshaker {
* @param req * @param req
* HTTP Request * HTTP Request
* @return future * @return future
* The {@link ChannelFuture} which is notified once the opening handshake completes * The {@link Future} which is notified once the opening handshake completes
*/ */
public ChannelFuture handshake(Channel channel, FullHttpRequest req) { public Future<Void> handshake(Channel channel, FullHttpRequest req) {
return handshake(channel, req, null, channel.newPromise()); return handshake(channel, req, null, channel.newPromise());
} }
@ -185,12 +185,12 @@ public abstract class WebSocketServerHandshaker {
* @param responseHeaders * @param responseHeaders
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added * Extra headers to add to the handshake response or {@code null} if no extra headers should be added
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the opening handshake is done * the {@link Promise} to be notified when the opening handshake is done
* @return future * @return future
* the {@link ChannelFuture} which is notified when the opening handshake is done * the {@link Future} which is notified when the opening handshake is done
*/ */
public final ChannelFuture handshake(Channel channel, FullHttpRequest req, public final Future<Void> handshake(Channel channel, FullHttpRequest req,
HttpHeaders responseHeaders, final ChannelPromise promise) { HttpHeaders responseHeaders, final Promise<Void> promise) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("{} WebSocket version {} server handshake", channel, version()); logger.debug("{} WebSocket version {} server handshake", channel, version());
@ -222,11 +222,11 @@ public abstract class WebSocketServerHandshaker {
encoderName = p.context(HttpResponseEncoder.class).name(); encoderName = p.context(HttpResponseEncoder.class).name();
p.addBefore(encoderName, "wsencoder", newWebSocketEncoder()); p.addBefore(encoderName, "wsencoder", newWebSocketEncoder());
} }
channel.writeAndFlush(response).addListener((ChannelFutureListener) future -> { channel.writeAndFlush(response).addListener(channel, (ch, future) -> {
if (future.isSuccess()) { if (future.isSuccess()) {
ChannelPipeline p1 = future.channel().pipeline(); ChannelPipeline p1 = ch.pipeline();
p1.remove(encoderName); p1.remove(encoderName);
promise.setSuccess(); promise.setSuccess(null);
} else { } else {
promise.setFailure(future.cause()); promise.setFailure(future.cause());
} }
@ -243,9 +243,9 @@ public abstract class WebSocketServerHandshaker {
* @param req * @param req
* HTTP Request * HTTP Request
* @return future * @return future
* The {@link ChannelFuture} which is notified once the opening handshake completes * The {@link Future} which is notified once the opening handshake completes
*/ */
public ChannelFuture handshake(Channel channel, HttpRequest req) { public Future<Void> handshake(Channel channel, HttpRequest req) {
return handshake(channel, req, null, channel.newPromise()); return handshake(channel, req, null, channel.newPromise());
} }
@ -261,12 +261,12 @@ public abstract class WebSocketServerHandshaker {
* @param responseHeaders * @param responseHeaders
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added * Extra headers to add to the handshake response or {@code null} if no extra headers should be added
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the opening handshake is done * the {@link Promise} to be notified when the opening handshake is done
* @return future * @return future
* the {@link ChannelFuture} which is notified when the opening handshake is done * the {@link Future} which is notified when the opening handshake is done
*/ */
public final ChannelFuture handshake(final Channel channel, HttpRequest req, public final Future<Void> handshake(final Channel channel, HttpRequest req,
final HttpHeaders responseHeaders, final ChannelPromise promise) { final HttpHeaders responseHeaders, final Promise<Void> promise) {
if (req instanceof FullHttpRequest) { if (req instanceof FullHttpRequest) {
return handshake(channel, (FullHttpRequest) req, responseHeaders, promise); return handshake(channel, (FullHttpRequest) req, responseHeaders, promise);
@ -340,7 +340,7 @@ public abstract class WebSocketServerHandshaker {
* @param frame * @param frame
* Closing Frame that was received. * Closing Frame that was received.
*/ */
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame) { public Future<Void> close(Channel channel, CloseWebSocketFrame frame) {
requireNonNull(channel, "channel"); requireNonNull(channel, "channel");
return close(channel, frame, channel.newPromise()); return close(channel, frame, channel.newPromise());
} }
@ -349,18 +349,18 @@ public abstract class WebSocketServerHandshaker {
* Performs the closing handshake. * Performs the closing handshake.
* *
* When called from within a {@link ChannelHandler} you most likely want to use * When called from within a {@link ChannelHandler} you most likely want to use
* {@link #close(ChannelHandlerContext, CloseWebSocketFrame, ChannelPromise)}. * {@link #close(ChannelHandlerContext, CloseWebSocketFrame, Promise)}.
* *
* @param channel * @param channel
* the {@link Channel} to use. * the {@link Channel} to use.
* @param frame * @param frame
* Closing Frame that was received. * Closing Frame that was received.
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the closing handshake is done * the {@link Promise} to be notified when the closing handshake is done
*/ */
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame, ChannelPromise promise) { public Future<Void> close(Channel channel, CloseWebSocketFrame frame, Promise<Void> promise) {
requireNonNull(channel, "channel"); requireNonNull(channel, "channel");
return close0(channel, frame, promise); return close0(channel, channel, frame, promise);
} }
/** /**
@ -371,7 +371,7 @@ public abstract class WebSocketServerHandshaker {
* @param frame * @param frame
* Closing Frame that was received. * Closing Frame that was received.
*/ */
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) { public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
requireNonNull(ctx, "ctx"); requireNonNull(ctx, "ctx");
return close(ctx, frame, ctx.newPromise()); return close(ctx, frame, ctx.newPromise());
} }
@ -384,15 +384,16 @@ public abstract class WebSocketServerHandshaker {
* @param frame * @param frame
* Closing Frame that was received. * Closing Frame that was received.
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the closing handshake is done. * the {@link Promise} to be notified when the closing handshake is done.
*/ */
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, ChannelPromise promise) { public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, Promise<Void> promise) {
requireNonNull(ctx, "ctx"); requireNonNull(ctx, "ctx");
return close0(ctx, frame, promise).addListener(ChannelFutureListener.CLOSE); return close0(ctx, ctx.channel(), frame, promise);
} }
private ChannelFuture close0(ChannelOutboundInvoker invoker, CloseWebSocketFrame frame, ChannelPromise promise) { private static Future<Void> close0(ChannelOutboundInvoker invoker, Channel channel, CloseWebSocketFrame frame,
return invoker.writeAndFlush(frame, promise).addListener(ChannelFutureListener.CLOSE); Promise<Void> promise) {
return invoker.writeAndFlush(frame, promise).addListener(channel, ChannelFutureListeners.CLOSE);
} }
/** /**

View File

@ -18,9 +18,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -28,6 +26,8 @@ import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues; import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -204,10 +204,10 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
* @param frame * @param frame
* Web Socket frame that was received. * Web Socket frame that was received.
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the closing handshake is done. * the {@link Promise} to be notified when the closing handshake is done.
*/ */
@Override @Override
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame, ChannelPromise promise) { public Future<Void> close(Channel channel, CloseWebSocketFrame frame, Promise<Void> promise) {
return channel.writeAndFlush(frame, promise); return channel.writeAndFlush(frame, promise);
} }
@ -219,11 +219,10 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
* @param frame * @param frame
* Closing Frame that was received. * Closing Frame that was received.
* @param promise * @param promise
* the {@link ChannelPromise} to be notified when the closing handshake is done. * the {@link Promise} to be notified when the closing handshake is done.
*/ */
@Override @Override
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, Promise<Void> promise) {
ChannelPromise promise) {
return ctx.writeAndFlush(frame, promise); return ctx.writeAndFlush(frame, promise);
} }

View File

@ -16,15 +16,15 @@
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.util.Objects; import java.util.Objects;
@ -163,14 +163,14 @@ public class WebSocketServerHandshakerFactory {
/** /**
* Return that we need cannot not support the web socket version * Return that we need cannot not support the web socket version
*/ */
public static ChannelFuture sendUnsupportedVersionResponse(Channel channel) { public static Future<Void> sendUnsupportedVersionResponse(Channel channel) {
return sendUnsupportedVersionResponse(channel, channel.newPromise()); return sendUnsupportedVersionResponse(channel, channel.newPromise());
} }
/** /**
* Return that we need cannot not support the web socket version * Return that we need cannot not support the web socket version
*/ */
public static ChannelFuture sendUnsupportedVersionResponse(Channel channel, ChannelPromise promise) { public static Future<Void> sendUnsupportedVersionResponse(Channel channel, Promise<Void> promise) {
HttpResponse res = new DefaultFullHttpResponse( HttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1, HttpVersion.HTTP_1_1,
HttpResponseStatus.UPGRADE_REQUIRED, channel.alloc().buffer(0)); HttpResponseStatus.UPGRADE_REQUIRED, channel.alloc().buffer(0));

View File

@ -17,16 +17,16 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Promise;
import java.util.Objects; import java.util.Objects;
@ -237,11 +237,11 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
WebSocketServerHandshaker handshaker = getHandshaker(ctx.channel()); WebSocketServerHandshaker handshaker = getHandshaker(ctx.channel());
if (handshaker != null) { if (handshaker != null) {
frame.retain(); frame.retain();
ChannelPromise promise = ctx.newPromise(); Promise<Void> promise = ctx.newPromise();
closeSent(promise); closeSent(promise);
handshaker.close(ctx, (CloseWebSocketFrame) frame, promise); handshaker.close(ctx, (CloseWebSocketFrame) frame, promise);
} else { } else {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
} }
return; return;
} }
@ -258,7 +258,7 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
if (cause instanceof WebSocketHandshakeException) { if (cause instanceof WebSocketHandshakeException) {
FullHttpResponse response = new DefaultFullHttpResponse( FullHttpResponse response = new DefaultFullHttpResponse(
HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.wrappedBuffer(cause.getMessage().getBytes())); HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.wrappedBuffer(cause.getMessage().getBytes()));
ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); ctx.channel().writeAndFlush(response).addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
} else { } else {
ctx.fireExceptionCaught(cause); ctx.fireExceptionCaught(cause);
ctx.close(); ctx.close();

View File

@ -15,12 +15,10 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
@ -30,14 +28,15 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.Ser
import io.netty.handler.ssl.SslHandler; import io.netty.handler.ssl.SslHandler;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener; import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static io.netty.handler.codec.http.HttpMethod.*; import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
import static io.netty.handler.codec.http.HttpUtil.*; import static io.netty.handler.codec.http.HttpUtil.isKeepAlive;
import static io.netty.handler.codec.http.HttpVersion.*; import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
/** /**
* Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}. * Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}.
@ -45,7 +44,7 @@ import static io.netty.handler.codec.http.HttpVersion.*;
class WebSocketServerProtocolHandshakeHandler implements ChannelHandler { class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
private final WebSocketServerProtocolConfig serverConfig; private final WebSocketServerProtocolConfig serverConfig;
private ChannelPromise handshakePromise; private Promise<Void> handshakePromise;
WebSocketServerProtocolHandshakeHandler(WebSocketServerProtocolConfig serverConfig) { WebSocketServerProtocolHandshakeHandler(WebSocketServerProtocolConfig serverConfig) {
this.serverConfig = Objects.requireNonNull(serverConfig, "serverConfig"); this.serverConfig = Objects.requireNonNull(serverConfig, "serverConfig");
@ -74,7 +73,7 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
getWebSocketLocation(ctx.pipeline(), req, serverConfig.websocketPath()), getWebSocketLocation(ctx.pipeline(), req, serverConfig.websocketPath()),
serverConfig.subprotocols(), serverConfig.decoderConfig()); serverConfig.subprotocols(), serverConfig.decoderConfig());
final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
final ChannelPromise localHandshakePromise = handshakePromise; Promise<Void> localHandshakePromise = handshakePromise;
if (handshaker == null) { if (handshaker == null) {
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel()); WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
} else { } else {
@ -85,13 +84,13 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
// See https://github.com/netty/netty/issues/9471. // See https://github.com/netty/netty/issues/9471.
WebSocketServerProtocolHandler.setHandshaker(ctx.channel(), handshaker); WebSocketServerProtocolHandler.setHandshaker(ctx.channel(), handshaker);
final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req); Future<Void> handshakeFuture = handshaker.handshake(ctx.channel(), req);
handshakeFuture.addListener((ChannelFutureListener) future -> { handshakeFuture.addListener(future -> {
if (!future.isSuccess()) { if (!future.isSuccess()) {
localHandshakePromise.tryFailure(future.cause()); localHandshakePromise.tryFailure(future.cause());
ctx.fireExceptionCaught(future.cause()); ctx.fireExceptionCaught(future.cause());
} else { } else {
localHandshakePromise.trySuccess(); localHandshakePromise.trySuccess(null);
// Kept for compatibility // Kept for compatibility
ctx.fireUserEventTriggered( ctx.fireUserEventTriggered(
WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE); WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE);
@ -126,9 +125,9 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
} }
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) { private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
ChannelFuture f = ctx.channel().writeAndFlush(res); Future<Void> f = ctx.channel().writeAndFlush(res);
if (!isKeepAlive(req) || res.status().code() != 200) { if (!isKeepAlive(req) || res.status().code() != 200) {
f.addListener(ChannelFutureListener.CLOSE); f.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
} }
} }
@ -143,7 +142,7 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
} }
private void applyHandshakeTimeout(ChannelHandlerContext ctx) { private void applyHandshakeTimeout(ChannelHandlerContext ctx) {
final ChannelPromise localHandshakePromise = handshakePromise; Promise<Void> localHandshakePromise = handshakePromise;
final long handshakeTimeoutMillis = serverConfig.handshakeTimeoutMillis(); final long handshakeTimeoutMillis = serverConfig.handshakeTimeoutMillis();
if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) { if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) {
return; return;
@ -159,6 +158,6 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS); }, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
// Cancel the handshake timeout when handshake is finished. // Cancel the handshake timeout when handshake is finished.
localHandshakePromise.addListener((FutureListener<Void>) f -> timeoutFuture.cancel(false)); localHandshakePromise.addListener(f -> timeoutFuture.cancel(false));
} }
} }

View File

@ -15,21 +15,21 @@
*/ */
package io.netty.handler.codec.http.websocketx.extensions; package io.netty.handler.codec.http.websocketx.extensions;
import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.CodecException; import io.netty.handler.codec.CodecException;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.util.concurrent.Promise;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
/** /**
* This handler negotiates and initializes the WebSocket Extensions. * This handler negotiates and initializes the WebSocket Extensions.
* *
@ -56,7 +56,7 @@ public class WebSocketClientExtensionHandler implements ChannelHandler {
} }
@Override @Override
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade(((HttpRequest) msg).headers())) { if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade(((HttpRequest) msg).headers())) {
HttpRequest request = (HttpRequest) msg; HttpRequest request = (HttpRequest) msg;
String headerValue = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS); String headerValue = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);

View File

@ -15,23 +15,22 @@
*/ */
package io.netty.handler.codec.http.websocketx.extensions; package io.netty.handler.codec.http.websocketx.extensions;
import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.concurrent.Promise;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
/** /**
* This handler negotiates and initializes the WebSocket Extensions. * This handler negotiates and initializes the WebSocket Extensions.
* *
@ -99,7 +98,7 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
} }
@Override @Override
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (msg instanceof HttpResponse) { if (msg instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) msg; HttpResponse httpResponse = (HttpResponse) msg;
//checking the status is faster than looking at headers //checking the status is faster than looking at headers
@ -113,7 +112,7 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
} }
private void handlePotentialUpgrade(final ChannelHandlerContext ctx, private void handlePotentialUpgrade(final ChannelHandlerContext ctx,
ChannelPromise promise, HttpResponse httpResponse) { Promise<Void> promise, HttpResponse httpResponse) {
HttpHeaders headers = httpResponse.headers(); HttpHeaders headers = httpResponse.headers();
if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) { if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) {
if (validExtensions != null) { if (validExtensions != null) {
@ -125,7 +124,7 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
} }
String newHeaderValue = WebSocketExtensionUtil String newHeaderValue = WebSocketExtensionUtil
.computeMergeExtensionsHeaderValue(headerValue, extraExtensions); .computeMergeExtensionsHeaderValue(headerValue, extraExtensions);
promise.addListener((ChannelFutureListener) future -> { promise.addListener(future -> {
if (future.isSuccess()) { if (future.isSuccess()) {
for (WebSocketServerExtension extension : validExtensions) { for (WebSocketServerExtension extension : validExtensions) {
WebSocketExtensionDecoder decoder = extension.newExtensionDecoder(); WebSocketExtensionDecoder decoder = extension.newExtensionDecoder();
@ -143,9 +142,9 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue); headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue);
} }
} }
promise.addListener((ChannelFutureListener) future -> { promise.addListener(future -> {
if (future.isSuccess()) { if (future.isSuccess()) {
ctx.pipeline().remove(WebSocketServerExtensionHandler.this); ctx.pipeline().remove(this);
} }
}); });
} }

View File

@ -20,7 +20,6 @@ import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
@ -156,12 +155,12 @@ public class HttpClientCodecTest {
sChannel.writeAndFlush(Unpooled.wrappedBuffer(("HTTP/1.0 200 OK\r\n" + sChannel.writeAndFlush(Unpooled.wrappedBuffer(("HTTP/1.0 200 OK\r\n" +
"Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" + "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" +
"Content-Type: text/html\r\n\r\n").getBytes(CharsetUtil.ISO_8859_1))) "Content-Type: text/html\r\n\r\n").getBytes(CharsetUtil.ISO_8859_1)))
.addListener((ChannelFutureListener) future -> { .addListener(future -> {
assertTrue(future.isSuccess()); assertTrue(future.isSuccess());
sChannel.writeAndFlush(Unpooled.wrappedBuffer( sChannel.writeAndFlush(Unpooled.wrappedBuffer(
"<html><body>hello half closed!</body></html>\r\n" "<html><body>hello half closed!</body></html>\r\n"
.getBytes(CharsetUtil.ISO_8859_1))) .getBytes(CharsetUtil.ISO_8859_1)))
.addListener((ChannelFutureListener) future1 -> { .addListener(future1 -> {
assertTrue(future1.isSuccess()); assertTrue(future1.isSuccess());
sChannel.shutdownOutput(); sChannel.shutdownOutput();
}); });

View File

@ -15,22 +15,21 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import java.util.Collection;
import java.util.Collections;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec; import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory; import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.Collection;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -94,13 +93,13 @@ public class HttpServerUpgradeHandlerTest {
} }
@Override @Override
public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) { public void write(final ChannelHandlerContext ctx, final Object msg, final Promise<Void> promise) {
// We ensure that we're in the read call and defer the write so we can // We ensure that we're in the read call and defer the write so we can
// make sure the pipeline was reformed irrespective of the flush completing. // make sure the pipeline was reformed irrespective of the flush completing.
assertTrue(inReadCall); assertTrue(inReadCall);
writeUpgradeMessage = true; writeUpgradeMessage = true;
ctx.channel().eventLoop().execute(() -> ctx.write(msg, promise)); ctx.channel().eventLoop().execute(() -> ctx.write(msg, promise));
promise.addListener((ChannelFutureListener) future -> writeFlushed = true); promise.addListener(future -> writeFlushed = true);
} }
}; };

View File

@ -17,7 +17,6 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
@ -28,13 +27,13 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent; import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.ServerHandshakeStateEvent; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.ServerHandshakeStateEvent;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import java.net.URI; import java.net.URI;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import org.junit.jupiter.api.Timeout; import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
@ -244,7 +243,7 @@ public class WebSocketHandshakeHandOverTest {
@Override @Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) { public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
if (evt == ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) { if (evt == ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) {
ctx.channel().closeFuture().addListener((ChannelFutureListener) future -> clientForceClosed = true); ctx.channel().closeFuture().addListener(future -> clientForceClosed = true);
handshaker.close(ctx.channel(), new CloseWebSocketFrame()); handshaker.close(ctx.channel(), new CloseWebSocketFrame());
} }
} }

View File

@ -18,13 +18,13 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.flow.FlowControlHandler; import io.netty.handler.flow.FlowControlHandler;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -153,18 +153,18 @@ public class WebSocketProtocolHandlerTest {
@Test @Test
public void testTimeout() throws Exception { public void testTimeout() throws Exception {
final AtomicReference<ChannelPromise> ref = new AtomicReference<ChannelPromise>(); final AtomicReference<Promise<Void>> ref = new AtomicReference<>();
WebSocketProtocolHandler handler = new WebSocketProtocolHandler( WebSocketProtocolHandler handler = new WebSocketProtocolHandler(
false, WebSocketCloseStatus.NORMAL_CLOSURE, 1) { }; false, WebSocketCloseStatus.NORMAL_CLOSURE, 1) { };
EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter() { EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter() {
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
ref.set(promise); ref.set(promise);
ReferenceCountUtil.release(msg); ReferenceCountUtil.release(msg);
} }
}, handler); }, handler);
ChannelFuture future = channel.writeAndFlush(new CloseWebSocketFrame()); Future<Void> future = channel.writeAndFlush(new CloseWebSocketFrame());
ChannelHandlerContext ctx = channel.pipeline().context(WebSocketProtocolHandler.class); ChannelHandlerContext ctx = channel.pipeline().context(WebSocketProtocolHandler.class);
handler.close(ctx, ctx.newPromise()); handler.close(ctx, ctx.newPromise());

View File

@ -18,30 +18,30 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Queue; import java.util.Queue;
import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpVersion.*; import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
@ -354,7 +354,7 @@ public class WebSocketServerProtocolHandlerTest {
assertFalse(client.isOpen()); assertFalse(client.isOpen());
assertFalse(server.isOpen()); assertFalse(server.isOpen());
CloseWebSocketFrame closeMessage = decode(server.<ByteBuf>readOutbound(), CloseWebSocketFrame.class); CloseWebSocketFrame closeMessage = decode(server.readOutbound(), CloseWebSocketFrame.class);
assertEquals(closeMessage.statusCode(), closeStatus.code()); assertEquals(closeMessage.statusCode(), closeStatus.code());
closeMessage.release(); closeMessage.release();
@ -378,7 +378,7 @@ public class WebSocketServerProtocolHandlerTest {
assertFalse(client.isOpen()); assertFalse(client.isOpen());
assertFalse(server.isOpen()); assertFalse(server.isOpen());
CloseWebSocketFrame closeMessage = decode(server.<ByteBuf>readOutbound(), CloseWebSocketFrame.class); CloseWebSocketFrame closeMessage = decode(server.readOutbound(), CloseWebSocketFrame.class);
assertEquals(closeMessage, new CloseWebSocketFrame(WebSocketCloseStatus.NORMAL_CLOSURE)); assertEquals(closeMessage, new CloseWebSocketFrame(WebSocketCloseStatus.NORMAL_CLOSURE));
closeMessage.release(); closeMessage.release();
@ -459,9 +459,9 @@ public class WebSocketServerProtocolHandlerTest {
private class MockOutboundHandler implements ChannelHandler { private class MockOutboundHandler implements ChannelHandler {
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
responses.add((FullHttpResponse) msg); responses.add((FullHttpResponse) msg);
promise.setSuccess(); promise.setSuccess(null);
} }
@Override @Override

View File

@ -15,25 +15,34 @@
*/ */
package io.netty.handler.codec.http.websocketx.extensions; package io.netty.handler.codec.http.websocketx.extensions;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.Test;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.Test; import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.Dummy2Decoder;
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.Dummy2Encoder;
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.*; import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.DummyDecoder;
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.DummyEncoder;
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.newUpgradeRequest;
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.newUpgradeResponse;
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.webSocketExtensionDataMatcher;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class WebSocketServerExtensionHandlerTest { public class WebSocketServerExtensionHandlerTest {
@ -209,7 +218,7 @@ public class WebSocketServerExtensionHandlerTest {
when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("main"))) when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("main")))
.thenReturn(mainExtensionMock); .thenReturn(mainExtensionMock);
when(mainExtensionMock.newResponseData()).thenReturn( when(mainExtensionMock.newResponseData()).thenReturn(
new WebSocketExtensionData("main", Collections.<String, String>emptyMap())); new WebSocketExtensionData("main", Collections.emptyMap()));
// execute // execute
WebSocketServerExtensionHandler extensionHandler = WebSocketServerExtensionHandler extensionHandler =
@ -220,7 +229,7 @@ public class WebSocketServerExtensionHandlerTest {
ch.writeInbound(req); ch.writeInbound(req);
HttpResponse res = newUpgradeResponse(null); HttpResponse res = newUpgradeResponse(null);
ChannelPromise failurePromise = ch.newPromise(); Promise<Void> failurePromise = ch.newPromise();
ch.writeOneOutbound(res, failurePromise); ch.writeOneOutbound(res, failurePromise);
failurePromise.setFailure(new IOException("Cannot write response")); failurePromise.setFailure(new IOException("Cannot write response"));

View File

@ -18,15 +18,12 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId; import io.netty.channel.ChannelId;
import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelOutboundBuffer; import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelConfig; import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.DefaultChannelPipeline; import io.netty.channel.DefaultChannelPipeline;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
@ -36,6 +33,8 @@ import io.netty.channel.WriteBufferWaterMark;
import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream; import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream;
import io.netty.util.DefaultAttributeMap; import io.netty.util.DefaultAttributeMap;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -97,7 +96,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
private static final AtomicIntegerFieldUpdater<AbstractHttp2StreamChannel> UNWRITABLE_UPDATER = private static final AtomicIntegerFieldUpdater<AbstractHttp2StreamChannel> UNWRITABLE_UPDATER =
AtomicIntegerFieldUpdater.newUpdater(AbstractHttp2StreamChannel.class, "unwritable"); AtomicIntegerFieldUpdater.newUpdater(AbstractHttp2StreamChannel.class, "unwritable");
private static void windowUpdateFrameWriteComplete(ChannelFuture future, Channel streamChannel) { private static void windowUpdateFrameWriteComplete(Channel streamChannel, Future<?> future) {
Throwable cause = future.cause(); Throwable cause = future.cause();
if (cause != null) { if (cause != null) {
Throwable unwrappedCause; Throwable unwrappedCause;
@ -112,9 +111,6 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
} }
private final ChannelFutureListener windowUpdateFrameWriteListener = future ->
windowUpdateFrameWriteComplete(future, AbstractHttp2StreamChannel.this);
/** /**
* The current status of the read-processing for a {@link AbstractHttp2StreamChannel}. * The current status of the read-processing for a {@link AbstractHttp2StreamChannel}.
*/ */
@ -140,7 +136,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
private final ChannelId channelId; private final ChannelId channelId;
private final ChannelPipeline pipeline; private final ChannelPipeline pipeline;
private final DefaultHttp2FrameStream stream; private final DefaultHttp2FrameStream stream;
private final ChannelPromise closePromise; private final Promise<Void> closePromise;
private volatile boolean registered; private volatile boolean registered;
@ -338,7 +334,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
@Override @Override
public ChannelFuture closeFuture() { public Future<Void> closeFuture() {
return closePromise; return closePromise;
} }
@ -394,107 +390,107 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
@Override @Override
public ChannelFuture bind(SocketAddress localAddress) { public Future<Void> bind(SocketAddress localAddress) {
return pipeline().bind(localAddress); return pipeline().bind(localAddress);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress) { public Future<Void> connect(SocketAddress remoteAddress) {
return pipeline().connect(remoteAddress); return pipeline().connect(remoteAddress);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return pipeline().connect(remoteAddress, localAddress); return pipeline().connect(remoteAddress, localAddress);
} }
@Override @Override
public ChannelFuture disconnect() { public Future<Void> disconnect() {
return pipeline().disconnect(); return pipeline().disconnect();
} }
@Override @Override
public ChannelFuture close() { public Future<Void> close() {
return pipeline().close(); return pipeline().close();
} }
@Override @Override
public ChannelFuture register() { public Future<Void> register() {
return pipeline().register(); return pipeline().register();
} }
@Override @Override
public ChannelFuture deregister() { public Future<Void> deregister() {
return pipeline().deregister(); return pipeline().deregister();
} }
@Override @Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return pipeline().bind(localAddress, promise); return pipeline().bind(localAddress, promise);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return pipeline().connect(remoteAddress, promise); return pipeline().connect(remoteAddress, promise);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return pipeline().connect(remoteAddress, localAddress, promise); return pipeline().connect(remoteAddress, localAddress, promise);
} }
@Override @Override
public ChannelFuture disconnect(ChannelPromise promise) { public Future<Void> disconnect(Promise<Void> promise) {
return pipeline().disconnect(promise); return pipeline().disconnect(promise);
} }
@Override @Override
public ChannelFuture close(ChannelPromise promise) { public Future<Void> close(Promise<Void> promise) {
return pipeline().close(promise); return pipeline().close(promise);
} }
@Override @Override
public ChannelFuture register(ChannelPromise promise) { public Future<Void> register(Promise<Void> promise) {
return pipeline().register(promise); return pipeline().register(promise);
} }
@Override @Override
public ChannelFuture deregister(ChannelPromise promise) { public Future<Void> deregister(Promise<Void> promise) {
return pipeline().deregister(promise); return pipeline().deregister(promise);
} }
@Override @Override
public ChannelFuture write(Object msg) { public Future<Void> write(Object msg) {
return pipeline().write(msg); return pipeline().write(msg);
} }
@Override @Override
public ChannelFuture write(Object msg, ChannelPromise promise) { public Future<Void> write(Object msg, Promise<Void> promise) {
return pipeline().write(msg, promise); return pipeline().write(msg, promise);
} }
@Override @Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return pipeline().writeAndFlush(msg, promise); return pipeline().writeAndFlush(msg, promise);
} }
@Override @Override
public ChannelFuture writeAndFlush(Object msg) { public Future<Void> writeAndFlush(Object msg) {
return pipeline().writeAndFlush(msg); return pipeline().writeAndFlush(msg);
} }
@Override @Override
public ChannelPromise newPromise() { public Promise<Void> newPromise() {
return pipeline().newPromise(); return pipeline().newPromise();
} }
@Override @Override
public ChannelFuture newSucceededFuture() { public Future<Void> newSucceededFuture() {
return pipeline().newSucceededFuture(); return pipeline().newSucceededFuture();
} }
@Override @Override
public ChannelFuture newFailedFuture(Throwable cause) { public Future<Void> newFailedFuture(Throwable cause) {
return pipeline().newFailedFuture(cause); return pipeline().newFailedFuture(cause);
} }
@ -568,7 +564,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
@Override @Override
public void connect(final SocketAddress remoteAddress, public void connect(final SocketAddress remoteAddress,
SocketAddress localAddress, final ChannelPromise promise) { SocketAddress localAddress, Promise<Void> promise) {
if (!promise.setUncancellable()) { if (!promise.setUncancellable()) {
return; return;
} }
@ -595,7 +591,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
@Override @Override
public void register(ChannelPromise promise) { public void register(Promise<Void> promise) {
if (!promise.setUncancellable()) { if (!promise.setUncancellable()) {
return; return;
} }
@ -606,7 +602,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
registered = true; registered = true;
promise.setSuccess(); promise.setSuccess(null);
pipeline().fireChannelRegistered(); pipeline().fireChannelRegistered();
if (isActive()) { if (isActive()) {
@ -618,7 +614,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
@Override @Override
public void bind(SocketAddress localAddress, ChannelPromise promise) { public void bind(SocketAddress localAddress, Promise<Void> promise) {
if (!promise.setUncancellable()) { if (!promise.setUncancellable()) {
return; return;
} }
@ -626,22 +622,22 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
@Override @Override
public void disconnect(ChannelPromise promise) { public void disconnect(Promise<Void> promise) {
close(promise); close(promise);
} }
@Override @Override
public void close(final ChannelPromise promise) { public void close(final Promise<Void> promise) {
if (!promise.setUncancellable()) { if (!promise.setUncancellable()) {
return; return;
} }
if (closeInitiated) { if (closeInitiated) {
if (closePromise.isDone()) { if (closePromise.isDone()) {
// Closed already. // Closed already.
promise.setSuccess(); promise.setSuccess(null);
} else { } else {
// This means close() was called before so we just register a listener and return // This means close() was called before so we just register a listener and return
closePromise.addListener((ChannelFutureListener) future -> promise.setSuccess()); closePromise.addListener(promise, (p, future) -> p.setSuccess(null));
} }
return; return;
} }
@ -675,8 +671,8 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
// The promise should be notified before we call fireChannelInactive(). // The promise should be notified before we call fireChannelInactive().
outboundClosed = true; outboundClosed = true;
closePromise.setSuccess(); closePromise.setSuccess(null);
promise.setSuccess(); promise.setSuccess(null);
fireChannelInactiveAndDeregister(newPromise(), wasActive); fireChannelInactiveAndDeregister(newPromise(), wasActive);
} }
@ -687,18 +683,18 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
@Override @Override
public void deregister(ChannelPromise promise) { public void deregister(Promise<Void> promise) {
fireChannelInactiveAndDeregister(promise, false); fireChannelInactiveAndDeregister(promise, false);
} }
private void fireChannelInactiveAndDeregister(final ChannelPromise promise, private void fireChannelInactiveAndDeregister(Promise<Void> promise,
final boolean fireChannelInactive) { final boolean fireChannelInactive) {
if (!promise.setUncancellable()) { if (!promise.setUncancellable()) {
return; return;
} }
if (!registered) { if (!registered) {
promise.setSuccess(); promise.setSuccess(null);
return; return;
} }
@ -723,8 +719,8 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
}); });
} }
private void safeSetSuccess(ChannelPromise promise) { private void safeSetSuccess(Promise<Void> promise) {
if (!promise.trySuccess()) { if (!promise.trySuccess(null)) {
logger.warn("Failed to mark a promise as success because it is done already: {}", promise); logger.warn("Failed to mark a promise as success because it is done already: {}", promise);
} }
} }
@ -813,7 +809,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
if (flowControlledBytes != 0) { if (flowControlledBytes != 0) {
int bytes = flowControlledBytes; int bytes = flowControlledBytes;
flowControlledBytes = 0; flowControlledBytes = 0;
ChannelFuture future = write0(parentContext(), new DefaultHttp2WindowUpdateFrame(bytes).stream(stream)); Future<Void> future = write0(parentContext(), new DefaultHttp2WindowUpdateFrame(bytes).stream(stream));
// window update frames are commonly swallowed by the Http2FrameCodec and the promise is synchronously // window update frames are commonly swallowed by the Http2FrameCodec and the promise is synchronously
// completed but the flow controller _may_ have generated a wire level WINDOW_UPDATE. Therefore we need, // completed but the flow controller _may_ have generated a wire level WINDOW_UPDATE. Therefore we need,
// to assume there was a write done that needs to be flushed or we risk flow control starvation. // to assume there was a write done that needs to be flushed or we risk flow control starvation.
@ -823,9 +819,10 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
// already. // already.
// See https://github.com/netty/netty/issues/9663 // See https://github.com/netty/netty/issues/9663
if (future.isDone()) { if (future.isDone()) {
windowUpdateFrameWriteComplete(future, AbstractHttp2StreamChannel.this); windowUpdateFrameWriteComplete(AbstractHttp2StreamChannel.this, future);
} else { } else {
future.addListener(windowUpdateFrameWriteListener); future.addListener(AbstractHttp2StreamChannel.this,
AbstractHttp2StreamChannel::windowUpdateFrameWriteComplete);
} }
} }
} }
@ -882,7 +879,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
@Override @Override
public void write(Object msg, final ChannelPromise promise) { public void write(Object msg, Promise<Void> promise) {
// After this point its not possible to cancel a write anymore. // After this point its not possible to cancel a write anymore.
if (!promise.setUncancellable()) { if (!promise.setUncancellable()) {
ReferenceCountUtil.release(msg); ReferenceCountUtil.release(msg);
@ -913,7 +910,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
} }
private void writeHttp2StreamFrame(Http2StreamFrame frame, final ChannelPromise promise) { private void writeHttp2StreamFrame(Http2StreamFrame frame, Promise<Void> promise) {
if (!firstFrameWritten && !isStreamIdValid(stream().id()) && !(frame instanceof Http2HeadersFrame)) { if (!firstFrameWritten && !isStreamIdValid(stream().id()) && !(frame instanceof Http2HeadersFrame)) {
ReferenceCountUtil.release(frame); ReferenceCountUtil.release(frame);
promise.setFailure( promise.setFailure(
@ -929,7 +926,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
firstWrite = firstFrameWritten = true; firstWrite = firstFrameWritten = true;
} }
ChannelFuture f = write0(parentContext(), frame); Future<Void> f = write0(parentContext(), frame);
if (f.isDone()) { if (f.isDone()) {
if (firstWrite) { if (firstWrite) {
firstWriteComplete(f, promise); firstWriteComplete(f, promise);
@ -939,7 +936,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} else { } else {
final long bytes = FlowControlledFrameSizeEstimator.HANDLE_INSTANCE.size(frame); final long bytes = FlowControlledFrameSizeEstimator.HANDLE_INSTANCE.size(frame);
incrementPendingOutboundBytes(bytes, false); incrementPendingOutboundBytes(bytes, false);
f.addListener((ChannelFutureListener) future -> { f.addListener(future -> {
if (firstWrite) { if (firstWrite) {
firstWriteComplete(future, promise); firstWriteComplete(future, promise);
} else { } else {
@ -951,10 +948,10 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
} }
private void firstWriteComplete(ChannelFuture future, ChannelPromise promise) { private void firstWriteComplete(Future<?> future, Promise<Void> promise) {
Throwable cause = future.cause(); Throwable cause = future.cause();
if (cause == null) { if (cause == null) {
promise.setSuccess(); promise.setSuccess(null);
} else { } else {
// If the first write fails there is not much we can do, just close // If the first write fails there is not much we can do, just close
closeForcibly(); closeForcibly();
@ -962,10 +959,10 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
} }
} }
private void writeComplete(ChannelFuture future, ChannelPromise promise) { private void writeComplete(Future<?> future, Promise<Void> promise) {
Throwable cause = future.cause(); Throwable cause = future.cause();
if (cause == null) { if (cause == null) {
promise.setSuccess(); promise.setSuccess(null);
} else { } else {
Throwable error = wrapStreamClosedError(cause); Throwable error = wrapStreamClosedError(cause);
// To make it more consistent with AbstractChannel we handle all IOExceptions here. // To make it more consistent with AbstractChannel we handle all IOExceptions here.
@ -1011,7 +1008,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
// There is nothing to flush so this is a NOOP. // There is nothing to flush so this is a NOOP.
return; return;
} }
// We need to set this to false before we call flush0(...) as ChannelFutureListener may produce more data // We need to set this to false before we call flush0(...) as FutureListener may produce more data
// that are explicit flushed. // that are explicit flushed.
writeDoneAndNoFlush = false; writeDoneAndNoFlush = false;
flush0(parentContext()); flush0(parentContext());
@ -1066,8 +1063,8 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
ctx.flush(); ctx.flush();
} }
protected ChannelFuture write0(ChannelHandlerContext ctx, Object msg) { protected Future<Void> write0(ChannelHandlerContext ctx, Object msg) {
ChannelPromise promise = ctx.newPromise(); Promise<Void> promise = ctx.newPromise();
ctx.write(msg, promise); ctx.write(msg, promise);
return promise; return promise;
} }

View File

@ -16,21 +16,21 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.compression.BrotliEncoder; import io.netty.handler.codec.compression.BrotliEncoder;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.handler.codec.compression.BrotliOptions; import io.netty.handler.codec.compression.BrotliOptions;
import io.netty.handler.codec.compression.CompressionOptions; import io.netty.handler.codec.compression.CompressionOptions;
import io.netty.handler.codec.compression.DeflateOptions; import io.netty.handler.codec.compression.DeflateOptions;
import io.netty.handler.codec.compression.GzipOptions; import io.netty.handler.codec.compression.GzipOptions;
import io.netty.handler.codec.compression.StandardCompressionOptions; import io.netty.handler.codec.compression.StandardCompressionOptions;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.handler.codec.compression.ZstdEncoder; import io.netty.handler.codec.compression.ZstdEncoder;
import io.netty.handler.codec.compression.ZstdOptions; import io.netty.handler.codec.compression.ZstdOptions;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseCombiner; import io.netty.util.concurrent.PromiseCombiner;
import io.netty.util.internal.ObjectUtil; import io.netty.util.internal.ObjectUtil;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
@ -142,8 +142,8 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
} }
@Override @Override
public ChannelFuture writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding, public Future<Void> writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
final boolean endOfStream, ChannelPromise promise) { final boolean endOfStream, Promise<Void> promise) {
final Http2Stream stream = connection().stream(streamId); final Http2Stream stream = connection().stream(streamId);
final EmbeddedChannel channel = stream == null ? null : (EmbeddedChannel) stream.getProperty(propertyKey); final EmbeddedChannel channel = stream == null ? null : (EmbeddedChannel) stream.getProperty(propertyKey);
if (channel == null) { if (channel == null) {
@ -164,7 +164,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
true, promise); true, promise);
} }
// END_STREAM is not set and the assumption is data is still forthcoming. // END_STREAM is not set and the assumption is data is still forthcoming.
promise.setSuccess(); promise.setSuccess(null);
return promise; return promise;
} }
@ -177,7 +177,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
compressedEndOfStream = nextBuf == null; compressedEndOfStream = nextBuf == null;
} }
ChannelPromise bufPromise = ctx.newPromise(); Promise<Void> bufPromise = ctx.newPromise();
combiner.add(bufPromise); combiner.add(bufPromise);
super.writeData(ctx, streamId, buf, padding, compressedEndOfStream, bufPromise); super.writeData(ctx, streamId, buf, padding, compressedEndOfStream, bufPromise);
if (nextBuf == null) { if (nextBuf == null) {
@ -199,14 +199,14 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
boolean endStream, ChannelPromise promise) { boolean endStream, Promise<Void> promise) {
try { try {
// Determine if compression is required and sanitize the headers. // Determine if compression is required and sanitize the headers.
EmbeddedChannel compressor = newCompressor(ctx, headers, endStream); EmbeddedChannel compressor = newCompressor(ctx, headers, endStream);
// Write the headers and create the stream object. // Write the headers and create the stream object.
ChannelFuture future = super.writeHeaders(ctx, streamId, headers, padding, endStream, promise); Future<Void> future = super.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
// After the stream object has been created, then attach the compressor as a property for data compression. // After the stream object has been created, then attach the compressor as a property for data compression.
bindCompressorToStream(compressor, streamId); bindCompressorToStream(compressor, streamId);
@ -219,15 +219,15 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
} }
@Override @Override
public ChannelFuture writeHeaders(final ChannelHandlerContext ctx, final int streamId, final Http2Headers headers, public Future<Void> writeHeaders(final ChannelHandlerContext ctx, final int streamId, final Http2Headers headers,
final int streamDependency, final short weight, final boolean exclusive, final int padding, final int streamDependency, final short weight, final boolean exclusive, final int padding,
final boolean endOfStream, final ChannelPromise promise) { final boolean endOfStream, final Promise<Void> promise) {
try { try {
// Determine if compression is required and sanitize the headers. // Determine if compression is required and sanitize the headers.
EmbeddedChannel compressor = newCompressor(ctx, headers, endOfStream); EmbeddedChannel compressor = newCompressor(ctx, headers, endOfStream);
// Write the headers and create the stream object. // Write the headers and create the stream object.
ChannelFuture future = super.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, Future<Void> future = super.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive,
padding, endOfStream, promise); padding, endOfStream, promise);
// After the stream object has been created, then attach the compressor as a property for data compression. // After the stream object has been created, then attach the compressor as a property for data compression.

View File

@ -15,9 +15,9 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
@ -34,73 +34,73 @@ public class DecoratingHttp2FrameWriter implements Http2FrameWriter {
} }
@Override @Override
public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
boolean endStream, ChannelPromise promise) { boolean endStream, Promise<Void> promise) {
return delegate.writeData(ctx, streamId, data, padding, endStream, promise); return delegate.writeData(ctx, streamId, data, padding, endStream, promise);
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
boolean endStream, ChannelPromise promise) { boolean endStream, Promise<Void> promise) {
return delegate.writeHeaders(ctx, streamId, headers, padding, endStream, promise); return delegate.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
int streamDependency, short weight, boolean exclusive, int padding, int streamDependency, short weight, boolean exclusive, int padding,
boolean endStream, ChannelPromise promise) { boolean endStream, Promise<Void> promise) {
return delegate return delegate
.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream, promise); .writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream, promise);
} }
@Override @Override
public ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
boolean exclusive, ChannelPromise promise) { boolean exclusive, Promise<Void> promise) {
return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise); return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
} }
@Override @Override
public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode, public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise) { Promise<Void> promise) {
return delegate.writeRstStream(ctx, streamId, errorCode, promise); return delegate.writeRstStream(ctx, streamId, errorCode, promise);
} }
@Override @Override
public ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings, ChannelPromise promise) { public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings, Promise<Void> promise) {
return delegate.writeSettings(ctx, settings, promise); return delegate.writeSettings(ctx, settings, promise);
} }
@Override @Override
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) { public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
return delegate.writeSettingsAck(ctx, promise); return delegate.writeSettingsAck(ctx, promise);
} }
@Override @Override
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) { public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
return delegate.writePing(ctx, ack, data, promise); return delegate.writePing(ctx, ack, data, promise);
} }
@Override @Override
public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
Http2Headers headers, int padding, ChannelPromise promise) { Http2Headers headers, int padding, Promise<Void> promise) {
return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise); return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise);
} }
@Override @Override
public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData, public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
ChannelPromise promise) { Promise<Void> promise) {
return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise); return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
} }
@Override @Override
public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement, public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
ChannelPromise promise) { Promise<Void> promise) {
return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise); return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
} }
@Override @Override
public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
ByteBuf payload, ChannelPromise promise) { ByteBuf payload, Promise<Void> promise) {
return delegate.writeFrame(ctx, frameType, streamId, flags, payload, promise); return delegate.writeFrame(ctx, frameType, streamId, flags, payload, promise);
} }

View File

@ -15,13 +15,14 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture; import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.CoalescingBufferQueue; import io.netty.channel.CoalescingBufferQueue;
import io.netty.handler.codec.http.HttpStatusClass; import io.netty.handler.codec.http.HttpStatusClass;
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator; import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -118,8 +119,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
} }
@Override @Override
public ChannelFuture writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding, public Future<Void> writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
final boolean endOfStream, ChannelPromise promise) { final boolean endOfStream, Promise<Void> promise) {
final Http2Stream stream; final Http2Stream stream;
try { try {
stream = requireStream(streamId); stream = requireStream(streamId);
@ -140,13 +141,13 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
// Hand control of the frame to the flow controller. // Hand control of the frame to the flow controller.
flowController().addFlowControlled(stream, flowController().addFlowControlled(stream,
new FlowControlledData(stream, data, padding, endOfStream, promise)); new FlowControlledData(stream, data, padding, endOfStream, promise, ctx.channel()));
return promise; return promise;
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
boolean endStream, ChannelPromise promise) { boolean endStream, Promise<Void> promise) {
return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream, promise); return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream, promise);
} }
@ -160,9 +161,10 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
} }
@Override @Override
public ChannelFuture writeHeaders(final ChannelHandlerContext ctx, final int streamId, public Future<Void> writeHeaders(final ChannelHandlerContext ctx, final int streamId,
final Http2Headers headers, final int streamDependency, final short weight, final Http2Headers headers, final int streamDependency, final short weight,
final boolean exclusive, final int padding, final boolean endOfStream, ChannelPromise promise) { final boolean exclusive, final int padding, final boolean endOfStream,
Promise<Void> promise) {
return writeHeaders0(ctx, streamId, headers, true, streamDependency, return writeHeaders0(ctx, streamId, headers, true, streamDependency,
weight, exclusive, padding, endOfStream, promise); weight, exclusive, padding, endOfStream, promise);
} }
@ -171,11 +173,11 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
* Write headers via {@link Http2FrameWriter}. If {@code hasPriority} is {@code false} it will ignore the * Write headers via {@link Http2FrameWriter}. If {@code hasPriority} is {@code false} it will ignore the
* {@code streamDependency}, {@code weight} and {@code exclusive} parameters. * {@code streamDependency}, {@code weight} and {@code exclusive} parameters.
*/ */
private static ChannelFuture sendHeaders(Http2FrameWriter frameWriter, ChannelHandlerContext ctx, int streamId, private static Future<Void> sendHeaders(Http2FrameWriter frameWriter, ChannelHandlerContext ctx, int streamId,
Http2Headers headers, final boolean hasPriority, Http2Headers headers, final boolean hasPriority,
int streamDependency, final short weight, int streamDependency, final short weight,
boolean exclusive, final int padding, boolean exclusive, final int padding,
boolean endOfStream, ChannelPromise promise) { boolean endOfStream, Promise<Void> promise) {
if (hasPriority) { if (hasPriority) {
return frameWriter.writeHeaders(ctx, streamId, headers, streamDependency, return frameWriter.writeHeaders(ctx, streamId, headers, streamDependency,
weight, exclusive, padding, endOfStream, promise); weight, exclusive, padding, endOfStream, promise);
@ -183,11 +185,11 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream, promise); return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream, promise);
} }
private ChannelFuture writeHeaders0(final ChannelHandlerContext ctx, final int streamId, private Future<Void> writeHeaders0(final ChannelHandlerContext ctx, final int streamId,
final Http2Headers headers, final boolean hasPriority, final Http2Headers headers, final boolean hasPriority,
final int streamDependency, final short weight, final int streamDependency, final short weight,
final boolean exclusive, final int padding, final boolean exclusive, final int padding,
final boolean endOfStream, ChannelPromise promise) { final boolean endOfStream, Promise<Void> promise) {
try { try {
Http2Stream stream = connection.stream(streamId); Http2Stream stream = connection.stream(streamId);
if (stream == null) { if (stream == null) {
@ -228,7 +230,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream); boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream);
ChannelFuture future = sendHeaders(frameWriter, ctx, streamId, headers, hasPriority, streamDependency, Future<Void> future = sendHeaders(frameWriter, ctx, streamId, headers, hasPriority, streamDependency,
weight, exclusive, padding, endOfStream, promise); weight, exclusive, padding, endOfStream, promise);
// Writing headers may fail during the encode state if they violate HPACK limits. // Writing headers may fail during the encode state if they violate HPACK limits.
@ -272,21 +274,21 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
} }
@Override @Override
public ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight, public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
boolean exclusive, ChannelPromise promise) { boolean exclusive, Promise<Void> promise) {
return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise); return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
} }
@Override @Override
public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode, public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise) { Promise<Void> promise) {
// Delegate to the lifecycle manager for proper updating of connection state. // Delegate to the lifecycle manager for proper updating of connection state.
return lifecycleManager.resetStream(ctx, streamId, errorCode, promise); return lifecycleManager.resetStream(ctx, streamId, errorCode, promise);
} }
@Override @Override
public ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings, public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
ChannelPromise promise) { Promise<Void> promise) {
outstandingLocalSettingsQueue.add(settings); outstandingLocalSettingsQueue.add(settings);
try { try {
Boolean pushEnabled = settings.pushEnabled(); Boolean pushEnabled = settings.pushEnabled();
@ -301,7 +303,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
} }
@Override @Override
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) { public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
if (outstandingRemoteSettingsQueue == null) { if (outstandingRemoteSettingsQueue == null) {
return frameWriter.writeSettingsAck(ctx, promise); return frameWriter.writeSettingsAck(ctx, promise);
} }
@ -310,8 +312,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
return promise.setFailure(new Http2Exception(INTERNAL_ERROR, "attempted to write a SETTINGS ACK with no " + return promise.setFailure(new Http2Exception(INTERNAL_ERROR, "attempted to write a SETTINGS ACK with no " +
" pending SETTINGS")); " pending SETTINGS"));
} }
SimpleChannelPromiseAggregator aggregator = new SimpleChannelPromiseAggregator(promise, ctx.channel(), SimpleChannelPromiseAggregator aggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
ctx.executor());
// Acknowledge receipt of the settings. We should do this before we process the settings to ensure our // 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 // 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. // these new settings. See https://github.com/netty/netty/issues/6520.
@ -319,10 +320,10 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
// We create a "new promise" to make sure that status from both the write and the application are taken into // We create a "new promise" to make sure that status from both the write and the application are taken into
// account independently. // account independently.
ChannelPromise applySettingsPromise = aggregator.newPromise(); Promise<Void> applySettingsPromise = aggregator.newPromise();
try { try {
remoteSettings(settings); remoteSettings(settings);
applySettingsPromise.setSuccess(); applySettingsPromise.setSuccess(null);
} catch (Throwable e) { } catch (Throwable e) {
applySettingsPromise.setFailure(e); applySettingsPromise.setFailure(e);
lifecycleManager.onError(ctx, true, e); lifecycleManager.onError(ctx, true, e);
@ -331,13 +332,13 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
} }
@Override @Override
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) { public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
return frameWriter.writePing(ctx, ack, data, promise); return frameWriter.writePing(ctx, ack, data, promise);
} }
@Override @Override
public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
Http2Headers headers, int padding, ChannelPromise promise) { Http2Headers headers, int padding, Promise<Void> promise) {
try { try {
if (connection.goAwayReceived()) { if (connection.goAwayReceived()) {
throw connectionError(PROTOCOL_ERROR, "Sending PUSH_PROMISE after GO_AWAY received."); throw connectionError(PROTOCOL_ERROR, "Sending PUSH_PROMISE after GO_AWAY received.");
@ -347,7 +348,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
// Reserve the promised stream. // Reserve the promised stream.
connection.local().reservePushStream(promisedStreamId, stream); connection.local().reservePushStream(promisedStreamId, stream);
ChannelFuture future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, Future<Void> future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding,
promise); promise);
// Writing headers may fail during the encode state if they violate HPACK limits. // Writing headers may fail during the encode state if they violate HPACK limits.
Throwable failureCause = future.cause(); Throwable failureCause = future.cause();
@ -372,21 +373,21 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
} }
@Override @Override
public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData, public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
ChannelPromise promise) { Promise<Void> promise) {
return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData, promise); return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData, promise);
} }
@Override @Override
public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement, public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
ChannelPromise promise) { Promise<Void> promise) {
return promise.setFailure(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" + return promise.setFailure(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" +
" objects to control window sizes")); " objects to control window sizes"));
} }
@Override @Override
public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags, public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
ByteBuf payload, ChannelPromise promise) { ByteBuf payload, Promise<Void> promise) {
return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload, promise); return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload, promise);
} }
@ -441,9 +442,9 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
private int dataSize; private int dataSize;
FlowControlledData(Http2Stream stream, ByteBuf buf, int padding, boolean endOfStream, FlowControlledData(Http2Stream stream, ByteBuf buf, int padding, boolean endOfStream,
ChannelPromise promise) { Promise<Void> promise, Channel channel) {
super(stream, padding, endOfStream, promise); super(stream, padding, endOfStream, promise);
queue = new CoalescingBufferQueue(promise.channel()); queue = new CoalescingBufferQueue(channel);
queue.add(buf, promise); queue.add(buf, promise);
dataSize = queue.readableBytes(); dataSize = queue.readableBytes();
} }
@ -481,7 +482,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
// There's no need to write any data frames because there are only empty data frames in the // There's no need to write any data frames because there are only empty data frames in the
// queue and it is not end of stream yet. Just complete their promises by getting the buffer // queue and it is not end of stream yet. Just complete their promises by getting the buffer
// corresponding to 0 bytes and writing it to the channel (to preserve notification order). // corresponding to 0 bytes and writing it to the channel (to preserve notification order).
ChannelPromise writePromise = ctx.newPromise().addListener(this); Promise<Void> writePromise = ctx.newPromise();
writePromise.addListener(this);
ctx.write(queue.remove(0, writePromise), writePromise); ctx.write(queue.remove(0, writePromise), writePromise);
} }
return; return;
@ -494,7 +496,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
// Determine how much data to write. // Determine how much data to write.
int writableData = min(queuedData, allowedBytes); int writableData = min(queuedData, allowedBytes);
ChannelPromise writePromise = ctx.newPromise().addListener(this); Promise<Void> writePromise = ctx.newPromise();
writePromise.addListener(this);
ByteBuf toWrite = queue.remove(writableData, writePromise); ByteBuf toWrite = queue.remove(writableData, writePromise);
dataSize = queue.readableBytes(); dataSize = queue.readableBytes();
@ -523,8 +526,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
} }
} }
private void notifyLifecycleManagerOnError(ChannelFuture future, final ChannelHandlerContext ctx) { private void notifyLifecycleManagerOnError(Future<Void> future, final ChannelHandlerContext ctx) {
future.addListener((ChannelFutureListener) future1 -> { future.addListener(future1 -> {
Throwable cause = future1.cause(); Throwable cause = future1.cause();
if (cause != null) { if (cause != null) {
lifecycleManager.onError(ctx, true, cause); lifecycleManager.onError(ctx, true, cause);
@ -546,7 +549,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
FlowControlledHeaders(Http2Stream stream, Http2Headers headers, boolean hasPriority, FlowControlledHeaders(Http2Stream stream, Http2Headers headers, boolean hasPriority,
int streamDependency, short weight, boolean exclusive, int streamDependency, short weight, boolean exclusive,
int padding, boolean endOfStream, ChannelPromise promise) { int padding, boolean endOfStream, Promise<Void> promise) {
super(stream, padding, endOfStream, promise); super(stream, padding, endOfStream, promise);
this.headers = headers; this.headers = headers;
this.hasPriority = hasPriority; this.hasPriority = hasPriority;
@ -575,7 +578,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
// closeStreamLocal(). // closeStreamLocal().
promise.addListener(this); promise.addListener(this);
ChannelFuture f = sendHeaders(frameWriter, ctx, stream.id(), headers, hasPriority, streamDependency, Future<Void> f = sendHeaders(frameWriter, ctx, stream.id(), headers, hasPriority, streamDependency,
weight, exclusive, padding, endOfStream, promise); weight, exclusive, padding, endOfStream, promise);
// Writing headers may fail during the encode state if they violate HPACK limits. // Writing headers may fail during the encode state if they violate HPACK limits.
Throwable failureCause = f.cause(); Throwable failureCause = f.cause();
@ -595,15 +598,14 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
/** /**
* Common base type for payloads to deliver via flow-control. * Common base type for payloads to deliver via flow-control.
*/ */
public abstract class FlowControlledBase implements Http2RemoteFlowController.FlowControlled, public abstract class FlowControlledBase implements Http2RemoteFlowController.FlowControlled, FutureListener<Void> {
ChannelFutureListener {
protected final Http2Stream stream; protected final Http2Stream stream;
protected ChannelPromise promise; protected Promise<Void> promise;
protected boolean endOfStream; protected boolean endOfStream;
protected int padding; protected int padding;
FlowControlledBase(final Http2Stream stream, int padding, boolean endOfStream, FlowControlledBase(final Http2Stream stream, int padding, boolean endOfStream,
final ChannelPromise promise) { final Promise<Void> promise) {
checkPositiveOrZero(padding, "padding"); checkPositiveOrZero(padding, "padding");
this.padding = padding; this.padding = padding;
this.endOfStream = endOfStream; this.endOfStream = endOfStream;
@ -619,7 +621,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
} }
@Override @Override
public void operationComplete(ChannelFuture future) throws Exception { public void operationComplete(Future<? extends Void> future) throws Exception {
if (!future.isSuccess()) { if (!future.isSuccess()) {
error(flowController().channelHandlerContext(), future.cause()); error(flowController().channelHandlerContext(), future.cause());
} }

View File

@ -16,12 +16,12 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator; import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
import io.netty.handler.codec.http2.Http2FrameWriter.Configuration; import io.netty.handler.codec.http2.Http2FrameWriter.Configuration;
import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector; 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 io.netty.util.internal.UnstableApi;
import static io.netty.buffer.Unpooled.directBuffer; import static io.netty.buffer.Unpooled.directBuffer;
@ -132,10 +132,10 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
public void close() { } public void close() { }
@Override @Override
public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
int padding, boolean endStream, ChannelPromise promise) { int padding, boolean endStream, Promise<Void> promise) {
final SimpleChannelPromiseAggregator promiseAggregator = final SimpleChannelPromiseAggregator promiseAggregator =
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor()); new SimpleChannelPromiseAggregator(promise, ctx.executor());
ByteBuf frameHeader = null; ByteBuf frameHeader = null;
try { try {
verifyStreamId(streamId, STREAM_ID); verifyStreamId(streamId, STREAM_ID);
@ -254,23 +254,23 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
Http2Headers headers, int padding, boolean endStream, ChannelPromise promise) { Http2Headers headers, int padding, boolean endStream, Promise<Void> promise) {
return writeHeadersInternal(ctx, streamId, headers, padding, endStream, return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
false, 0, (short) 0, false, promise); false, 0, (short) 0, false, promise);
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
Http2Headers headers, int streamDependency, short weight, boolean exclusive, Http2Headers headers, int streamDependency, short weight, boolean exclusive,
int padding, boolean endStream, ChannelPromise promise) { int padding, boolean endStream, Promise<Void> promise) {
return writeHeadersInternal(ctx, streamId, headers, padding, endStream, return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
true, streamDependency, weight, exclusive, promise); true, streamDependency, weight, exclusive, promise);
} }
@Override @Override
public ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId, public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId,
int streamDependency, short weight, boolean exclusive, ChannelPromise promise) { int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
try { try {
verifyStreamId(streamId, STREAM_ID); verifyStreamId(streamId, STREAM_ID);
verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY); verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY);
@ -288,8 +288,8 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
} }
@Override @Override
public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode, public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise) { Promise<Void> promise) {
try { try {
verifyStreamId(streamId, STREAM_ID); verifyStreamId(streamId, STREAM_ID);
verifyErrorCode(errorCode); verifyErrorCode(errorCode);
@ -304,8 +304,8 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
} }
@Override @Override
public ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings, public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
ChannelPromise promise) { Promise<Void> promise) {
try { try {
requireNonNull(settings, "settings"); requireNonNull(settings, "settings");
int payloadLength = SETTING_ENTRY_LENGTH * settings.size(); int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
@ -322,7 +322,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
} }
@Override @Override
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) { public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
try { try {
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH); ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0); writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0);
@ -333,7 +333,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
} }
@Override @Override
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) { public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
Http2Flags flags = ack ? new Http2Flags().ack(true) : new Http2Flags(); Http2Flags flags = ack ? new Http2Flags().ack(true) : new Http2Flags();
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + PING_FRAME_PAYLOAD_LENGTH); 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 // Assume nothing below will throw until buf is written. That way we don't have to take care of ownership
@ -344,11 +344,10 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
} }
@Override @Override
public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId, public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
int promisedStreamId, Http2Headers headers, int padding, ChannelPromise promise) { Http2Headers headers, int padding, Promise<Void> promise) {
ByteBuf headerBlock = null; ByteBuf headerBlock = null;
SimpleChannelPromiseAggregator promiseAggregator = SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
try { try {
verifyStreamId(streamId, STREAM_ID); verifyStreamId(streamId, STREAM_ID);
verifyStreamId(promisedStreamId, "Promised Stream ID"); verifyStreamId(promisedStreamId, "Promised Stream ID");
@ -402,10 +401,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
} }
@Override @Override
public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
ByteBuf debugData, ChannelPromise promise) { ByteBuf debugData, Promise<Void> promise) {
SimpleChannelPromiseAggregator promiseAggregator = SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
try { try {
verifyStreamOrConnectionId(lastStreamId, "Last Stream ID"); verifyStreamOrConnectionId(lastStreamId, "Last Stream ID");
verifyErrorCode(errorCode); verifyErrorCode(errorCode);
@ -437,8 +435,8 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
} }
@Override @Override
public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId, public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
int windowSizeIncrement, ChannelPromise promise) { int windowSizeIncrement, Promise<Void> promise) {
try { try {
verifyStreamOrConnectionId(streamId, STREAM_ID); verifyStreamOrConnectionId(streamId, STREAM_ID);
verifyWindowSizeIncrement(windowSizeIncrement); verifyWindowSizeIncrement(windowSizeIncrement);
@ -453,10 +451,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
} }
@Override @Override
public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
Http2Flags flags, ByteBuf payload, ChannelPromise promise) { Http2Flags flags, ByteBuf payload, Promise<Void> promise) {
SimpleChannelPromiseAggregator promiseAggregator = SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
try { try {
verifyStreamOrConnectionId(streamId, STREAM_ID); verifyStreamOrConnectionId(streamId, STREAM_ID);
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH); ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
@ -481,12 +478,11 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
return promiseAggregator.doneAllocatingPromises(); return promiseAggregator.doneAllocatingPromises();
} }
private ChannelFuture writeHeadersInternal(ChannelHandlerContext ctx, private Future<Void> writeHeadersInternal(ChannelHandlerContext ctx,
int streamId, Http2Headers headers, int padding, boolean endStream, int streamId, Http2Headers headers, int padding, boolean endStream,
boolean hasPriority, int streamDependency, short weight, boolean exclusive, ChannelPromise promise) { boolean hasPriority, int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
ByteBuf headerBlock = null; ByteBuf headerBlock = null;
SimpleChannelPromiseAggregator promiseAggregator = SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
try { try {
verifyStreamId(streamId, STREAM_ID); verifyStreamId(streamId, STREAM_ID);
if (hasPriority) { if (hasPriority) {
@ -551,7 +547,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
/** /**
* Writes as many continuation frames as needed until {@code padding} and {@code headerBlock} are consumed. * Writes as many continuation frames as needed until {@code padding} and {@code headerBlock} are consumed.
*/ */
private ChannelFuture writeContinuationFrames(ChannelHandlerContext ctx, int streamId, private Future<Void> writeContinuationFrames(ChannelHandlerContext ctx, int streamId,
ByteBuf headerBlock, SimpleChannelPromiseAggregator promiseAggregator) { ByteBuf headerBlock, SimpleChannelPromiseAggregator promiseAggregator) {
Http2Flags flags = new Http2Flags(); Http2Flags flags = new Http2Flags();

View File

@ -18,13 +18,12 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise;
import io.netty.handler.ssl.ApplicationProtocolNames; import io.netty.handler.ssl.ApplicationProtocolNames;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import static io.netty.buffer.Unpooled.directBuffer; import static io.netty.buffer.Unpooled.directBuffer;
@ -255,18 +254,18 @@ public final class Http2CodecUtil {
} }
/** /**
* Provides the ability to associate the outcome of multiple {@link ChannelPromise} * Provides the ability to associate the outcome of multiple {@link Promise}
* objects into a single {@link ChannelPromise} object. * objects into a single {@link Promise} object.
*/ */
static final class SimpleChannelPromiseAggregator extends DefaultChannelPromise { static final class SimpleChannelPromiseAggregator extends DefaultPromise<Void> {
private final ChannelPromise promise; private final Promise<Void> promise;
private int expectedCount; private int expectedCount;
private int doneCount; private int doneCount;
private Throwable aggregateFailure; private Throwable aggregateFailure;
private boolean doneAllocating; private boolean doneAllocating;
SimpleChannelPromiseAggregator(ChannelPromise promise, Channel c, EventExecutor e) { SimpleChannelPromiseAggregator(Promise<Void> promise, EventExecutor e) {
super(c, e); super(e);
assert promise != null && !promise.isDone(); assert promise != null && !promise.isDone();
this.promise = promise; this.promise = promise;
} }
@ -276,7 +275,7 @@ public final class Http2CodecUtil {
* @return A new promise which will be aggregated. * @return A new promise which will be aggregated.
* {@code null} if {@link #doneAllocatingPromises()} was previously called. * {@code null} if {@link #doneAllocatingPromises()} was previously called.
*/ */
public ChannelPromise newPromise() { public Promise<Void> newPromise() {
assert !doneAllocating : "Done allocating. No more promises can be allocated."; assert !doneAllocating : "Done allocating. No more promises can be allocated.";
++expectedCount; ++expectedCount;
return this; return this;
@ -287,7 +286,7 @@ public final class Http2CodecUtil {
* The aggregation can not be successful until this method is called. * The aggregation can not be successful until this method is called.
* @return The promise that is the aggregation of all promises allocated with {@link #newPromise()}. * @return The promise that is the aggregation of all promises allocated with {@link #newPromise()}.
*/ */
public ChannelPromise doneAllocatingPromises() { public Promise<Void> doneAllocatingPromises() {
if (!doneAllocating) { if (!doneAllocating) {
doneAllocating = true; doneAllocating = true;
if (doneCount == expectedCount || expectedCount == 0) { if (doneCount == expectedCount || expectedCount == 0) {
@ -319,7 +318,7 @@ public final class Http2CodecUtil {
* because that may be expected. * because that may be expected.
*/ */
@Override @Override
public ChannelPromise setFailure(Throwable cause) { public Promise<Void> setFailure(Throwable cause) {
if (allowFailure()) { if (allowFailure()) {
++doneCount; ++doneCount;
setAggregateFailure(cause); setAggregateFailure(cause);
@ -331,7 +330,7 @@ public final class Http2CodecUtil {
} }
@Override @Override
public ChannelPromise setSuccess(Void result) { public Promise<Void> setSuccess(Void result) {
if (awaitingPromises()) { if (awaitingPromises()) {
++doneCount; ++doneCount;
if (allPromisesDone()) { if (allPromisesDone()) {
@ -367,9 +366,9 @@ public final class Http2CodecUtil {
return doneCount == expectedCount && doneAllocating; return doneCount == expectedCount && doneAllocating;
} }
private ChannelPromise setPromise() { private Promise<Void> setPromise() {
if (aggregateFailure == null) { if (aggregateFailure == null) {
promise.setSuccess(); promise.setSuccess(null);
return super.setSuccess(null); return super.setSuccess(null);
} else { } else {
promise.setFailure(aggregateFailure); promise.setFailure(aggregateFailure);
@ -379,7 +378,7 @@ public final class Http2CodecUtil {
private boolean tryPromise() { private boolean tryPromise() {
if (aggregateFailure == null) { if (aggregateFailure == null) {
promise.trySuccess(); promise.trySuccess(null);
return super.trySuccess(null); return super.trySuccess(null);
} else { } else {
promise.tryFailure(aggregateFailure); promise.tryFailure(aggregateFailure);

View File

@ -15,9 +15,9 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
@ -61,8 +61,9 @@ public interface Http2ConnectionEncoder extends Http2FrameWriter {
/** /**
* Writes the given data to the internal {@link Http2FrameWriter} without performing any * Writes the given data to the internal {@link Http2FrameWriter} without performing any
* state checks on the connection/stream. * state checks on the connection/stream.
* @return
*/ */
@Override @Override
ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
Http2Flags flags, ByteBuf payload, ChannelPromise promise); Http2Flags flags, ByteBuf payload, Promise<Void> promise);
} }

View File

@ -17,15 +17,16 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http2.Http2Exception.CompositeStreamException; import io.netty.handler.codec.http2.Http2Exception.CompositeStreamException;
import io.netty.handler.codec.http2.Http2Exception.StreamException; import io.netty.handler.codec.http2.Http2Exception.StreamException;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.ScheduledFuture; import io.netty.util.concurrent.ScheduledFuture;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
@ -74,7 +75,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
private final Http2ConnectionEncoder encoder; private final Http2ConnectionEncoder encoder;
private final Http2Settings initialSettings; private final Http2Settings initialSettings;
private final boolean decoupleCloseAndGoAway; private final boolean decoupleCloseAndGoAway;
private ChannelFutureListener closeListener; private FutureListener<Object> closeListener;
private BaseDecoder byteDecoder; private BaseDecoder byteDecoder;
private long gracefulShutdownTimeoutMillis; private long gracefulShutdownTimeoutMillis;
@ -352,12 +353,12 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
final boolean isClient = !connection().isServer(); final boolean isClient = !connection().isServer();
if (isClient) { if (isClient) {
// Clients must send the preface string as the first bytes on the connection. // Clients must send the preface string as the first bytes on the connection.
ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE); ctx.write(connectionPrefaceBuf()).addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
} }
// Both client and server must send their initial settings. // Both client and server must send their initial settings.
encoder.writeSettings(ctx, initialSettings, ctx.newPromise()).addListener( encoder.writeSettings(ctx, initialSettings, ctx.newPromise())
ChannelFutureListener.CLOSE_ON_FAILURE); .addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
if (isClient) { if (isClient) {
// If this handler is extended by the user and we directly fire the userEvent from this context then // If this handler is extended by the user and we directly fire the userEvent from this context then
@ -436,23 +437,23 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
} }
@Override @Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) { public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
ctx.bind(localAddress, promise); ctx.bind(localAddress, promise);
} }
@Override @Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) { Promise<Void> promise) {
ctx.connect(remoteAddress, localAddress, promise); ctx.connect(remoteAddress, localAddress, promise);
} }
@Override @Override
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) { public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.disconnect(promise); ctx.disconnect(promise);
} }
@Override @Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) { public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
if (decoupleCloseAndGoAway) { if (decoupleCloseAndGoAway) {
ctx.close(promise); ctx.close(promise);
return; return;
@ -468,21 +469,21 @@ 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 // 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. // flushed to the OS.
// https://github.com/netty/netty/issues/5307 // https://github.com/netty/netty/issues/5307
ChannelFuture f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise()); Future<Void> f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise());
ctx.flush(); ctx.flush();
doGracefulShutdown(ctx, f, promise); doGracefulShutdown(ctx, f, promise);
} }
private ChannelFutureListener newClosingChannelFutureListener( private FutureListener<Object> newClosingChannelFutureListener(
ChannelHandlerContext ctx, ChannelPromise promise) { ChannelHandlerContext ctx, Promise<Void> promise) {
long gracefulShutdownTimeoutMillis = this.gracefulShutdownTimeoutMillis; long gracefulShutdownTimeoutMillis = this.gracefulShutdownTimeoutMillis;
return gracefulShutdownTimeoutMillis < 0 ? return gracefulShutdownTimeoutMillis < 0 ?
new ClosingChannelFutureListener(ctx, promise) : new ClosingChannelFutureListener(ctx, promise) :
new ClosingChannelFutureListener(ctx, promise, gracefulShutdownTimeoutMillis, MILLISECONDS); new ClosingChannelFutureListener(ctx, promise, gracefulShutdownTimeoutMillis, MILLISECONDS);
} }
private void doGracefulShutdown(ChannelHandlerContext ctx, ChannelFuture future, final ChannelPromise promise) { private void doGracefulShutdown(ChannelHandlerContext ctx, Future<Void> future, final Promise<Void> promise) {
final ChannelFutureListener listener = newClosingChannelFutureListener(ctx, promise); FutureListener<Object> listener = newClosingChannelFutureListener(ctx, promise);
if (isGracefulShutdownComplete()) { if (isGracefulShutdownComplete()) {
// If there are no active streams, close immediately after the GO_AWAY write completes or the timeout // If there are no active streams, close immediately after the GO_AWAY write completes or the timeout
// elapsed. // elapsed.
@ -495,7 +496,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
if (closeListener == null) { if (closeListener == null) {
closeListener = listener; closeListener = listener;
} else if (promise != null) { } else if (promise != null) {
final ChannelFutureListener oldCloseListener = closeListener; FutureListener<Object> oldCloseListener = closeListener;
closeListener = future1 -> { closeListener = future1 -> {
try { try {
oldCloseListener.operationComplete(future1); oldCloseListener.operationComplete(future1);
@ -508,12 +509,12 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
} }
@Override @Override
public void register(ChannelHandlerContext ctx, ChannelPromise promise) { public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.register(promise); ctx.register(promise);
} }
@Override @Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) { public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.deregister(promise); ctx.deregister(promise);
} }
@ -523,7 +524,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
ctx.write(msg, promise); ctx.write(msg, promise);
} }
@ -574,7 +575,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
* @param future If closing, the future after which to close the channel. * @param future If closing, the future after which to close the channel.
*/ */
@Override @Override
public void closeStreamLocal(Http2Stream stream, ChannelFuture future) { public void closeStreamLocal(Http2Stream stream, Future<Void> future) {
switch (stream.state()) { switch (stream.state()) {
case HALF_CLOSED_LOCAL: case HALF_CLOSED_LOCAL:
case OPEN: case OPEN:
@ -594,7 +595,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
* @param future If closing, the future after which to close the channel. * @param future If closing, the future after which to close the channel.
*/ */
@Override @Override
public void closeStreamRemote(Http2Stream stream, ChannelFuture future) { public void closeStreamRemote(Http2Stream stream, Future<Void> future) {
switch (stream.state()) { switch (stream.state()) {
case HALF_CLOSED_REMOTE: case HALF_CLOSED_REMOTE:
case OPEN: case OPEN:
@ -607,13 +608,13 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
} }
@Override @Override
public void closeStream(final Http2Stream stream, ChannelFuture future) { public void closeStream(final Http2Stream stream, Future<Void> future) {
stream.close(); stream.close();
if (future.isDone()) { if (future.isDone()) {
checkCloseConnection(future); checkCloseConnection(future);
} else { } else {
future.addListener((ChannelFutureListener) this::checkCloseConnection); future.addListener(this::checkCloseConnection);
} }
} }
@ -661,8 +662,8 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
http2Ex = new Http2Exception(INTERNAL_ERROR, cause.getMessage(), cause); http2Ex = new Http2Exception(INTERNAL_ERROR, cause.getMessage(), cause);
} }
ChannelPromise promise = ctx.newPromise(); Promise<Void> promise = ctx.newPromise();
ChannelFuture future = goAway(ctx, http2Ex, ctx.newPromise()); Future<Void> future = goAway(ctx, http2Ex, ctx.newPromise());
if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) { if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) {
doGracefulShutdown(ctx, future, promise); doGracefulShutdown(ctx, future, promise);
} else { } else {
@ -743,20 +744,20 @@ 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 * 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. * we could create a new stream.
*/ */
private ChannelFuture resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode, private Future<Void> resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise) { Promise<Void> promise) {
ChannelFuture future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise); Future<Void> future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise);
if (future.isDone()) { if (future.isDone()) {
closeConnectionOnError(ctx, future); closeConnectionOnError(ctx, future);
} else { } else {
future.addListener((ChannelFutureListener) future1 -> closeConnectionOnError(ctx, future1)); future.addListener(ctx, this::closeConnectionOnError);
} }
return future; return future;
} }
@Override @Override
public ChannelFuture resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode, public Future<Void> resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise) { Promise<Void> promise) {
final Http2Stream stream = connection().stream(streamId); final Http2Stream stream = connection().stream(streamId);
if (stream == null) { if (stream == null) {
return resetUnknownStream(ctx, streamId, errorCode, promise); return resetUnknownStream(ctx, streamId, errorCode, promise);
@ -765,11 +766,11 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
return resetStream(ctx, stream, errorCode, promise); return resetStream(ctx, stream, errorCode, promise);
} }
private ChannelFuture resetStream(final ChannelHandlerContext ctx, final Http2Stream stream, private Future<Void> resetStream(final ChannelHandlerContext ctx, final Http2Stream stream,
long errorCode, ChannelPromise promise) { long errorCode, Promise<Void> promise) {
if (stream.isResetSent()) { if (stream.isResetSent()) {
// Don't write a RST_STREAM frame if we have already written one. // Don't write a RST_STREAM frame if we have already written one.
return promise.setSuccess(); return promise.setSuccess(null);
} }
// Synchronously set the resetSent flag to prevent any subsequent calls // Synchronously set the resetSent flag to prevent any subsequent calls
// from resulting in multiple reset frames being sent. // from resulting in multiple reset frames being sent.
@ -778,32 +779,32 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
// call resetStream(...) again. // call resetStream(...) again.
stream.resetSent(); stream.resetSent();
final ChannelFuture future; final Future<Void> future;
// If the remote peer is not aware of the steam, then we are not allowed to send a RST_STREAM // If the remote peer is not aware of the steam, then we are not allowed to send a RST_STREAM
// https://tools.ietf.org/html/rfc7540#section-6.4. // https://tools.ietf.org/html/rfc7540#section-6.4.
if (stream.state() == IDLE || if (stream.state() == IDLE ||
connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) { connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) {
future = promise.setSuccess(); future = promise.setSuccess(null);
} else { } else {
future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise); future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise);
} }
if (future.isDone()) { if (future.isDone()) {
processRstStreamWriteResult(ctx, stream, future); processRstStreamWriteResult(ctx, stream, future);
} else { } else {
future.addListener((ChannelFutureListener) future1 -> processRstStreamWriteResult(ctx, stream, future1)); future.addListener(future1 -> processRstStreamWriteResult(ctx, stream, future1));
} }
return future; return future;
} }
@Override @Override
public ChannelFuture goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode, public Future<Void> goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode,
final ByteBuf debugData, ChannelPromise promise) { final ByteBuf debugData, Promise<Void> promise) {
final Http2Connection connection = connection(); final Http2Connection connection = connection();
try { try {
if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) { if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) {
debugData.release(); debugData.release();
promise.trySuccess(); promise.trySuccess(null);
return promise; return promise;
} }
} catch (Throwable cause) { } catch (Throwable cause) {
@ -815,12 +816,12 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
// Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and // 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. // result in an IllegalRefCountException.
debugData.retain(); debugData.retain();
ChannelFuture future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise); Future<Void> future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
if (future.isDone()) { if (future.isDone()) {
processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future); processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future);
} else { } else {
future.addListener((ChannelFutureListener) future1 -> future.addListener(future1 ->
processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future1)); processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future1));
} }
@ -831,11 +832,11 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
* Closes the connection if the graceful shutdown process has completed. * Closes the connection if the graceful shutdown process has completed.
* @param future Represents the status that will be passed to the {@link #closeListener}. * @param future Represents the status that will be passed to the {@link #closeListener}.
*/ */
private void checkCloseConnection(ChannelFuture future) { private void checkCloseConnection(Future<?> future) {
// If this connection is closing and the graceful shutdown has completed, close the connection // If this connection is closing and the graceful shutdown has completed, close the connection
// once this operation completes. // once this operation completes.
if (closeListener != null && isGracefulShutdownComplete()) { if (closeListener != null && isGracefulShutdownComplete()) {
ChannelFutureListener closeListener = this.closeListener; FutureListener<Object> closeListener = this.closeListener;
// This method could be called multiple times // This method could be called multiple times
// and we don't want to notify the closeListener multiple times. // and we don't want to notify the closeListener multiple times.
this.closeListener = null; this.closeListener = null;
@ -851,7 +852,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
* Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush * Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush
* immediately, this is the responsibility of the caller. * immediately, this is the responsibility of the caller.
*/ */
private ChannelFuture goAway(ChannelHandlerContext ctx, Http2Exception cause, ChannelPromise promise) { private Future<Void> goAway(ChannelHandlerContext ctx, Http2Exception cause, Promise<Void> promise) {
long errorCode = cause != null ? cause.error().code() : NO_ERROR.code(); long errorCode = cause != null ? cause.error().code() : NO_ERROR.code();
int lastKnownStream; int lastKnownStream;
if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) { if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) {
@ -866,16 +867,17 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), promise); return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), promise);
} }
private void processRstStreamWriteResult(ChannelHandlerContext ctx, Http2Stream stream, ChannelFuture future) { @SuppressWarnings("unchecked")
private void processRstStreamWriteResult(ChannelHandlerContext ctx, Http2Stream stream, Future<?> future) {
if (future.isSuccess()) { if (future.isSuccess()) {
closeStream(stream, future); closeStream(stream, (Future<Void>) future);
} else { } else {
// The connection will be closed and so no need to change the resetSent flag to false. // The connection will be closed and so no need to change the resetSent flag to false.
onConnectionError(ctx, true, future.cause(), null); onConnectionError(ctx, true, future.cause(), null);
} }
} }
private void closeConnectionOnError(ChannelHandlerContext ctx, ChannelFuture future) { private void closeConnectionOnError(ChannelHandlerContext ctx, Future<?> future) {
if (!future.isSuccess()) { if (!future.isSuccess()) {
onConnectionError(ctx, true, future.cause(), null); onConnectionError(ctx, true, future.cause(), null);
} }
@ -889,7 +891,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
} }
private static void processGoAwayWriteResult(final ChannelHandlerContext ctx, final int lastStreamId, private static void processGoAwayWriteResult(final ChannelHandlerContext ctx, final int lastStreamId,
final long errorCode, final ByteBuf debugData, ChannelFuture future) { final long errorCode, final ByteBuf debugData, Future<?> future) {
try { try {
if (future.isSuccess()) { if (future.isSuccess()) {
if (errorCode != NO_ERROR.code()) { if (errorCode != NO_ERROR.code()) {
@ -917,19 +919,19 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
/** /**
* Closes the channel when the future completes. * Closes the channel when the future completes.
*/ */
private static final class ClosingChannelFutureListener implements ChannelFutureListener { private static final class ClosingChannelFutureListener implements FutureListener<Object> {
private final ChannelHandlerContext ctx; private final ChannelHandlerContext ctx;
private final ChannelPromise promise; private final Promise<Void> promise;
private final ScheduledFuture<?> timeoutTask; private final ScheduledFuture<?> timeoutTask;
private boolean closed; private boolean closed;
ClosingChannelFutureListener(ChannelHandlerContext ctx, ChannelPromise promise) { ClosingChannelFutureListener(ChannelHandlerContext ctx, Promise<Void> promise) {
this.ctx = ctx; this.ctx = ctx;
this.promise = promise; this.promise = promise;
timeoutTask = null; timeoutTask = null;
} }
ClosingChannelFutureListener(final ChannelHandlerContext ctx, final ChannelPromise promise, ClosingChannelFutureListener(final ChannelHandlerContext ctx, final Promise<Void> promise,
long timeout, TimeUnit unit) { long timeout, TimeUnit unit) {
this.ctx = ctx; this.ctx = ctx;
this.promise = promise; this.promise = promise;
@ -937,7 +939,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
} }
@Override @Override
public void operationComplete(ChannelFuture sentGoAwayFuture) { public void operationComplete(Future<?> sentGoAwayFuture) {
if (timeoutTask != null) { if (timeoutTask != null) {
timeoutTask.cancel(false); timeoutTask.cancel(false);
} }

View File

@ -14,10 +14,10 @@
*/ */
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; 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.ObjectUtil;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
@ -31,14 +31,9 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ControlFrameLimitEncoder.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ControlFrameLimitEncoder.class);
private final int maxOutstandingControlFrames; private final int maxOutstandingControlFrames;
private final ChannelFutureListener outstandingControlFramesListener = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
outstandingControlFrames--;
}
};
private Http2LifecycleManager lifecycleManager; private Http2LifecycleManager lifecycleManager;
private int outstandingControlFrames; private int outstandingControlFrames;
private final FutureListener<Void> outstandingControlFramesListener = future -> outstandingControlFrames--;
private boolean limitReached; private boolean limitReached;
Http2ControlFrameLimitEncoder(Http2ConnectionEncoder delegate, int maxOutstandingControlFrames) { Http2ControlFrameLimitEncoder(Http2ConnectionEncoder delegate, int maxOutstandingControlFrames) {
@ -54,8 +49,8 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
} }
@Override @Override
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) { public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise); Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
if (newPromise == null) { if (newPromise == null) {
return promise; return promise;
} }
@ -63,10 +58,10 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
} }
@Override @Override
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) { public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
// Only apply the limit to ping acks. // Only apply the limit to ping acks.
if (ack) { if (ack) {
ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise); Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
if (newPromise == null) { if (newPromise == null) {
return promise; return promise;
} }
@ -76,16 +71,16 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
} }
@Override @Override
public ChannelFuture writeRstStream( public Future<Void> writeRstStream(
ChannelHandlerContext ctx, int streamId, long errorCode, ChannelPromise promise) { ChannelHandlerContext ctx, int streamId, long errorCode, Promise<Void> promise) {
ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise); Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
if (newPromise == null) { if (newPromise == null) {
return promise; return promise;
} }
return super.writeRstStream(ctx, streamId, errorCode, newPromise); return super.writeRstStream(ctx, streamId, errorCode, newPromise);
} }
private ChannelPromise handleOutstandingControlFrames(ChannelHandlerContext ctx, ChannelPromise promise) { private Promise<Void> handleOutstandingControlFrames(ChannelHandlerContext ctx, Promise<Void> promise) {
if (!limitReached) { if (!limitReached) {
if (outstandingControlFrames == maxOutstandingControlFrames) { if (outstandingControlFrames == maxOutstandingControlFrames) {
// Let's try to flush once as we may be able to flush some of the control frames. // Let's try to flush once as we may be able to flush some of the control frames.
@ -106,7 +101,7 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
// We did not reach the limit yet, add the listener to decrement the number of outstanding control frames // We did not reach the limit yet, add the listener to decrement the number of outstanding control frames
// once the promise was completed // once the promise was completed
return promise.addListener(outstandingControlFramesListener); promise.addListener(outstandingControlFramesListener);
} }
return promise; return promise;
} }

View File

@ -15,9 +15,9 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
/** /**
@ -40,6 +40,6 @@ public interface Http2DataWriter {
* @param promise the promise for the write. * @param promise the promise for the write.
* @return the future for the write. * @return the future for the write.
*/ */
ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, Future<Void> writeData(ChannelHandlerContext ctx, int streamId,
ByteBuf data, int padding, boolean endStream, ChannelPromise promise); ByteBuf data, int padding, boolean endStream, Promise<Void> promise);
} }

View File

@ -17,10 +17,8 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.handler.codec.UnsupportedMessageTypeException;
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeEvent; import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeEvent;
import io.netty.handler.codec.http2.Http2Connection.PropertyKey; import io.netty.handler.codec.http2.Http2Connection.PropertyKey;
@ -32,6 +30,7 @@ import io.netty.util.ReferenceCounted;
import io.netty.util.collection.IntObjectHashMap; import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap; import io.netty.util.collection.IntObjectMap;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import io.netty.util.internal.logging.InternalLogLevel; import io.netty.util.internal.logging.InternalLogLevel;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
@ -91,10 +90,10 @@ import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
* *
* <pre>{@code * <pre>{@code
* final Http2Stream2 stream = handler.newStream(); * final Http2Stream2 stream = handler.newStream();
* ctx.write(headersFrame.stream(stream)).addListener(new ChannelFutureListener() { * ctx.write(headersFrame.stream(stream)).addListener(new FutureListener<Void>() {
* *
* @Override * @Override
* public void operationComplete(ChannelFuture f) { * public void operationComplete(Future<Void> f) {
* if (f.isSuccess()) { * if (f.isSuccess()) {
* // Stream is active and stream.id() returns a valid stream identifier. * // Stream is active and stream.id() returns a valid stream identifier.
* System.out.println("New stream with id " + stream.id() + " created."); * System.out.println("New stream with id " + stream.id() + " created.");
@ -112,7 +111,7 @@ import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
* } * }
* }</pre> * }</pre>
* *
* <p>If a new stream cannot be created due to stream id exhaustion of the endpoint, the {@link ChannelPromise} of the * <p>If a new stream cannot be created due to stream id exhaustion of the endpoint, the {@link Promise} of the
* HEADERS frame will fail with a {@link Http2NoMoreStreamIdsException}. * HEADERS frame will fail with a {@link Http2NoMoreStreamIdsException}.
* *
* <p>The HTTP/2 standard allows for an endpoint to limit the maximum number of concurrently active streams via the * <p>The HTTP/2 standard allows for an endpoint to limit the maximum number of concurrently active streams via the
@ -193,7 +192,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
if (connection().numActiveStreams() > 0) { if (connection().numActiveStreams() > 0) {
connection().forEachActiveStream(stream -> { connection().forEachActiveStream(stream -> {
try { try {
return streamVisitor.visit((Http2FrameStream) stream.getProperty(streamKey)); return streamVisitor.visit(stream.getProperty(streamKey));
} catch (Throwable cause) { } catch (Throwable cause) {
onError(ctx, false, cause); onError(ctx, false, cause);
return false; return false;
@ -282,7 +281,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
* streams. * streams.
*/ */
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (msg instanceof Http2DataFrame) { if (msg instanceof Http2DataFrame) {
Http2DataFrame dataFrame = (Http2DataFrame) msg; Http2DataFrame dataFrame = (Http2DataFrame) msg;
encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(), encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(),
@ -300,7 +299,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
} else { } else {
consumeBytes(frameStream.id(), frame.windowSizeIncrement()); consumeBytes(frameStream.id(), frame.windowSizeIncrement());
} }
promise.setSuccess(); promise.setSuccess(null);
} catch (Throwable t) { } catch (Throwable t) {
promise.setFailure(t); promise.setFailure(t);
} }
@ -355,7 +354,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
Http2Stream stream = connection().stream(streamId); Http2Stream stream = connection().stream(streamId);
// Upgraded requests are ineligible for stream control. We add the null check // Upgraded requests are ineligible for stream control. We add the null check
// in case the stream has been deregistered. // in case the stream has been deregistered.
if (stream != null && streamId == Http2CodecUtil.HTTP_UPGRADE_STREAM_ID) { if (stream != null && streamId == HTTP_UPGRADE_STREAM_ID) {
Boolean upgraded = stream.getProperty(upgradeKey); Boolean upgraded = stream.getProperty(upgradeKey);
if (Boolean.TRUE.equals(upgraded)) { if (Boolean.TRUE.equals(upgraded)) {
return false; return false;
@ -365,7 +364,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
return connection().local().flowController().consumeBytes(stream, bytes); return connection().local().flowController().consumeBytes(stream, bytes);
} }
private void writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame, ChannelPromise promise) { private void writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame, Promise<Void> promise) {
if (frame.lastStreamId() > -1) { if (frame.lastStreamId() > -1) {
frame.release(); frame.release();
throw new IllegalArgumentException("Last stream id must not be set on GOAWAY frame"); throw new IllegalArgumentException("Last stream id must not be set on GOAWAY frame");
@ -381,7 +380,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
} }
private void writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame, private void writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame,
final ChannelPromise promise) { final Promise<Void> promise) {
if (isStreamIdValid(headersFrame.stream().id())) { if (isStreamIdValid(headersFrame.stream().id())) {
encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(), encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(),
@ -408,7 +407,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
} }
private void writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame, private void writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame,
final ChannelPromise promise) { final Promise<Void> promise) {
if (isStreamIdValid(pushPromiseFrame.pushStream().id())) { if (isStreamIdValid(pushPromiseFrame.pushStream().id())) {
encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(), encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(),
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise); pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise);
@ -423,7 +422,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
numBufferedStreams++; numBufferedStreams++;
// Clean up the stream being initialized if writing the headers fails and also // Clean up the stream being initialized if writing the headers fails and also
// decrement the number of buffered streams. // decrement the number of buffered streams.
promise.addListener((ChannelFuture f) -> { promise.addListener(f -> {
numBufferedStreams--; numBufferedStreams--;
handleHeaderFuture(f, streamId); handleHeaderFuture(f, streamId);
}); });
@ -432,7 +431,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
} }
private boolean initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream, private boolean initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream,
ChannelPromise promise) { Promise<Void> promise) {
final Http2Connection connection = connection(); final Http2Connection connection = connection();
final int streamId = connection.local().incrementAndGetNextStreamId(); final int streamId = connection.local().incrementAndGetNextStreamId();
if (streamId < 0) { if (streamId < 0) {
@ -467,7 +466,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
} }
private void onStreamActive0(Http2Stream stream) { private void onStreamActive0(Http2Stream stream) {
if (stream.id() != Http2CodecUtil.HTTP_UPGRADE_STREAM_ID && if (stream.id() != HTTP_UPGRADE_STREAM_ID &&
connection().local().isValidStreamId(stream.id())) { connection().local().isValidStreamId(stream.id())) {
return; return;
} }

View File

@ -16,9 +16,9 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import java.io.Closeable; import java.io.Closeable;
@ -64,8 +64,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* <p> * <p>
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error. * If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
*/ */
ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
int padding, boolean endStream, ChannelPromise promise); int padding, boolean endStream, Promise<Void> promise);
/** /**
* Writes a HEADERS frame with priority specified to the remote endpoint. * Writes a HEADERS frame with priority specified to the remote endpoint.
@ -90,9 +90,9 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* <p> * <p>
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error. * If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
*/ */
ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream, int streamDependency, short weight, boolean exclusive, int padding, boolean endStream,
ChannelPromise promise); Promise<Void> promise);
/** /**
* Writes a PRIORITY frame to the remote endpoint. * Writes a PRIORITY frame to the remote endpoint.
@ -106,8 +106,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* @param promise the promise for the write. * @param promise the promise for the write.
* @return the future for the write. * @return the future for the write.
*/ */
ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency,
short weight, boolean exclusive, ChannelPromise promise); short weight, boolean exclusive, Promise<Void> promise);
/** /**
* Writes a RST_STREAM frame to the remote endpoint. * Writes a RST_STREAM frame to the remote endpoint.
@ -118,8 +118,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* @param promise the promise for the write. * @param promise the promise for the write.
* @return the future for the write. * @return the future for the write.
*/ */
ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode, Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise); Promise<Void> promise);
/** /**
* Writes a SETTINGS frame to the remote endpoint. * Writes a SETTINGS frame to the remote endpoint.
@ -129,8 +129,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* @param promise the promise for the write. * @param promise the promise for the write.
* @return the future for the write. * @return the future for the write.
*/ */
ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings, Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
ChannelPromise promise); Promise<Void> promise);
/** /**
* Writes a SETTINGS acknowledgment to the remote endpoint. * Writes a SETTINGS acknowledgment to the remote endpoint.
@ -139,7 +139,7 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* @param promise the promise for the write. * @param promise the promise for the write.
* @return the future for the write. * @return the future for the write.
*/ */
ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise); Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise);
/** /**
* Writes a PING frame to the remote endpoint. * Writes a PING frame to the remote endpoint.
@ -151,8 +151,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* @param promise the promise for the write. * @param promise the promise for the write.
* @return the future for the write. * @return the future for the write.
*/ */
ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data,
ChannelPromise promise); Promise<Void> promise);
/** /**
* Writes a PUSH_PROMISE frame to the remote endpoint. * Writes a PUSH_PROMISE frame to the remote endpoint.
@ -173,8 +173,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* <p> * <p>
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error. * If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
*/ */
ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId, Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
Http2Headers headers, int padding, ChannelPromise promise); Http2Headers headers, int padding, Promise<Void> promise);
/** /**
* Writes a GO_AWAY frame to the remote endpoint. * Writes a GO_AWAY frame to the remote endpoint.
@ -186,8 +186,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* @param promise the promise for the write. * @param promise the promise for the write.
* @return the future for the write. * @return the future for the write.
*/ */
ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
ByteBuf debugData, ChannelPromise promise); ByteBuf debugData, Promise<Void> promise);
/** /**
* Writes a WINDOW_UPDATE frame to the remote endpoint. * Writes a WINDOW_UPDATE frame to the remote endpoint.
@ -199,8 +199,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* @param promise the promise for the write. * @param promise the promise for the write.
* @return the future for the write. * @return the future for the write.
*/ */
ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId, Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
int windowSizeIncrement, ChannelPromise promise); int windowSizeIncrement, Promise<Void> promise);
/** /**
* Generic write method for any HTTP/2 frame. This allows writing of non-standard frames. * Generic write method for any HTTP/2 frame. This allows writing of non-standard frames.
@ -213,8 +213,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
* @param promise the promise for the write. * @param promise the promise for the write.
* @return the future for the write. * @return the future for the write.
*/ */
ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
Http2Flags flags, ByteBuf payload, ChannelPromise promise); Http2Flags flags, ByteBuf payload, Promise<Void> promise);
/** /**
* Get the configuration related elements for this {@link Http2FrameWriter} * Get the configuration related elements for this {@link Http2FrameWriter}

View File

@ -15,9 +15,9 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
/** /**
@ -29,19 +29,19 @@ public interface Http2LifecycleManager {
/** /**
* Closes the local side of the {@code stream}. Depending on the {@code stream} state this may result in * Closes the local side of the {@code stream}. Depending on the {@code stream} state this may result in
* {@code stream} being closed. See {@link #closeStream(Http2Stream, ChannelFuture)}. * {@code stream} being closed. See {@link #closeStream(Http2Stream, Future)}.
* @param stream the stream to be half closed. * @param stream the stream to be half closed.
* @param future See {@link #closeStream(Http2Stream, ChannelFuture)}. * @param future See {@link #closeStream(Http2Stream, Future)}.
*/ */
void closeStreamLocal(Http2Stream stream, ChannelFuture future); void closeStreamLocal(Http2Stream stream, Future<Void> future);
/** /**
* Closes the remote side of the {@code stream}. Depending on the {@code stream} state this may result in * Closes the remote side of the {@code stream}. Depending on the {@code stream} state this may result in
* {@code stream} being closed. See {@link #closeStream(Http2Stream, ChannelFuture)}. * {@code stream} being closed. See {@link #closeStream(Http2Stream, Future)}.
* @param stream the stream to be half closed. * @param stream the stream to be half closed.
* @param future See {@link #closeStream(Http2Stream, ChannelFuture)}. * @param future See {@link #closeStream(Http2Stream, Future)}.
*/ */
void closeStreamRemote(Http2Stream stream, ChannelFuture future); void closeStreamRemote(Http2Stream stream, Future<Void> future);
/** /**
* Closes and deactivates the given {@code stream}. A listener is also attached to {@code future} and upon * Closes and deactivates the given {@code stream}. A listener is also attached to {@code future} and upon
@ -50,7 +50,7 @@ public interface Http2LifecycleManager {
* @param future when completed if {@link Http2Connection#numActiveStreams()} is 0 then the underlying channel * @param future when completed if {@link Http2Connection#numActiveStreams()} is 0 then the underlying channel
* will be closed. * will be closed.
*/ */
void closeStream(Http2Stream stream, ChannelFuture future); void closeStream(Http2Stream stream, Future<Void> future);
/** /**
* Ensure the stream identified by {@code streamId} is reset. If our local state does not indicate the stream has * Ensure the stream identified by {@code streamId} is reset. If our local state does not indicate the stream has
@ -64,8 +64,8 @@ public interface Http2LifecycleManager {
* {@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 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. * {@code RST_STREAM} frame has been sent then the return status may indicate success immediately.
*/ */
ChannelFuture resetStream(ChannelHandlerContext ctx, int streamId, long errorCode, Future<Void> resetStream(ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise); Promise<Void> promise);
/** /**
* Prevents the peer from creating streams and close the connection if {@code errorCode} is not * Prevents the peer from creating streams and close the connection if {@code errorCode} is not
@ -83,15 +83,15 @@ public interface Http2LifecycleManager {
* {@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 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. * {@code GO_AWAY} frame has been sent then the return status may indicate success immediately.
*/ */
ChannelFuture goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, Future<Void> goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
ByteBuf debugData, ChannelPromise promise); ByteBuf debugData, Promise<Void> promise);
/** /**
* Processes the given error. * Processes the given error.
* *
* @param ctx The context used for communication and buffer allocation if necessary. * @param ctx The context used for communication and buffer allocation if necessary.
* @param outbound {@code true} if the error was caused by an outbound operation and so the corresponding * @param outbound {@code true} if the error was caused by an outbound operation and so the corresponding
* {@link ChannelPromise} was failed as well. * {@link Promise} was failed as well.
* @param cause the error. * @param cause the error.
*/ */
void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause); void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause);

View File

@ -18,13 +18,12 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop; import io.netty.channel.EventLoop;
import io.netty.util.ReferenceCounted; import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -178,11 +177,11 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
streamChannel = new Http2MultiplexCodecStreamChannel(stream, inboundStreamHandler); streamChannel = new Http2MultiplexCodecStreamChannel(stream, inboundStreamHandler);
} }
ChannelFuture future = streamChannel.register(); Future<Void> future = streamChannel.register();
if (future.isDone()) { if (future.isDone()) {
Http2MultiplexHandler.registerDone(future); Http2MultiplexHandler.registerDone(streamChannel, future);
} else { } else {
future.addListener(Http2MultiplexHandler.CHILD_CHANNEL_REGISTRATION_LISTENER); future.addListener(streamChannel, Http2MultiplexHandler.CHILD_CHANNEL_REGISTRATION_LISTENER);
} }
break; break;
case CLOSED: case CLOSED:
@ -312,8 +311,8 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
} }
@Override @Override
protected ChannelFuture write0(ChannelHandlerContext ctx, Object msg) { protected Future<Void> write0(ChannelHandlerContext ctx, Object msg) {
ChannelPromise promise = ctx.newPromise(); Promise<Void> promise = ctx.newPromise();
Http2MultiplexCodec.this.write(ctx, msg, promise); Http2MultiplexCodec.this.write(ctx, msg, promise);
return promise; return promise;
} }

View File

@ -18,8 +18,6 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
@ -27,6 +25,8 @@ import io.netty.channel.EventLoop;
import io.netty.channel.ServerChannel; import io.netty.channel.ServerChannel;
import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream; import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream;
import io.netty.util.ReferenceCounted; import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureContextListener;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import javax.net.ssl.SSLException; import javax.net.ssl.SSLException;
@ -86,7 +86,8 @@ import static io.netty.handler.codec.http2.Http2Exception.connectionError;
@UnstableApi @UnstableApi
public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler { public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler {
static final ChannelFutureListener CHILD_CHANNEL_REGISTRATION_LISTENER = Http2MultiplexHandler::registerDone; static final FutureContextListener<Channel, Void> CHILD_CHANNEL_REGISTRATION_LISTENER =
Http2MultiplexHandler::registerDone;
private final ChannelHandler inboundStreamHandler; private final ChannelHandler inboundStreamHandler;
private final ChannelHandler upgradeStreamHandler; private final ChannelHandler upgradeStreamHandler;
@ -124,12 +125,11 @@ public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler {
this.upgradeStreamHandler = upgradeStreamHandler; this.upgradeStreamHandler = upgradeStreamHandler;
} }
static void registerDone(ChannelFuture future) { static void registerDone(Channel childChannel, Future<?> future) {
// Handle any errors that occurred on the local thread while registering. Even though // Handle any errors that occurred on the local thread while registering. Even though
// failures can happen after this point, they will be handled by the channel by closing the // failures can happen after this point, they will be handled by the channel by closing the
// childChannel. // childChannel.
if (!future.isSuccess()) { if (!future.isSuccess()) {
Channel childChannel = future.channel();
if (childChannel.isRegistered()) { if (childChannel.isRegistered()) {
childChannel.close(); childChannel.close();
} else { } else {
@ -231,11 +231,11 @@ public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler {
} else { } else {
ch = new Http2MultiplexHandlerStreamChannel(stream, inboundStreamHandler); ch = new Http2MultiplexHandlerStreamChannel(stream, inboundStreamHandler);
} }
ChannelFuture future = ch.register(); Future<Void> future = ch.register();
if (future.isDone()) { if (future.isDone()) {
registerDone(future); registerDone(ch, future);
} else { } else {
future.addListener(CHILD_CHANNEL_REGISTRATION_LISTENER); future.addListener(ch, CHILD_CHANNEL_REGISTRATION_LISTENER);
} }
break; break;
case CLOSED: case CLOSED:

View File

@ -15,15 +15,15 @@
*/ */
package io.netty.handler.codec.http2; 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; import static io.netty.handler.codec.http2.Http2FrameLogger.Direction.OUTBOUND;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.internal.UnstableApi;
/** /**
* Decorator around a {@link Http2FrameWriter} that logs all outbound frames before calling the * Decorator around a {@link Http2FrameWriter} that logs all outbound frames before calling the
* writer. * writer.
@ -39,23 +39,23 @@ public class Http2OutboundFrameLogger implements Http2FrameWriter {
} }
@Override @Override
public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
int padding, boolean endStream, ChannelPromise promise) { int padding, boolean endStream, Promise<Void> promise) {
logger.logData(OUTBOUND, ctx, streamId, data, padding, 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, promise);
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
Http2Headers headers, int padding, boolean endStream, ChannelPromise promise) { Http2Headers headers, int padding, boolean endStream, Promise<Void> promise) {
logger.logHeaders(OUTBOUND, ctx, streamId, headers, padding, 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, promise);
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
Http2Headers headers, int streamDependency, short weight, boolean exclusive, Http2Headers headers, int streamDependency, short weight, boolean exclusive,
int padding, boolean endStream, ChannelPromise promise) { int padding, boolean endStream, Promise<Void> promise) {
logger.logHeaders(OUTBOUND, ctx, streamId, headers, streamDependency, weight, exclusive, logger.logHeaders(OUTBOUND, ctx, streamId, headers, streamDependency, weight, exclusive,
padding, endStream); padding, endStream);
return writer.writeHeaders(ctx, streamId, headers, streamDependency, weight, return writer.writeHeaders(ctx, streamId, headers, streamDependency, weight,
@ -63,35 +63,35 @@ public class Http2OutboundFrameLogger implements Http2FrameWriter {
} }
@Override @Override
public ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId, public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId,
int streamDependency, short weight, boolean exclusive, ChannelPromise promise) { int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
logger.logPriority(OUTBOUND, ctx, streamId, streamDependency, weight, 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, promise);
} }
@Override @Override
public ChannelFuture writeRstStream(ChannelHandlerContext ctx, public Future<Void> writeRstStream(ChannelHandlerContext ctx,
int streamId, long errorCode, ChannelPromise promise) { int streamId, long errorCode, Promise<Void> promise) {
logger.logRstStream(OUTBOUND, ctx, streamId, errorCode); logger.logRstStream(OUTBOUND, ctx, streamId, errorCode);
return writer.writeRstStream(ctx, streamId, errorCode, promise); return writer.writeRstStream(ctx, streamId, errorCode, promise);
} }
@Override @Override
public ChannelFuture writeSettings(ChannelHandlerContext ctx, public Future<Void> writeSettings(ChannelHandlerContext ctx,
Http2Settings settings, ChannelPromise promise) { Http2Settings settings, Promise<Void> promise) {
logger.logSettings(OUTBOUND, ctx, settings); logger.logSettings(OUTBOUND, ctx, settings);
return writer.writeSettings(ctx, settings, promise); return writer.writeSettings(ctx, settings, promise);
} }
@Override @Override
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) { public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
logger.logSettingsAck(OUTBOUND, ctx); logger.logSettingsAck(OUTBOUND, ctx);
return writer.writeSettingsAck(ctx, promise); return writer.writeSettingsAck(ctx, promise);
} }
@Override @Override
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack,
long data, ChannelPromise promise) { long data, Promise<Void> promise) {
if (ack) { if (ack) {
logger.logPingAck(OUTBOUND, ctx, data); logger.logPingAck(OUTBOUND, ctx, data);
} else { } else {
@ -101,29 +101,30 @@ public class Http2OutboundFrameLogger implements Http2FrameWriter {
} }
@Override @Override
public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId, public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId,
int promisedStreamId, Http2Headers headers, int padding, ChannelPromise promise) { int promisedStreamId, Http2Headers headers, int padding,
Promise<Void> promise) {
logger.logPushPromise(OUTBOUND, ctx, streamId, promisedStreamId, headers, 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, promise);
} }
@Override @Override
public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
ByteBuf debugData, ChannelPromise promise) { ByteBuf debugData, Promise<Void> promise) {
logger.logGoAway(OUTBOUND, ctx, lastStreamId, errorCode, debugData); logger.logGoAway(OUTBOUND, ctx, lastStreamId, errorCode, debugData);
return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise); return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
} }
@Override @Override
public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx,
int streamId, int windowSizeIncrement, ChannelPromise promise) { int streamId, int windowSizeIncrement, Promise<Void> promise) {
logger.logWindowsUpdate(OUTBOUND, ctx, streamId, windowSizeIncrement); logger.logWindowsUpdate(OUTBOUND, ctx, streamId, windowSizeIncrement);
return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise); return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
} }
@Override @Override
public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
Http2Flags flags, ByteBuf payload, ChannelPromise promise) { Http2Flags flags, ByteBuf payload, Promise<Void> promise) {
logger.logUnknownFrame(OUTBOUND, ctx, frameType, streamId, flags, 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, promise);
} }

View File

@ -15,11 +15,7 @@
*/ */
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import static java.util.Objects.requireNonNull;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
@ -38,6 +34,8 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import static java.util.Objects.requireNonNull;
@UnstableApi @UnstableApi
public final class Http2StreamChannelBootstrap { public final class Http2StreamChannelBootstrap {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2StreamChannelBootstrap.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2StreamChannelBootstrap.class);
@ -186,8 +184,8 @@ public final class Http2StreamChannelBootstrap {
return; return;
} }
ChannelFuture future = streamChannel.register(); Future<Void> future = streamChannel.register();
future.addListener((ChannelFutureListener) future1 -> { future.addListener(future1 -> {
if (future1.isSuccess()) { if (future1.isSuccess()) {
promise.setSuccess(streamChannel); promise.setSuccess(streamChannel);
} else if (future1.isCancelled()) { } else if (future1.isCancelled()) {

View File

@ -17,7 +17,6 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.EmptyHttpHeaders; import io.netty.handler.codec.http.EmptyHttpHeaders;
import io.netty.handler.codec.http.FullHttpMessage; import io.netty.handler.codec.http.FullHttpMessage;
import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpContent;
@ -27,6 +26,7 @@ import io.netty.handler.codec.http.HttpScheme;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator; import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
/** /**
@ -77,7 +77,7 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
* Handles conversion of {@link HttpMessage} and {@link HttpContent} to HTTP/2 frames. * Handles conversion of {@link HttpMessage} and {@link HttpContent} to HTTP/2 frames.
*/ */
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) { if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) {
ctx.write(msg, promise); ctx.write(msg, promise);
@ -86,7 +86,7 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
boolean release = true; boolean release = true;
SimpleChannelPromiseAggregator promiseAggregator = SimpleChannelPromiseAggregator promiseAggregator =
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor()); new SimpleChannelPromiseAggregator(promise, ctx.executor());
try { try {
Http2ConnectionEncoder encoder = encoder(); Http2ConnectionEncoder encoder = encoder();
boolean endStream = false; boolean endStream = false;

View File

@ -17,10 +17,10 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -153,16 +153,16 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
int padding, boolean endStream, ChannelPromise promise) { int padding, boolean endStream, Promise<Void> promise) {
return writeHeaders(ctx, streamId, headers, 0, Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT, return writeHeaders(ctx, streamId, headers, 0, Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT,
false, padding, endStream, promise); false, padding, endStream, promise);
} }
@Override @Override
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
int streamDependency, short weight, boolean exclusive, int streamDependency, short weight, boolean exclusive,
int padding, boolean endOfStream, ChannelPromise promise) { int padding, boolean endOfStream, Promise<Void> promise) {
if (closed) { if (closed) {
return promise.setFailure(new Http2ChannelClosedException()); return promise.setFailure(new Http2ChannelClosedException());
} }
@ -184,8 +184,8 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
} }
@Override @Override
public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode, public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
ChannelPromise promise) { Promise<Void> promise) {
if (isExistingStream(streamId)) { if (isExistingStream(streamId)) {
return super.writeRstStream(ctx, streamId, errorCode, promise); return super.writeRstStream(ctx, streamId, errorCode, promise);
} }
@ -198,7 +198,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
// about the stream anymore and thus there is not point in failing the promises and invoking // about the stream anymore and thus there is not point in failing the promises and invoking
// error handling routines. // error handling routines.
stream.close(null); stream.close(null);
promise.setSuccess(); promise.setSuccess(null);
} else { } else {
promise.setFailure(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId)); promise.setFailure(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
} }
@ -206,8 +206,8 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
} }
@Override @Override
public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
int padding, boolean endOfStream, ChannelPromise promise) { int padding, boolean endOfStream, Promise<Void> promise) {
if (isExistingStream(streamId)) { if (isExistingStream(streamId)) {
return super.writeData(ctx, streamId, data, padding, endOfStream, promise); return super.writeData(ctx, streamId, data, padding, endOfStream, promise);
} }
@ -311,9 +311,9 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
} }
private abstract static class Frame { private abstract static class Frame {
final ChannelPromise promise; final Promise<Void> promise;
Frame(ChannelPromise promise) { Frame(Promise<Void> promise) {
this.promise = promise; this.promise = promise;
} }
@ -322,7 +322,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
*/ */
void release(Throwable t) { void release(Throwable t) {
if (t == null) { if (t == null) {
promise.setSuccess(); promise.setSuccess(null);
} else { } else {
promise.setFailure(t); promise.setFailure(t);
} }
@ -340,7 +340,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
final boolean endOfStream; final boolean endOfStream;
HeadersFrame(Http2Headers headers, int streamDependency, short weight, boolean exclusive, HeadersFrame(Http2Headers headers, int streamDependency, short weight, boolean exclusive,
int padding, boolean endOfStream, ChannelPromise promise) { int padding, boolean endOfStream, Promise<Void> promise) {
super(promise); super(promise);
this.headers = headers; this.headers = headers;
this.streamDependency = streamDependency; this.streamDependency = streamDependency;
@ -361,7 +361,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
final int padding; final int padding;
final boolean endOfStream; final boolean endOfStream;
DataFrame(ByteBuf data, int padding, boolean endOfStream, ChannelPromise promise) { DataFrame(ByteBuf data, int padding, boolean endOfStream, Promise<Void> promise) {
super(promise); super(promise);
this.data = data; this.data = data;
this.padding = padding; this.padding = padding;

View File

@ -23,7 +23,6 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.MultithreadEventLoopGroup; import io.netty.channel.MultithreadEventLoopGroup;
import io.netty.channel.nio.NioHandler; import io.netty.channel.nio.NioHandler;
import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel;
@ -34,6 +33,7 @@ import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -432,7 +432,7 @@ public class DataCompressionHttp2Test {
return clientChannel.pipeline().firstContext(); return clientChannel.pipeline().firstContext();
} }
private ChannelPromise newPromiseClient() { private Promise<Void> newPromiseClient() {
return ctxClient().newPromise(); return ctxClient().newPromise();
} }
} }

View File

@ -17,13 +17,13 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -35,7 +35,6 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import java.util.Collections;
import java.util.IdentityHashMap; import java.util.IdentityHashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -48,11 +47,9 @@ import static io.netty.handler.codec.http2.Http2Stream.State.IDLE;
import static io.netty.handler.codec.http2.Http2Stream.State.OPEN; import static io.netty.handler.codec.http2.Http2Stream.State.OPEN;
import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_REMOTE; import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_REMOTE;
import static io.netty.util.CharsetUtil.UTF_8; import static io.netty.util.CharsetUtil.UTF_8;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
@ -82,7 +79,7 @@ public class DefaultHttp2ConnectionDecoderTest {
private static final int STATE_RECV_TRAILERS = 1 << 1; private static final int STATE_RECV_TRAILERS = 1 << 1;
private Http2ConnectionDecoder decoder; private Http2ConnectionDecoder decoder;
private ChannelPromise promise; private Promise<Void> promise;
@Mock @Mock
private Http2Connection connection; private Http2Connection connection;
@ -106,7 +103,7 @@ public class DefaultHttp2ConnectionDecoderTest {
private Channel channel; private Channel channel;
@Mock @Mock
private ChannelFuture future; private Future<Void> future;
@Mock @Mock
private Http2Stream stream; private Http2Stream stream;
@ -133,7 +130,7 @@ public class DefaultHttp2ConnectionDecoderTest {
public void setup() throws Exception { public void setup() throws Exception {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
final AtomicInteger headersReceivedState = new AtomicInteger(); final AtomicInteger headersReceivedState = new AtomicInteger();
when(channel.isActive()).thenReturn(true); when(channel.isActive()).thenReturn(true);
@ -623,6 +620,7 @@ public class DefaultHttp2ConnectionDecoderTest {
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true)); eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true));
} }
@SuppressWarnings("unchecked")
@Test @Test
public void headersDependencyNotCreatedShouldCreateAndSucceed() throws Exception { public void headersDependencyNotCreatedShouldCreateAndSucceed() throws Exception {
final short weight = 1; final short weight = 1;
@ -631,7 +629,7 @@ public class DefaultHttp2ConnectionDecoderTest {
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(STREAM_DEPENDENCY_ID), verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(STREAM_DEPENDENCY_ID),
eq(weight), eq(true), eq(0), eq(true)); eq(weight), eq(true), eq(0), eq(true));
verify(remoteFlow).updateDependencyTree(eq(STREAM_ID), eq(STREAM_DEPENDENCY_ID), eq(weight), eq(true)); verify(remoteFlow).updateDependencyTree(eq(STREAM_ID), eq(STREAM_DEPENDENCY_ID), eq(weight), eq(true));
verify(lifecycleManager).closeStreamRemote(eq(stream), any(ChannelFuture.class)); verify(lifecycleManager).closeStreamRemote(eq(stream), any(Future.class));
} }
@Test @Test

View File

@ -20,16 +20,16 @@ import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelConfig; import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.DefaultChannelPromise;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http2.Http2RemoteFlowController.FlowControlled; 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.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise;
import junit.framework.AssertionFailedError; import junit.framework.AssertionFailedError;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -49,8 +49,8 @@ import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE; import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE;
import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_LOCAL; import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_LOCAL;
import static io.netty.util.CharsetUtil.UTF_8; import static io.netty.util.CharsetUtil.UTF_8;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
@ -76,6 +76,7 @@ import static org.mockito.Mockito.when;
/** /**
* Tests for {@link DefaultHttp2ConnectionEncoder} * Tests for {@link DefaultHttp2ConnectionEncoder}
*/ */
@SuppressWarnings("unchecked")
public class DefaultHttp2ConnectionEncoderTest { public class DefaultHttp2ConnectionEncoderTest {
private static final int STREAM_ID = 2; private static final int STREAM_ID = 2;
private static final int PUSH_STREAM_ID = 4; private static final int PUSH_STREAM_ID = 4;
@ -131,19 +132,19 @@ public class DefaultHttp2ConnectionEncoderTest {
when(writer.configuration()).thenReturn(writerConfig); when(writer.configuration()).thenReturn(writerConfig);
when(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy); when(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy);
when(frameSizePolicy.maxFrameSize()).thenReturn(64); when(frameSizePolicy.maxFrameSize()).thenReturn(64);
doAnswer((Answer<ChannelFuture>) in -> ((ChannelPromise) in.getArguments()[2]) doAnswer((Answer<Future<Void>>) in -> ((Promise<Void>) in.getArguments()[2])
.setSuccess()).when(writer).writeSettings(eq(ctx), any(Http2Settings.class), any(ChannelPromise.class)); .setSuccess(null)).when(writer).writeSettings(eq(ctx), any(Http2Settings.class), any(Promise.class));
doAnswer((Answer<ChannelFuture>) in -> { doAnswer((Answer<Future<Void>>) in -> {
((ByteBuf) in.getArguments()[3]).release(); ((ByteBuf) in.getArguments()[3]).release();
return ((ChannelPromise) in.getArguments()[4]).setSuccess(); return ((Promise<Void>) in.getArguments()[4]).setSuccess(null);
}).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class), any(ChannelPromise.class)); }).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class), any(Promise.class));
writtenData = new ArrayList<>(); writtenData = new ArrayList<>();
writtenPadding = new ArrayList<>(); writtenPadding = new ArrayList<>();
when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(), when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
any(ChannelPromise.class))).then((Answer<ChannelFuture>) in -> { any(Promise.class))).then((Answer<Future<Void>>) in -> {
// Make sure we only receive stream closure on the last frame and that void promises // Make sure we only receive stream closure on the last frame and that void promises
// are used for all writes except the last one. // are used for all writes except the last one.
ChannelPromise promise = (ChannelPromise) in.getArguments()[5]; Promise<Void> promise = (Promise<Void>) in.getArguments()[5];
if (streamClosed) { if (streamClosed) {
fail("Stream already closed"); fail("Stream already closed");
} else { } else {
@ -155,36 +156,36 @@ public class DefaultHttp2ConnectionEncoderTest {
// Release the buffer just as DefaultHttp2FrameWriter does // Release the buffer just as DefaultHttp2FrameWriter does
data.release(); data.release();
// Let the promise succeed to trigger listeners. // Let the promise succeed to trigger listeners.
return promise.setSuccess(); return promise.setSuccess(null);
}); });
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(),
anyInt(), anyBoolean(), any(ChannelPromise.class))) anyInt(), anyBoolean(), any(Promise.class)))
.then((Answer<ChannelFuture>) invocationOnMock -> { .then((Answer<Future<Void>>) invocationOnMock -> {
ChannelPromise promise = invocationOnMock.getArgument(8); Promise<Void> promise = invocationOnMock.getArgument(8);
if (streamClosed) { if (streamClosed) {
fail("Stream already closed"); fail("Stream already closed");
} else { } else {
streamClosed = (Boolean) invocationOnMock.getArguments()[5]; streamClosed = (Boolean) invocationOnMock.getArguments()[5];
} }
return promise.setSuccess(); return promise.setSuccess(null);
}); });
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class),
anyInt(), anyBoolean(), any(ChannelPromise.class))) anyInt(), anyBoolean(), any(Promise.class)))
.then((Answer<ChannelFuture>) invocationOnMock -> { .then((Answer<Future<Void>>) invocationOnMock -> {
ChannelPromise promise = invocationOnMock.getArgument(5); Promise<Void> promise = invocationOnMock.getArgument(5);
if (streamClosed) { if (streamClosed) {
fail("Stream already closed"); fail("Stream already closed");
} else { } else {
streamClosed = invocationOnMock.getArgument(4); streamClosed = invocationOnMock.getArgument(4);
} }
return promise.setSuccess(); return promise.setSuccess(null);
}); });
payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class); payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class);
doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture()); doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture());
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
when(ctx.channel()).thenReturn(channel); when(ctx.channel()).thenReturn(channel);
doAnswer((Answer<ChannelPromise>) in -> newPromise()).when(ctx).newPromise(); doAnswer((Answer<Promise<Void>>) in -> newPromise()).when(ctx).newPromise();
doAnswer((Answer<ChannelFuture>) in -> newSucceededFuture()).when(ctx).newSucceededFuture(); doAnswer((Answer<Future<Void>>) in -> newSucceededFuture()).when(ctx).newSucceededFuture();
when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden")); when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden"));
when(channel.alloc()).thenReturn(PooledByteBufAllocator.DEFAULT); when(channel.alloc()).thenReturn(PooledByteBufAllocator.DEFAULT);
@ -209,7 +210,7 @@ public class DefaultHttp2ConnectionEncoderTest {
private void dataWriteShouldSignalThatFrameWasConsumedOnError0(boolean endOfStream) throws Exception { private void dataWriteShouldSignalThatFrameWasConsumedOnError0(boolean endOfStream) throws Exception {
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
final ByteBuf data = dummyData(); final ByteBuf data = dummyData();
ChannelPromise p = newPromise(); Promise<Void> p = newPromise();
encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream, p); encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream, p);
FlowControlled controlled = payloadCaptor.getValue(); FlowControlled controlled = payloadCaptor.getValue();
@ -230,7 +231,7 @@ public class DefaultHttp2ConnectionEncoderTest {
public void dataWriteShouldSucceed() throws Exception { public void dataWriteShouldSucceed() throws Exception {
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
final ByteBuf data = dummyData(); final ByteBuf data = dummyData();
ChannelPromise p = newPromise(); Promise<Void> p = newPromise();
encoder.writeData(ctx, STREAM_ID, data, 0, true, p); encoder.writeData(ctx, STREAM_ID, data, 0, true, p);
assertEquals(8, payloadCaptor.getValue().size()); assertEquals(8, payloadCaptor.getValue().size());
payloadCaptor.getValue().write(ctx, 8); payloadCaptor.getValue().write(ctx, 8);
@ -245,9 +246,9 @@ public class DefaultHttp2ConnectionEncoderTest {
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
final ByteBuf data = dummyData().retain(); final ByteBuf data = dummyData().retain();
ChannelPromise promise1 = newPromise(); Promise<Void> promise1 = newPromise();
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise1); encoder.writeData(ctx, STREAM_ID, data, 0, true, promise1);
ChannelPromise promise2 = newPromise(); Promise<Void> promise2 = newPromise();
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise2); encoder.writeData(ctx, STREAM_ID, data, 0, true, promise2);
// Now merge the two payloads. // Now merge the two payloads.
@ -288,7 +289,7 @@ public class DefaultHttp2ConnectionEncoderTest {
private void assertSplitPaddingOnEmptyBuffer(ByteBuf data) throws Exception { private void assertSplitPaddingOnEmptyBuffer(ByteBuf data) throws Exception {
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
when(frameSizePolicy.maxFrameSize()).thenReturn(5); when(frameSizePolicy.maxFrameSize()).thenReturn(5);
ChannelPromise p = newPromise(); Promise<Void> p = newPromise();
encoder.writeData(ctx, STREAM_ID, data, 10, true, p); encoder.writeData(ctx, STREAM_ID, data, 10, true, p);
assertEquals(10, payloadCaptor.getValue().size()); assertEquals(10, payloadCaptor.getValue().size());
payloadCaptor.getValue().write(ctx, 10); payloadCaptor.getValue().write(ctx, 10);
@ -304,7 +305,7 @@ public class DefaultHttp2ConnectionEncoderTest {
public void headersWriteForUnknownStreamShouldCreateStream() throws Exception { public void headersWriteForUnknownStreamShouldCreateStream() throws Exception {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
final int streamId = 6; final int streamId = 6;
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0), verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
eq(false), eq(promise)); eq(false), eq(promise));
@ -317,7 +318,7 @@ public class DefaultHttp2ConnectionEncoderTest {
Http2Stream parent = createStream(STREAM_ID, false); Http2Stream parent = createStream(STREAM_ID, false);
reservePushStream(PUSH_STREAM_ID, parent); reservePushStream(PUSH_STREAM_ID, parent);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise); encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state()); assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state());
verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
@ -328,11 +329,11 @@ public class DefaultHttp2ConnectionEncoderTest {
public void trailersDoNotEndStreamThrows() { public void trailersDoNotEndStreamThrows() {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
final int streamId = 6; final int streamId = 6;
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
ChannelPromise promise2 = newPromise(); Promise<Void> promise2 = newPromise();
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2); Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
assertTrue(future.isDone()); assertTrue(future.isDone());
assertFalse(future.isSuccess()); assertFalse(future.isSuccess());
@ -344,14 +345,14 @@ public class DefaultHttp2ConnectionEncoderTest {
public void trailersDoNotEndStreamWithDataThrows() { public void trailersDoNotEndStreamWithDataThrows() {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
final int streamId = 6; final int streamId = 6;
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
Http2Stream stream = connection.stream(streamId); Http2Stream stream = connection.stream(streamId);
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true); when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
ChannelPromise promise2 = newPromise(); Promise<Void> promise2 = newPromise();
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2); Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
assertTrue(future.isDone()); assertTrue(future.isDone());
assertFalse(future.isSuccess()); assertFalse(future.isSuccess());
@ -372,13 +373,13 @@ public class DefaultHttp2ConnectionEncoderTest {
private void tooManyHeadersThrows(boolean eos) { private void tooManyHeadersThrows(boolean eos) {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
final int streamId = 6; final int streamId = 6;
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
ChannelPromise promise2 = newPromise(); Promise<Void> promise2 = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
ChannelPromise promise3 = newPromise(); Promise<Void> promise3 = newPromise();
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3); Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
assertTrue(future.isDone()); assertTrue(future.isDone());
assertFalse(future.isSuccess()); assertFalse(future.isSuccess());
@ -415,16 +416,16 @@ public class DefaultHttp2ConnectionEncoderTest {
for (int i = 0; i < infoHeaderCount; ++i) { for (int i = 0; i < infoHeaderCount; ++i) {
encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false, newPromise()); encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false, newPromise());
} }
ChannelPromise promise2 = newPromise(); Promise<Void> promise2 = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
ChannelPromise promise3 = newPromise(); Promise<Void> promise3 = newPromise();
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3); Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
assertTrue(future.isDone()); assertTrue(future.isDone());
assertEquals(eos, future.isSuccess()); assertEquals(eos, future.isSuccess());
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders), verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
eq(0), eq(false), any(ChannelPromise.class)); eq(0), eq(false), any(Promise.class));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise2)); eq(0), eq(false), eq(promise2));
if (eos) { if (eos) {
@ -452,17 +453,17 @@ public class DefaultHttp2ConnectionEncoderTest {
private void tooManyHeadersWithDataThrows(boolean eos) { private void tooManyHeadersWithDataThrows(boolean eos) {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
final int streamId = 6; final int streamId = 6;
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
Http2Stream stream = connection.stream(streamId); Http2Stream stream = connection.stream(streamId);
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true); when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
ChannelPromise promise2 = newPromise(); Promise<Void> promise2 = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
ChannelPromise promise3 = newPromise(); Promise<Void> promise3 = newPromise();
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3); Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
assertTrue(future.isDone()); assertTrue(future.isDone());
assertFalse(future.isSuccess()); assertFalse(future.isSuccess());
@ -503,16 +504,16 @@ public class DefaultHttp2ConnectionEncoderTest {
Http2Stream stream = connection.stream(streamId); Http2Stream stream = connection.stream(streamId);
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true); when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
ChannelPromise promise2 = newPromise(); Promise<Void> promise2 = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
ChannelPromise promise3 = newPromise(); Promise<Void> promise3 = newPromise();
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3); Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
assertTrue(future.isDone()); assertTrue(future.isDone());
assertEquals(eos, future.isSuccess()); assertEquals(eos, future.isSuccess());
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders), verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
eq(0), eq(false), any(ChannelPromise.class)); eq(0), eq(false), any(Promise.class));
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise2)); eq(0), eq(false), eq(promise2));
if (eos) { if (eos) {
@ -525,7 +526,7 @@ public class DefaultHttp2ConnectionEncoderTest {
public void pushPromiseWriteAfterGoAwayReceivedShouldFail() throws Exception { public void pushPromiseWriteAfterGoAwayReceivedShouldFail() throws Exception {
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
goAwayReceived(0); goAwayReceived(0);
ChannelFuture future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, Future<Void> future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0,
newPromise()); newPromise());
assertTrue(future.isDone()); assertTrue(future.isDone());
assertFalse(future.isSuccess()); assertFalse(future.isSuccess());
@ -534,7 +535,7 @@ public class DefaultHttp2ConnectionEncoderTest {
@Test @Test
public void pushPromiseWriteShouldReserveStream() throws Exception { public void pushPromiseWriteShouldReserveStream() throws Exception {
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
ChannelPromise promise = newPromise(); Promise<Void> 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, promise);
assertEquals(RESERVED_LOCAL, stream(PUSH_STREAM_ID).state()); assertEquals(RESERVED_LOCAL, stream(PUSH_STREAM_ID).state());
verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID), verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID),
@ -545,14 +546,14 @@ public class DefaultHttp2ConnectionEncoderTest {
public void priorityWriteAfterGoAwayShouldSucceed() throws Exception { public void priorityWriteAfterGoAwayShouldSucceed() throws Exception {
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
goAwayReceived(Integer.MAX_VALUE); goAwayReceived(Integer.MAX_VALUE);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writePriority(ctx, STREAM_ID, 0, (short) 255, true, promise); 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)); verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true), eq(promise));
} }
@Test @Test
public void priorityWriteShouldSetPriorityForStream() throws Exception { public void priorityWriteShouldSetPriorityForStream() throws Exception {
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
short weight = 255; short weight = 255;
encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise); encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
@ -566,7 +567,7 @@ public class DefaultHttp2ConnectionEncoderTest {
@Test @Test
public void priorityWriteOnPreviouslyExistingStreamShouldSucceed() throws Exception { public void priorityWriteOnPreviouslyExistingStreamShouldSucceed() throws Exception {
createStream(STREAM_ID, false).close(); createStream(STREAM_ID, false).close();
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
short weight = 255; short weight = 255;
encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise); 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)); verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true), eq(promise));
@ -578,7 +579,7 @@ public class DefaultHttp2ConnectionEncoderTest {
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
createStream(parentStreamId, false).close(); createStream(parentStreamId, false).close();
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
short weight = 255; short weight = 255;
encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true, promise); 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)); verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true), eq(promise));
@ -586,7 +587,7 @@ public class DefaultHttp2ConnectionEncoderTest {
@Test @Test
public void rstStreamWriteForUnknownStreamShouldIgnore() throws Exception { public void rstStreamWriteForUnknownStreamShouldIgnore() throws Exception {
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code(), promise); encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code(), promise);
verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong(), eq(promise)); verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong(), eq(promise));
} }
@ -599,14 +600,14 @@ public class DefaultHttp2ConnectionEncoderTest {
// Now verify that a stream reset is performed. // Now verify that a stream reset is performed.
stream(STREAM_ID); stream(STREAM_ID);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise); encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong(), eq(promise)); verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong(), eq(promise));
} }
@Test @Test
public void pingWriteAfterGoAwayShouldSucceed() throws Exception { public void pingWriteAfterGoAwayShouldSucceed() throws Exception {
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
goAwayReceived(0); goAwayReceived(0);
encoder.writePing(ctx, false, 0L, promise); encoder.writePing(ctx, false, 0L, promise);
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise)); verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
@ -614,7 +615,7 @@ public class DefaultHttp2ConnectionEncoderTest {
@Test @Test
public void pingWriteShouldSucceed() throws Exception { public void pingWriteShouldSucceed() throws Exception {
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writePing(ctx, false, 0L, promise); encoder.writePing(ctx, false, 0L, promise);
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise)); verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
} }
@ -622,7 +623,7 @@ public class DefaultHttp2ConnectionEncoderTest {
@Test @Test
public void settingsWriteAfterGoAwayShouldSucceed() throws Exception { public void settingsWriteAfterGoAwayShouldSucceed() throws Exception {
goAwayReceived(0); goAwayReceived(0);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeSettings(ctx, new Http2Settings(), promise); encoder.writeSettings(ctx, new Http2Settings(), promise);
verify(writer).writeSettings(eq(ctx), any(Http2Settings.class), eq(promise)); verify(writer).writeSettings(eq(ctx), any(Http2Settings.class), eq(promise));
} }
@ -634,7 +635,7 @@ public class DefaultHttp2ConnectionEncoderTest {
settings.maxConcurrentStreams(1000); settings.maxConcurrentStreams(1000);
settings.headerTableSize(2000); settings.headerTableSize(2000);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeSettings(ctx, settings, promise); encoder.writeSettings(ctx, settings, promise);
verify(writer).writeSettings(eq(ctx), eq(settings), eq(promise)); verify(writer).writeSettings(eq(ctx), eq(settings), eq(promise));
} }
@ -645,7 +646,7 @@ public class DefaultHttp2ConnectionEncoderTest {
Http2Stream stream = createStream(STREAM_ID, false); Http2Stream stream = createStream(STREAM_ID, false);
ByteBuf data = dummyData(); ByteBuf data = dummyData();
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true, promise); encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true, promise);
assertTrue(promise.isSuccess()); assertTrue(promise.isSuccess());
verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class)); verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
@ -658,7 +659,7 @@ public class DefaultHttp2ConnectionEncoderTest {
public void headersWriteShouldHalfCloseStream() throws Exception { public void headersWriteShouldHalfCloseStream() throws Exception {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise); encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
assertTrue(promise.isSuccess()); assertTrue(promise.isSuccess());
@ -670,7 +671,7 @@ public class DefaultHttp2ConnectionEncoderTest {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
Http2Stream parent = createStream(STREAM_ID, false); Http2Stream parent = createStream(STREAM_ID, false);
Http2Stream stream = reservePushStream(PUSH_STREAM_ID, parent); Http2Stream stream = reservePushStream(PUSH_STREAM_ID, parent);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise); encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
assertEquals(HALF_CLOSED_REMOTE, stream.state()); assertEquals(HALF_CLOSED_REMOTE, stream.state());
assertTrue(promise.isSuccess()); assertTrue(promise.isSuccess());
@ -679,11 +680,11 @@ public class DefaultHttp2ConnectionEncoderTest {
@Test @Test
public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception { public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception {
final ChannelPromise promise = newPromise(); final Promise<Void> promise = newPromise();
final Throwable ex = new RuntimeException(); final Throwable ex = new RuntimeException();
// Fake an encoding error, like HPACK's HeaderListSizeException // 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))) when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
.thenAnswer((Answer<ChannelFuture>) invocation -> { .thenAnswer((Answer<Future<Void>>) invocation -> {
promise.setFailure(ex); promise.setFailure(ex);
return promise; return promise;
}); });
@ -702,11 +703,11 @@ public class DefaultHttp2ConnectionEncoderTest {
@Test @Test
public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception { public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception {
final ChannelPromise promise = newPromise(); final Promise<Void> promise = newPromise();
final Throwable ex = new RuntimeException(); final Throwable ex = new RuntimeException();
// Fake an encoding error, like HPACK's HeaderListSizeException // 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))) when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
.thenAnswer((Answer<ChannelFuture>) invocation -> { .thenAnswer((Answer<Future<Void>>) invocation -> {
promise.setFailure(ex); promise.setFailure(ex);
return promise; return promise;
}); });
@ -724,7 +725,7 @@ public class DefaultHttp2ConnectionEncoderTest {
@Test @Test
public void encoderDelegatesGoAwayToLifeCycleManager() { public void encoderDelegatesGoAwayToLifeCycleManager() {
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null, promise); encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null, promise);
verify(lifecycleManager).goAway(eq(ctx), eq(STREAM_ID), eq(Http2Error.INTERNAL_ERROR.code()), verify(lifecycleManager).goAway(eq(ctx), eq(STREAM_ID), eq(Http2Error.INTERNAL_ERROR.code()),
eq((ByteBuf) null), eq(promise)); eq((ByteBuf) null), eq(promise));
@ -735,7 +736,7 @@ public class DefaultHttp2ConnectionEncoderTest {
public void dataWriteToClosedStreamShouldFail() throws Exception { public void dataWriteToClosedStreamShouldFail() throws Exception {
createStream(STREAM_ID, false).close(); createStream(STREAM_ID, false).close();
ByteBuf data = mock(ByteBuf.class); ByteBuf data = mock(ByteBuf.class);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise); encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
assertTrue(promise.isDone()); assertTrue(promise.isDone());
assertFalse(promise.isSuccess()); assertFalse(promise.isSuccess());
@ -747,7 +748,7 @@ public class DefaultHttp2ConnectionEncoderTest {
public void dataWriteToHalfClosedLocalStreamShouldFail() throws Exception { public void dataWriteToHalfClosedLocalStreamShouldFail() throws Exception {
createStream(STREAM_ID, true); createStream(STREAM_ID, true);
ByteBuf data = mock(ByteBuf.class); ByteBuf data = mock(ByteBuf.class);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise); encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
assertTrue(promise.isDone()); assertTrue(promise.isDone());
assertFalse(promise.isSuccess()); assertFalse(promise.isSuccess());
@ -769,7 +770,7 @@ public class DefaultHttp2ConnectionEncoderTest {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
createStream(STREAM_ID, false); createStream(STREAM_ID, false);
goAwaySent(0); goAwaySent(0);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise); encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise)); eq(0), eq(false), eq(promise));
@ -788,7 +789,7 @@ public class DefaultHttp2ConnectionEncoderTest {
public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception { public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
goAwayReceived(STREAM_ID); goAwayReceived(STREAM_ID);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise); encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise)); eq(0), eq(false), eq(promise));
@ -798,7 +799,7 @@ public class DefaultHttp2ConnectionEncoderTest {
public void headersWithNoPriority() { public void headersWithNoPriority() {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
final int streamId = 6; final int streamId = 6;
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise); encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
eq(0), eq(false), eq(promise)); eq(0), eq(false), eq(promise));
@ -808,7 +809,7 @@ public class DefaultHttp2ConnectionEncoderTest {
public void headersWithPriority() { public void headersWithPriority() {
writeAllFlowControlledFrames(); writeAllFlowControlledFrames();
final int streamId = 6; final int streamId = 6;
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 10, DEFAULT_PRIORITY_WEIGHT, encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 10, DEFAULT_PRIORITY_WEIGHT,
true, 1, false, promise); true, 1, false, promise);
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(10), verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(10),
@ -844,12 +845,12 @@ public class DefaultHttp2ConnectionEncoderTest {
connection.goAwaySent(lastStreamId, 0, EMPTY_BUFFER); connection.goAwaySent(lastStreamId, 0, EMPTY_BUFFER);
} }
private ChannelPromise newPromise() { private static Promise<Void> newPromise() {
return new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); return new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
} }
private ChannelFuture newSucceededFuture() { private static Future<Void> newSucceededFuture() {
return newPromise().setSuccess(); return newPromise().setSuccess(null);
} }
private static ByteBuf dummyData() { private static ByteBuf dummyData() {

View File

@ -18,18 +18,17 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise;
import io.netty.util.ReferenceCountUtil; 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.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -38,11 +37,13 @@ import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.when;
/** /**
* Tests for {@link DefaultHttp2FrameWriter}. * Tests for {@link DefaultHttp2FrameWriter}.
*/ */
@SuppressWarnings("unchecked")
public class DefaultHttp2FrameWriterTest { public class DefaultHttp2FrameWriterTest {
private DefaultHttp2FrameWriter frameWriter; private DefaultHttp2FrameWriter frameWriter;
@ -50,7 +51,7 @@ public class DefaultHttp2FrameWriterTest {
private ByteBuf expectedOutbound; private ByteBuf expectedOutbound;
private ChannelPromise promise; private Promise<Void> promise;
private Http2HeadersEncoder http2HeadersEncoder; private Http2HeadersEncoder http2HeadersEncoder;
@ -58,7 +59,7 @@ public class DefaultHttp2FrameWriterTest {
private Channel channel; private Channel channel;
@Mock @Mock
private ChannelFuture future; private Future<Void> future;
@Mock @Mock
private ChannelHandlerContext ctx; private ChannelHandlerContext ctx;
@ -76,7 +77,7 @@ public class DefaultHttp2FrameWriterTest {
expectedOutbound = Unpooled.EMPTY_BUFFER; expectedOutbound = Unpooled.EMPTY_BUFFER;
promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
Answer<Object> answer = var1 -> { Answer<Object> answer = var1 -> {
Object msg = var1.getArgument(0); Object msg = var1.getArgument(0);
@ -87,7 +88,7 @@ public class DefaultHttp2FrameWriterTest {
return future; return future;
}; };
when(ctx.write(any())).then(answer); when(ctx.write(any())).then(answer);
when(ctx.write(any(), any(ChannelPromise.class))).then(answer); when(ctx.write(any(), any(Promise.class))).then(answer);
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
when(ctx.channel()).thenReturn(channel); when(ctx.channel()).thenReturn(channel);
when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE); when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE);

View File

@ -15,6 +15,20 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http2.Http2Stream.State;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Promise;
import junit.framework.AssertionFailedError;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import static io.netty.handler.codec.http2.DefaultHttp2LocalFlowController.DEFAULT_WINDOW_UPDATE_RATIO; import static io.netty.handler.codec.http2.DefaultHttp2LocalFlowController.DEFAULT_WINDOW_UPDATE_RATIO;
import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID; import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_WINDOW_SIZE; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_WINDOW_SIZE;
@ -32,20 +46,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http2.Http2Stream.State;
import io.netty.util.concurrent.EventExecutor;
import junit.framework.AssertionFailedError;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
/** /**
* Tests for {@link DefaultHttp2LocalFlowController}. * Tests for {@link DefaultHttp2LocalFlowController}.
*/ */
@ -64,7 +64,7 @@ public class DefaultHttp2LocalFlowControllerTest {
private EventExecutor executor; private EventExecutor executor;
@Mock @Mock
private ChannelPromise promise; private Promise<Void> promise;
private DefaultHttp2Connection connection; private DefaultHttp2Connection connection;
@ -424,9 +424,10 @@ public class DefaultHttp2LocalFlowControllerTest {
verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt(), eq(promise)); verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt(), eq(promise));
} }
@SuppressWarnings("unchecked")
private void verifyWindowUpdateNotSent() { private void verifyWindowUpdateNotSent() {
verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(), verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
any(ChannelPromise.class)); any(Promise.class));
} }
private int window(int streamId) { private int window(int streamId) {

View File

@ -19,8 +19,6 @@ import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
@ -33,7 +31,6 @@ import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -97,9 +94,7 @@ public class DefaultHttp2PushPromiseFrameTest {
@Test @Test
public void send() { public void send() {
connectionFuture.addListener((GenericFutureListener<Future<Channel>>) future -> { connectionFuture.addListener(future -> clientHandler.write());
clientHandler.write();
});
} }
@AfterEach @AfterEach
@ -126,9 +121,7 @@ public class DefaultHttp2PushPromiseFrameTest {
Http2PushPromiseFrame pushPromiseFrame = new DefaultHttp2PushPromiseFrame(pushRequestHeaders); Http2PushPromiseFrame pushPromiseFrame = new DefaultHttp2PushPromiseFrame(pushRequestHeaders);
pushPromiseFrame.stream(receivedFrame.stream()); pushPromiseFrame.stream(receivedFrame.stream());
pushPromiseFrame.pushStream(newPushFrameStream); pushPromiseFrame.pushStream(newPushFrameStream);
ctx.writeAndFlush(pushPromiseFrame).addListener(new ChannelFutureListener() { ctx.writeAndFlush(pushPromiseFrame).addListener(future -> {
@Override
public void operationComplete(ChannelFuture future) {
contentMap.put(newPushFrameStream.id(), "Meow, I am Pushed via HTTP/2"); contentMap.put(newPushFrameStream.id(), "Meow, I am Pushed via HTTP/2");
// Write headers for actual request // Write headers for actual request
@ -137,19 +130,15 @@ public class DefaultHttp2PushPromiseFrameTest {
http2Headers.add("push", "false"); http2Headers.add("push", "false");
Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(http2Headers, false); Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(http2Headers, false);
headersFrame.stream(receivedFrame.stream()); headersFrame.stream(receivedFrame.stream());
ChannelFuture channelFuture = ctx.writeAndFlush(headersFrame); Future<Void> channelFuture = ctx.writeAndFlush(headersFrame);
// Write Data of actual request // Write Data of actual request
channelFuture.addListener(new ChannelFutureListener() { channelFuture.addListener(fut -> {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Http2DataFrame dataFrame = new DefaultHttp2DataFrame( Http2DataFrame dataFrame = new DefaultHttp2DataFrame(
Unpooled.wrappedBuffer("Meow".getBytes()), true); Unpooled.wrappedBuffer("Meow".getBytes()), true);
dataFrame.stream(receivedFrame.stream()); dataFrame.stream(receivedFrame.stream());
ctx.writeAndFlush(dataFrame); ctx.writeAndFlush(dataFrame);
}
}); });
}
}); });
} else if (msg instanceof Http2PriorityFrame) { } else if (msg instanceof Http2PriorityFrame) {
Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg; Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg;
@ -177,7 +166,7 @@ public class DefaultHttp2PushPromiseFrameTest {
private static final class ClientHandler extends Http2ChannelDuplexHandler { private static final class ClientHandler extends Http2ChannelDuplexHandler {
private ChannelHandlerContext ctx; private volatile ChannelHandlerContext ctx;
@Override @Override
public void channelActive(ChannelHandlerContext ctx) throws InterruptedException { public void channelActive(ChannelHandlerContext ctx) throws InterruptedException {

View File

@ -18,14 +18,13 @@ package io.netty.handler.codec.http2;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.api.function.Executable;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import org.opentest4j.AssertionFailedError; import org.opentest4j.AssertionFailedError;
@ -80,7 +79,7 @@ public abstract class DefaultHttp2RemoteFlowControllerTest {
private EventExecutor executor; private EventExecutor executor;
@Mock @Mock
private ChannelPromise promise; private Promise<Void> promise;
@Mock @Mock
private Http2RemoteFlowController.Listener listener; private Http2RemoteFlowController.Listener listener;

View File

@ -71,7 +71,10 @@ public class Http2ClientUpgradeCodecTest {
// Flush the channel to ensure we write out all buffered data // Flush the channel to ensure we write out all buffered data
channel.flush(); channel.flush();
channel.eventLoop().submit(() -> {
codec.upgradeTo(ctx, null); codec.upgradeTo(ctx, null);
return null;
}).sync();
assertNotNull(channel.pipeline().get("connectionHandler")); assertNotNull(channel.pipeline().get("connectionHandler"));
if (multiplexer != null) { if (multiplexer != null) {

View File

@ -19,19 +19,17 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelConfig; import io.netty.channel.DefaultChannelConfig;
import io.netty.channel.DefaultChannelPromise;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http2.Http2Exception.ShutdownHint; import io.netty.handler.codec.http2.Http2Exception.ShutdownHint;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -78,12 +76,13 @@ import static org.mockito.Mockito.when;
/** /**
* Tests for {@link Http2ConnectionHandler} * Tests for {@link Http2ConnectionHandler}
*/ */
@SuppressWarnings("unchecked")
public class Http2ConnectionHandlerTest { public class Http2ConnectionHandlerTest {
private static final int STREAM_ID = 1; private static final int STREAM_ID = 1;
private static final int NON_EXISTANT_STREAM_ID = 13; private static final int NON_EXISTANT_STREAM_ID = 13;
private Http2ConnectionHandler handler; private Http2ConnectionHandler handler;
private ChannelPromise promise; private Promise<Void> promise;
@Mock @Mock
private Http2Connection connection; private Http2Connection connection;
@ -119,7 +118,7 @@ public class Http2ConnectionHandlerTest {
private ChannelPipeline pipeline; private ChannelPipeline pipeline;
@Mock @Mock
private ChannelFuture future; private Future<Void> future;
@Mock @Mock
private Http2Stream stream; private Http2Stream stream;
@ -140,7 +139,7 @@ public class Http2ConnectionHandlerTest {
public void setup() throws Exception { public void setup() throws Exception {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
when(channel.metadata()).thenReturn(new ChannelMetadata(false)); when(channel.metadata()).thenReturn(new ChannelMetadata(false));
DefaultChannelConfig config = new DefaultChannelConfig(channel); DefaultChannelConfig config = new DefaultChannelConfig(channel);
@ -152,22 +151,21 @@ public class Http2ConnectionHandlerTest {
when(encoder.frameWriter()).thenReturn(frameWriter); when(encoder.frameWriter()).thenReturn(frameWriter);
when(encoder.flowController()).thenReturn(remoteFlow); when(encoder.flowController()).thenReturn(remoteFlow);
when(decoder.flowController()).thenReturn(localFlow); when(decoder.flowController()).thenReturn(localFlow);
doAnswer((Answer<ChannelFuture>) invocation -> { doAnswer((Answer<Future<Void>>) invocation -> {
ByteBuf buf = invocation.getArgument(3); ByteBuf buf = invocation.getArgument(3);
goAwayDebugCap = buf.toString(UTF_8); goAwayDebugCap = buf.toString(UTF_8);
buf.release(); buf.release();
return future; return future;
}).when(frameWriter).writeGoAway( }).when(frameWriter).writeGoAway(
any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class), any(ChannelPromise.class)); any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class), any(Promise.class));
doAnswer((Answer<ChannelFuture>) invocation -> { doAnswer((Answer<Future<Void>>) invocation -> {
Object o = invocation.getArguments()[0]; Object o = invocation.getArguments()[0];
if (o instanceof ChannelFutureListener) { if (o instanceof FutureListener) {
((ChannelFutureListener) o).operationComplete(future); ((FutureListener<Void>) o).operationComplete(future);
} }
return future; return future;
}).when(future).addListener(any(GenericFutureListener.class)); }).when(future).addListener(any(FutureListener.class));
when(future.cause()).thenReturn(fakeException); when(future.cause()).thenReturn(fakeException);
when(future.channel()).thenReturn(channel);
when(channel.isActive()).thenReturn(true); when(channel.isActive()).thenReturn(true);
when(channel.pipeline()).thenReturn(pipeline); when(channel.pipeline()).thenReturn(pipeline);
when(connection.remote()).thenReturn(remote); when(connection.remote()).thenReturn(remote);
@ -252,7 +250,7 @@ public class Http2ConnectionHandlerTest {
final Answer<Object> verifier = in -> { final Answer<Object> verifier = in -> {
assertEquals(in.getArgument(0), evt); // sanity check... assertEquals(in.getArgument(0), evt); // sanity check...
verify(ctx).write(eq(connectionPrefaceBuf())); verify(ctx).write(eq(connectionPrefaceBuf()));
verify(encoder).writeSettings(eq(ctx), any(Http2Settings.class), any(ChannelPromise.class)); verify(encoder).writeSettings(eq(ctx), any(Http2Settings.class), any(Promise.class));
verified.set(true); verified.set(true);
return null; return null;
}; };
@ -526,13 +524,13 @@ public class Http2ConnectionHandlerTest {
handler = newHandler(); handler = newHandler();
when(stream.id()).thenReturn(STREAM_ID); when(stream.id()).thenReturn(STREAM_ID);
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
anyLong(), any(ChannelPromise.class))).thenReturn(future); anyLong(), any(Promise.class))).thenReturn(future);
when(stream.state()).thenReturn(CLOSED); when(stream.state()).thenReturn(CLOSED);
when(stream.isHeadersSent()).thenReturn(true); when(stream.isHeadersSent()).thenReturn(true);
// The stream is "closed" but is still known about by the connection (connection().stream(..) // 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. // will return the stream). We should still write a RST_STREAM frame in this scenario.
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise); handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(ChannelPromise.class)); verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
} }
@Test @Test
@ -540,7 +538,7 @@ public class Http2ConnectionHandlerTest {
handler = newHandler(); handler = newHandler();
when(stream.state()).thenReturn(IDLE); when(stream.state()).thenReturn(IDLE);
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise); handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(ChannelPromise.class)); verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
verify(stream).close(); verify(stream).close();
} }
@ -550,9 +548,9 @@ public class Http2ConnectionHandlerTest {
handler = newHandler(); handler = newHandler();
when(future.isDone()).thenReturn(true); when(future.isDone()).thenReturn(true);
when(future.isSuccess()).thenReturn(true); when(future.isSuccess()).thenReturn(true);
doAnswer((Answer<ChannelFuture>) invocation -> { doAnswer((Answer<Future<Void>>) invocation -> {
Object[] args = invocation.getArguments(); Object[] args = invocation.getArguments();
GenericFutureListener<ChannelFuture> listener = (GenericFutureListener<ChannelFuture>) args[0]; FutureListener<Void> listener = (FutureListener<Void>) args[0];
// Simulate that all streams have become inactive by the time the future completes. // Simulate that all streams have become inactive by the time the future completes.
doAnswer((Answer<Http2Stream>) in -> null).when(connection).forEachActiveStream( doAnswer((Answer<Http2Stream>) in -> null).when(connection).forEachActiveStream(
any(Http2StreamVisitor.class)); any(Http2StreamVisitor.class));
@ -560,7 +558,7 @@ public class Http2ConnectionHandlerTest {
// Simulate the future being completed. // Simulate the future being completed.
listener.operationComplete(future); listener.operationComplete(future);
return future; return future;
}).when(future).addListener(any(GenericFutureListener.class)); }).when(future).addListener(any(FutureListener.class));
handler.close(ctx, promise); handler.close(ctx, promise);
if (future.isDone()) { if (future.isDone()) {
when(connection.numActiveStreams()).thenReturn(0); when(connection.numActiveStreams()).thenReturn(0);
@ -568,7 +566,7 @@ public class Http2ConnectionHandlerTest {
handler.closeStream(stream, future); handler.closeStream(stream, future);
// Simulate another stream close call being made after the context should already be closed. // Simulate another stream close call being made after the context should already be closed.
handler.closeStream(stream, future); handler.closeStream(stream, future);
verify(ctx, times(1)).close(any(ChannelPromise.class)); verify(ctx, times(1)).close(any(Promise.class));
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -579,9 +577,9 @@ public class Http2ConnectionHandlerTest {
when(future.isDone()).thenReturn(true); when(future.isDone()).thenReturn(true);
when(future.isSuccess()).thenReturn(true); when(future.isSuccess()).thenReturn(true);
doAnswer((Answer<Void>) invocation -> { doAnswer((Answer<Void>) invocation -> {
((GenericFutureListener) invocation.getArgument(0)).operationComplete(future); ((FutureListener<Void>) invocation.getArgument(0)).operationComplete(future);
return null; return null;
}).when(future).addListener(any(GenericFutureListener.class)); }).when(future).addListener(any(FutureListener.class));
handler = newHandler(); handler = newHandler();
handler.goAway(ctx, STREAM_ID, errorCode, data, promise); handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
@ -602,7 +600,7 @@ public class Http2ConnectionHandlerTest {
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data), verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data),
eq(promise)); eq(promise));
verify(connection).goAwaySent(eq(STREAM_ID + 2), eq(errorCode), eq(data)); verify(connection).goAwaySent(eq(STREAM_ID + 2), eq(errorCode), eq(data));
promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
handler.goAway(ctx, STREAM_ID, errorCode, data, promise); handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
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), eq(promise));
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data)); verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
@ -655,9 +653,9 @@ public class Http2ConnectionHandlerTest {
when(channel.isActive()).thenReturn(false); when(channel.isActive()).thenReturn(false);
handler.channelInactive(ctx); handler.channelInactive(ctx);
verify(frameWriter, never()).writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), verify(frameWriter, never()).writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(),
any(ByteBuf.class), any(ChannelPromise.class)); any(ByteBuf.class), any(Promise.class));
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong(), verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong(),
any(ChannelPromise.class)); any(Promise.class));
} }
@Test @Test
@ -727,21 +725,20 @@ public class Http2ConnectionHandlerTest {
return stream; return stream;
}); });
when(stream.isResetSent()).then((Answer<Boolean>) invocationOnMock -> resetSent.get()); when(stream.isResetSent()).then((Answer<Boolean>) invocationOnMock -> resetSent.get());
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(ChannelPromise.class))) when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class)))
.then((Answer<ChannelFuture>) invocationOnMock -> { .then((Answer<Future<Void>>) invocationOnMock -> {
ChannelPromise promise = invocationOnMock.getArgument(3); Promise<Void> promise = invocationOnMock.getArgument(3);
return promise.setSuccess(); return promise.setSuccess(null);
}); });
ChannelPromise promise = Promise<Void> promise =
new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
final ChannelPromise promise2 = final Promise<Void> promise2 =
new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
promise.addListener((ChannelFutureListener) future -> promise.addListener(future -> handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise2));
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise2));
handler.resetStream(ctx, STREAM_ID, CANCEL.code(), promise); handler.resetStream(ctx, STREAM_ID, CANCEL.code(), promise);
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(ChannelPromise.class)); verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
assertTrue(promise.isSuccess()); assertTrue(promise.isSuccess());
assertTrue(promise2.isSuccess()); assertTrue(promise2.isSuccess());
} }

View File

@ -21,13 +21,10 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.MultithreadEventLoopGroup; import io.netty.channel.MultithreadEventLoopGroup;
import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalChannel;
@ -38,6 +35,7 @@ import io.netty.util.AsciiString;
import io.netty.util.IllegalReferenceCountException; import io.netty.util.IllegalReferenceCountException;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -261,10 +259,10 @@ public class Http2ConnectionRoundtripTest {
runInChannel(clientChannel, () -> { runInChannel(clientChannel, () -> {
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, false, newPromise()) http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, false, newPromise())
.addListener((ChannelFutureListener) future -> clientHeadersWriteException.set(future.cause())); .addListener(future -> clientHeadersWriteException.set(future.cause()));
// It is expected that this write should fail locally and the remote peer will never see this. // 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, newPromise())
.addListener((ChannelFutureListener) future -> { .addListener(future -> {
clientDataWriteException.set(future.cause()); clientDataWriteException.set(future.cause());
clientDataWrite.countDown(); clientDataWrite.countDown();
}); });
@ -289,7 +287,7 @@ public class Http2ConnectionRoundtripTest {
runInChannel(clientChannel, () -> { runInChannel(clientChannel, () -> {
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true, http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true,
newPromise()).addListener((ChannelFutureListener) future -> { newPromise()).addListener(future -> {
clientHeadersWriteException2.set(future.cause()); clientHeadersWriteException2.set(future.cause());
clientHeadersLatch.countDown(); clientHeadersLatch.countDown();
}); });
@ -481,7 +479,7 @@ public class Http2ConnectionRoundtripTest {
// Now have the server attempt to send a headers frame simulating some asynchronous work. // Now have the server attempt to send a headers frame simulating some asynchronous work.
runInChannel(serverConnectedChannel, () -> { runInChannel(serverConnectedChannel, () -> {
http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true, serverNewPromise()) http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true, serverNewPromise())
.addListener((ChannelFutureListener) future -> { .addListener(future -> {
serverWriteHeadersCauseRef.set(future.cause()); serverWriteHeadersCauseRef.set(future.cause());
serverWriteHeadersLatch.countDown(); serverWriteHeadersLatch.countDown();
}); });
@ -507,7 +505,7 @@ public class Http2ConnectionRoundtripTest {
// Create a latch to track when the close occurs. // Create a latch to track when the close occurs.
final CountDownLatch closeLatch = new CountDownLatch(1); final CountDownLatch closeLatch = new CountDownLatch(1);
clientChannel.closeFuture().addListener((ChannelFutureListener) future -> closeLatch.countDown()); clientChannel.closeFuture().addListener(future -> closeLatch.countDown());
// Create a single stream by sending a HEADERS frame to the server. // Create a single stream by sending a HEADERS frame to the server.
final Http2Headers headers = dummyHeaders(); final Http2Headers headers = dummyHeaders();
@ -545,7 +543,7 @@ public class Http2ConnectionRoundtripTest {
// Create a latch to track when the close occurs. // Create a latch to track when the close occurs.
final CountDownLatch closeLatch = new CountDownLatch(1); final CountDownLatch closeLatch = new CountDownLatch(1);
clientChannel.closeFuture().addListener((ChannelFutureListener) future -> closeLatch.countDown()); clientChannel.closeFuture().addListener(future -> closeLatch.countDown());
// Create a single stream by sending a HEADERS frame to the server. // Create a single stream by sending a HEADERS frame to the server.
runInChannel(clientChannel, () -> { runInChannel(clientChannel, () -> {
@ -594,7 +592,7 @@ public class Http2ConnectionRoundtripTest {
throws Exception { throws Exception {
bootstrapEnv(1, 1, 2, 1); bootstrapEnv(1, 1, 2, 1);
final ChannelPromise emptyDataPromise = newPromise(); final Promise<Void> emptyDataPromise = newPromise();
runInChannel(clientChannel, () -> { runInChannel(clientChannel, () -> {
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false, http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
newPromise()); newPromise());
@ -639,15 +637,15 @@ public class Http2ConnectionRoundtripTest {
throws Exception { throws Exception {
bootstrapEnv(1, 1, 2, 1); bootstrapEnv(1, 1, 2, 1);
final ChannelPromise dataPromise = newPromise(); final Promise<Void> dataPromise = newPromise();
final ChannelPromise assertPromise = newPromise(); final Promise<Void> assertPromise = newPromise();
runInChannel(clientChannel, () -> { runInChannel(clientChannel, () -> {
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false, http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
newPromise()); newPromise());
clientChannel.pipeline().addFirst(new ChannelHandler() { clientChannel.pipeline().addFirst(new ChannelHandler() {
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
ReferenceCountUtil.release(msg); ReferenceCountUtil.release(msg);
try { try {
@ -673,7 +671,7 @@ public class Http2ConnectionRoundtripTest {
// The Frame should have been removed after the write failed. // The Frame should have been removed after the write failed.
assertFalse(http2Client.encoder().flowController() assertFalse(http2Client.encoder().flowController()
.hasFlowControlled(http2Client.connection().stream(3))); .hasFlowControlled(http2Client.connection().stream(3)));
assertPromise.setSuccess(); assertPromise.setSuccess(null);
} catch (Throwable error) { } catch (Throwable error) {
assertPromise.setFailure(error); assertPromise.setFailure(error);
} }
@ -695,7 +693,7 @@ public class Http2ConnectionRoundtripTest {
// Create a latch to track when the close occurs. // Create a latch to track when the close occurs.
final CountDownLatch closeLatch = new CountDownLatch(1); final CountDownLatch closeLatch = new CountDownLatch(1);
clientChannel.closeFuture().addListener((ChannelFutureListener) future -> closeLatch.countDown()); clientChannel.closeFuture().addListener(future -> closeLatch.countDown());
// Create a single stream by sending a HEADERS frame to the server. // Create a single stream by sending a HEADERS frame to the server.
final Http2Headers headers = dummyHeaders(); final Http2Headers headers = dummyHeaders();
@ -790,20 +788,20 @@ public class Http2ConnectionRoundtripTest {
verify(clientListener).onGoAwayRead(any(ChannelHandlerContext.class), eq(3), eq(NO_ERROR.code()), verify(clientListener).onGoAwayRead(any(ChannelHandlerContext.class), eq(3), eq(NO_ERROR.code()),
any(ByteBuf.class)); any(ByteBuf.class));
final AtomicReference<ChannelFuture> clientWriteAfterGoAwayFutureRef = new AtomicReference<>(); final AtomicReference<Future<Void>> clientWriteAfterGoAwayFutureRef = new AtomicReference<>();
final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1); final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1);
runInChannel(clientChannel, () -> { runInChannel(clientChannel, () -> {
ChannelFuture f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0, Future<Void> f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0,
true, newPromise()); true, newPromise());
clientWriteAfterGoAwayFutureRef.set(f); clientWriteAfterGoAwayFutureRef.set(f);
http2Client.flush(ctx()); http2Client.flush(ctx());
f.addListener((ChannelFutureListener) future -> clientWriteAfterGoAwayLatch.countDown()); f.addListener(future -> clientWriteAfterGoAwayLatch.countDown());
}); });
// Wait for the client's write operation to complete. // Wait for the client's write operation to complete.
assertTrue(clientWriteAfterGoAwayLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS)); assertTrue(clientWriteAfterGoAwayLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
ChannelFuture clientWriteAfterGoAwayFuture = clientWriteAfterGoAwayFutureRef.get(); Future<Void> clientWriteAfterGoAwayFuture = clientWriteAfterGoAwayFutureRef.get();
assertNotNull(clientWriteAfterGoAwayFuture); assertNotNull(clientWriteAfterGoAwayFuture);
Throwable clientCause = clientWriteAfterGoAwayFuture.cause(); Throwable clientCause = clientWriteAfterGoAwayFuture.cause();
assertThat(clientCause, is(instanceOf(Http2Exception.StreamException.class))); assertThat(clientCause, is(instanceOf(Http2Exception.StreamException.class)));
@ -1106,11 +1104,11 @@ public class Http2ConnectionRoundtripTest {
return serverConnectedChannel.pipeline().firstContext(); return serverConnectedChannel.pipeline().firstContext();
} }
private ChannelPromise newPromise() { private Promise<Void> newPromise() {
return ctx().newPromise(); return ctx().newPromise();
} }
private ChannelPromise serverNewPromise() { private Promise<Void> serverNewPromise() {
return serverCtx().newPromise(); return serverCtx().newPromise();
} }

View File

@ -19,15 +19,15 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise;
import io.netty.channel.DefaultMessageSizeEstimator; import io.netty.channel.DefaultMessageSizeEstimator;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.ImmediateEventExecutor; import io.netty.util.concurrent.ImmediateEventExecutor;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -36,20 +36,30 @@ import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Queue; import java.util.Queue;
import static io.netty.handler.codec.http2.Http2CodecUtil.*; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
import static io.netty.handler.codec.http2.Http2Error.CANCEL; import static io.netty.handler.codec.http2.Http2Error.CANCEL;
import static io.netty.handler.codec.http2.Http2Error.ENHANCE_YOUR_CALM; import static io.netty.handler.codec.http2.Http2Error.ENHANCE_YOUR_CALM;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/** /**
* Tests for {@link Http2ControlFrameLimitEncoder}. * Tests for {@link Http2ControlFrameLimitEncoder}.
*/ */
@SuppressWarnings("unchecked")
public class Http2ControlFrameLimitEncoderTest { public class Http2ControlFrameLimitEncoderTest {
private Http2ControlFrameLimitEncoder encoder; private Http2ControlFrameLimitEncoder encoder;
@ -74,7 +84,7 @@ public class Http2ControlFrameLimitEncoderTest {
private int numWrites; private int numWrites;
private final Queue<ChannelPromise> goAwayPromises = new ArrayDeque<ChannelPromise>(); private final Queue<Promise<Void>> goAwayPromises = new ArrayDeque<Promise<Void>>();
/** /**
* Init fields and do mocking. * Init fields and do mocking.
@ -91,22 +101,22 @@ public class Http2ControlFrameLimitEncoderTest {
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy); when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE); when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(ChannelPromise.class))) when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class)))
.thenAnswer((Answer<ChannelFuture>) invocationOnMock -> handlePromise(invocationOnMock, 3)); .thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise(invocationOnMock, 3));
when(writer.writeSettingsAck(any(ChannelHandlerContext.class), any(ChannelPromise.class))) when(writer.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
.thenAnswer((Answer<ChannelFuture>) invocationOnMock -> handlePromise(invocationOnMock, 1)); .thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise(invocationOnMock, 1));
when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong(), any(ChannelPromise.class))) when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong(), any(Promise.class)))
.thenAnswer((Answer<ChannelFuture>) invocationOnMock -> { .thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
ChannelPromise promise = handlePromise(invocationOnMock, 3); Promise<Void> promise = handlePromise(invocationOnMock, 3);
if (invocationOnMock.getArgument(1) == Boolean.FALSE) { if (invocationOnMock.getArgument(1) == Boolean.FALSE) {
promise.trySuccess(); promise.trySuccess(null);
} }
return promise; return promise;
}); });
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class), when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock -> { any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
ReferenceCountUtil.release(invocationOnMock.getArgument(3)); ReferenceCountUtil.release(invocationOnMock.getArgument(3));
ChannelPromise promise = invocationOnMock.getArgument(4); Promise<Void> promise = invocationOnMock.getArgument(4);
goAwayPromises.offer(promise); goAwayPromises.offer(promise);
return promise; return promise;
}); });
@ -128,7 +138,7 @@ public class Http2ControlFrameLimitEncoderTest {
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
when(executor.inEventLoop()).thenReturn(true); when(executor.inEventLoop()).thenReturn(true);
doAnswer((Answer<ChannelPromise>) invocation -> newPromise()).when(ctx).newPromise(); doAnswer((Answer<Promise>) invocation -> newPromise()).when(ctx).newPromise();
when(ctx.executor()).thenReturn(executor); when(ctx.executor()).thenReturn(executor);
when(channel.isActive()).thenReturn(false); when(channel.isActive()).thenReturn(false);
when(channel.config()).thenReturn(config); when(channel.config()).thenReturn(config);
@ -142,10 +152,10 @@ public class Http2ControlFrameLimitEncoderTest {
handler.handlerAdded(ctx); handler.handlerAdded(ctx);
} }
private ChannelPromise handlePromise(InvocationOnMock invocationOnMock, int promiseIdx) { private Promise<Void> handlePromise(InvocationOnMock invocationOnMock, int promiseIdx) {
ChannelPromise promise = invocationOnMock.getArgument(promiseIdx); Promise<Void> promise = invocationOnMock.getArgument(promiseIdx);
if (++numWrites == 2) { if (++numWrites == 2) {
promise.setSuccess(); promise.setSuccess(null);
} }
return promise; return promise;
} }
@ -155,14 +165,14 @@ public class Http2ControlFrameLimitEncoderTest {
// Close and release any buffered frames. // Close and release any buffered frames.
encoder.close(); encoder.close();
// Notify all goAway ChannelPromise instances now as these will also release the retained ByteBuf for the // Notify all goAway Promise instances now as these will also release the retained ByteBuf for the
// debugData. // debugData.
for (;;) { for (;;) {
ChannelPromise promise = goAwayPromises.poll(); Promise<Void> promise = goAwayPromises.poll();
if (promise == null) { if (promise == null) {
break; break;
} }
promise.setSuccess(); promise.setSuccess(null);
} }
} }
@ -246,11 +256,11 @@ public class Http2ControlFrameLimitEncoderTest {
verify(ctx, times(invocations)).close(); verify(ctx, times(invocations)).close();
if (failed) { if (failed) {
verify(writer, times(1)).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(ENHANCE_YOUR_CALM.code()), verify(writer, times(1)).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(ENHANCE_YOUR_CALM.code()),
any(ByteBuf.class), any(ChannelPromise.class)); any(ByteBuf.class), any(Promise.class));
} }
} }
private ChannelPromise newPromise() { private static Promise<Void> newPromise() {
return new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); return new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
} }
} }

View File

@ -16,11 +16,8 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.handler.codec.UnsupportedMessageTypeException;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
@ -39,6 +36,7 @@ import io.netty.util.AsciiString;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted; import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.DefaultPromise; import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise; import io.netty.util.concurrent.Promise;
import io.netty.util.internal.ReflectionUtil; import io.netty.util.internal.ReflectionUtil;
@ -84,6 +82,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
/** /**
* Unit tests for {@link Http2FrameCodec}. * Unit tests for {@link Http2FrameCodec}.
*/ */
@SuppressWarnings("unchecked")
public class Http2FrameCodecTest { public class Http2FrameCodecTest {
// For verifying outbound frames // For verifying outbound frames
@ -384,7 +383,7 @@ public class Http2FrameCodecTest {
UnknownHttp2Frame frame = new UnknownHttp2Frame(); UnknownHttp2Frame frame = new UnknownHttp2Frame();
assertEquals(1, frame.refCnt()); assertEquals(1, frame.refCnt());
ChannelFuture f = channel.write(frame); Future<Void> f = channel.write(frame);
f.await(); f.await();
assertTrue(f.isDone()); assertTrue(f.isDone());
assertFalse(f.isSuccess()); assertFalse(f.isSuccess());
@ -493,7 +492,7 @@ public class Http2FrameCodecTest {
Http2FrameStream stream2 = inboundHeaders.stream(); Http2FrameStream stream2 = inboundHeaders.stream();
int before = connection.local().flowController().unconsumedBytes(stream); int before = connection.local().flowController().unconsumedBytes(stream);
ChannelFuture f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2)); Future<Void> f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2));
int after = connection.local().flowController().unconsumedBytes(stream); int after = connection.local().flowController().unconsumedBytes(stream);
assertEquals(100, before - after); assertEquals(100, before - after);
assertTrue(f.isSuccess()); assertTrue(f.isSuccess());
@ -512,7 +511,7 @@ public class Http2FrameCodecTest {
Http2FrameStream stream2 = inboundHeaders.stream(); Http2FrameStream stream2 = inboundHeaders.stream();
// Fails, cause trying to return too many bytes to the flow controller // Fails, cause trying to return too many bytes to the flow controller
ChannelFuture f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2)); Future<Void> f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2));
assertTrue(f.isDone()); assertTrue(f.isDone());
assertFalse(f.isSuccess()); assertFalse(f.isSuccess());
assertThat(f.cause(), instanceOf(Http2Exception.class)); assertThat(f.cause(), instanceOf(Http2Exception.class));
@ -597,7 +596,7 @@ public class Http2FrameCodecTest {
channel.write(unknownFrame); channel.write(unknownFrame);
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq(unknownFrame.frameType()), verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq(unknownFrame.frameType()),
eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer), any(ChannelPromise.class)); eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer), any(Promise.class));
} }
@Test @Test
@ -605,7 +604,7 @@ public class Http2FrameCodecTest {
Http2Settings settings = new Http2Settings(); Http2Settings settings = new Http2Settings();
channel.write(new DefaultHttp2SettingsFrame(settings)); channel.write(new DefaultHttp2SettingsFrame(settings));
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), same(settings), any(ChannelPromise.class)); verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), same(settings), any(Promise.class));
} }
@Test @Test
@ -619,14 +618,14 @@ public class Http2FrameCodecTest {
final Promise<Void> listenerExecuted = new DefaultPromise<>(GlobalEventExecutor.INSTANCE); final Promise<Void> listenerExecuted = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers(), false).stream(stream)) channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers(), false).stream(stream))
.addListener((ChannelFutureListener) future -> { .addListener(future -> {
assertTrue(future.isSuccess()); assertTrue(future.isSuccess());
assertTrue(isStreamIdValid(stream.id())); assertTrue(isStreamIdValid(stream.id()));
listenerExecuted.setSuccess(null); listenerExecuted.setSuccess(null);
} }
); );
ByteBuf data = Unpooled.buffer().writeZero(100); ByteBuf data = Unpooled.buffer().writeZero(100);
ChannelFuture f = channel.writeAndFlush(new DefaultHttp2DataFrame(data).stream(stream)); Future<Void> f = channel.writeAndFlush(new DefaultHttp2DataFrame(data).stream(stream));
assertTrue(f.isSuccess()); assertTrue(f.isSuccess());
listenerExecuted.syncUninterruptibly(); listenerExecuted.syncUninterruptibly();
@ -641,8 +640,8 @@ public class Http2FrameCodecTest {
Http2FrameStream stream1 = frameCodec.newStream(); Http2FrameStream stream1 = frameCodec.newStream();
Http2FrameStream stream2 = frameCodec.newStream(); Http2FrameStream stream2 = frameCodec.newStream();
ChannelPromise promise1 = channel.newPromise(); Promise<Void> promise1 = channel.newPromise();
ChannelPromise promise2 = channel.newPromise(); Promise<Void> promise2 = channel.newPromise();
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1); channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1);
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2); channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2);
@ -672,9 +671,9 @@ public class Http2FrameCodecTest {
Http2FrameStream stream2 = frameCodec.newStream(); Http2FrameStream stream2 = frameCodec.newStream();
Http2FrameStream stream3 = frameCodec.newStream(); Http2FrameStream stream3 = frameCodec.newStream();
ChannelPromise promise1 = channel.newPromise(); Promise<Void> promise1 = channel.newPromise();
ChannelPromise promise2 = channel.newPromise(); Promise<Void> promise2 = channel.newPromise();
ChannelPromise promise3 = channel.newPromise(); Promise<Void> promise3 = channel.newPromise();
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1); channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1);
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2); channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2);
@ -712,8 +711,8 @@ public class Http2FrameCodecTest {
Http2FrameStream stream1 = frameCodec.newStream(); Http2FrameStream stream1 = frameCodec.newStream();
Http2FrameStream stream2 = frameCodec.newStream(); Http2FrameStream stream2 = frameCodec.newStream();
ChannelPromise stream1HeaderPromise = channel.newPromise(); Promise<Void> stream1HeaderPromise = channel.newPromise();
ChannelPromise stream2HeaderPromise = channel.newPromise(); Promise<Void> stream2HeaderPromise = channel.newPromise();
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1),
stream1HeaderPromise); stream1HeaderPromise);
@ -733,15 +732,18 @@ public class Http2FrameCodecTest {
} }
@Test @Test
public void streamIdentifiersExhausted() throws Http2Exception { public void streamIdentifiersExhausted() throws Exception {
int maxServerStreamId = Integer.MAX_VALUE - 1; int maxServerStreamId = Integer.MAX_VALUE - 1;
channel.eventLoop().submit(() -> {
assertNotNull(frameCodec.connection().local().createStream(maxServerStreamId, false)); assertNotNull(frameCodec.connection().local().createStream(maxServerStreamId, false));
return null;
}).sync();
Http2FrameStream stream = frameCodec.newStream(); Http2FrameStream stream = frameCodec.newStream();
assertNotNull(stream); assertNotNull(stream);
ChannelPromise writePromise = channel.newPromise(); Promise<Void> writePromise = channel.newPromise();
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream), writePromise); channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream), writePromise);
Http2GoAwayFrame goAwayFrame = inboundHandler.readInbound(); Http2GoAwayFrame goAwayFrame = inboundHandler.readInbound();
@ -866,7 +868,7 @@ public class Http2FrameCodecTest {
final AtomicBoolean listenerExecuted = new AtomicBoolean(); final AtomicBoolean listenerExecuted = new AtomicBoolean();
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2)) channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2))
.addListener((ChannelFutureListener) future -> { .addListener(future -> {
assertTrue(future.isSuccess()); assertTrue(future.isSuccess());
assertEquals(State.OPEN, stream2.state()); assertEquals(State.OPEN, stream2.state());
listenerExecuted.set(true); listenerExecuted.set(true);
@ -900,7 +902,7 @@ public class Http2FrameCodecTest {
// Simulate consuming the frame and update the flow-controller. // Simulate consuming the frame and update the flow-controller.
Http2DataFrame data = (Http2DataFrame) msg; Http2DataFrame data = (Http2DataFrame) msg;
ctx.writeAndFlush(new DefaultHttp2WindowUpdateFrame(data.initialFlowControlledBytes()) ctx.writeAndFlush(new DefaultHttp2WindowUpdateFrame(data.initialFlowControlledBytes())
.stream(data.stream())).addListener((ChannelFutureListener) future -> { .stream(data.stream())).addListener(future -> {
Throwable cause = future.cause(); Throwable cause = future.cause();
if (cause != null) { if (cause != null) {
ctx.fireExceptionCaught(cause); ctx.fireExceptionCaught(cause);

View File

@ -19,15 +19,15 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.Attribute; import io.netty.util.Attribute;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -224,91 +224,91 @@ final class Http2FrameInboundWriter {
} }
@Override @Override
public ChannelFuture bind(SocketAddress localAddress) { public Future<Void> bind(SocketAddress localAddress) {
return channel.bind(localAddress); return channel.bind(localAddress);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress) { public Future<Void> connect(SocketAddress remoteAddress) {
return channel.connect(remoteAddress); return channel.connect(remoteAddress);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return channel.connect(remoteAddress, localAddress); return channel.connect(remoteAddress, localAddress);
} }
@Override @Override
public ChannelFuture disconnect() { public Future<Void> disconnect() {
return channel.disconnect(); return channel.disconnect();
} }
@Override @Override
public ChannelFuture close() { public Future<Void> close() {
return channel.close(); return channel.close();
} }
@Override @Override
public ChannelFuture register() { public Future<Void> register() {
return channel.register(); return channel.register();
} }
@Override @Override
public ChannelFuture deregister() { public Future<Void> deregister() {
return channel.deregister(); return channel.deregister();
} }
@Override @Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return channel.bind(localAddress, promise); return channel.bind(localAddress, promise);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return channel.connect(remoteAddress, promise); return channel.connect(remoteAddress, promise);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return channel.connect(remoteAddress, localAddress, promise); return channel.connect(remoteAddress, localAddress, promise);
} }
@Override @Override
public ChannelFuture disconnect(ChannelPromise promise) { public Future<Void> disconnect(Promise<Void> promise) {
return channel.disconnect(promise); return channel.disconnect(promise);
} }
@Override @Override
public ChannelFuture close(ChannelPromise promise) { public Future<Void> close(Promise<Void> promise) {
return channel.close(promise); return channel.close(promise);
} }
@Override @Override
public ChannelFuture register(ChannelPromise promise) { public Future<Void> register(Promise<Void> promise) {
return channel.register(promise); return channel.register(promise);
} }
@Override @Override
public ChannelFuture deregister(ChannelPromise promise) { public Future<Void> deregister(Promise<Void> promise) {
return channel.deregister(promise); return channel.deregister(promise);
} }
@Override @Override
public ChannelFuture write(Object msg) { public Future<Void> write(Object msg) {
return write(msg, newPromise()); return write(msg, newPromise());
} }
@Override @Override
public ChannelFuture write(Object msg, ChannelPromise promise) { public Future<Void> write(Object msg, Promise<Void> promise) {
return writeAndFlush(msg, promise); return writeAndFlush(msg, promise);
} }
@Override @Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
try { try {
channel.writeInbound(msg); channel.writeInbound(msg);
channel.runPendingTasks(); channel.runPendingTasks();
promise.setSuccess(); promise.setSuccess(null);
} catch (Throwable cause) { } catch (Throwable cause) {
promise.setFailure(cause); promise.setFailure(cause);
} }
@ -316,22 +316,22 @@ final class Http2FrameInboundWriter {
} }
@Override @Override
public ChannelFuture writeAndFlush(Object msg) { public Future<Void> writeAndFlush(Object msg) {
return writeAndFlush(msg, newPromise()); return writeAndFlush(msg, newPromise());
} }
@Override @Override
public ChannelPromise newPromise() { public Promise<Void> newPromise() {
return channel.newPromise(); return channel.newPromise();
} }
@Override @Override
public ChannelFuture newSucceededFuture() { public Future<Void> newSucceededFuture() {
return channel.newSucceededFuture(); return channel.newSucceededFuture();
} }
@Override @Override
public ChannelFuture newFailedFuture(Throwable cause) { public Future<Void> newFailedFuture(Throwable cause) {
return channel.newFailedFuture(cause); return channel.newFailedFuture(cause);
} }
} }

View File

@ -22,11 +22,11 @@ import io.netty.buffer.EmptyByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.GlobalEventExecutor; import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -34,7 +34,6 @@ import org.junit.jupiter.api.function.Executable;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import java.util.LinkedList; import java.util.LinkedList;
@ -55,10 +54,10 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyShort; import static org.mockito.Mockito.anyShort;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.isA;
import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer; 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.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -66,6 +65,7 @@ import static org.mockito.Mockito.when;
/** /**
* Tests encoding/decoding each HTTP2 frame type. * Tests encoding/decoding each HTTP2 frame type.
*/ */
@SuppressWarnings("unchecked")
public class Http2FrameRoundtripTest { public class Http2FrameRoundtripTest {
private static final byte[] MESSAGE = "hello world".getBytes(UTF_8); private static final byte[] MESSAGE = "hello world".getBytes(UTF_8);
private static final int STREAM_ID = 0x7FFFFFFF; private static final int STREAM_ID = 0x7FFFFFFF;
@ -100,8 +100,8 @@ public class Http2FrameRoundtripTest {
when(ctx.channel()).thenReturn(channel); when(ctx.channel()).thenReturn(channel);
doAnswer((Answer<ByteBuf>) in -> Unpooled.buffer()).when(alloc).buffer(); doAnswer((Answer<ByteBuf>) in -> Unpooled.buffer()).when(alloc).buffer();
doAnswer((Answer<ByteBuf>) in -> Unpooled.buffer((Integer) in.getArguments()[0])).when(alloc).buffer(anyInt()); doAnswer((Answer<ByteBuf>) in -> Unpooled.buffer((Integer) in.getArguments()[0])).when(alloc).buffer(anyInt());
doAnswer((Answer<ChannelPromise>) invocation -> doAnswer((Answer<Promise<Void>>) invocation ->
new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).when(ctx).newPromise(); new DefaultPromise<Void>(GlobalEventExecutor.INSTANCE)).when(ctx).newPromise();
writer = new DefaultHttp2FrameWriter(new DefaultHttp2HeadersEncoder(NEVER_SENSITIVE, newTestEncoder())); writer = new DefaultHttp2FrameWriter(new DefaultHttp2HeadersEncoder(NEVER_SENSITIVE, newTestEncoder()));
reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(false, newTestDecoder())); reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(false, newTestDecoder()));
@ -433,7 +433,7 @@ public class Http2FrameRoundtripTest {
private ByteBuf captureWrites() { private ByteBuf captureWrites() {
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class); ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
verify(ctx, atLeastOnce()).write(captor.capture(), isA(ChannelPromise.class)); verify(ctx, atLeastOnce()).write(captor.capture(), isA(Promise.class));
CompositeByteBuf composite = releaseLater(Unpooled.compositeBuffer()); CompositeByteBuf composite = releaseLater(Unpooled.compositeBuffer());
for (ByteBuf buf : captor.getAllValues()) { for (ByteBuf buf : captor.getAllValues()) {
buf = releaseLater(buf.retain()); buf = releaseLater(buf.retain());

View File

@ -66,7 +66,10 @@ public abstract class Http2MultiplexClientUpgradeTest<C extends Http2FrameCodec>
C codec = newCodec(upgradeHandler); C codec = newCodec(upgradeHandler);
EmbeddedChannel ch = new EmbeddedChannel(codec, newMultiplexer(upgradeHandler)); EmbeddedChannel ch = new EmbeddedChannel(codec, newMultiplexer(upgradeHandler));
ch.eventLoop().submit(() -> {
codec.onHttpClientUpgrade(); codec.onHttpClientUpgrade();
return null;
}).sync();
assertFalse(upgradeHandler.stateOnActive.localSideOpen()); assertFalse(upgradeHandler.stateOnActive.localSideOpen());
assertTrue(upgradeHandler.stateOnActive.remoteSideOpen()); assertTrue(upgradeHandler.stateOnActive.remoteSideOpen());

View File

@ -17,11 +17,8 @@ package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.WriteBufferWaterMark; import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpHeaderNames;
@ -31,12 +28,12 @@ import io.netty.handler.codec.http2.Http2Exception.StreamException;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.hamcrest.CoreMatchers; import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.api.function.Executable;
import org.mockito.ArgumentMatcher;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock; import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
@ -75,6 +72,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@SuppressWarnings("unchecked")
public abstract class Http2MultiplexTest<C extends Http2FrameCodec> { public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
private final Http2Headers request = new DefaultHttp2Headers() private final Http2Headers request = new DefaultHttp2Headers()
.method(HttpMethod.GET.asciiName()).scheme(HttpScheme.HTTPS.name()) .method(HttpMethod.GET.asciiName()).scheme(HttpScheme.HTTPS.name())
@ -153,7 +151,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
assertTrue(childChannel.isActive()); assertTrue(childChannel.isActive());
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq((byte) 99), eqStreamId(childChannel), verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq((byte) 99), eqStreamId(childChannel),
any(Http2Flags.class), any(ByteBuf.class), any(ChannelPromise.class)); any(Http2Flags.class), any(ByteBuf.class), any(Promise.class));
} }
private Http2StreamChannel newInboundStream(int streamId, boolean endStream, final ChannelHandler childHandler) { private Http2StreamChannel newInboundStream(int streamId, boolean endStream, final ChannelHandler childHandler) {
@ -561,18 +559,18 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() { public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() {
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
any(Http2Headers.class), anyInt(), anyBoolean(), any(Http2Headers.class), anyInt(), anyBoolean(),
any(ChannelPromise.class))).thenAnswer(new Answer<ChannelFuture>() { any(Promise.class))).thenAnswer(new Answer<Future<Void>>() {
private boolean headersWritten; private boolean headersWritten;
@Override @Override
public ChannelFuture answer(InvocationOnMock invocationOnMock) { public Future<Void> answer(InvocationOnMock invocationOnMock) {
// We want to fail to write the first headers frame. This is what happens if the connection // We want to fail to write the first headers frame. This is what happens if the connection
// refuses to allocate a new stream due to having received a GOAWAY. // refuses to allocate a new stream due to having received a GOAWAY.
if (!headersWritten) { if (!headersWritten) {
headersWritten = true; headersWritten = true;
return ((ChannelPromise) invocationOnMock.getArgument(5)).setFailure(new Exception("boom")); return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(new Exception("boom"));
} }
return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess(); return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
} }
}); });
@ -636,11 +634,11 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
Http2Headers headers = new DefaultHttp2Headers(); Http2Headers headers = new DefaultHttp2Headers();
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
eq(headers), anyInt(), anyBoolean(), eq(headers), anyInt(), anyBoolean(),
any(ChannelPromise.class))).thenAnswer(invocationOnMock -> { any(Promise.class))).thenAnswer(invocationOnMock -> {
return ((ChannelPromise) invocationOnMock.getArgument(5)).setFailure( return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(
new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed")); new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed"));
}); });
final ChannelFuture future = childChannel.writeAndFlush( final Future<Void> future = childChannel.writeAndFlush(
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers())); new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()));
parentChannel.flush(); parentChannel.flush();
@ -700,12 +698,12 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
Http2Headers headers = new DefaultHttp2Headers(); Http2Headers headers = new DefaultHttp2Headers();
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
eq(headers), anyInt(), anyBoolean(), any(ChannelPromise.class))).thenAnswer(invocationOnMock -> { eq(headers), anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(invocationOnMock -> {
return ((ChannelPromise) invocationOnMock.getArgument(5)).setFailure( return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(
new Http2NoMoreStreamIdsException()); new Http2NoMoreStreamIdsException());
}); });
final ChannelFuture future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers)); final Future<Void> future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
parentChannel.flush(); parentChannel.flush();
assertFalse(childChannel.isActive()); assertFalse(childChannel.isActive());
@ -735,10 +733,10 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
// Create a promise before actually doing the close, because otherwise we would be adding a listener to a future // Create a promise before actually doing the close, because otherwise we would be adding a listener to a future
// that is already completed because we are using EmbeddedChannel which executes code in the JUnit thread. // that is already completed because we are using EmbeddedChannel which executes code in the JUnit thread.
ChannelPromise p = childChannel.newPromise(); Promise<Void> p = childChannel.newPromise();
p.addListener((ChannelFutureListener) future -> { p.addListener(childChannel, (channel, future) -> {
channelOpen.set(future.channel().isOpen()); channelOpen.set(channel.isOpen());
channelActive.set(future.channel().isActive()); channelActive.set(channel.isActive());
}); });
childChannel.close(p).syncUninterruptibly(); childChannel.close(p).syncUninterruptibly();
@ -758,9 +756,9 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
final AtomicBoolean channelOpen = new AtomicBoolean(true); final AtomicBoolean channelOpen = new AtomicBoolean(true);
final AtomicBoolean channelActive = new AtomicBoolean(true); final AtomicBoolean channelActive = new AtomicBoolean(true);
childChannel.closeFuture().addListener((ChannelFutureListener) future -> { childChannel.closeFuture().addListener(childChannel, (channel, future) -> {
channelOpen.set(future.channel().isOpen()); channelOpen.set(channel.isOpen());
channelActive.set(future.channel().isActive()); channelActive.set(channel.isActive());
}); });
childChannel.close().syncUninterruptibly(); childChannel.close().syncUninterruptibly();
@ -771,7 +769,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
@Test @Test
public void channelClosedWhenWriteFutureFails() { public void channelClosedWhenWriteFutureFails() {
final Queue<ChannelPromise> writePromises = new ArrayDeque<ChannelPromise>(); final Queue<Promise<Void>> writePromises = new ArrayDeque<>();
LastInboundHandler inboundHandler = new LastInboundHandler(); LastInboundHandler inboundHandler = new LastInboundHandler();
Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler);
@ -785,20 +783,20 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
Http2Headers headers = new DefaultHttp2Headers(); Http2Headers headers = new DefaultHttp2Headers();
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
eq(headers), anyInt(), anyBoolean(), eq(headers), anyInt(), anyBoolean(),
any(ChannelPromise.class))).thenAnswer(invocationOnMock -> { any(Promise.class))).thenAnswer(invocationOnMock -> {
ChannelPromise promise = invocationOnMock.getArgument(5); Promise<Void> promise = invocationOnMock.getArgument(5);
writePromises.offer(promise); writePromises.offer(promise);
return promise; return promise;
}); });
ChannelFuture f = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers)); Future<Void> f = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
assertFalse(f.isDone()); assertFalse(f.isDone());
f.addListener((ChannelFutureListener) future-> { f.addListener(childChannel, (channel, future)-> {
channelOpen.set(future.channel().isOpen()); channelOpen.set(channel.isOpen());
channelActive.set(future.channel().isActive()); channelActive.set(channel.isActive());
}); });
ChannelPromise first = writePromises.poll(); Promise<Void> first = writePromises.poll();
first.setFailure(new ClosedChannelException()); first.setFailure(new ClosedChannelException());
f.awaitUninterruptibly(); f.awaitUninterruptibly();
@ -885,7 +883,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
assertTrue(childChannel.isWritable()); assertTrue(childChannel.isWritable());
assertEquals(bytesBeforeUnwritable, childChannel.bytesBeforeUnwritable()); assertEquals(bytesBeforeUnwritable, childChannel.bytesBeforeUnwritable());
ChannelFuture future = childChannel.writeAndFlush(new DefaultHttp2DataFrame( Future<Void> future = childChannel.writeAndFlush(new DefaultHttp2DataFrame(
Unpooled.buffer().writeZero((int) bytesBeforeUnwritable))); Unpooled.buffer().writeZero((int) bytesBeforeUnwritable)));
assertFalse(childChannel.isWritable()); assertFalse(childChannel.isWritable());
assertTrue(parentChannel.isWritable()); assertTrue(parentChannel.isWritable());
@ -978,7 +976,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler); Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler);
childChannel.unsafe().close(childChannel.newPromise()); childChannel.unsafe().close(childChannel.newPromise());
ChannelPromise promise = childChannel.newPromise(); Promise<Void> promise = childChannel.newPromise();
childChannel.unsafe().close(promise); childChannel.unsafe().close(promise);
promise.syncUninterruptibly(); promise.syncUninterruptibly();
childChannel.closeFuture().syncUninterruptibly(); childChannel.closeFuture().syncUninterruptibly();

View File

@ -19,7 +19,6 @@ import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap; import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
@ -44,8 +43,6 @@ import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.NetUtil; import io.netty.util.NetUtil;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Disabled;
@ -221,8 +218,7 @@ public class Http2MultiplexTransportTest {
if (msg instanceof Http2HeadersFrame && ((Http2HeadersFrame) msg).isEndStream()) { if (msg instanceof Http2HeadersFrame && ((Http2HeadersFrame) msg).isEndStream()) {
executorService.schedule(() -> { executorService.schedule(() -> {
ctx.writeAndFlush(new DefaultHttp2HeadersFrame( ctx.writeAndFlush(new DefaultHttp2HeadersFrame(
new DefaultHttp2Headers(), false)).addListener( new DefaultHttp2Headers(), false)).addListener(future -> {
(ChannelFutureListener) future -> {
ctx.write(new DefaultHttp2DataFrame( ctx.write(new DefaultHttp2DataFrame(
Unpooled.copiedBuffer("Hello World", Unpooled.copiedBuffer("Hello World",
CharsetUtil.US_ASCII), true)); CharsetUtil.US_ASCII), true));
@ -406,14 +402,11 @@ public class Http2MultiplexTransportTest {
latch.countDown(); latch.countDown();
} }
}); });
h2Bootstrap.open().addListener(new FutureListener<Channel>() { h2Bootstrap.open().addListener(future -> {
@Override
public void operationComplete(Future<Channel> future) {
if (future.isSuccess()) { if (future.isSuccess()) {
future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame( future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame(
new DefaultHttp2Headers(), false)); new DefaultHttp2Headers(), false));
} }
}
}); });
} else if (handshakeCompletionEvent.cause() instanceof SSLException) { } else if (handshakeCompletionEvent.cause() instanceof SSLException) {
@ -548,14 +541,11 @@ public class Http2MultiplexTransportTest {
ReferenceCountUtil.release(msg); ReferenceCountUtil.release(msg);
} }
}); });
h2Bootstrap.open().addListener(new FutureListener<Channel>() { h2Bootstrap.open().addListener(future -> {
@Override
public void operationComplete(Future<Channel> future) {
if (future.isSuccess()) { if (future.isSuccess()) {
future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame( future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame(
new DefaultHttp2Headers(), true)); new DefaultHttp2Headers(), true));
} }
}
}); });
} }
} }

View File

@ -21,7 +21,6 @@ import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.EncoderException; import io.netty.handler.codec.EncoderException;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
@ -39,13 +38,14 @@ import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpScheme; import io.netty.handler.codec.http.HttpScheme;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.HttpUtil; import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider; import io.netty.handler.ssl.SslProvider;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.api.function.Executable;
@ -56,10 +56,10 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class Http2StreamFrameToHttpObjectCodecTest { public class Http2StreamFrameToHttpObjectCodecTest {
@ -454,7 +454,7 @@ public class Http2StreamFrameToHttpObjectCodecTest {
EmbeddedChannel ch = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT), EmbeddedChannel ch = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT),
new ChannelHandler() { new ChannelHandler() {
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (msg instanceof Http2StreamFrame) { if (msg instanceof Http2StreamFrame) {
frames.add((Http2StreamFrame) msg); frames.add((Http2StreamFrame) msg);
ctx.write(Unpooled.EMPTY_BUFFER, promise); ctx.write(Unpooled.EMPTY_BUFFER, promise);
@ -884,10 +884,10 @@ public class Http2StreamFrameToHttpObjectCodecTest {
EmbeddedChannel tlsCh = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT), EmbeddedChannel tlsCh = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT),
new ChannelHandler() { new ChannelHandler() {
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (msg instanceof Http2StreamFrame) { if (msg instanceof Http2StreamFrame) {
frames.add((Http2StreamFrame) msg); frames.add((Http2StreamFrame) msg);
promise.setSuccess(); promise.setSuccess(null);
} else { } else {
ctx.write(msg, promise); ctx.write(msg, promise);
} }
@ -897,10 +897,10 @@ public class Http2StreamFrameToHttpObjectCodecTest {
EmbeddedChannel plaintextCh = new EmbeddedChannel( EmbeddedChannel plaintextCh = new EmbeddedChannel(
new ChannelHandler() { new ChannelHandler() {
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (msg instanceof Http2StreamFrame) { if (msg instanceof Http2StreamFrame) {
frames.add((Http2StreamFrame) msg); frames.add((Http2StreamFrame) msg);
promise.setSuccess(); promise.setSuccess(null);
} else { } else {
ctx.write(msg, promise); ctx.write(msg, promise);
} }

View File

@ -19,19 +19,13 @@ import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator; import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.channel.DefaultChannelPromise;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener; import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.ImmediateEventExecutor;
import junit.framework.AssertionFailedError;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import java.util.Random; import java.util.Random;
@ -56,6 +50,7 @@ import static org.mockito.Mockito.when;
/** /**
* Utilities for the integration tests. * Utilities for the integration tests.
*/ */
@SuppressWarnings("unchecked")
public final class Http2TestUtil { public final class Http2TestUtil {
/** /**
* Interface that allows for running a operation that throws a {@link Http2Exception}. * Interface that allows for running a operation that throws a {@link Http2Exception}.
@ -367,55 +362,55 @@ public final class Http2TestUtil {
when(frameWriter.configuration()).thenReturn(configuration); when(frameWriter.configuration()).thenReturn(configuration);
when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class), when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class),
any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock -> any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
((ChannelPromise) invocationOnMock.getArgument(2)).setSuccess()); ((Promise<Void>) invocationOnMock.getArgument(2)).setSuccess(null));
when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class), any(ChannelPromise.class))) when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
.thenAnswer((Answer<ChannelFuture>) invocationOnMock -> .thenAnswer((Answer<Future<Void>>) invocationOnMock ->
((ChannelPromise) invocationOnMock.getArgument(1)).setSuccess()); ((Promise<Void>) invocationOnMock.getArgument(1)).setSuccess(null));
when(frameWriter.writeGoAway(any(ChannelHandlerContext.class), anyInt(), when(frameWriter.writeGoAway(any(ChannelHandlerContext.class), anyInt(),
anyLong(), any(ByteBuf.class), any(ChannelPromise.class))).thenAnswer(invocationOnMock -> { anyLong(), any(ByteBuf.class), any(Promise.class))).thenAnswer(invocationOnMock -> {
buffers.offer((ByteBuf) invocationOnMock.getArgument(3)); buffers.offer((ByteBuf) invocationOnMock.getArgument(3));
return ((ChannelPromise) invocationOnMock.getArgument(4)).setSuccess(); return ((Promise<Void>) invocationOnMock.getArgument(4)).setSuccess(null);
}); });
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(), when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
anyBoolean(), any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock -> anyBoolean(), any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess()); ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null));
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock -> any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
((ChannelPromise) invocationOnMock.getArgument(8)).setSuccess()); ((Promise<Void>) invocationOnMock.getArgument(8)).setSuccess(null));
when(frameWriter.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), when(frameWriter.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(),
anyBoolean(), any(ChannelPromise.class))).thenAnswer(invocationOnMock -> { anyBoolean(), any(Promise.class))).thenAnswer(invocationOnMock -> {
buffers.offer((ByteBuf) invocationOnMock.getArgument(2)); buffers.offer((ByteBuf) invocationOnMock.getArgument(2));
return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess(); return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
}); });
when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(), when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(),
anyLong(), any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock -> anyLong(), any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
((ChannelPromise) invocationOnMock.getArgument(3)).setSuccess()); ((Promise<Void>) invocationOnMock.getArgument(3)).setSuccess(null));
when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(), when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
any(ChannelPromise.class))).then((Answer<ChannelFuture>) invocationOnMock -> any(Promise.class))).then((Answer<Future<Void>>) invocationOnMock ->
((ChannelPromise) invocationOnMock.getArgument(3)).setSuccess()); ((Promise<Void>) invocationOnMock.getArgument(3)).setSuccess(null));
when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class), when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class),
anyInt(), anyChannelPromise())).thenAnswer((Answer<ChannelFuture>) invocationOnMock -> anyInt(), anyChannelPromise())).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess()); ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null));
when(frameWriter.writeFrame(any(ChannelHandlerContext.class), anyByte(), anyInt(), any(Http2Flags.class), when(frameWriter.writeFrame(any(ChannelHandlerContext.class), anyByte(), anyInt(), any(Http2Flags.class),
any(ByteBuf.class), anyChannelPromise())).thenAnswer(invocationOnMock -> { any(ByteBuf.class), anyChannelPromise())).thenAnswer(invocationOnMock -> {
buffers.offer((ByteBuf) invocationOnMock.getArgument(4)); buffers.offer((ByteBuf) invocationOnMock.getArgument(4));
return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess(); return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
}); });
return frameWriter; return frameWriter;
} }
static ChannelPromise anyChannelPromise() { static Promise<Void> anyChannelPromise() {
return any(ChannelPromise.class); return any(Promise.class);
} }
static Http2Settings anyHttp2Settings() { static Http2Settings anyHttp2Settings() {

View File

@ -19,12 +19,10 @@ import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.MultithreadEventLoopGroup; import io.netty.channel.MultithreadEventLoopGroup;
import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalAddress;
import io.netty.channel.local.LocalChannel; import io.netty.channel.local.LocalChannel;
@ -43,6 +41,7 @@ import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown; import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -146,7 +145,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.add(new AsciiString("foo"), new AsciiString("goo2")) .add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2")); .add(new AsciiString("foo2"), new AsciiString("goo2"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -169,7 +168,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.add(new AsciiString("foo"), new AsciiString("goo2")) .add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2")); .add(new AsciiString("foo2"), new AsciiString("goo2"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -190,7 +189,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.add(HttpHeaderNames.COOKIE, "c=d") .add(HttpHeaderNames.COOKIE, "c=d")
.add(HttpHeaderNames.COOKIE, "e=f"); .add(HttpHeaderNames.COOKIE, "e=f");
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -206,7 +205,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.path(new AsciiString("/where?q=now&f=then#section1")) .path(new AsciiString("/where?q=now&f=then#section1"))
.scheme(new AsciiString("http")); .scheme(new AsciiString("http"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -223,7 +222,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.path(new AsciiString("/where%2B0?q=now%2B0&f=then%2B0#section1%2B0")) .path(new AsciiString("/where%2B0?q=now%2B0&f=then%2B0#section1%2B0"))
.scheme(new AsciiString("http")); .scheme(new AsciiString("http"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -241,7 +240,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.path(new AsciiString("/pub/WWW/TheProject.html")) .path(new AsciiString("/pub/WWW/TheProject.html"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("https")); .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("https"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -257,7 +256,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.path(new AsciiString("/pub/WWW/TheProject.html")) .path(new AsciiString("/pub/WWW/TheProject.html"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http")); .authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -271,7 +270,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("CONNECT")).path(new AsciiString("/")) new DefaultHttp2Headers().method(new AsciiString("CONNECT")).path(new AsciiString("/"))
.scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80")); .scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -287,7 +286,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("OPTIONS")).path(new AsciiString("*")) new DefaultHttp2Headers().method(new AsciiString("OPTIONS")).path(new AsciiString("*"))
.scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80")); .scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -305,7 +304,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/")) new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
.scheme(new AsciiString("http")).authority(new AsciiString("[::1]:80")); .scheme(new AsciiString("http")).authority(new AsciiString("[::1]:80"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -321,7 +320,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/")) new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
.scheme(new AsciiString("http")).authority(new AsciiString("localhost:80")); .scheme(new AsciiString("http")).authority(new AsciiString("localhost:80"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -337,7 +336,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/")) new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
.scheme(new AsciiString("http")).authority(new AsciiString("1.2.3.4:80")); .scheme(new AsciiString("http")).authority(new AsciiString("1.2.3.4:80"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise)); verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
} }
@ -348,8 +347,8 @@ public class HttpToHttp2ConnectionHandlerTest {
final HttpHeaders httpHeaders = request.headers(); final HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5); httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders.set(HttpHeaderNames.HOST, "localhost"); httpHeaders.set(HttpHeaderNames.HOST, "localhost");
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise); Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isDone()); assertTrue(writePromise.isDone());
@ -367,8 +366,8 @@ public class HttpToHttp2ConnectionHandlerTest {
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), -1); httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), -1);
httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http"); httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http");
httpHeaders.set(HttpHeaderNames.HOST, "localhost"); httpHeaders.set(HttpHeaderNames.HOST, "localhost");
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise); Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isDone()); assertTrue(writePromise.isDone());
@ -406,8 +405,8 @@ public class HttpToHttp2ConnectionHandlerTest {
.add(new AsciiString("foo"), new AsciiString("goo")) .add(new AsciiString("foo"), new AsciiString("goo"))
.add(new AsciiString("foo"), new AsciiString("goo2")) .add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2")); .add(new AsciiString("foo2"), new AsciiString("goo2"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise); Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isSuccess()); assertTrue(writePromise.isSuccess());
@ -452,8 +451,8 @@ public class HttpToHttp2ConnectionHandlerTest {
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers() final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
.add(new AsciiString("trailing"), new AsciiString("bar")); .add(new AsciiString("trailing"), new AsciiString("bar"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise); Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isSuccess()); assertTrue(writePromise.isSuccess());
@ -504,12 +503,12 @@ public class HttpToHttp2ConnectionHandlerTest {
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers() final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
.add(new AsciiString("trailing"), new AsciiString("bar")); .add(new AsciiString("trailing"), new AsciiString("bar"));
ChannelPromise writePromise = newPromise(); Promise<Void> writePromise = newPromise();
ChannelFuture writeFuture = clientChannel.write(request, writePromise); Future<Void> writeFuture = clientChannel.write(request, writePromise);
ChannelPromise contentPromise = newPromise(); Promise<Void> contentPromise = newPromise();
ChannelFuture contentFuture = clientChannel.write(httpContent, contentPromise); Future<Void> contentFuture = clientChannel.write(httpContent, contentPromise);
ChannelPromise lastContentPromise = newPromise(); Promise<Void> lastContentPromise = newPromise();
ChannelFuture lastContentFuture = clientChannel.write(lastHttpContent, lastContentPromise); Future<Void> lastContentFuture = clientChannel.write(lastHttpContent, lastContentPromise);
clientChannel.flush(); clientChannel.flush();
@ -598,7 +597,7 @@ public class HttpToHttp2ConnectionHandlerTest {
assertTrue(serverChannelLatch.await(WAIT_TIME_SECONDS, SECONDS)); assertTrue(serverChannelLatch.await(WAIT_TIME_SECONDS, SECONDS));
} }
private void verifyHeadersOnly(Http2Headers expected, ChannelPromise writePromise, ChannelFuture writeFuture) private void verifyHeadersOnly(Http2Headers expected, Promise<Void> writePromise, Future<Void> writeFuture)
throws Exception { throws Exception {
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS)); assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isSuccess()); assertTrue(writePromise.isSuccess());
@ -623,7 +622,7 @@ public class HttpToHttp2ConnectionHandlerTest {
return clientChannel.pipeline().firstContext(); return clientChannel.pipeline().firstContext();
} }
private ChannelPromise newPromise() { private Promise<Void> newPromise() {
return ctx().newPromise(); return ctx().newPromise();
} }
} }

View File

@ -23,7 +23,6 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.MultithreadEventLoopGroup; import io.netty.channel.MultithreadEventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalAddress;
@ -44,6 +43,7 @@ import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AsciiString; import io.netty.util.AsciiString;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future; import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -732,7 +732,7 @@ public class InboundHttp2ToHttpAdapterTest {
return clientChannel.pipeline().firstContext(); return clientChannel.pipeline().firstContext();
} }
private ChannelPromise newPromiseClient() { private Promise<Void> newPromiseClient() {
return ctxClient().newPromise(); return ctxClient().newPromise();
} }
@ -740,7 +740,7 @@ public class InboundHttp2ToHttpAdapterTest {
return serverConnectedChannel.pipeline().firstContext(); return serverConnectedChannel.pipeline().firstContext();
} }
private ChannelPromise newPromiseServer() { private Promise<Void> newPromiseServer() {
return ctxServer().newPromise(); return ctxServer().newPromise();
} }

View File

@ -15,6 +15,34 @@
package io.netty.handler.codec.http2; package io.netty.handler.codec.http2;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.DefaultMessageSizeEstimator;
import io.netty.handler.codec.http2.StreamBufferingEncoder.Http2GoAwayException;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor;
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;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.verification.VerificationMode;
import java.util.ArrayList;
import java.util.List;
import static io.netty.buffer.Unpooled.EMPTY_BUFFER; import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT; import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT;
@ -30,45 +58,18 @@ import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyLong; import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.anyShort; import static org.mockito.Mockito.anyShort;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.buffer.UnpooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelMetadata;
import io.netty.channel.ChannelPromise;
import io.netty.channel.DefaultChannelPromise;
import io.netty.channel.DefaultMessageSizeEstimator;
import io.netty.handler.codec.http2.StreamBufferingEncoder.Http2GoAwayException;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.ImmediateEventExecutor;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.mockito.verification.VerificationMode;
import java.util.ArrayList;
import java.util.List;
/** /**
* Tests for {@link StreamBufferingEncoder}. * Tests for {@link StreamBufferingEncoder}.
*/ */
@SuppressWarnings("unchecked")
public class StreamBufferingEncoderTest { public class StreamBufferingEncoderTest {
private StreamBufferingEncoder encoder; private StreamBufferingEncoder encoder;
@ -106,16 +107,16 @@ public class StreamBufferingEncoderTest {
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy); when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE); when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(), when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
any(ChannelPromise.class))).thenAnswer(successAnswer()); any(Promise.class))).thenAnswer(successAnswer());
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(ChannelPromise.class))).thenAnswer( when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class))).thenAnswer(
successAnswer()); successAnswer());
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class), when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
any(ChannelPromise.class))) any(Promise.class)))
.thenAnswer(successAnswer()); .thenAnswer(successAnswer());
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
anyInt(), anyBoolean(), any(ChannelPromise.class))).thenAnswer(noopAnswer()); anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(noopAnswer());
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(ChannelPromise.class))) anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Promise.class)))
.thenAnswer(noopAnswer()); .thenAnswer(noopAnswer());
connection = new DefaultHttp2Connection(false); connection = new DefaultHttp2Connection(false);
@ -136,7 +137,7 @@ public class StreamBufferingEncoderTest {
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT); when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
when(executor.inEventLoop()).thenReturn(true); when(executor.inEventLoop()).thenReturn(true);
doAnswer((Answer<ChannelPromise>) invocation -> newPromise()).when(ctx).newPromise(); doAnswer((Answer<Promise<Void>>) invocation -> newPromise()).when(ctx).newPromise();
when(ctx.executor()).thenReturn(executor); when(ctx.executor()).thenReturn(executor);
when(channel.isActive()).thenReturn(false); when(channel.isActive()).thenReturn(false);
when(channel.config()).thenReturn(config); when(channel.config()).thenReturn(config);
@ -172,7 +173,7 @@ public class StreamBufferingEncoderTest {
// Contiguous data writes are coalesced // Contiguous data writes are coalesced
ArgumentCaptor<ByteBuf> bufCaptor = ArgumentCaptor.forClass(ByteBuf.class); ArgumentCaptor<ByteBuf> bufCaptor = ArgumentCaptor.forClass(ByteBuf.class);
verify(writer, times(1)).writeData(any(ChannelHandlerContext.class), eq(3), verify(writer, times(1)).writeData(any(ChannelHandlerContext.class), eq(3),
bufCaptor.capture(), eq(0), eq(false), any(ChannelPromise.class)); bufCaptor.capture(), eq(0), eq(false), any(Promise.class));
assertEquals(expectedBytes, bufCaptor.getValue().readableBytes()); assertEquals(expectedBytes, bufCaptor.getValue().readableBytes());
} }
@ -228,7 +229,7 @@ public class StreamBufferingEncoderTest {
setMaxConcurrentStreams(0); setMaxConcurrentStreams(0);
connection.goAwayReceived(1, 8, EMPTY_BUFFER); connection.goAwayReceived(1, 8, EMPTY_BUFFER);
ChannelPromise promise = newPromise(); Promise<Void> promise = newPromise();
encoderWriteHeaders(3, promise); encoderWriteHeaders(3, promise);
assertEquals(0, encoder.numBufferedStreams()); assertEquals(0, encoder.numBufferedStreams());
assertTrue(promise.isDone()); assertTrue(promise.isDone());
@ -241,7 +242,7 @@ public class StreamBufferingEncoderTest {
setMaxConcurrentStreams(5); setMaxConcurrentStreams(5);
int streamId = 3; int streamId = 3;
List<ChannelFuture> futures = new ArrayList<>(); List<Future<Void>> futures = new ArrayList<>();
for (int i = 0; i < 9; i++) { for (int i = 0; i < 9; i++) {
futures.add(encoderWriteHeaders(streamId, newPromise())); futures.add(encoderWriteHeaders(streamId, newPromise()));
streamId += 2; streamId += 2;
@ -254,7 +255,7 @@ public class StreamBufferingEncoderTest {
assertEquals(5, connection.numActiveStreams()); assertEquals(5, connection.numActiveStreams());
assertEquals(0, encoder.numBufferedStreams()); assertEquals(0, encoder.numBufferedStreams());
int failCount = 0; int failCount = 0;
for (ChannelFuture f : futures) { for (Future<Void> f : futures) {
if (f.cause() != null) { if (f.cause() != null) {
assertTrue(f.cause() instanceof Http2GoAwayException); assertTrue(f.cause() instanceof Http2GoAwayException);
failCount++; failCount++;
@ -269,7 +270,7 @@ public class StreamBufferingEncoderTest {
setMaxConcurrentStreams(1); setMaxConcurrentStreams(1);
encoderWriteHeaders(3, newPromise()); encoderWriteHeaders(3, newPromise());
connection.goAwayReceived(11, 8, EMPTY_BUFFER); connection.goAwayReceived(11, 8, EMPTY_BUFFER);
ChannelFuture f = encoderWriteHeaders(5, newPromise()); Future<Void> f = encoderWriteHeaders(5, newPromise());
assertTrue(f.cause() instanceof Http2GoAwayException); assertTrue(f.cause() instanceof Http2GoAwayException);
assertEquals(0, encoder.numBufferedStreams()); assertEquals(0, encoder.numBufferedStreams());
@ -281,15 +282,15 @@ public class StreamBufferingEncoderTest {
setMaxConcurrentStreams(1); setMaxConcurrentStreams(1);
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(), when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
anyBoolean(), any(ChannelPromise.class))).thenAnswer(successAnswer()); anyBoolean(), any(Promise.class))).thenAnswer(successAnswer());
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(), when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(ChannelPromise.class))).thenAnswer(successAnswer()); anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(successAnswer());
ChannelFuture f1 = encoderWriteHeaders(3, newPromise()); Future<Void> f1 = encoderWriteHeaders(3, newPromise());
assertEquals(0, encoder.numBufferedStreams()); assertEquals(0, encoder.numBufferedStreams());
ChannelFuture f2 = encoderWriteHeaders(5, newPromise()); Future<Void> f2 = encoderWriteHeaders(5, newPromise());
assertEquals(1, encoder.numBufferedStreams()); assertEquals(1, encoder.numBufferedStreams());
ChannelFuture f3 = encoderWriteHeaders(7, newPromise()); Future<Void> f3 = encoderWriteHeaders(7, newPromise());
assertEquals(2, encoder.numBufferedStreams()); assertEquals(2, encoder.numBufferedStreams());
ByteBuf empty = Unpooled.buffer(0); ByteBuf empty = Unpooled.buffer(0);
@ -333,7 +334,7 @@ public class StreamBufferingEncoderTest {
encoderWriteHeaders(3, newPromise()); encoderWriteHeaders(3, newPromise());
assertEquals(1, encoder.numBufferedStreams()); assertEquals(1, encoder.numBufferedStreams());
ChannelPromise rstStreamPromise = newPromise(); Promise<Void> rstStreamPromise = newPromise();
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstStreamPromise); encoder.writeRstStream(ctx, 3, CANCEL.code(), rstStreamPromise);
assertTrue(rstStreamPromise.isSuccess()); assertTrue(rstStreamPromise.isSuccess());
assertEquals(0, encoder.numBufferedStreams()); assertEquals(0, encoder.numBufferedStreams());
@ -457,7 +458,7 @@ public class StreamBufferingEncoderTest {
setMaxConcurrentStreams(0); setMaxConcurrentStreams(0);
// Simulate numeric overflow for the next stream ID. // Simulate numeric overflow for the next stream ID.
ChannelFuture f = encoderWriteHeaders(-1, newPromise()); Future<Void> f = encoderWriteHeaders(-1, newPromise());
// Verify that the write fails. // Verify that the write fails.
assertNotNull(f.cause()); assertNotNull(f.cause());
@ -468,15 +469,15 @@ public class StreamBufferingEncoderTest {
encoder.writeSettingsAck(ctx, newPromise()); encoder.writeSettingsAck(ctx, newPromise());
setMaxConcurrentStreams(0); setMaxConcurrentStreams(0);
ByteBuf data = mock(ByteBuf.class); ByteBuf data = mock(ByteBuf.class);
ChannelFuture f1 = encoderWriteHeaders(3, newPromise()); Future<Void> f1 = encoderWriteHeaders(3, newPromise());
assertEquals(1, encoder.numBufferedStreams()); assertEquals(1, encoder.numBufferedStreams());
ChannelFuture f2 = encoder.writeData(ctx, 3, data, 0, false, newPromise()); Future<Void> f2 = encoder.writeData(ctx, 3, data, 0, false, newPromise());
ChannelPromise rstPromise = mock(ChannelPromise.class); Promise<Void> rstPromise = mock(Promise.class);
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstPromise); encoder.writeRstStream(ctx, 3, CANCEL.code(), rstPromise);
assertEquals(0, encoder.numBufferedStreams()); assertEquals(0, encoder.numBufferedStreams());
verify(rstPromise).setSuccess(); verify(rstPromise).setSuccess(null);
assertTrue(f1.isSuccess()); assertTrue(f1.isSuccess());
assertTrue(f2.isSuccess()); assertTrue(f2.isSuccess());
verify(data).release(); verify(data).release();
@ -487,9 +488,9 @@ public class StreamBufferingEncoderTest {
encoder.writeSettingsAck(ctx, newPromise()); encoder.writeSettingsAck(ctx, newPromise());
connection.local().maxActiveStreams(0); connection.local().maxActiveStreams(0);
ChannelFuture f1 = encoderWriteHeaders(3, newPromise()); Future<Void> f1 = encoderWriteHeaders(3, newPromise());
ChannelFuture f2 = encoderWriteHeaders(5, newPromise()); Future<Void> f2 = encoderWriteHeaders(5, newPromise());
ChannelFuture f3 = encoderWriteHeaders(7, newPromise()); Future<Void> f3 = encoderWriteHeaders(7, newPromise());
encoder.close(); encoder.close();
assertNotNull(f1.cause()); assertNotNull(f1.cause());
@ -502,7 +503,7 @@ public class StreamBufferingEncoderTest {
encoder.writeSettingsAck(ctx, newPromise()); encoder.writeSettingsAck(ctx, newPromise());
encoder.close(); encoder.close();
ChannelFuture f = encoderWriteHeaders(3, newPromise()); Future<Void> f = encoderWriteHeaders(3, newPromise());
assertNotNull(f.cause()); assertNotNull(f.cause());
} }
@ -516,7 +517,7 @@ public class StreamBufferingEncoderTest {
} }
} }
private ChannelFuture encoderWriteHeaders(int streamId, ChannelPromise promise) { private Future<Void> encoderWriteHeaders(int streamId, Promise<Void> promise) {
encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT, encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT,
false, 0, false, promise); false, 0, false, promise);
try { try {
@ -530,28 +531,28 @@ public class StreamBufferingEncoderTest {
private void writeVerifyWriteHeaders(VerificationMode mode, int streamId) { private void writeVerifyWriteHeaders(VerificationMode mode, int streamId) {
verify(writer, mode).writeHeaders(eq(ctx), eq(streamId), any(Http2Headers.class), eq(0), verify(writer, mode).writeHeaders(eq(ctx), eq(streamId), any(Http2Headers.class), eq(0),
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0),
eq(false), any(ChannelPromise.class)); eq(false), any(Promise.class));
} }
private Answer<ChannelFuture> successAnswer() { private static Answer<Future<Void>> successAnswer() {
return invocation -> { return invocation -> {
for (Object a : invocation.getArguments()) { for (Object a : invocation.getArguments()) {
ReferenceCountUtil.safeRelease(a); ReferenceCountUtil.safeRelease(a);
} }
ChannelPromise future = newPromise(); Promise<Void> future = newPromise();
future.setSuccess(); future.setSuccess(null);
return future; return future;
}; };
} }
private Answer<ChannelFuture> noopAnswer() { private static Answer<Future<Void>> noopAnswer() {
return new Answer<ChannelFuture>() { return new Answer<Future<Void>>() {
@Override @Override
public ChannelFuture answer(InvocationOnMock invocation) throws Throwable { public Future<Void> answer(InvocationOnMock invocation) throws Throwable {
for (Object a : invocation.getArguments()) { for (Object a : invocation.getArguments()) {
if (a instanceof ChannelPromise) { if (a instanceof Promise) {
return (ChannelFuture) a; return (Future<Void>) a;
} }
} }
return newPromise(); return newPromise();
@ -559,8 +560,8 @@ public class StreamBufferingEncoderTest {
}; };
} }
private ChannelPromise newPromise() { private static Promise<Void> newPromise() {
return new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE); return new DefaultPromise<Void>(ImmediateEventExecutor.INSTANCE);
} }
private static ByteBuf data() { private static ByteBuf data() {

View File

@ -18,17 +18,17 @@ package io.netty.handler.codec.memcache.binary;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.CombinedChannelDuplexHandler; import io.netty.channel.CombinedChannelDuplexHandler;
import io.netty.handler.codec.PrematureChannelClosureException; import io.netty.handler.codec.PrematureChannelClosureException;
import io.netty.handler.codec.memcache.LastMemcacheContent; import io.netty.handler.codec.memcache.LastMemcacheContent;
import io.netty.util.Attribute; import io.netty.util.Attribute;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi; import io.netty.util.internal.UnstableApi;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -108,52 +108,63 @@ public final class BinaryMemcacheClientCodec extends
return ctx.channel(); return ctx.channel();
} }
@Override
public EventExecutor executor() { public EventExecutor executor() {
return ctx.executor(); return ctx.executor();
} }
@Override
public String name() { public String name() {
return ctx.name(); return ctx.name();
} }
@Override
public ChannelHandler handler() { public ChannelHandler handler() {
return ctx.handler(); return ctx.handler();
} }
@Override
public boolean isRemoved() { public boolean isRemoved() {
return ctx.isRemoved(); return ctx.isRemoved();
} }
@Override
public ChannelHandlerContext fireChannelRegistered() { public ChannelHandlerContext fireChannelRegistered() {
ctx.fireChannelRegistered(); ctx.fireChannelRegistered();
return this; return this;
} }
@Override
public ChannelHandlerContext fireChannelUnregistered() { public ChannelHandlerContext fireChannelUnregistered() {
ctx.fireChannelUnregistered(); ctx.fireChannelUnregistered();
return this; return this;
} }
@Override
public ChannelHandlerContext fireChannelActive() { public ChannelHandlerContext fireChannelActive() {
ctx.fireChannelActive(); ctx.fireChannelActive();
return this; return this;
} }
@Override
public ChannelHandlerContext fireChannelInactive() { public ChannelHandlerContext fireChannelInactive() {
ctx.fireChannelInactive(); ctx.fireChannelInactive();
return this; return this;
} }
@Override
public ChannelHandlerContext fireExceptionCaught(Throwable cause) { public ChannelHandlerContext fireExceptionCaught(Throwable cause) {
ctx.fireExceptionCaught(cause); ctx.fireExceptionCaught(cause);
return this; return this;
} }
@Override
public ChannelHandlerContext fireUserEventTriggered(Object evt) { public ChannelHandlerContext fireUserEventTriggered(Object evt) {
ctx.fireUserEventTriggered(evt); ctx.fireUserEventTriggered(evt);
return this; return this;
} }
@Override
public ChannelHandlerContext fireChannelRead(Object msg) { public ChannelHandlerContext fireChannelRead(Object msg) {
if (failOnMissingResponse && msg instanceof LastMemcacheContent) { if (failOnMissingResponse && msg instanceof LastMemcacheContent) {
requestResponseCounter.decrementAndGet(); requestResponseCounter.decrementAndGet();
@ -162,128 +173,155 @@ public final class BinaryMemcacheClientCodec extends
return this; return this;
} }
@Override
public ChannelHandlerContext fireChannelReadComplete() { public ChannelHandlerContext fireChannelReadComplete() {
ctx.fireChannelReadComplete(); ctx.fireChannelReadComplete();
return this; return this;
} }
@Override
public ChannelHandlerContext fireChannelWritabilityChanged() { public ChannelHandlerContext fireChannelWritabilityChanged() {
ctx.fireChannelWritabilityChanged(); ctx.fireChannelWritabilityChanged();
return this; return this;
} }
@Override
public ChannelHandlerContext read() { public ChannelHandlerContext read() {
ctx.read(); ctx.read();
return this; return this;
} }
@Override
public ChannelHandlerContext flush() { public ChannelHandlerContext flush() {
ctx.flush(); ctx.flush();
return this; return this;
} }
@Override
public ChannelPipeline pipeline() { public ChannelPipeline pipeline() {
return ctx.pipeline(); return ctx.pipeline();
} }
@Override
public ByteBufAllocator alloc() { public ByteBufAllocator alloc() {
return ctx.alloc(); return ctx.alloc();
} }
@Override
@Deprecated @Deprecated
public <T> Attribute<T> attr(AttributeKey<T> key) { public <T> Attribute<T> attr(AttributeKey<T> key) {
return ctx.attr(key); return ctx.attr(key);
} }
@Override
@Deprecated @Deprecated
public <T> boolean hasAttr(AttributeKey<T> key) { public <T> boolean hasAttr(AttributeKey<T> key) {
return ctx.hasAttr(key); return ctx.hasAttr(key);
} }
public ChannelFuture bind(SocketAddress localAddress) { @Override
public Future<Void> bind(SocketAddress localAddress) {
return ctx.bind(localAddress); return ctx.bind(localAddress);
} }
public ChannelFuture connect(SocketAddress remoteAddress) { @Override
public Future<Void> connect(SocketAddress remoteAddress) {
return ctx.connect(remoteAddress); return ctx.connect(remoteAddress);
} }
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { @Override
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return ctx.connect(remoteAddress, localAddress); return ctx.connect(remoteAddress, localAddress);
} }
public ChannelFuture disconnect() { @Override
public Future<Void> disconnect() {
return ctx.disconnect(); return ctx.disconnect();
} }
public ChannelFuture close() { @Override
public Future<Void> close() {
return ctx.close(); return ctx.close();
} }
public ChannelFuture deregister() { @Override
public Future<Void> deregister() {
return ctx.deregister(); return ctx.deregister();
} }
@Override @Override
public ChannelFuture register() { public Future<Void> register() {
return ctx.register(); return ctx.register();
} }
@Override @Override
public ChannelFuture register(ChannelPromise promise) { public Future<Void> register(Promise<Void> promise) {
return ctx.register(promise); return ctx.register(promise);
} }
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { @Override
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return ctx.bind(localAddress, promise); return ctx.bind(localAddress, promise);
} }
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { @Override
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, promise); return ctx.connect(remoteAddress, promise);
} }
public ChannelFuture connect( @Override
SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { public Future<Void> connect(
SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, localAddress, promise); return ctx.connect(remoteAddress, localAddress, promise);
} }
public ChannelFuture disconnect(ChannelPromise promise) { @Override
public Future<Void> disconnect(Promise<Void> promise) {
return ctx.disconnect(promise); return ctx.disconnect(promise);
} }
public ChannelFuture close(ChannelPromise promise) { @Override
public Future<Void> close(Promise<Void> promise) {
return ctx.close(promise); return ctx.close(promise);
} }
public ChannelFuture deregister(ChannelPromise promise) { @Override
public Future<Void> deregister(Promise<Void> promise) {
return ctx.deregister(promise); return ctx.deregister(promise);
} }
public ChannelFuture write(Object msg) { @Override
public Future<Void> write(Object msg) {
return ctx.write(msg); return ctx.write(msg);
} }
public ChannelFuture write(Object msg, ChannelPromise promise) { @Override
public Future<Void> write(Object msg, Promise<Void> promise) {
return ctx.write(msg, promise); return ctx.write(msg, promise);
} }
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { @Override
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return ctx.writeAndFlush(msg, promise); return ctx.writeAndFlush(msg, promise);
} }
public ChannelFuture writeAndFlush(Object msg) { @Override
public Future<Void> writeAndFlush(Object msg) {
return ctx.writeAndFlush(msg); return ctx.writeAndFlush(msg);
} }
public ChannelPromise newPromise() { @Override
public Promise<Void> newPromise() {
return ctx.newPromise(); return ctx.newPromise();
} }
public ChannelFuture newSucceededFuture() { @Override
public Future<Void> newSucceededFuture() {
return ctx.newSucceededFuture(); return ctx.newSucceededFuture();
} }
public ChannelFuture newFailedFuture(Throwable cause) { @Override
public Future<Void> newFailedFuture(Throwable cause) {
return ctx.newFailedFuture(cause); return ctx.newFailedFuture(cause);
} }
}; };

View File

@ -18,7 +18,7 @@ package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise; import io.netty.util.concurrent.Promise;
import io.netty.util.internal.TypeParameterMatcher; import io.netty.util.internal.TypeParameterMatcher;
/** /**
@ -102,7 +102,7 @@ public abstract class ByteToMessageCodec<I> extends ChannelHandlerAdapter {
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
encoder.write(ctx, msg, promise); encoder.write(ctx, msg, promise);
} }

View File

@ -15,31 +15,30 @@
*/ */
package io.netty.handler.codec; package io.netty.handler.codec;
import static io.netty.util.internal.ObjectUtil.checkPositive;
import static java.util.Objects.requireNonNull;
import io.netty.buffer.ByteBufConvertible;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufConvertible;
import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandler;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelConfig;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.socket.ChannelInputShutdownEvent; import io.netty.channel.socket.ChannelInputShutdownEvent;
import io.netty.util.Attribute; import io.netty.util.Attribute;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.net.SocketAddress; import java.net.SocketAddress;
import static io.netty.util.internal.ObjectUtil.checkPositive;
import static java.lang.Integer.MAX_VALUE; import static java.lang.Integer.MAX_VALUE;
import static java.util.Objects.requireNonNull;
/** /**
* {@link ChannelHandler} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an * {@link ChannelHandler} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an
@ -137,7 +136,7 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
composite.capacity(composite.writerIndex()); composite.capacity(composite.writerIndex());
} }
} else { } else {
composite = alloc.compositeBuffer(Integer.MAX_VALUE).addFlattenedComponents(true, cumulation); composite = alloc.compositeBuffer(MAX_VALUE).addFlattenedComponents(true, cumulation);
} }
composite.addFlattenedComponents(true, in); composite.addFlattenedComponents(true, in);
in = null; in = null;
@ -235,8 +234,8 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
@Override @Override
public final void handlerAdded(ChannelHandlerContext ctx) throws Exception { public final void handlerAdded(ChannelHandlerContext ctx) throws Exception {
this.context = new ByteToMessageDecoderContext(ctx); context = new ByteToMessageDecoderContext(ctx);
handlerAdded0(this.context); handlerAdded0(context);
} }
protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception { protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
@ -257,7 +256,7 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
buf.release(); buf.release();
} }
} }
handlerRemoved0(this.context); handlerRemoved0(context);
} }
/** /**
@ -635,107 +634,107 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
} }
@Override @Override
public ChannelFuture bind(SocketAddress localAddress) { public Future<Void> bind(SocketAddress localAddress) {
return ctx.bind(localAddress); return ctx.bind(localAddress);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress) { public Future<Void> connect(SocketAddress remoteAddress) {
return ctx.connect(remoteAddress); return ctx.connect(remoteAddress);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) { public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return ctx.connect(remoteAddress, localAddress); return ctx.connect(remoteAddress, localAddress);
} }
@Override @Override
public ChannelFuture disconnect() { public Future<Void> disconnect() {
return ctx.disconnect(); return ctx.disconnect();
} }
@Override @Override
public ChannelFuture close() { public Future<Void> close() {
return ctx.close(); return ctx.close();
} }
@Override @Override
public ChannelFuture deregister() { public Future<Void> deregister() {
return ctx.deregister(); return ctx.deregister();
} }
@Override @Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) { public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return ctx.bind(localAddress, promise); return ctx.bind(localAddress, promise);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, promise); return ctx.connect(remoteAddress, promise);
} }
@Override @Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, localAddress, promise); return ctx.connect(remoteAddress, localAddress, promise);
} }
@Override @Override
public ChannelFuture disconnect(ChannelPromise promise) { public Future<Void> disconnect(Promise<Void> promise) {
return ctx.disconnect(promise); return ctx.disconnect(promise);
} }
@Override @Override
public ChannelFuture close(ChannelPromise promise) { public Future<Void> close(Promise<Void> promise) {
return ctx.close(promise); return ctx.close(promise);
} }
@Override @Override
public ChannelFuture register() { public Future<Void> register() {
return ctx.register(); return ctx.register();
} }
@Override @Override
public ChannelFuture register(ChannelPromise promise) { public Future<Void> register(Promise<Void> promise) {
return ctx.register(promise); return ctx.register(promise);
} }
@Override @Override
public ChannelFuture deregister(ChannelPromise promise) { public Future<Void> deregister(Promise<Void> promise) {
return ctx.deregister(promise); return ctx.deregister(promise);
} }
@Override @Override
public ChannelFuture write(Object msg) { public Future<Void> write(Object msg) {
return ctx.write(msg); return ctx.write(msg);
} }
@Override @Override
public ChannelFuture write(Object msg, ChannelPromise promise) { public Future<Void> write(Object msg, Promise<Void> promise) {
return ctx.write(msg, promise); return ctx.write(msg, promise);
} }
@Override @Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) { public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return ctx.writeAndFlush(msg, promise); return ctx.writeAndFlush(msg, promise);
} }
@Override @Override
public ChannelFuture writeAndFlush(Object msg) { public Future<Void> writeAndFlush(Object msg) {
return ctx.writeAndFlush(msg); return ctx.writeAndFlush(msg);
} }
@Override @Override
public ChannelPromise newPromise() { public Promise<Void> newPromise() {
return ctx.newPromise(); return ctx.newPromise();
} }
@Override @Override
public ChannelFuture newSucceededFuture() { public Future<Void> newSucceededFuture() {
return ctx.newSucceededFuture(); return ctx.newSucceededFuture();
} }
@Override @Override
public ChannelFuture newFailedFuture(Throwable cause) { public Future<Void> newFailedFuture(Throwable cause) {
return ctx.newFailedFuture(cause); return ctx.newFailedFuture(cause);
} }
} }

View File

@ -15,21 +15,21 @@
*/ */
package io.netty.handler.codec; package io.netty.handler.codec;
import static java.util.Objects.requireNonNull;
import io.netty.buffer.ByteBufConvertible; import io.netty.buffer.ByteBufConvertible;
import io.netty.channel.AddressedEnvelope; import io.netty.channel.AddressedEnvelope;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.protobuf.ProtobufEncoder; import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.util.List; import java.util.List;
import static java.util.Objects.requireNonNull;
/** /**
* An encoder that encodes the content in {@link AddressedEnvelope} to {@link DatagramPacket} using * An encoder that encodes the content in {@link AddressedEnvelope} to {@link DatagramPacket} using
* the specified message encoder. E.g., * the specified message encoder. E.g.,
@ -91,29 +91,29 @@ public class DatagramPacketEncoder<M> extends MessageToMessageEncoder<AddressedE
} }
@Override @Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) { public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
encoder.bind(ctx, localAddress, promise); encoder.bind(ctx, localAddress, promise);
} }
@Override @Override
public void connect( public void connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress, ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, ChannelPromise promise) { SocketAddress localAddress, Promise<Void> promise) {
encoder.connect(ctx, remoteAddress, localAddress, promise); encoder.connect(ctx, remoteAddress, localAddress, promise);
} }
@Override @Override
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) { public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
encoder.disconnect(ctx, promise); encoder.disconnect(ctx, promise);
} }
@Override @Override
public void close(ChannelHandlerContext ctx, ChannelPromise promise) { public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
encoder.close(ctx, promise); encoder.close(ctx, promise);
} }
@Override @Override
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) { public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
encoder.deregister(ctx, promise); encoder.deregister(ctx, promise);
} }

View File

@ -18,12 +18,13 @@ package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder; import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.CompositeByteBuf;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureContextListener;
import static io.netty.buffer.Unpooled.EMPTY_BUFFER; import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero; import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
@ -58,7 +59,7 @@ public abstract class MessageAggregator<I, S, C extends ByteBufHolder, O extends
private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS; private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
private ChannelHandlerContext ctx; private ChannelHandlerContext ctx;
private ChannelFutureListener continueResponseWriteListener; private FutureContextListener<ChannelHandlerContext, Void> continueResponseWriteListener;
private boolean aggregating; private boolean aggregating;
@ -221,11 +222,11 @@ public abstract class MessageAggregator<I, S, C extends ByteBufHolder, O extends
Object continueResponse = newContinueResponse(m, maxContentLength, ctx.pipeline()); Object continueResponse = newContinueResponse(m, maxContentLength, ctx.pipeline());
if (continueResponse != null) { if (continueResponse != null) {
// Cache the write listener for reuse. // Cache the write listener for reuse.
ChannelFutureListener listener = continueResponseWriteListener; FutureContextListener<ChannelHandlerContext, Void> listener = continueResponseWriteListener;
if (listener == null) { if (listener == null) {
continueResponseWriteListener = listener = future -> { continueResponseWriteListener = listener = (context, future) -> {
if (!future.isSuccess()) { if (!future.isSuccess()) {
ctx.fireExceptionCaught(future.cause()); context.fireExceptionCaught(future.cause());
} }
}; };
} }
@ -234,10 +235,10 @@ public abstract class MessageAggregator<I, S, C extends ByteBufHolder, O extends
boolean closeAfterWrite = closeAfterContinueResponse(continueResponse); boolean closeAfterWrite = closeAfterContinueResponse(continueResponse);
handlingOversizedMessage = ignoreContentAfterContinueResponse(continueResponse); handlingOversizedMessage = ignoreContentAfterContinueResponse(continueResponse);
final ChannelFuture future = ctx.writeAndFlush(continueResponse).addListener(listener); Future<Void> future = ctx.writeAndFlush(continueResponse).addListener(ctx, listener);
if (closeAfterWrite) { if (closeAfterWrite) {
future.addListener(ChannelFutureListener.CLOSE); future.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
return; return;
} }
if (handlingOversizedMessage) { if (handlingOversizedMessage) {

View File

@ -21,8 +21,8 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.TypeParameterMatcher; import io.netty.util.internal.TypeParameterMatcher;
@ -96,7 +96,7 @@ public abstract class MessageToByteEncoder<I> extends ChannelHandlerAdapter {
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
ByteBuf buf = null; ByteBuf buf = null;
try { try {
if (acceptOutboundMessage(msg)) { if (acceptOutboundMessage(msg)) {

View File

@ -17,8 +17,8 @@ package io.netty.handler.codec;
import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.ReferenceCounted; import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.TypeParameterMatcher; import io.netty.util.internal.TypeParameterMatcher;
import java.util.List; import java.util.List;
@ -112,7 +112,7 @@ public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends Cha
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
encoder.write(ctx, msg, promise); encoder.write(ctx, msg, promise);
} }

View File

@ -19,9 +19,9 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.util.ReferenceCountUtil; import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted; import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseCombiner; import io.netty.util.concurrent.PromiseCombiner;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import io.netty.util.internal.TypeParameterMatcher; import io.netty.util.internal.TypeParameterMatcher;
@ -78,7 +78,7 @@ public abstract class MessageToMessageEncoder<I> extends ChannelHandlerAdapter {
} }
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
CodecOutputList out = null; CodecOutputList out = null;
try { try {
if (acceptOutboundMessage(msg)) { if (acceptOutboundMessage(msg)) {
@ -118,7 +118,7 @@ public abstract class MessageToMessageEncoder<I> extends ChannelHandlerAdapter {
} }
} }
private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, ChannelPromise promise) { private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, Promise<Void> promise) {
final PromiseCombiner combiner = new PromiseCombiner(ctx.executor()); final PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
for (int i = 0; i < out.size(); i++) { for (int i = 0; i < out.size(); i++) {
combiner.add(ctx.write(out.getUnsafe(i))); combiner.add(ctx.write(out.getUnsafe(i)));

View File

@ -16,18 +16,22 @@
package io.netty.handler.codec.compression; package io.netty.handler.codec.compression;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier; import io.netty.util.concurrent.PromiseNotifier;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static io.netty.handler.codec.compression.Bzip2Constants.*; import static io.netty.handler.codec.compression.Bzip2Constants.BASE_BLOCK_SIZE;
import static io.netty.handler.codec.compression.Bzip2Constants.END_OF_STREAM_MAGIC_1;
import static io.netty.handler.codec.compression.Bzip2Constants.END_OF_STREAM_MAGIC_2;
import static io.netty.handler.codec.compression.Bzip2Constants.MAGIC_NUMBER;
import static io.netty.handler.codec.compression.Bzip2Constants.MAX_BLOCK_SIZE;
import static io.netty.handler.codec.compression.Bzip2Constants.MIN_BLOCK_SIZE;
/** /**
* Compresses a {@link ByteBuf} using the Bzip2 algorithm. * Compresses a {@link ByteBuf} using the Bzip2 algorithm.
@ -167,25 +171,25 @@ public class Bzip2Encoder extends MessageToByteEncoder<ByteBuf> {
/** /**
* Close this {@link Bzip2Encoder} and so finish the encoding. * Close this {@link Bzip2Encoder} and so finish the encoding.
* *
* The returned {@link ChannelFuture} will be notified once the operation completes. * The returned {@link Future} will be notified once the operation completes.
*/ */
public ChannelFuture close() { public Future<Void> close() {
return close(ctx().newPromise()); return close(ctx().newPromise());
} }
/** /**
* Close this {@link Bzip2Encoder} and so finish the encoding. * Close this {@link Bzip2Encoder} and so finish the encoding.
* The given {@link ChannelFuture} will be notified once the operation * The given {@link Future} will be notified once the operation
* completes and will also be returned. * completes and will also be returned.
*/ */
public ChannelFuture close(final ChannelPromise promise) { public Future<Void> close(Promise<Void> promise) {
ChannelHandlerContext ctx = ctx(); ChannelHandlerContext ctx = ctx();
EventExecutor executor = ctx.executor(); EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) { if (executor.inEventLoop()) {
return finishEncode(ctx, promise); return finishEncode(ctx, promise);
} else { } else {
executor.execute(() -> { executor.execute(() -> {
ChannelFuture f = finishEncode(ctx(), promise); Future<Void> f = finishEncode(ctx(), promise);
PromiseNotifier.cascade(f, promise); PromiseNotifier.cascade(f, promise);
}); });
return promise; return promise;
@ -193,9 +197,9 @@ public class Bzip2Encoder extends MessageToByteEncoder<ByteBuf> {
} }
@Override @Override
public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) { public void close(final ChannelHandlerContext ctx, Promise<Void> promise) {
ChannelFuture f = finishEncode(ctx, ctx.newPromise()); Future<Void> f = finishEncode(ctx, ctx.newPromise());
f.addListener((ChannelFutureListener) f1 -> ctx.close(promise)); f.addListener(f1 -> ctx.close(promise));
if (!f.isDone()) { if (!f.isDone()) {
// Ensure the channel is closed even if the write operation completes in time. // Ensure the channel is closed even if the write operation completes in time.
@ -205,9 +209,9 @@ public class Bzip2Encoder extends MessageToByteEncoder<ByteBuf> {
} }
} }
private ChannelFuture finishEncode(final ChannelHandlerContext ctx, ChannelPromise promise) { private Future<Void> finishEncode(final ChannelHandlerContext ctx, Promise<Void> promise) {
if (finished) { if (finished) {
promise.setSuccess(); promise.setSuccess(null);
return promise; return promise;
} }
finished = true; finished = true;

View File

@ -15,14 +15,11 @@
*/ */
package io.netty.handler.codec.compression; package io.netty.handler.codec.compression;
import static java.util.Objects.requireNonNull;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier; import io.netty.util.concurrent.PromiseNotifier;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@ -30,6 +27,8 @@ import java.util.concurrent.TimeUnit;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import static java.util.Objects.requireNonNull;
/** /**
* Compresses a {@link ByteBuf} using the deflate algorithm. * Compresses a {@link ByteBuf} using the deflate algorithm.
*/ */
@ -156,20 +155,20 @@ public class JdkZlibEncoder extends ZlibEncoder {
} }
@Override @Override
public ChannelFuture close() { public Future<Void> close() {
return close(ctx().newPromise()); return close(ctx().newPromise());
} }
@Override @Override
public ChannelFuture close(final ChannelPromise promise) { public Future<Void> close(Promise<Void> promise) {
ChannelHandlerContext ctx = ctx(); ChannelHandlerContext ctx = ctx();
EventExecutor executor = ctx.executor(); EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) { if (executor.inEventLoop()) {
return finishEncode(ctx, promise); return finishEncode(ctx, promise);
} else { } else {
final ChannelPromise p = ctx.newPromise(); Promise<Void> p = ctx.newPromise();
executor.execute(() -> { executor.execute(() -> {
ChannelFuture f = finishEncode(ctx(), p); Future<Void> f = finishEncode(ctx(), p);
PromiseNotifier.cascade(f, promise); PromiseNotifier.cascade(f, promise);
}); });
return p; return p;
@ -262,9 +261,9 @@ public class JdkZlibEncoder extends ZlibEncoder {
} }
@Override @Override
public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) { public void close(final ChannelHandlerContext ctx, Promise<Void> promise) {
ChannelFuture f = finishEncode(ctx, ctx.newPromise()); Future<Void> f = finishEncode(ctx, ctx.newPromise());
f.addListener((ChannelFutureListener) f1 -> ctx.close(promise)); f.addListener(f1 -> ctx.close(promise));
if (!f.isDone()) { if (!f.isDone()) {
// Ensure the channel is closed even if the write operation completes in time. // Ensure the channel is closed even if the write operation completes in time.
@ -274,9 +273,9 @@ public class JdkZlibEncoder extends ZlibEncoder {
} }
} }
private ChannelFuture finishEncode(final ChannelHandlerContext ctx, ChannelPromise promise) { private Future<Void> finishEncode(final ChannelHandlerContext ctx, Promise<Void> promise) {
if (finished) { if (finished) {
promise.setSuccess(); promise.setSuccess(null);
return promise; return promise;
} }

View File

@ -18,14 +18,13 @@ package io.netty.handler.codec.compression;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.EncoderException; import io.netty.handler.codec.EncoderException;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier; import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.internal.ObjectUtil; import io.netty.util.internal.ObjectUtil;
import net.jpountz.lz4.LZ4Compressor; import net.jpountz.lz4.LZ4Compressor;
@ -49,7 +48,6 @@ import static io.netty.handler.codec.compression.Lz4Constants.MAGIC_NUMBER;
import static io.netty.handler.codec.compression.Lz4Constants.MAX_BLOCK_SIZE; import static io.netty.handler.codec.compression.Lz4Constants.MAX_BLOCK_SIZE;
import static io.netty.handler.codec.compression.Lz4Constants.MIN_BLOCK_SIZE; import static io.netty.handler.codec.compression.Lz4Constants.MIN_BLOCK_SIZE;
import static io.netty.handler.codec.compression.Lz4Constants.TOKEN_OFFSET; import static io.netty.handler.codec.compression.Lz4Constants.TOKEN_OFFSET;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
/** /**
@ -306,9 +304,9 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
ctx.flush(); ctx.flush();
} }
private ChannelFuture finishEncode(final ChannelHandlerContext ctx, ChannelPromise promise) { private Future<Void> finishEncode(final ChannelHandlerContext ctx, Promise<Void> promise) {
if (finished) { if (finished) {
promise.setSuccess(); promise.setSuccess(null);
return promise; return promise;
} }
finished = true; finished = true;
@ -340,25 +338,25 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
/** /**
* Close this {@link Lz4FrameEncoder} and so finish the encoding. * Close this {@link Lz4FrameEncoder} and so finish the encoding.
* *
* The returned {@link ChannelFuture} will be notified once the operation completes. * The returned {@link java.util.concurrent.Future} will be notified once the operation completes.
*/ */
public ChannelFuture close() { public Future<Void> close() {
return close(ctx().newPromise()); return close(ctx().newPromise());
} }
/** /**
* Close this {@link Lz4FrameEncoder} and so finish the encoding. * Close this {@link Lz4FrameEncoder} and so finish the encoding.
* The given {@link ChannelFuture} will be notified once the operation * The given {@link java.util.concurrent.Future} will be notified once the operation
* completes and will also be returned. * completes and will also be returned.
*/ */
public ChannelFuture close(final ChannelPromise promise) { public Future<Void> close(Promise<Void> promise) {
ChannelHandlerContext ctx = ctx(); ChannelHandlerContext ctx = ctx();
EventExecutor executor = ctx.executor(); EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) { if (executor.inEventLoop()) {
return finishEncode(ctx, promise); return finishEncode(ctx, promise);
} else { } else {
executor.execute(() -> { executor.execute(() -> {
ChannelFuture f = finishEncode(ctx(), promise); Future<Void> f = finishEncode(ctx(), promise);
PromiseNotifier.cascade(f, promise); PromiseNotifier.cascade(f, promise);
}); });
return promise; return promise;
@ -366,9 +364,9 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
} }
@Override @Override
public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) { public void close(final ChannelHandlerContext ctx, Promise<Void> promise) {
ChannelFuture f = finishEncode(ctx, ctx.newPromise()); Future<Void> f = finishEncode(ctx, ctx.newPromise());
f.addListener((ChannelFutureListener) f1 -> ctx.close(promise)); f.addListener(f1 -> ctx.close(promise));
if (!f.isDone()) { if (!f.isDone()) {
// Ensure the channel is closed even if the write operation completes in time. // Ensure the channel is closed even if the write operation completes in time.

View File

@ -16,9 +16,9 @@
package io.netty.handler.codec.compression; package io.netty.handler.codec.compression;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
/** /**
* Compresses a {@link ByteBuf} using the deflate algorithm. * Compresses a {@link ByteBuf} using the deflate algorithm.
@ -42,16 +42,16 @@ public abstract class ZlibEncoder extends MessageToByteEncoder<ByteBuf> {
/** /**
* Close this {@link ZlibEncoder} and so finish the encoding. * Close this {@link ZlibEncoder} and so finish the encoding.
* *
* The returned {@link ChannelFuture} will be notified once the * The returned {@link Future} will be notified once the
* operation completes. * operation completes.
*/ */
public abstract ChannelFuture close(); public abstract Future<Void> close();
/** /**
* Close this {@link ZlibEncoder} and so finish the encoding. * Close this {@link ZlibEncoder} and so finish the encoding.
* The given {@link ChannelFuture} will be notified once the operation * The given {@link Future} will be notified once the operation
* completes and will also be returned. * completes and will also be returned.
*/ */
public abstract ChannelFuture close(ChannelPromise promise); public abstract Future<Void> close(Promise<Void> promise);
} }

View File

@ -15,19 +15,19 @@
*/ */
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.List;
public class MessageToMessageEncoderTest { public class MessageToMessageEncoderTest {
/** /**
@ -59,7 +59,7 @@ public class MessageToMessageEncoderTest {
ChannelHandler writeThrower = new ChannelHandler() { ChannelHandler writeThrower = new ChannelHandler() {
private boolean firstWritten; private boolean firstWritten;
@Override @Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (firstWritten) { if (firstWritten) {
ctx.write(msg, promise); ctx.write(msg, promise);
} else { } else {
@ -71,7 +71,7 @@ public class MessageToMessageEncoderTest {
EmbeddedChannel channel = new EmbeddedChannel(writeThrower, encoder); EmbeddedChannel channel = new EmbeddedChannel(writeThrower, encoder);
Object msg = new Object(); Object msg = new Object();
ChannelFuture write = channel.writeAndFlush(msg); Future<Void> write = channel.writeAndFlush(msg);
assertSame(firstWriteException, write.cause()); assertSame(firstWriteException, write.cause());
assertSame(msg, channel.readOutbound()); assertSame(msg, channel.readOutbound());
assertFalse(channel.finish()); assertFalse(channel.finish());

View File

@ -22,7 +22,6 @@ import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufInputStream; import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
@ -278,7 +277,7 @@ public class Lz4FrameEncoderTest extends AbstractEncoderTest {
final int size = 27; final int size = 27;
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(size, size); ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(size, size);
finalClientChannel.writeAndFlush(buf.writerIndex(buf.writerIndex() + size)) finalClientChannel.writeAndFlush(buf.writerIndex(buf.writerIndex() + size))
.addListener((ChannelFutureListener) future -> { .addListener(future -> {
try { try {
writeFailCauseRef.set(future.cause()); writeFailCauseRef.set(future.cause());
} finally { } finally {

View File

@ -82,12 +82,12 @@ public abstract class AbstractEventExecutor extends AbstractExecutorService impl
@Override @Override
public <V> Future<V> newSucceededFuture(V result) { public <V> Future<V> newSucceededFuture(V result) {
return new SucceededFuture<>(this, result); return DefaultPromise.newSuccessfulPromise(this, result);
} }
@Override @Override
public <V> Future<V> newFailedFuture(Throwable cause) { public <V> Future<V> newFailedFuture(Throwable cause) {
return new FailedFuture<>(this, cause); return DefaultPromise.newFailedPromise(this, cause);
} }
@Override @Override
@ -107,12 +107,12 @@ public abstract class AbstractEventExecutor extends AbstractExecutorService impl
@Override @Override
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) { protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return newRunnableFuture(this.newPromise(), runnable, value); return newRunnableFuture(newPromise(), runnable, value);
} }
@Override @Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return newRunnableFuture(this.newPromise(), callable); return newRunnableFuture(newPromise(), callable);
} }
/** /**

View File

@ -38,6 +38,8 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
private static final Comparator<RunnableScheduledFutureNode<?>> SCHEDULED_FUTURE_TASK_COMPARATOR = private static final Comparator<RunnableScheduledFutureNode<?>> SCHEDULED_FUTURE_TASK_COMPARATOR =
Comparable::compareTo; Comparable::compareTo;
private static final RunnableScheduledFutureNode<?>[]
EMPTY_RUNNABLE_SCHEDULED_FUTURE_NODES = new RunnableScheduledFutureNode<?>[0];
private PriorityQueue<RunnableScheduledFutureNode<?>> scheduledTaskQueue; private PriorityQueue<RunnableScheduledFutureNode<?>> scheduledTaskQueue;
@ -88,7 +90,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
} }
final RunnableScheduledFutureNode<?>[] scheduledTasks = final RunnableScheduledFutureNode<?>[] scheduledTasks =
scheduledTaskQueue.toArray(new RunnableScheduledFutureNode<?>[0]); scheduledTaskQueue.toArray(EMPTY_RUNNABLE_SCHEDULED_FUTURE_NODES);
for (RunnableScheduledFutureNode<?> task: scheduledTasks) { for (RunnableScheduledFutureNode<?> task: scheduledTasks) {
task.cancel(false); task.cancel(false);
@ -145,11 +147,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
if (scheduledTaskQueue == null) { if (scheduledTaskQueue == null) {
return null; return null;
} }
RunnableScheduledFutureNode<?> node = scheduledTaskQueue.peek(); return scheduledTaskQueue.peek();
if (node == null) {
return null;
}
return node;
} }
/** /**
@ -236,9 +234,9 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
} }
private <V> void add0(RunnableScheduledFuture<V> task) { private <V> void add0(RunnableScheduledFuture<V> task) {
final RunnableScheduledFutureNode node; final RunnableScheduledFutureNode<V> node;
if (task instanceof RunnableScheduledFutureNode) { if (task instanceof RunnableScheduledFutureNode) {
node = (RunnableScheduledFutureNode) task; node = (RunnableScheduledFutureNode<V>) task;
} else { } else {
node = new DefaultRunnableScheduledFutureNode<V>(task); node = new DefaultRunnableScheduledFutureNode<V>(task);
} }
@ -270,7 +268,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
*/ */
protected <V> RunnableScheduledFuture<V> newScheduledTaskFor( protected <V> RunnableScheduledFuture<V> newScheduledTaskFor(
Callable<V> callable, long deadlineNanos, long period) { Callable<V> callable, long deadlineNanos, long period) {
return newRunnableScheduledFuture(this, this.newPromise(), callable, deadlineNanos, period); return newRunnableScheduledFuture(this, newPromise(), callable, deadlineNanos, period);
} }
interface RunnableScheduledFutureNode<V> extends PriorityQueueNode, RunnableScheduledFuture<V> { } interface RunnableScheduledFutureNode<V> extends PriorityQueueNode, RunnableScheduledFuture<V> { }
@ -304,12 +302,18 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
} }
@Override @Override
public RunnableScheduledFuture<V> addListener( public RunnableScheduledFuture<V> addListener(FutureListener<? super V> listener) {
GenericFutureListener<? extends Future<? super V>> listener) {
future.addListener(listener); future.addListener(listener);
return this; return this;
} }
@Override
public <C> RunnableScheduledFuture<V> addListener(C context,
FutureContextListener<? super C, ? super V> listener) {
future.addListener(context, listener);
return this;
}
@Override @Override
public boolean isPeriodic() { public boolean isPeriodic() {
return future.isPeriodic(); return future.isPeriodic();

View File

@ -1,140 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util.concurrent;
import static java.util.Objects.requireNonNull;
import java.util.concurrent.TimeUnit;
/**
* A skeletal {@link Future} implementation which represents a {@link Future} which has been completed already.
*/
public abstract class CompleteFuture<V> implements Future<V> {
private final EventExecutor executor;
// It is fine to not make this volatile as even if we override the value in there it does not matter as
// DefaultFutureCompletionStage has no state itself and is just a wrapper around this CompletableFuture instance.
private DefaultFutureCompletionStage<V> stage;
/**
* Creates a new instance.
*
* @param executor the {@link EventExecutor} associated with this future
*/
protected CompleteFuture(EventExecutor executor) {
this.executor = executor;
}
/**
* Return the {@link EventExecutor} which is used by this {@link CompleteFuture}.
*/
@Override
public EventExecutor executor() {
return executor;
}
@Override
public Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
requireNonNull(listener, "listener");
DefaultPromise.safeExecute(executor(), () -> DefaultPromise.notifyListener0(this, listener));
return this;
}
@Override
public Future<V> await() throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
return this;
}
@Override
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
return true;
}
@Override
public Future<V> sync() throws InterruptedException {
return this;
}
@Override
public Future<V> syncUninterruptibly() {
return this;
}
@Override
public boolean await(long timeoutMillis) throws InterruptedException {
if (Thread.interrupted()) {
throw new InterruptedException();
}
return true;
}
@Override
public Future<V> awaitUninterruptibly() {
return this;
}
@Override
public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
return true;
}
@Override
public boolean awaitUninterruptibly(long timeoutMillis) {
return true;
}
@Override
public boolean isDone() {
return true;
}
@Override
public boolean isCancellable() {
return false;
}
@Override
public boolean isCancelled() {
return false;
}
/**
* {@inheritDoc}
*
* @param mayInterruptIfRunning this value has no effect in this implementation.
*/
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return false;
}
@Override
public FutureCompletionStage<V> asStage() {
DefaultFutureCompletionStage<V> stageAdapter = stage;
if (stageAdapter == null) {
stage = stageAdapter = new DefaultFutureCompletionStage<>(this);
}
return stageAdapter;
}
}

View File

@ -28,7 +28,7 @@ import java.util.function.Function;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
/** /**
* Wraps a {@link io.netty.util.concurrent.Future} and provides a {@link FutureCompletionStage} implementation * Wraps a {@link Future} and provides a {@link FutureCompletionStage} implementation
* on top of it. * on top of it.
* *
* @param <V> the value type. * @param <V> the value type.
@ -41,7 +41,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
// Just a marker // Just a marker
private static final Executor SAME_AS_FUTURE = task -> { private static final Executor SAME_AS_FUTURE = task -> {
throw new UnsupportedOperationException("Only a marker, should never been called!"); throw new UnsupportedOperationException("Marker executor. Should never be called!");
}; };
private final Future<V> future; private final Future<V> future;
@ -191,7 +191,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
future.addListener(future -> { future.addListener(future -> {
Throwable cause = future.cause(); Throwable cause = future.cause();
if (cause == null) { if (cause == null) {
@SuppressWarnings("unchecked") V value = (V) future.getNow(); V value = future.getNow();
if (executeDirectly(executor)) { if (executeDirectly(executor)) {
thenApplyAsync0(promise, value, fn); thenApplyAsync0(promise, value, fn);
} else { } else {
@ -224,7 +224,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
future.addListener(future -> { future.addListener(future -> {
Throwable cause = future.cause(); Throwable cause = future.cause();
if (cause == null) { if (cause == null) {
@SuppressWarnings("unchecked") V value = (V) future.getNow(); V value = future.getNow();
if (executeDirectly(executor)) { if (executeDirectly(executor)) {
thenAcceptAsync0(promise, value, action); thenAcceptAsync0(promise, value, action);
} else { } else {
@ -333,6 +333,8 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
Promise<U> promise = executor().newPromise(); Promise<U> promise = executor().newPromise();
BiConsumer<V, Throwable> consumer = new AtomicBiConsumer<V, U>(promise) { BiConsumer<V, Throwable> consumer = new AtomicBiConsumer<V, U>(promise) {
private static final long serialVersionUID = -8454630185124276599L;
@Override @Override
protected U apply(V value) { protected U apply(V value) {
return fn.apply(value); return fn.apply(value);
@ -351,6 +353,8 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
Promise<Void> promise = executor().newPromise(); Promise<Void> promise = executor().newPromise();
BiConsumer<V, Throwable> consumer = new AtomicBiConsumer<V, Void>(promise) { BiConsumer<V, Throwable> consumer = new AtomicBiConsumer<V, Void>(promise) {
private static final long serialVersionUID = -8429618092318150682L;
@Override @Override
protected Void apply(V value) { protected Void apply(V value) {
action.accept(value); action.accept(value);
@ -370,6 +374,8 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
Promise<Void> promise = executor().newPromise(); Promise<Void> promise = executor().newPromise();
BiConsumer<Object, Throwable> consumer = new AtomicBiConsumer<Object, Void>(promise) { BiConsumer<Object, Throwable> consumer = new AtomicBiConsumer<Object, Void>(promise) {
private static final long serialVersionUID = 5994110691767731494L;
@Override @Override
protected Void apply(Object value) { protected Void apply(Object value) {
action.run(); action.run();
@ -391,7 +397,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
future.addListener(f -> { future.addListener(f -> {
Throwable cause = f.cause(); Throwable cause = f.cause();
if (cause == null) { if (cause == null) {
@SuppressWarnings("unchecked") V value = (V) f.getNow(); V value = f.getNow();
if (executeDirectly(executor)) { if (executeDirectly(executor)) {
thenComposeAsync0(promise, fn, value); thenComposeAsync0(promise, fn, value);
} else { } else {
@ -430,7 +436,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
future.addListener(f -> { future.addListener(f -> {
Throwable error = f.cause(); Throwable error = f.cause();
if (error == null) { if (error == null) {
@SuppressWarnings("unchecked") V value = (V) f.getNow(); V value = f.getNow();
promise.setSuccess(value); promise.setSuccess(value);
} else { } else {
final V result; final V result;
@ -464,10 +470,9 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
} }
private static <V> void whenCompleteAsync0( private static <V> void whenCompleteAsync0(
Promise<V> promise, Future<? super V> f, BiConsumer<? super V, ? super Throwable> action) { Promise<V> promise, Future<? extends V> f, BiConsumer<? super V, ? super Throwable> action) {
Throwable cause = f.cause(); Throwable cause = f.cause();
V value = cause == null ? f.getNow() : null;
@SuppressWarnings("unchecked") V value = cause == null ? (V) f.getNow() : null;
try { try {
action.accept(value, cause); action.accept(value, cause);
} catch (Throwable error) { } catch (Throwable error) {
@ -532,6 +537,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
private abstract static class AtomicBiConsumer<V, U> extends AtomicReference<Object> private abstract static class AtomicBiConsumer<V, U> extends AtomicReference<Object>
implements BiConsumer<V, Throwable> { implements BiConsumer<V, Throwable> {
private static final long serialVersionUID = 880039612531973027L;
private final Promise<U> promise; private final Promise<U> promise;

View File

@ -15,53 +15,77 @@
*/ */
package io.netty.util.concurrent; package io.netty.util.concurrent;
import io.netty.util.internal.logging.InternalLogger;
import java.util.Arrays; import java.util.Arrays;
import java.util.EventListener;
final class DefaultFutureListeners { final class DefaultFutureListeners {
private Object[] listeners;
private GenericFutureListener<? extends Future<?>>[] listeners;
private int size; private int size;
@SuppressWarnings("unchecked") DefaultFutureListeners() {
DefaultFutureListeners( listeners = new Object[4];
GenericFutureListener<? extends Future<?>> first, GenericFutureListener<? extends Future<?>> second) {
listeners = new GenericFutureListener[2];
listeners[0] = first;
listeners[1] = second;
size = 2;
} }
public void add(GenericFutureListener<? extends Future<?>> l) { public void add(Object listener, Object context) {
GenericFutureListener<? extends Future<?>>[] listeners = this.listeners; Object[] listeners = this.listeners;
final int size = this.size; int index = size << 1;
if (size == listeners.length) { if (index == listeners.length) {
this.listeners = listeners = Arrays.copyOf(listeners, size << 1); this.listeners = listeners = Arrays.copyOf(listeners, listeners.length << 1);
} }
listeners[size] = l; listeners[index] = listener;
this.size = size + 1; listeners[index + 1] = context;
size++;
} }
public void remove(GenericFutureListener<? extends Future<?>> l) { public void remove(EventListener listener) {
final GenericFutureListener<? extends Future<?>>[] listeners = this.listeners; final Object[] listeners = this.listeners;
int size = this.size; for (int i = 0, len = listeners.length; i < len; i += 2) {
for (int i = 0; i < size; i ++) { Object candidateListener = listeners[i]; // With associated context at listeners[i + 1]
if (listeners[i] == l) { if (candidateListener == listener) {
int listenersToMove = size - i - 1; int listenersToMove = len - i - 2;
if (listenersToMove > 0) { if (listenersToMove > 0) {
System.arraycopy(listeners, i + 1, listeners, i, listenersToMove); System.arraycopy(listeners, i + 2, listeners, i, listenersToMove);
} }
listeners[-- size] = null; listeners[len - 2] = null;
this.size = size; listeners[len - 1] = null;
size--;
return; return;
} }
} }
} }
public GenericFutureListener<? extends Future<?>>[] listeners() { @SuppressWarnings("unchecked")
return listeners; public <V> void notifyListeners(DefaultPromise<V> promise, InternalLogger logger) {
int size = this.size;
Object[] listeners = this.listeners;
for (int i = 0, len = size << 1; i < len; i += 2) {
Object listener = listeners[i];
Object context = listeners[i + 1];
try {
// Since a listener could in theory be both a FutureListener and a FutureContextListener,
// we use the presence of a context object to determine which one was meant when the listener
// was added. The context reference will never be null if the FutureContextListener was intended,
// even if the context passed was null. In that case, the reference will point to the
// NULL_CONTEXT, and we have to convert it back to null here.
if (context != null) {
FutureContextListener<Object, V> fcl = (FutureContextListener<Object, V>) listener;
fcl.operationComplete(context == DefaultPromise.NULL_CONTEXT ? null : context, promise);
} else if (listener instanceof FutureListener) {
FutureListener<V> fl = (FutureListener<V>) listener;
fl.operationComplete(promise);
} else if (listener != null) {
logger.warn("Unknown future listener type: {} of type {}", listener, listener.getClass());
} else {
break; // Listeners are always densely packed in the array, so finding a null means we're done.
}
} catch (Throwable t) {
if (logger.isWarnEnabled()) {
String className = listener.getClass().getName();
logger.warn("An exception was thrown by " + className + ".operationComplete()", t);
}
}
} }
public int size() {
return size;
} }
} }

View File

@ -42,6 +42,7 @@ public class DefaultPromise<V> implements Promise<V> {
private static final CauseHolder CANCELLATION_CAUSE_HOLDER = new CauseHolder( private static final CauseHolder CANCELLATION_CAUSE_HOLDER = new CauseHolder(
StacklessCancellationException.newInstance(DefaultPromise.class, "cancel(...)")); StacklessCancellationException.newInstance(DefaultPromise.class, "cancel(...)"));
private static final StackTraceElement[] CANCELLATION_STACK = CANCELLATION_CAUSE_HOLDER.cause.getStackTrace(); private static final StackTraceElement[] CANCELLATION_STACK = CANCELLATION_CAUSE_HOLDER.cause.getStackTrace();
static final Object NULL_CONTEXT = new Object();
private volatile Object result; private volatile Object result;
private final EventExecutor executor; private final EventExecutor executor;
@ -51,9 +52,12 @@ public class DefaultPromise<V> implements Promise<V> {
private DefaultFutureCompletionStage<V> stage; private DefaultFutureCompletionStage<V> stage;
/** /**
* One or more listeners. Can be a {@link GenericFutureListener} or a {@link DefaultFutureListeners}. * One or more listeners. Can be a {@link FutureListener} or a {@link DefaultFutureListeners}.
* If {@code null}, it means either 1) no listeners were added yet or 2) all listeners were notified. * If {@code null}, it means either 1) no listeners were added yet or 2) all listeners were notified.
* * <p>
* Note that if a {@link FutureContextListener} is added, we immediately upgrade to a {@link DefaultFutureListeners}
* as we otherwise wouldn't have room to store the associated context object.
* <p>
* Threading - synchronized(this). We must support adding listeners when there is no EventExecutor. * Threading - synchronized(this). We must support adding listeners when there is no EventExecutor.
*/ */
private Object listeners; private Object listeners;
@ -63,22 +67,61 @@ public class DefaultPromise<V> implements Promise<V> {
private short waiters; private short waiters;
/** /**
* Creates a new instance. * Creates a new unfulfilled promise.
* *
* It is preferable to use {@link EventExecutor#newPromise()} to create a new promise * It is preferable to use {@link EventExecutor#newPromise()} to create a new promise
* *
* @param executor * @param executor
* the {@link EventExecutor} which is used to notify the promise once it is complete. * The {@link EventExecutor} which is used to notify the promise once it is complete.
* It is assumed this executor will protect against {@link StackOverflowError} exceptions. * It is assumed this executor will protect against {@link StackOverflowError} exceptions.
* The executor may be used to avoid {@link StackOverflowError} by executing a {@link Runnable} if the stack * The executor may be used to avoid {@link StackOverflowError} by executing a {@link Runnable} if the stack
* depth exceeds a threshold. * depth exceeds a threshold.
*
*/ */
public DefaultPromise(EventExecutor executor) { public DefaultPromise(EventExecutor executor) {
this.executor = requireNonNull(executor, "executor"); this.executor = requireNonNull(executor, "executor");
stage = new DefaultFutureCompletionStage<>(this); stage = new DefaultFutureCompletionStage<>(this);
} }
/**
* Creates a new promise that has already been completed successfully.
*
* @param executor
* The {@link EventExecutor} which is used to notify the promise once it is complete.
* It is assumed this executor will protect against {@link StackOverflowError} exceptions.
* The executor may be used to avoid {@link StackOverflowError} by executing a {@link Runnable} if the stack
* depth exceeds a threshold.
* @param result The result of the successful promise.
*/
public static <V> Promise<V> newSuccessfulPromise(EventExecutor executor, V result) {
return new DefaultPromise<>(executor, result);
}
/**
* Creates a new promise that has already failed.
*
* @param executor
* The {@link EventExecutor} which is used to notify the promise once it is complete.
* It is assumed this executor will protect against {@link StackOverflowError} exceptions.
* The executor may be used to avoid {@link StackOverflowError} by executing a {@link Runnable} if the stack
* depth exceeds a threshold.
* @param cause The {@link Throwable} that caused the failure of the returned promise.
*/
public static <V> Promise<V> newFailedPromise(EventExecutor executor, Throwable cause) {
return new DefaultPromise<>(cause, executor);
}
private DefaultPromise(EventExecutor executor, Object result) {
this.executor = requireNonNull(executor, "executor");
this.result = result == null? SUCCESS : result;
stage = new DefaultFutureCompletionStage<>(this);
}
private DefaultPromise(Throwable cause, EventExecutor executor) {
this.executor = requireNonNull(executor, "executor");
result = new CauseHolder(requireNonNull(cause, "cause"));
stage = new DefaultFutureCompletionStage<>(this);
}
@Override @Override
public Promise<V> setSuccess(V result) { public Promise<V> setSuccess(V result) {
if (setSuccess0(result)) { if (setSuccess0(result)) {
@ -161,13 +204,10 @@ public class DefaultPromise<V> implements Promise<V> {
} }
@Override @Override
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) { public Promise<V> addListener(FutureListener<? super V> listener) {
requireNonNull(listener, "listener"); requireNonNull(listener, "listener");
synchronized (this) { addListener0(listener, null);
addListener0(listener);
}
if (isDone()) { if (isDone()) {
notifyListeners(); notifyListeners();
} }
@ -176,7 +216,19 @@ public class DefaultPromise<V> implements Promise<V> {
} }
@Override @Override
public Promise<V> await() throws InterruptedException { public <C> Promise<V> addListener(C context, FutureContextListener<? super C, ? super V> listener) {
requireNonNull(listener, "listener");
addListener0(listener, context == null ? NULL_CONTEXT : context);
if (isDone()) {
notifyListeners();
}
return this;
}
@Override
public Future<V> await() throws InterruptedException {
if (isDone()) { if (isDone()) {
return this; return this;
} }
@ -201,7 +253,7 @@ public class DefaultPromise<V> implements Promise<V> {
} }
@Override @Override
public Promise<V> awaitUninterruptibly() { public Future<V> awaitUninterruptibly() {
if (isDone()) { if (isDone()) {
return this; return this;
} }
@ -339,14 +391,14 @@ public class DefaultPromise<V> implements Promise<V> {
} }
@Override @Override
public Promise<V> sync() throws InterruptedException { public Future<V> sync() throws InterruptedException {
await(); await();
rethrowIfFailed(); rethrowIfFailed();
return this; return this;
} }
@Override @Override
public Promise<V> syncUninterruptibly() { public Future<V> syncUninterruptibly() {
awaitUninterruptibly(); awaitUninterruptibly();
rethrowIfFailed(); rethrowIfFailed();
return this; return this;
@ -410,6 +462,7 @@ public class DefaultPromise<V> implements Promise<V> {
safeExecute(executor(), this::notifyListenersNow); safeExecute(executor(), this::notifyListenersNow);
} }
@SuppressWarnings("unchecked")
private void notifyListenersNow() { private void notifyListenersNow() {
Object listeners; Object listeners;
synchronized (this) { synchronized (this) {
@ -424,7 +477,7 @@ public class DefaultPromise<V> implements Promise<V> {
if (listeners instanceof DefaultFutureListeners) { if (listeners instanceof DefaultFutureListeners) {
notifyListeners0((DefaultFutureListeners) listeners); notifyListeners0((DefaultFutureListeners) listeners);
} else { } else {
notifyListener0(this, (GenericFutureListener<?>) listeners); notifyListener0(this, (FutureListener<V>) listeners);
} }
synchronized (this) { synchronized (this) {
if (this.listeners == null) { if (this.listeners == null) {
@ -437,15 +490,10 @@ public class DefaultPromise<V> implements Promise<V> {
} }
private void notifyListeners0(DefaultFutureListeners listeners) { private void notifyListeners0(DefaultFutureListeners listeners) {
GenericFutureListener<?>[] a = listeners.listeners(); listeners.notifyListeners(this, logger);
int size = listeners.size();
for (int i = 0; i < size; i ++) {
notifyListener0(this, a[i]);
}
} }
@SuppressWarnings({ "unchecked", "rawtypes" }) static <V> void notifyListener0(Future<V> future, FutureListener<? super V> l) {
static void notifyListener0(Future future, GenericFutureListener l) {
try { try {
l.operationComplete(future); l.operationComplete(future);
} catch (Throwable t) { } catch (Throwable t) {
@ -455,13 +503,18 @@ public class DefaultPromise<V> implements Promise<V> {
} }
} }
private void addListener0(GenericFutureListener<? extends Future<? super V>> listener) { private synchronized void addListener0(Object listener, Object context) {
if (listeners == null) { if (listeners == null && context == null) {
listeners = listener; listeners = listener;
} else if (listeners instanceof DefaultFutureListeners) { } else if (listeners instanceof DefaultFutureListeners) {
((DefaultFutureListeners) listeners).add(listener); ((DefaultFutureListeners) listeners).add(listener, context);
} else { } else {
listeners = new DefaultFutureListeners((GenericFutureListener<?>) listeners, listener); DefaultFutureListeners listeners = new DefaultFutureListeners();
if (this.listeners != null) {
listeners.add(this.listeners, null);
}
listeners.add(listener, context);
this.listeners = listeners;
} }
} }

View File

@ -1,69 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.util.concurrent;
import io.netty.util.internal.PlatformDependent;
import static java.util.Objects.requireNonNull;
/**
* The {@link CompleteFuture} which is failed already. It is
* recommended to use {@link EventExecutor#newFailedFuture(Throwable)}
* instead of calling the constructor of this future.
*/
public final class FailedFuture<V> extends CompleteFuture<V> {
private final Throwable cause;
/**
* Creates a new instance.
*
* @param executor the {@link EventExecutor} associated with this future
* @param cause the cause of failure
*/
public FailedFuture(EventExecutor executor, Throwable cause) {
super(executor);
requireNonNull(cause, "cause");
this.cause = cause;
}
@Override
public Throwable cause() {
return cause;
}
@Override
public boolean isSuccess() {
return false;
}
@Override
public Future<V> sync() {
PlatformDependent.throwException(cause);
return this;
}
@Override
public Future<V> syncUninterruptibly() {
PlatformDependent.throwException(cause);
return this;
}
@Override
public V getNow() {
return null;
}
}

View File

@ -23,10 +23,150 @@ import java.util.concurrent.TimeoutException;
/** /**
* The result of an asynchronous operation. * The result of an asynchronous operation.
* <p>
* An asynchronous operation is one that might be completed outside a given
* thread of execution. The operation can either be performing computation,
* or I/O, or both.
* <p>
* All I/O operations in Netty are asynchronous. It means any I/O calls will
* return immediately with no guarantee that the requested I/O operation has
* been completed at the end of the call. Instead, you will be returned with
* a {@link Future} instance which gives you the information about the
* result or status of the I/O operation.
* <p>
* A {@link Future} is either <em>uncompleted</em> or <em>completed</em>.
* When an I/O operation begins, a new future object is created. The new future
* is uncompleted initially - it is neither succeeded, failed, nor cancelled
* because the I/O operation is not finished yet. If the I/O operation is
* finished either successfully, with failure, or by cancellation, the future is
* marked as completed with more specific information, such as the cause of the
* failure. Please note that even failure and cancellation belong to the
* completed state.
* <pre>
* +---------------------------+
* | Completed successfully |
* +---------------------------+
* +----> isDone() = true |
* +--------------------------+ | | isSuccess() = true |
* | Uncompleted | | +===========================+
* +--------------------------+ | | Completed with failure |
* | isDone() = false | | +---------------------------+
* | isSuccess() = false |----+----> isDone() = true |
* | isCancelled() = false | | | cause() = non-null |
* | cause() = null | | +===========================+
* +--------------------------+ | | Completed by cancellation |
* | +---------------------------+
* +----> isDone() = true |
* | isCancelled() = true |
* +---------------------------+
* </pre>
*
* Various methods are provided to let you check if the I/O operation has been
* completed, wait for the completion, and retrieve the result of the I/O
* operation. It also allows you to add {@link FutureListener}s so you
* can get notified when the I/O operation is completed.
*
* <h3>Prefer {@link #addListener(FutureListener)} to {@link #await()}</h3>
*
* It is recommended to prefer {@link #addListener(FutureListener)}, or
* {@link #addListener(Object, FutureContextListener)}, to {@link #await()}
* wherever possible to get notified when an I/O operation is done and to
* do any follow-up tasks.
* <p>
* The {@link #addListener(FutureListener)} method is non-blocking. It simply adds
* the specified {@link FutureListener} to the {@link Future}, and the I/O thread
* will notify the listeners when the I/O operation associated with the future is
* done. The {@link FutureListener} and {@link FutureContextListener} callbacks
* yield the best performance and resource utilization because it does not block at
* all, but it could be tricky to implement a sequential logic if you are not used to
* event-driven programming.
* <p>
* By contrast, {@link #await()} is a blocking operation. Once called, the
* caller thread blocks until the operation is done. It is easier to implement
* a sequential logic with {@link #await()}, but the caller thread blocks
* unnecessarily until the I/O operation is done and there's relatively
* expensive cost of inter-thread notification. Moreover, there's a chance of
* dead-lock in a particular circumstance, which is described below.
*
* <h3>Do not call {@link #await()} inside a {@link io.netty.channel.ChannelHandler}</h3>
* <p>
* The event handler methods in {@link io.netty.channel.ChannelHandler} are usually
* called by an I/O thread. If {@link #await()} is called by an event handler method,
* which is called by the I/O thread, the I/O operation it is waiting for might never
* complete because {@link #await()} can block the I/O operation it is waiting for,
* which is a dead-lock.
* <pre>
* // BAD - NEVER DO THIS
* {@code @Override}
* public void channelRead({@link io.netty.channel.ChannelHandlerContext} ctx, Object msg) {
* {@link Future} future = ctx.channel().close();
* future.awaitUninterruptibly();
* // Perform post-closure operation
* // ...
* }
*
* // GOOD
* {@code @Override}
* public void channelRead({@link io.netty.channel.ChannelHandlerContext} ctx, Object msg) {
* {@link Future} future = ctx.channel().close();
* future.addListener(new {@link FutureListener}() {
* public void operationComplete({@link Future} future) {
* // Perform post-closure operation
* // ...
* }
* });
* }
* </pre>
* <p>
* In spite of the disadvantages mentioned above, there are certainly the cases
* where it is more convenient to call {@link #await()}. In such a case, please
* make sure you do not call {@link #await()} in an I/O thread. Otherwise,
* {@link BlockingOperationException} will be raised to prevent a dead-lock.
*
* <h3>Do not confuse I/O timeout and await timeout</h3>
*
* The timeout value you specify with {@link #await(long)},
* {@link #await(long, TimeUnit)}, {@link #awaitUninterruptibly(long)}, or
* {@link #awaitUninterruptibly(long, TimeUnit)} are not related with I/O
* timeout at all. If an I/O operation times out, the future will be marked as
* 'completed with failure,' as depicted in the diagram above. For example,
* connect timeout should be configured via a transport-specific option:
* <pre>
* // BAD - NEVER DO THIS
* {@link io.netty.bootstrap.Bootstrap} b = ...;
* {@link Future} f = b.connect(...);
* f.awaitUninterruptibly(10, TimeUnit.SECONDS);
* if (f.isCancelled()) {
* // Connection attempt cancelled by user
* } else if (!f.isSuccess()) {
* // You might get a NullPointerException here because the future
* // might not be completed yet.
* f.cause().printStackTrace();
* } else {
* // Connection established successfully
* }
*
* // GOOD
* {@link io.netty.bootstrap.Bootstrap} b = ...;
* // Configure the connect timeout option.
* <b>b.option({@link io.netty.channel.ChannelOption}.CONNECT_TIMEOUT_MILLIS, 10000);</b>
* {@link Future} f = b.connect(...);
* f.awaitUninterruptibly();
*
* // Now we are sure the future is completed.
* assert f.isDone();
*
* if (f.isCancelled()) {
* // Connection attempt cancelled by user
* } else if (!f.isSuccess()) {
* f.cause().printStackTrace();
* } else {
* // Connection established successfully
* }
* </pre>
*/ */
@SuppressWarnings("ClassNameSameAsAncestorName") @SuppressWarnings("ClassNameSameAsAncestorName")
public interface Future<V> extends java.util.concurrent.Future<V> { public interface Future<V> extends java.util.concurrent.Future<V> {
/** /**
* Returns {@code true} if and only if the I/O operation was completed * Returns {@code true} if and only if the I/O operation was completed
* successfully. * successfully.
@ -49,12 +189,27 @@ public interface Future<V> extends java.util.concurrent.Future<V> {
Throwable cause(); Throwable cause();
/** /**
* Adds the specified listener to this future. The * Adds the specified listener to this future.
* specified listener is notified when this future is * The specified listener is notified when this future is {@linkplain #isDone() done}.
* {@linkplain #isDone() done}. If this future is already * If this future is already completed, the specified listener is notified immediately.
* completed, the specified listener is notified immediately. *
* @param listener The listener to be called when this future completes.
* The listener will be passed this future as an argument.
* @return this future object.
*/ */
Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener); Future<V> addListener(FutureListener<? super V> listener);
/**
* Adds the specified listener to this future.
* The specified listener is notified when this future is {@linkplain #isDone() done}.
* If this future is already completed, the specified listener is notified immediately.
*
* @param context The context object that will be passed to the listener when this future completes.
* @param listener The listener to be called when this future completes.
* The listener will be passed the given context, and this future.
* @return this future object.
*/
<C> Future<V> addListener(C context, FutureContextListener<? super C, ? super V> listener);
/** /**
* Waits for this future until it is done, and rethrows the cause of the failure if this future * Waits for this future until it is done, and rethrows the cause of the failure if this future

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2013 The Netty Project * Copyright 2021 The Netty Project
* *
* The Netty Project licenses this file to you under the Apache License, * The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance * version 2.0 (the "License"); you may not use this file except in compliance
@ -19,14 +19,18 @@ import java.util.EventListener;
/** /**
* Listens to the result of a {@link Future}. The result of the asynchronous operation is notified once this listener * Listens to the result of a {@link Future}. The result of the asynchronous operation is notified once this listener
* is added by calling {@link Future#addListener(GenericFutureListener)}. * is added by calling {@link Future#addListener(Object, FutureContextListener)}.
* <pre>
* Future f = new DefaultPromise(..);
* f.addListener(context, (context, future) -> { .. });
* </pre>
*/ */
public interface GenericFutureListener<F extends Future<?>> extends EventListener { @FunctionalInterface
public interface FutureContextListener<C, V> {
/** /**
* Invoked when the operation associated with the {@link Future} has been completed. * Invoked when the operation associated with the {@link Future} has been completed.
* *
* @param future the source {@link Future} which called this callback * @param future the source {@link Future} which called this callback
*/ */
void operationComplete(F future) throws Exception; void operationComplete(C context, Future<? extends V> future) throws Exception;
} }

View File

@ -16,13 +16,22 @@
package io.netty.util.concurrent; package io.netty.util.concurrent;
import java.util.EventListener;
/** /**
* A subtype of {@link GenericFutureListener} that hides type parameter for convenience. * Listens to the result of a {@link Future}. The result of the asynchronous operation is notified once this listener
* is added by calling {@link Future#addListener(FutureListener)}.
* <pre> * <pre>
* Future f = new DefaultPromise(..); * Future f = new DefaultPromise(..);
* f.addListener(new FutureListener() { * f.addListener(future -> { .. });
* public void operationComplete(Future f) { .. }
* });
* </pre> * </pre>
*/ */
public interface FutureListener<V> extends GenericFutureListener<Future<V>> { } @FunctionalInterface
public interface FutureListener<V> {
/**
* Invoked when the operation associated with the {@link Future} has been completed.
*
* @param future the source {@link Future} which called this callback
*/
void operationComplete(Future<? extends V> future) throws Exception;
}

View File

@ -67,7 +67,8 @@ public final class GlobalEventExecutor extends AbstractScheduledEventExecutor im
private final AtomicBoolean started = new AtomicBoolean(); private final AtomicBoolean started = new AtomicBoolean();
volatile Thread thread; volatile Thread thread;
private final Future<?> terminationFuture = new FailedFuture<>(this, new UnsupportedOperationException()); private final Future<?> terminationFuture = DefaultPromise.newFailedPromise(
this, new UnsupportedOperationException());
private GlobalEventExecutor() { private GlobalEventExecutor() {
threadFactory = ThreadExecutorMap.apply(new DefaultThreadFactory( threadFactory = ThreadExecutorMap.apply(new DefaultThreadFactory(

View File

@ -54,7 +54,7 @@ public final class ImmediateEventExecutor extends AbstractEventExecutor {
} }
}; };
private final Future<?> terminationFuture = new FailedFuture<>( private final Future<?> terminationFuture = DefaultPromise.newFailedPromise(
GlobalEventExecutor.INSTANCE, new UnsupportedOperationException()); GlobalEventExecutor.INSTANCE, new UnsupportedOperationException());
private ImmediateEventExecutor() { } private ImmediateEventExecutor() { }

View File

@ -15,67 +15,53 @@
*/ */
package io.netty.util.concurrent; package io.netty.util.concurrent;
import java.util.concurrent.CancellationException;
/** /**
* Special {@link Future} which is writable. * Special {@link Future} which is writable.
*/ */
public interface Promise<V> extends Future<V> { public interface Promise<V> extends Future<V> {
/** /**
* Marks this future as a success and notifies all * Marks this future as a success and notifies all listeners.
* listeners. * <p>
*
* If it is success or failed already it will throw an {@link IllegalStateException}. * If it is success or failed already it will throw an {@link IllegalStateException}.
*/ */
Promise<V> setSuccess(V result); Promise<V> setSuccess(V result);
/** /**
* Marks this future as a success and notifies all * Marks this future as a success and notifies all listeners.
* listeners.
* *
* @return {@code true} if and only if successfully marked this future as * @return {@code true} if and only if successfully marked this future as a success. Otherwise {@code false} because
* a success. Otherwise {@code false} because this future is * this future is already marked as either a success or a failure.
* already marked as either a success or a failure.
*/ */
boolean trySuccess(V result); boolean trySuccess(V result);
/** /**
* Marks this future as a failure and notifies all * Marks this future as a failure and notifies all listeners.
* listeners. * <p>
*
* If it is success or failed already it will throw an {@link IllegalStateException}. * If it is success or failed already it will throw an {@link IllegalStateException}.
*/ */
Promise<V> setFailure(Throwable cause); Promise<V> setFailure(Throwable cause);
/** /**
* Marks this future as a failure and notifies all * Marks this future as a failure and notifies all listeners.
* listeners.
* *
* @return {@code true} if and only if successfully marked this future as * @return {@code true} if and only if successfully marked this future as a failure. Otherwise {@code false} because
* a failure. Otherwise {@code false} because this future is * this future is already marked as either a success or a failure.
* already marked as either a success or a failure.
*/ */
boolean tryFailure(Throwable cause); boolean tryFailure(Throwable cause);
/** /**
* Make this future impossible to cancel. * Make this future impossible to cancel.
* *
* @return {@code true} if and only if successfully marked this future as uncancellable or it is already done * @return {@code true} if and only if successfully marked this future as uncancellable, or it is already done
* without being cancelled. {@code false} if this future has been cancelled already. * without being cancelled. Otherwise {@code false} if this future has been cancelled already.
*/ */
boolean setUncancellable(); boolean setUncancellable();
@Override @Override
Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener); Promise<V> addListener(FutureListener<? super V> listener);
@Override @Override
Promise<V> await() throws InterruptedException; <C> Promise<V> addListener(C context, FutureContextListener<? super C, ? super V> listener);
@Override
Promise<V> awaitUninterruptibly();
@Override
Promise<V> sync() throws InterruptedException;
@Override
Promise<V> syncUninterruptibly();
} }

View File

@ -38,7 +38,7 @@ public final class PromiseCombiner {
private int doneCount; private int doneCount;
private Promise<Void> aggregatePromise; private Promise<Void> aggregatePromise;
private Throwable cause; private Throwable cause;
private final GenericFutureListener<Future<?>> listener = new GenericFutureListener<Future<?>>() { private final FutureListener<Object> listener = new FutureListener<>() {
@Override @Override
public void operationComplete(final Future<?> future) { public void operationComplete(final Future<?> future) {
if (executor.inEventLoop()) { if (executor.inEventLoop()) {
@ -62,14 +62,6 @@ public final class PromiseCombiner {
private final EventExecutor executor; private final EventExecutor executor;
/**
* Deprecated use {@link PromiseCombiner#PromiseCombiner(EventExecutor)}.
*/
@Deprecated
public PromiseCombiner() {
this(ImmediateEventExecutor.INSTANCE);
}
/** /**
* The {@link EventExecutor} to use for notifications. You must call {@link #add(Future)}, {@link #addAll(Future[])} * The {@link EventExecutor} to use for notifications. You must call {@link #add(Future)}, {@link #addAll(Future[])}
* and {@link #finish(Promise)} from within the {@link EventExecutor} thread. * and {@link #finish(Promise)} from within the {@link EventExecutor} thread.
@ -80,56 +72,28 @@ public final class PromiseCombiner {
this.executor = requireNonNull(executor, "executor"); this.executor = requireNonNull(executor, "executor");
} }
/**
* Adds a new promise to be combined. New promises may be added until an aggregate promise is added via the
* {@link PromiseCombiner#finish(Promise)} method.
*
* @param promise the promise to add to this promise combiner
*
* @deprecated Replaced by {@link PromiseCombiner#add(Future)}.
*/
@Deprecated
public void add(Promise promise) {
add((Future) promise);
}
/** /**
* Adds a new future to be combined. New futures may be added until an aggregate promise is added via the * Adds a new future to be combined. New futures may be added until an aggregate promise is added via the
* {@link PromiseCombiner#finish(Promise)} method. * {@link PromiseCombiner#finish(Promise)} method.
* *
* @param future the future to add to this promise combiner * @param future the future to add to this promise combiner
*/ */
@SuppressWarnings({ "unchecked", "rawtypes" }) public void add(Future<?> future) {
public void add(Future future) {
checkAddAllowed(); checkAddAllowed();
checkInEventLoop(); checkInEventLoop();
++expectedCount; ++expectedCount;
future.addListener(listener); future.addListener(listener);
} }
/**
* Adds new promises to be combined. New promises may be added until an aggregate promise is added via the
* {@link PromiseCombiner#finish(Promise)} method.
*
* @param promises the promises to add to this promise combiner
*
* @deprecated Replaced by {@link PromiseCombiner#addAll(Future[])}
*/
@Deprecated
public void addAll(Promise... promises) {
addAll((Future[]) promises);
}
/** /**
* Adds new futures to be combined. New futures may be added until an aggregate promise is added via the * Adds new futures to be combined. New futures may be added until an aggregate promise is added via the
* {@link PromiseCombiner#finish(Promise)} method. * {@link PromiseCombiner#finish(Promise)} method.
* *
* @param futures the futures to add to this promise combiner * @param futures the futures to add to this promise combiner
*/ */
@SuppressWarnings({ "unchecked", "rawtypes" }) public void addAll(Future<?>... futures) {
public void addAll(Future... futures) { for (Future<?> future : futures) {
for (Future future : futures) { add(future);
this.add(future);
} }
} }
@ -163,7 +127,7 @@ public final class PromiseCombiner {
} }
private boolean tryPromise() { private boolean tryPromise() {
return (cause == null) ? aggregatePromise.trySuccess(null) : aggregatePromise.tryFailure(cause); return cause == null? aggregatePromise.trySuccess(null) : aggregatePromise.tryFailure(cause);
} }
private void checkAddAllowed() { private void checkAddAllowed() {

View File

@ -23,13 +23,12 @@ import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
/** /**
* {@link GenericFutureListener} implementation which takes other {@link Promise}s * A {@link FutureListener} implementation which takes other {@link Promise}s
* and notifies them on completion. * and notifies them on completion.
* *
* @param <V> the type of value returned by the future * @param <V> the type of value returned by the future
* @param <F> the type of future
*/ */
public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureListener<F> { public class PromiseNotifier<V> implements FutureListener<V> {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PromiseNotifier.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(PromiseNotifier.class);
private final Promise<? super V>[] promises; private final Promise<? super V>[] promises;
@ -38,7 +37,7 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
/** /**
* Create a new instance. * Create a new instance.
* *
* @param promises the {@link Promise}s to notify once this {@link GenericFutureListener} is notified. * @param promises the {@link Promise}s to notify once this {@link FutureListener} is notified.
*/ */
@SafeVarargs @SafeVarargs
public PromiseNotifier(Promise<? super V>... promises) { public PromiseNotifier(Promise<? super V>... promises) {
@ -49,7 +48,7 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
* Create a new instance. * Create a new instance.
* *
* @param logNotifyFailure {@code true} if logging should be done in case notification fails. * @param logNotifyFailure {@code true} if logging should be done in case notification fails.
* @param promises the {@link Promise}s to notify once this {@link GenericFutureListener} is notified. * @param promises the {@link Promise}s to notify once this {@link FutureListener} is notified.
*/ */
@SafeVarargs @SafeVarargs
public PromiseNotifier(boolean logNotifyFailure, Promise<? super V>... promises) { public PromiseNotifier(boolean logNotifyFailure, Promise<? super V>... promises) {
@ -69,10 +68,9 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
* @param future the {@link Future} which will be used to listen to for notifying the {@link Promise}. * @param future the {@link Future} which will be used to listen to for notifying the {@link Promise}.
* @param promise the {@link Promise} which will be notified * @param promise the {@link Promise} which will be notified
* @param <V> the type of the value. * @param <V> the type of the value.
* @param <F> the type of the {@link Future}
* @return the passed in {@link Future} * @return the passed in {@link Future}
*/ */
public static <V, F extends Future<V>> F cascade(final F future, final Promise<? super V> promise) { public static <V> Future<V> cascade(final Future<V> future, final Promise<? super V> promise) {
return cascade(true, future, promise); return cascade(true, future, promise);
} }
@ -85,29 +83,12 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
* @param future the {@link Future} which will be used to listen to for notifying the {@link Promise}. * @param future the {@link Future} which will be used to listen to for notifying the {@link Promise}.
* @param promise the {@link Promise} which will be notified * @param promise the {@link Promise} which will be notified
* @param <V> the type of the value. * @param <V> the type of the value.
* @param <F> the type of the {@link Future}
* @return the passed in {@link Future} * @return the passed in {@link Future}
*/ */
public static <V, F extends Future<V>> F cascade(boolean logNotifyFailure, final F future, public static <V> Future<V> cascade(boolean logNotifyFailure, final Future<V> future,
final Promise<? super V> promise) { final Promise<? super V> promise) {
promise.addListener(new FutureListener<Object>() { promise.addListener(future, PromiseNotifier::propagateCancel);
@Override future.addListener(new PromiseNotifier<V>(logNotifyFailure, promise), PromiseNotifier::propagateComplete);
public void operationComplete(Future<Object> f) {
if (f.isCancelled()) {
future.cancel(false);
}
}
});
future.addListener(new PromiseNotifier<V, F>(logNotifyFailure, promise) {
@Override
public void operationComplete(F f) throws Exception {
if (promise.isCancelled() && f.isCancelled()) {
// Just return if we propagate a cancel from the promise to the future and both are notified already
return;
}
super.operationComplete(future);
}
});
return future; return future;
} }
@ -123,19 +104,12 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
* @param successResult the result that will be propagated to the promise on success * @param successResult the result that will be propagated to the promise on success
* @return the passed in {@link Future} * @return the passed in {@link Future}
*/ */
public static <R, F extends Future<Void>> F cascade(boolean logNotifyFailure, F future, public static <R> Future<Void> cascade(boolean logNotifyFailure, Future<Void> future,
Promise<R> promise, R successResult) { Promise<R> promise, R successResult) {
promise.addListener(new FutureListener<Object>() { promise.addListener(future, PromiseNotifier::propagateCancel);
future.addListener(new FutureListener<Void>() {
@Override @Override
public void operationComplete(Future<Object> f) { public void operationComplete(Future<? extends Void> f) throws Exception {
if (f.isCancelled()) {
future.cancel(false);
}
}
});
future.addListener(new GenericFutureListener<F>() {
@Override
public void operationComplete(F f) throws Exception {
if (promise.isCancelled() && f.isCancelled()) { if (promise.isCancelled() && f.isCancelled()) {
// Just return if we propagate a cancel from the promise to the future and both are notified already // Just return if we propagate a cancel from the promise to the future and both are notified already
return; return;
@ -157,8 +131,26 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
return future; return future;
} }
static <V, F extends Future<?>> void propagateCancel(F target, Future<? extends V> source) {
if (source.isCancelled()) {
target.cancel(false);
}
}
static <V> void propagateComplete(PromiseNotifier<V> target, Future<? extends V> source) throws Exception {
boolean allCancelled = target.promises.length > 0;
for (Promise<? super V> promise : target.promises) {
allCancelled &= promise.isCancelled();
}
if (allCancelled && source.isCancelled()) {
// Just return if we propagate a cancel from the promise to the future and both are notified already
return;
}
target.operationComplete(source);
}
@Override @Override
public void operationComplete(F future) throws Exception { public void operationComplete(Future<? extends V> future) throws Exception {
InternalLogger internalLogger = logNotifyFailure ? logger : null; InternalLogger internalLogger = logNotifyFailure ? logger : null;
if (future.isSuccess()) { if (future.isSuccess()) {
V result = future.get(); V result = future.get();

View File

@ -112,10 +112,6 @@ class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
return false; return false;
} }
protected final boolean trySuccessInternal(V result) {
return super.trySuccess(result);
}
@Override @Override
public final boolean setUncancellable() { public final boolean setUncancellable() {
throw new IllegalStateException(); throw new IllegalStateException();
@ -125,11 +121,6 @@ class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
return super.setUncancellable(); return super.setUncancellable();
} }
@Override
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
return super.addListener(listener);
}
@Override @Override
protected StringBuilder toStringBuilder() { protected StringBuilder toStringBuilder() {
StringBuilder buf = super.toStringBuilder(); StringBuilder buf = super.toStringBuilder();

View File

@ -22,7 +22,10 @@ package io.netty.util.concurrent;
public interface RunnableFuture<V> extends java.util.concurrent.RunnableFuture<V>, Future<V> { public interface RunnableFuture<V> extends java.util.concurrent.RunnableFuture<V>, Future<V> {
@Override @Override
RunnableFuture<V> addListener(GenericFutureListener<? extends Future<? super V>> listener); RunnableFuture<V> addListener(FutureListener<? super V> listener);
@Override
<C> RunnableFuture<V> addListener(C context, FutureContextListener<? super C, ? super V> listener);
@Override @Override
RunnableFuture<V> sync() throws InterruptedException; RunnableFuture<V> sync() throws InterruptedException;

Some files were not shown because too many files have changed in this diff Show More