Don't take Promise as argument in Channel API. (#11346)

Motivation:

At the moment the outbound operations of ChannelHandler take a Promise as argument. This Promise needs to be carried forward to the next handler in the pipeline until it hits the transport. This is API choice has a few quirks which we should aim to remove:

 - There is a difference between if you add a FutureListener to the Promise or the Future that is returned by the outbound method in terms of the ordering of execution of the listeners. Sometimes we add the listener to the promise while in reality we usually always want to add it to the future to ensure the listerns are executed in the "correct order".
- It is quite easy to "loose" a promise by forgetting to use the right method which also takes a promise
- We have no idea what EventExecutor is used for the passed in Promise which may invalid our assumption of threading.

While changing the method signature of the outbound operations of the ChannelHandler is a good step forward we should also take care of just remove all the methods from ChannelOutboundInvoker (and its sub-types) that take a Promise and just always use the methods that return a Future only.

Modifications:

- Change the signature of the methods that took a Promise to not take one anymore and just return a Future
- Remove all operations for ChannelOutboundInvoker that take a Promise.
- Adjust all code to cope with the API changes

Result:

Cleaner API which is easier to reason about and easier to use.
This commit is contained in:
Norman Maurer 2021-08-25 14:12:33 +02:00 committed by GitHub
parent a34b440e22
commit 5c879bc963
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
108 changed files with 1087 additions and 2274 deletions

View File

@ -184,57 +184,11 @@ abstract class DelegatingChannelHandlerContext implements ChannelHandlerContext
return ctx.register();
}
@Override
public Future<Void> register(Promise<Void> promise) {
return ctx.register(promise);
}
@Override
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return ctx.bind(localAddress, promise);
}
@Override
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, promise);
}
@Override
public Future<Void> connect(
SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, localAddress, promise);
}
@Override
public Future<Void> disconnect(Promise<Void> promise) {
return ctx.disconnect(promise);
}
@Override
public Future<Void> close(Promise<Void> promise) {
return ctx.close(promise);
}
@Override
public Future<Void> deregister(Promise<Void> promise) {
return ctx.deregister(promise);
}
@Override
public Future<Void> write(Object msg) {
return ctx.write(msg);
}
@Override
public Future<Void> write(Object msg, Promise<Void> promise) {
return ctx.write(msg, promise);
}
@Override
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return ctx.writeAndFlush(msg, promise);
}
@Override
public Future<Void> writeAndFlush(Object msg) {
return ctx.writeAndFlush(msg);

View File

@ -16,9 +16,8 @@ package io.netty.handler.codec.http;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AsciiString;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import java.net.SocketAddress;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Set;
@ -121,68 +120,26 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator {
}
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
ctx.bind(localAddress, promise);
}
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
Promise<Void> promise) {
ctx.connect(remoteAddress, localAddress, promise);
}
@Override
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.disconnect(promise);
}
@Override
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.close(promise);
}
@Override
public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.deregister(promise);
}
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (!(msg instanceof HttpRequest)) {
ctx.write(msg, promise);
return;
return ctx.write(msg);
}
if (upgradeRequested) {
promise.setFailure(new IllegalStateException(
return ctx.newFailedFuture(new IllegalStateException(
"Attempting to write HTTP request with upgrade in progress"));
return;
}
upgradeRequested = true;
setUpgradeRequestHeaders(ctx, (HttpRequest) msg);
// Continue writing the request.
ctx.write(msg, promise);
Future<Void> f = ctx.write(msg);
// Notify that the upgrade request was issued.
ctx.fireUserEventTriggered(UpgradeEvent.UPGRADE_ISSUED);
// Now we wait for the next HTTP response to see if we switch protocols.
}
@Override
public void flush(ChannelHandlerContext ctx) {
ctx.flush();
return f;
}
@Override

View File

@ -19,7 +19,7 @@ import io.netty.channel.ChannelFutureListeners;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import static io.netty.handler.codec.http.HttpUtil.isContentLengthSet;
import static io.netty.handler.codec.http.HttpUtil.isKeepAlive;
@ -68,7 +68,7 @@ public class HttpServerKeepAliveHandler implements ChannelHandler {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
// modify message on way out to add headers if needed
if (msg instanceof HttpResponse) {
final HttpResponse response = (HttpResponse) msg;
@ -84,10 +84,12 @@ public class HttpServerKeepAliveHandler implements ChannelHandler {
setKeepAlive(response, false);
}
}
if (msg instanceof LastHttpContent && !shouldKeepAlive()) {
promise.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
boolean shouldClose = msg instanceof LastHttpContent && !shouldKeepAlive();
Future<Void> future = ctx.write(msg);
if (shouldClose) {
future.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
}
ctx.write(msg, promise);
return future;
}
private void trackResponse(HttpResponse response) {

View File

@ -26,7 +26,6 @@ import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
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.InternalLoggerFactory;
@ -215,7 +214,7 @@ public class CorsHandler implements ChannelHandler {
}
@Override
public void write(final ChannelHandlerContext ctx, final Object msg, Promise<Void> promise) {
public Future<Void> write(final ChannelHandlerContext ctx, final Object msg) {
if (config != null && config.isCorsSupportEnabled() && msg instanceof HttpResponse) {
final HttpResponse response = (HttpResponse) msg;
if (setOrigin(response)) {
@ -223,7 +222,7 @@ public class CorsHandler implements ChannelHandler {
setExposeHeaders(response);
}
}
ctx.write(msg, promise);
return ctx.write(msg);
}
private static void forbidden(final ChannelHandlerContext ctx, final HttpRequest request) {

View File

@ -491,25 +491,7 @@ public abstract class WebSocketClientHandshaker {
*/
public Future<Void> close(Channel channel, CloseWebSocketFrame frame) {
requireNonNull(channel, "channel");
return close(channel, frame, channel.newPromise());
}
/**
* Performs the closing handshake
*
* When called from within a {@link ChannelHandler} you most likely want to use
* {@link #close(ChannelHandlerContext, CloseWebSocketFrame, Promise)}.
*
* @param channel
* Channel
* @param frame
* Closing Frame that was received
* @param promise
* the {@link Promise} to be notified when the closing handshake is done
*/
public Future<Void> close(Channel channel, CloseWebSocketFrame frame, Promise<Void> promise) {
requireNonNull(channel, "channel");
return close0(channel, channel, frame, promise);
return close0(channel, channel, frame);
}
/**
@ -522,34 +504,19 @@ public abstract class WebSocketClientHandshaker {
*/
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
requireNonNull(ctx, "ctx");
return close(ctx, frame, ctx.newPromise());
}
/**
* Performs the closing handshake
*
* @param ctx
* the {@link ChannelHandlerContext} to use.
* @param frame
* Closing Frame that was received
* @param promise
* the {@link Promise} to be notified when the closing handshake is done
*/
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, Promise<Void> promise) {
requireNonNull(ctx, "ctx");
return close0(ctx, ctx.channel(), frame, promise);
return close0(ctx, ctx.channel(), frame);
}
private Future<Void> close0(final ChannelOutboundInvoker invoker, final Channel channel,
CloseWebSocketFrame frame, Promise<Void> promise) {
invoker.writeAndFlush(frame, promise);
CloseWebSocketFrame frame) {
Future<Void> f = invoker.writeAndFlush(frame);
final long forceCloseTimeoutMillis = this.forceCloseTimeoutMillis;
final WebSocketClientHandshaker handshaker = this;
if (forceCloseTimeoutMillis <= 0 || !channel.isActive() || forceCloseInit != 0) {
return promise;
return f;
}
promise.addListener(future -> {
f.addListener(future -> {
// If flush operation failed, there is no reason to expect
// a server to receive CloseFrame. Thus this should be handled
// by the application separately.
@ -568,7 +535,7 @@ public abstract class WebSocketClientHandshaker {
});
}
});
return promise;
return f;
}
/**

View File

@ -19,6 +19,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.concurrent.ScheduledFuture;
@ -82,30 +83,32 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
}
@Override
public void close(final ChannelHandlerContext ctx, final Promise<Void> promise) {
public Future<Void> close(final ChannelHandlerContext ctx) {
if (closeStatus == null || !ctx.channel().isActive()) {
ctx.close(promise);
} else {
if (closeSent == null) {
write(ctx, new CloseWebSocketFrame(closeStatus), ctx.newPromise());
}
flush(ctx);
applyCloseSentTimeout(ctx);
closeSent.addListener(future -> ctx.close(promise));
return ctx.close();
}
final Future<Void> future = closeSent == null ? write(ctx, new CloseWebSocketFrame(closeStatus)) : closeSent;
flush(ctx);
applyCloseSentTimeout(ctx);
Promise<Void> promise = ctx.newPromise();
future.addListener(f -> ctx.close().addListener(new PromiseNotifier<>(promise)));
return promise;
}
@Override
public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(final ChannelHandlerContext ctx, Object msg) {
if (closeSent != null) {
ReferenceCountUtil.release(msg);
promise.setFailure(new ClosedChannelException());
} else if (msg instanceof CloseWebSocketFrame) {
return ctx.newFailedFuture(new ClosedChannelException());
}
if (msg instanceof CloseWebSocketFrame) {
Promise<Void> promise = ctx.newPromise();
closeSent(promise);
ctx.write(msg).addListener(new PromiseNotifier<>(false, closeSent));
} else {
ctx.write(msg, promise);
return promise;
}
return ctx.write(msg);
}
void closeSent(Promise<Void> promise) {

View File

@ -41,7 +41,6 @@ import io.netty.util.internal.logging.InternalLoggerFactory;
import java.nio.channels.ClosedChannelException;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
import static java.util.Objects.requireNonNull;
@ -342,25 +341,7 @@ public abstract class WebSocketServerHandshaker {
*/
public Future<Void> close(Channel channel, CloseWebSocketFrame frame) {
requireNonNull(channel, "channel");
return close(channel, frame, channel.newPromise());
}
/**
* Performs the closing handshake.
*
* When called from within a {@link ChannelHandler} you most likely want to use
* {@link #close(ChannelHandlerContext, CloseWebSocketFrame, Promise)}.
*
* @param channel
* the {@link Channel} to use.
* @param frame
* Closing Frame that was received.
* @param promise
* the {@link Promise} to be notified when the closing handshake is done
*/
public Future<Void> close(Channel channel, CloseWebSocketFrame frame, Promise<Void> promise) {
requireNonNull(channel, "channel");
return close0(channel, channel, frame, promise);
return close0(channel, channel, frame);
}
/**
@ -373,27 +354,11 @@ public abstract class WebSocketServerHandshaker {
*/
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
requireNonNull(ctx, "ctx");
return close(ctx, frame, ctx.newPromise());
return close0(ctx, ctx.channel(), frame);
}
/**
* Performs the closing handshake.
*
* @param ctx
* the {@link ChannelHandlerContext} to use.
* @param frame
* Closing Frame that was received.
* @param promise
* the {@link Promise} to be notified when the closing handshake is done.
*/
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, Promise<Void> promise) {
requireNonNull(ctx, "ctx");
return close0(ctx, ctx.channel(), frame, promise);
}
private static Future<Void> close0(ChannelOutboundInvoker invoker, Channel channel, CloseWebSocketFrame frame,
Promise<Void> promise) {
return invoker.writeAndFlush(frame, promise).addListener(channel, ChannelFutureListeners.CLOSE);
private static Future<Void> close0(ChannelOutboundInvoker invoker, Channel channel, CloseWebSocketFrame frame) {
return invoker.writeAndFlush(frame).addListener(channel, ChannelFutureListeners.CLOSE);
}
/**

View File

@ -27,7 +27,6 @@ import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.util.regex.Pattern;
@ -203,12 +202,11 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
* the {@link Channel} to use.
* @param frame
* Web Socket frame that was received.
* @param promise
* the {@link Promise} to be notified when the closing handshake is done.
* @return the {@link Future} which will be notified once the operations completes.
*/
@Override
public Future<Void> close(Channel channel, CloseWebSocketFrame frame, Promise<Void> promise) {
return channel.writeAndFlush(frame, promise);
public Future<Void> close(Channel channel, CloseWebSocketFrame frame) {
return channel.writeAndFlush(frame);
}
/**
@ -218,12 +216,11 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
* the {@link ChannelHandlerContext} to use.
* @param frame
* Closing Frame that was received.
* @param promise
* the {@link Promise} to be notified when the closing handshake is done.
* @return the {@link ChannelFuture} which will be notified once the operations completes.
*/
@Override
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, Promise<Void> promise) {
return ctx.writeAndFlush(frame, promise);
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
return ctx.writeAndFlush(frame);
}
@Override

View File

@ -16,6 +16,7 @@
package io.netty.handler.codec.http.websocketx;
import io.netty.channel.Channel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest;
@ -24,7 +25,6 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import java.util.Objects;
@ -164,18 +164,11 @@ public class WebSocketServerHandshakerFactory {
* Return that we need cannot not support the web socket version
*/
public static Future<Void> sendUnsupportedVersionResponse(Channel channel) {
return sendUnsupportedVersionResponse(channel, channel.newPromise());
}
/**
* Return that we need cannot not support the web socket version
*/
public static Future<Void> sendUnsupportedVersionResponse(Channel channel, Promise<Void> promise) {
HttpResponse res = new DefaultFullHttpResponse(
HttpVersion.HTTP_1_1,
HttpResponseStatus.UPGRADE_REQUIRED, channel.alloc().buffer(0));
res.headers().set(HttpHeaderNames.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
HttpUtil.setContentLength(res, 0);
return channel.writeAndFlush(res, promise);
return channel.writeAndFlush(res);
}
}

View File

@ -27,6 +27,7 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import java.util.Objects;
@ -239,7 +240,7 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
frame.retain();
Promise<Void> promise = ctx.newPromise();
closeSent(promise);
handshaker.close(ctx, (CloseWebSocketFrame) frame, promise);
handshaker.close(ctx, (CloseWebSocketFrame) frame).addListener(new PromiseNotifier<>(promise));
} else {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
}

View File

@ -21,7 +21,7 @@ import io.netty.handler.codec.CodecException;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import java.util.ArrayList;
import java.util.Arrays;
@ -56,7 +56,7 @@ public class WebSocketClientExtensionHandler implements ChannelHandler {
}
@Override
public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(final ChannelHandlerContext ctx, Object msg) {
if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade(((HttpRequest) msg).headers())) {
HttpRequest request = (HttpRequest) msg;
String headerValue = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
@ -71,7 +71,7 @@ public class WebSocketClientExtensionHandler implements ChannelHandler {
request.headers().set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue);
}
ctx.write(msg, promise);
return ctx.write(msg);
}
@Override

View File

@ -22,7 +22,8 @@ import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import java.util.ArrayList;
import java.util.Arrays;
@ -98,55 +99,56 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
}
@Override
public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(final ChannelHandlerContext ctx, Object msg) {
if (msg instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) msg;
//checking the status is faster than looking at headers
//so we do this first
if (HttpResponseStatus.SWITCHING_PROTOCOLS.equals(httpResponse.status())) {
handlePotentialUpgrade(ctx, promise, httpResponse);
}
}
HttpHeaders headers = httpResponse.headers();
ctx.write(msg, promise);
}
private void handlePotentialUpgrade(final ChannelHandlerContext ctx,
Promise<Void> promise, HttpResponse httpResponse) {
HttpHeaders headers = httpResponse.headers();
if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) {
if (validExtensions != null) {
String headerValue = headers.getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
List<WebSocketExtensionData> extraExtensions =
new ArrayList<WebSocketExtensionData>(extensionHandshakers.size());
for (WebSocketServerExtension extension : validExtensions) {
extraExtensions.add(extension.newResponseData());
}
String newHeaderValue = WebSocketExtensionUtil
.computeMergeExtensionsHeaderValue(headerValue, extraExtensions);
promise.addListener(future -> {
if (future.isSuccess()) {
FutureListener<Void> listener = null;
if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) {
if (validExtensions != null) {
String headerValue = headers.getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
List<WebSocketExtensionData> extraExtensions =
new ArrayList<WebSocketExtensionData>(extensionHandshakers.size());
for (WebSocketServerExtension extension : validExtensions) {
WebSocketExtensionDecoder decoder = extension.newExtensionDecoder();
WebSocketExtensionEncoder encoder = extension.newExtensionEncoder();
String name = ctx.name();
ctx.pipeline()
extraExtensions.add(extension.newResponseData());
}
String newHeaderValue = WebSocketExtensionUtil
.computeMergeExtensionsHeaderValue(headerValue, extraExtensions);
listener = future -> {
if (future.isSuccess()) {
for (WebSocketServerExtension extension : validExtensions) {
WebSocketExtensionDecoder decoder = extension.newExtensionDecoder();
WebSocketExtensionEncoder encoder = extension.newExtensionEncoder();
String name = ctx.name();
ctx.pipeline()
.addAfter(name, decoder.getClass().getName(), decoder)
.addAfter(name, encoder.getClass().getName(), encoder);
.addAfter(name, decoder.getClass().getName(), decoder)
.addAfter(name, encoder.getClass().getName(), encoder);
}
}
};
if (newHeaderValue != null) {
headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue);
}
}
});
if (newHeaderValue != null) {
headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue);
Future<Void> f = ctx.write(httpResponse);
if (listener != null) {
f.addListener(listener);
}
f.addListener(future -> {
if (future.isSuccess()) {
ctx.pipeline().remove(this);
}
});
return f;
}
}
promise.addListener(future -> {
if (future.isSuccess()) {
ctx.pipeline().remove(this);
}
});
}
return ctx.write(msg);
}
}

View File

@ -24,7 +24,9 @@ import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec;
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import org.junit.jupiter.api.Test;
import java.util.Collection;
@ -93,13 +95,15 @@ public class HttpServerUpgradeHandlerTest {
}
@Override
public void write(final ChannelHandlerContext ctx, final Object msg, final Promise<Void> promise) {
public Future<Void> write(final ChannelHandlerContext ctx, final Object msg) {
// 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.
assertTrue(inReadCall);
writeUpgradeMessage = true;
ctx.channel().eventLoop().execute(() -> ctx.write(msg, promise));
promise.addListener(future -> writeFlushed = true);
Promise<Void> promise = ctx.newPromise();
ctx.channel().eventLoop().execute(() -> ctx.write(msg).addListener(new PromiseNotifier<>(promise)));
promise.addListener(f -> writeFlushed = true);
return promise;
}
};

View File

@ -213,7 +213,7 @@ public class WebSocketHandshakeHandOverTest {
// Close the channel while the handshake is in progress. The channel could be closed before the handshake is
// complete due to a number of varied reasons. To reproduce the test scenario for this test case,
// we would manually close the channel.
clientWsHandler.close(ctx, ctx.newPromise());
clientWsHandler.close(ctx);
// At this stage handshake is incomplete but the handshake future should be completed exceptionally since
// channel is closed.

View File

@ -24,7 +24,6 @@ import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.flow.FlowControlHandler;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
@ -153,20 +152,22 @@ public class WebSocketProtocolHandlerTest {
@Test
public void testTimeout() throws Exception {
final AtomicReference<Promise<Void>> ref = new AtomicReference<>();
final AtomicReference<Future<Void>> ref = new AtomicReference<>();
WebSocketProtocolHandler handler = new WebSocketProtocolHandler(
false, WebSocketCloseStatus.NORMAL_CLOSURE, 1) { };
EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
ref.set(promise);
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
Future<Void> future = ctx.newPromise();
ref.set(future);
ReferenceCountUtil.release(msg);
return future;
}
}, handler);
Future<Void> future = channel.writeAndFlush(new CloseWebSocketFrame());
ChannelHandlerContext ctx = channel.pipeline().context(WebSocketProtocolHandler.class);
handler.close(ctx, ctx.newPromise());
handler.close(ctx);
do {
Thread.sleep(10);

View File

@ -32,7 +32,7 @@ import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@ -459,9 +459,9 @@ public class WebSocketServerProtocolHandlerTest {
private class MockOutboundHandler implements ChannelHandler {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
responses.add((FullHttpResponse) msg);
promise.setSuccess(null);
return ctx.newSucceededFuture();
}
@Override

View File

@ -19,10 +19,8 @@ import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpRequest;
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.util.Collections;
import java.util.List;
@ -211,31 +209,4 @@ public class WebSocketServerExtensionHandlerTest {
verify(fallbackHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("unknown"));
verify(fallbackHandshakerMock).handshakeExtension(webSocketExtensionDataMatcher("unknown2"));
}
@Test
public void testExtensionHandlerNotRemovedByFailureWritePromise() {
// initialize
when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("main")))
.thenReturn(mainExtensionMock);
when(mainExtensionMock.newResponseData()).thenReturn(
new WebSocketExtensionData("main", Collections.emptyMap()));
// execute
WebSocketServerExtensionHandler extensionHandler =
new WebSocketServerExtensionHandler(mainHandshakerMock);
EmbeddedChannel ch = new EmbeddedChannel(extensionHandler);
HttpRequest req = newUpgradeRequest("main");
ch.writeInbound(req);
HttpResponse res = newUpgradeResponse(null);
Promise<Void> failurePromise = ch.newPromise();
ch.writeOneOutbound(res, failurePromise);
failurePromise.setFailure(new IOException("Cannot write response"));
// test
assertNull(ch.readOutbound());
assertNotNull(ch.pipeline().context(extensionHandler));
assertTrue(ch.finish());
}
}

View File

@ -941,9 +941,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
}
protected Future<Void> write0(ChannelHandlerContext ctx, Object msg) {
Promise<Void> promise = ctx.newPromise();
ctx.write(msg, promise);
return promise;
return ctx.write(msg);
}
protected abstract boolean isParentReadInProgress();

View File

@ -23,6 +23,7 @@ import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregato
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.internal.UnstableApi;
import java.util.ArrayDeque;
@ -485,7 +486,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
// corresponding to 0 bytes and writing it to the channel (to preserve notification order).
Promise<Void> writePromise = ctx.newPromise();
writePromise.addListener(this);
ctx.write(queue.remove(0, writePromise), writePromise);
ctx.write(queue.remove(0, writePromise)).addListener(new PromiseNotifier<>(writePromise));
}
return;
}

View File

@ -22,6 +22,7 @@ import io.netty.handler.codec.http2.Http2FrameWriter.Configuration;
import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.internal.UnstableApi;
import static io.netty.buffer.Unpooled.directBuffer;
@ -151,10 +152,12 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
writeFrameHeaderInternal(frameHeader, maxFrameSize, DATA, flags, streamId);
do {
// Write the header.
ctx.write(frameHeader.retainedSlice(), promiseAggregator.newPromise());
ctx.write(frameHeader.retainedSlice()).addListener(
new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write the payload.
ctx.write(data.readRetainedSlice(maxFrameSize), promiseAggregator.newPromise());
ctx.write(data.readRetainedSlice(maxFrameSize)).addListener(
new PromiseNotifier<>(promiseAggregator.newPromise()));
remainingData -= maxFrameSize;
// Stop iterating if remainingData == maxFrameSize so we can take care of reference counts below.
@ -170,12 +173,12 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
ByteBuf frameHeader2 = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
flags.endOfStream(endStream);
writeFrameHeaderInternal(frameHeader2, remainingData, DATA, flags, streamId);
ctx.write(frameHeader2, promiseAggregator.newPromise());
ctx.write(frameHeader2).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write the payload.
ByteBuf lastFrame = data.readSlice(remainingData);
data = null;
ctx.write(lastFrame, promiseAggregator.newPromise());
ctx.write(lastFrame).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
} else {
if (remainingData != maxFrameSize) {
if (frameHeader != null) {
@ -193,12 +196,12 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
lastFrame = frameHeader.slice();
frameHeader = null;
}
ctx.write(lastFrame, promiseAggregator.newPromise());
ctx.write(lastFrame).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write the payload.
lastFrame = data.readableBytes() != maxFrameSize ? data.readSlice(maxFrameSize) : data;
data = null;
ctx.write(lastFrame, promiseAggregator.newPromise());
ctx.write(lastFrame).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
}
do {
@ -215,22 +218,23 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
flags.paddingPresent(framePaddingBytes > 0);
writeFrameHeaderInternal(frameHeader2, framePaddingBytes + frameDataBytes, DATA, flags, streamId);
writePaddingLength(frameHeader2, framePaddingBytes);
ctx.write(frameHeader2, promiseAggregator.newPromise());
ctx.write(frameHeader2).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write the payload.
if (frameDataBytes != 0 && data != null) { // Make sure Data is not null
if (remainingData == 0) {
ByteBuf lastFrame = data.readSlice(frameDataBytes);
data = null;
ctx.write(lastFrame, promiseAggregator.newPromise());
ctx.write(lastFrame).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
} else {
ctx.write(data.readRetainedSlice(frameDataBytes), promiseAggregator.newPromise());
ctx.write(data.readRetainedSlice(frameDataBytes))
.addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
}
}
// Write the frame padding.
if (paddingBytes(framePaddingBytes) > 0) {
ctx.write(ZERO_BUFFER.slice(0, paddingBytes(framePaddingBytes)),
promiseAggregator.newPromise());
ctx.write(ZERO_BUFFER.slice(0, paddingBytes(framePaddingBytes)))
.addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
}
} while (remainingData != 0 || padding != 0);
}
@ -281,7 +285,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
buf.writeInt(exclusive ? (int) (0x80000000L | streamDependency) : streamDependency);
// Adjust the weight so that it fits into a single byte on the wire.
buf.writeByte(weight - 1);
return ctx.write(buf, promise);
return ctx.write(buf).addListener(new PromiseNotifier<>(promise));
} catch (Throwable t) {
return promise.setFailure(t);
}
@ -297,7 +301,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
ByteBuf buf = ctx.alloc().buffer(RST_STREAM_FRAME_LENGTH);
writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, RST_STREAM, new Http2Flags(), streamId);
buf.writeInt((int) errorCode);
return ctx.write(buf, promise);
return ctx.write(buf).addListener(new PromiseNotifier<>(promise));
} catch (Throwable t) {
return promise.setFailure(t);
}
@ -315,7 +319,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
buf.writeChar(entry.key());
buf.writeInt(entry.value().intValue());
}
return ctx.write(buf, promise);
return ctx.write(buf).addListener(new PromiseNotifier<>(promise));
} catch (Throwable t) {
return promise.setFailure(t);
}
@ -326,7 +330,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
try {
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0);
return ctx.write(buf, promise);
return ctx.write(buf).addListener(new PromiseNotifier<>(promise));
} catch (Throwable t) {
return promise.setFailure(t);
}
@ -340,7 +344,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
// in the catch block.
writeFrameHeaderInternal(buf, PING_FRAME_PAYLOAD_LENGTH, PING, flags, 0);
buf.writeLong(data);
return ctx.write(buf, promise);
return ctx.write(buf).addListener(new PromiseNotifier<>(promise));
}
@Override
@ -373,14 +377,15 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
// Write out the promised stream ID.
buf.writeInt(promisedStreamId);
ctx.write(buf, promiseAggregator.newPromise());
ctx.write(buf).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write the first fragment.
ctx.write(fragment, promiseAggregator.newPromise());
ctx.write(fragment).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write out the padding, if any.
if (paddingBytes(padding) > 0) {
ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding)), promiseAggregator.newPromise());
ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding)))
.addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
}
if (!flags.endOfHeaders()) {
@ -415,7 +420,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
writeFrameHeaderInternal(buf, payloadLength, GO_AWAY, new Http2Flags(), 0);
buf.writeInt(lastStreamId);
buf.writeInt((int) errorCode);
ctx.write(buf, promiseAggregator.newPromise());
ctx.write(buf).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
} catch (Throwable t) {
try {
debugData.release();
@ -427,7 +432,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
}
try {
ctx.write(debugData, promiseAggregator.newPromise());
ctx.write(debugData).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
} catch (Throwable t) {
promiseAggregator.setFailure(t);
}
@ -444,7 +449,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
ByteBuf buf = ctx.alloc().buffer(WINDOW_UPDATE_FRAME_LENGTH);
writeFrameHeaderInternal(buf, INT_FIELD_LENGTH, WINDOW_UPDATE, new Http2Flags(), streamId);
buf.writeInt(windowSizeIncrement);
return ctx.write(buf, promise);
return ctx.write(buf).addListener(new PromiseNotifier<>(promise));
} catch (Throwable t) {
return promise.setFailure(t);
}
@ -460,7 +465,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
// Assume nothing below will throw until buf is written. That way we don't have to take care of ownership
// in the catch block.
writeFrameHeaderInternal(buf, payload.readableBytes(), frameType, flags, streamId);
ctx.write(buf, promiseAggregator.newPromise());
ctx.write(buf).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
} catch (Throwable t) {
try {
payload.release();
@ -471,7 +476,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
return promiseAggregator;
}
try {
ctx.write(payload, promiseAggregator.newPromise());
ctx.write(payload).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
} catch (Throwable t) {
promiseAggregator.setFailure(t);
}
@ -517,14 +522,15 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
// Adjust the weight so that it fits into a single byte on the wire.
buf.writeByte(weight - 1);
}
ctx.write(buf, promiseAggregator.newPromise());
ctx.write(buf).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write the first fragment.
ctx.write(fragment, promiseAggregator.newPromise());
ctx.write(fragment).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write out the padding, if any.
if (paddingBytes(padding) > 0) {
ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding)), promiseAggregator.newPromise());
ctx.write(ZERO_BUFFER.slice(0, paddingBytes(padding)))
.addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
}
if (!flags.endOfHeaders()) {
@ -562,17 +568,17 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
ByteBuf fragment = headerBlock.readRetainedSlice(fragmentReadableBytes);
if (headerBlock.isReadable()) {
ctx.write(buf.retain(), promiseAggregator.newPromise());
ctx.write(buf.retain()).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
} else {
// The frame header is different for the last frame, so re-allocate and release the old buffer
flags = flags.endOfHeaders(true);
buf.release();
buf = ctx.alloc().buffer(CONTINUATION_FRAME_HEADER_LENGTH);
writeFrameHeaderInternal(buf, fragmentReadableBytes, CONTINUATION, flags, streamId);
ctx.write(buf, promiseAggregator.newPromise());
ctx.write(buf).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
}
ctx.write(fragment, promiseAggregator.newPromise());
ctx.write(fragment).addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
} while (headerBlock.isReadable());
}

View File

@ -27,12 +27,12 @@ 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.PromiseNotifier;
import io.netty.util.concurrent.ScheduledFuture;
import io.netty.util.internal.UnstableApi;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;
import static io.netty.buffer.ByteBufUtil.hexDump;
@ -437,31 +437,13 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
}
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
ctx.bind(localAddress, promise);
}
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
Promise<Void> promise) {
ctx.connect(remoteAddress, localAddress, promise);
}
@Override
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.disconnect(promise);
}
@Override
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> close(ChannelHandlerContext ctx) {
if (decoupleCloseAndGoAway) {
ctx.close(promise);
return;
return ctx.close();
}
// Avoid NotYetConnectedException and avoid sending before connection preface
if (!ctx.channel().isActive() || !prefaceSent()) {
ctx.close(promise);
return;
return ctx.close();
}
// If the user has already sent a GO_AWAY frame they may be attempting to do a graceful shutdown which requires
@ -471,7 +453,9 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
// https://github.com/netty/netty/issues/5307
Future<Void> f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise());
ctx.flush();
Promise<Void> promise = ctx.newPromise();
doGracefulShutdown(ctx, f, promise);
return promise;
}
private FutureListener<Object> newClosingChannelFutureListener(
@ -508,26 +492,6 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
}
}
@Override
public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.deregister(promise);
}
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
ctx.read();
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
ctx.write(msg, promise);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
// Trigger flush after read on the assumption that flush is cheap if there is nothing to write and that
@ -955,10 +919,16 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
return;
}
closed = true;
if (promise == null) {
ctx.close();
} else {
ctx.close(promise);
// Before trying to close we need to check if the handler still exists in the pipeline as it may
// have been removed already.
if (!ctx.isRemoved()) {
if (promise == null) {
ctx.close();
} else {
ctx.close().addListener(new PromiseNotifier<>(promise));
}
} else if (promise != null) {
promise.setSuccess(null);
}
}
}

View File

@ -31,6 +31,7 @@ import io.netty.util.collection.IntObjectHashMap;
import io.netty.util.collection.IntObjectMap;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.internal.UnstableApi;
import io.netty.util.internal.logging.InternalLogLevel;
import io.netty.util.internal.logging.InternalLogger;
@ -160,7 +161,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
**/
private int numBufferedStreams;
private final IntObjectMap<DefaultHttp2FrameStream> frameStreamToInitializeMap =
new IntObjectHashMap<DefaultHttp2FrameStream>(8);
new IntObjectHashMap<>(8);
Http2FrameCodec(Http2ConnectionEncoder encoder, Http2ConnectionDecoder decoder, Http2Settings initialSettings,
boolean decoupleCloseAndGoAway) {
@ -281,7 +282,8 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
* streams.
*/
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
Promise<Void> promise = ctx.newPromise();
if (msg instanceof Http2DataFrame) {
Http2DataFrame dataFrame = (Http2DataFrame) msg;
encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(),
@ -338,11 +340,12 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
encoder().writeFrame(ctx, unknownFrame.frameType(), unknownFrame.stream().id(),
unknownFrame.flags(), unknownFrame.content(), promise);
} else if (!(msg instanceof Http2Frame)) {
ctx.write(msg, promise);
ctx.write(msg).addListener(new PromiseNotifier<>(promise));
} else {
ReferenceCountUtil.release(msg);
promise.setFailure(new UnsupportedMessageTypeException(msg));
}
return promise;
}
private void increaseInitialConnectionWindow(int deltaBytes) throws Http2Exception {

View File

@ -23,7 +23,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.EventLoop;
import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi;
import java.util.ArrayDeque;
@ -312,9 +311,7 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
@Override
protected Future<Void> write0(ChannelHandlerContext ctx, Object msg) {
Promise<Void> promise = ctx.newPromise();
Http2MultiplexCodec.this.write(ctx, msg, promise);
return promise;
return Http2MultiplexCodec.this.write(ctx, msg);
}
@Override

View File

@ -26,6 +26,7 @@ import io.netty.handler.codec.http.HttpScheme;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.UnstableApi;
@ -77,14 +78,13 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
* Handles conversion of {@link HttpMessage} and {@link HttpContent} to HTTP/2 frames.
*/
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) {
ctx.write(msg, promise);
return;
return ctx.write(msg);
}
boolean release = true;
Promise<Void> promise = ctx.newPromise();
SimpleChannelPromiseAggregator promiseAggregator =
new SimpleChannelPromiseAggregator(promise, ctx.executor());
try {
@ -142,6 +142,7 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
}
promiseAggregator.doneAllocatingPromises();
}
return promise;
}
private static void writeHeaders(ChannelHandlerContext ctx, Http2ConnectionEncoder encoder, int streamId,

View File

@ -43,7 +43,6 @@ import static org.mockito.Mockito.when;
/**
* Tests for {@link DefaultHttp2FrameWriter}.
*/
@SuppressWarnings("unchecked")
public class DefaultHttp2FrameWriterTest {
private DefaultHttp2FrameWriter frameWriter;
@ -88,7 +87,6 @@ public class DefaultHttp2FrameWriterTest {
return future;
};
when(ctx.write(any())).then(answer);
when(ctx.write(any(), any(Promise.class))).then(answer);
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
when(ctx.channel()).thenReturn(channel);
when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE);

View File

@ -197,6 +197,10 @@ public class Http2ConnectionHandlerTest {
ReferenceCountUtil.release(msg);
return null;
}).when(ctx).fireChannelRead(any());
doAnswer((Answer<Future<Void>>) in ->
DefaultPromise.newSuccessfulPromise(executor, null)).when(ctx).write(any());
doAnswer((Answer<Future<Void>>) in ->
DefaultPromise.newSuccessfulPromise(executor, null)).when(ctx).close();
}
private Http2ConnectionHandler newHandler() throws Exception {
@ -559,14 +563,14 @@ public class Http2ConnectionHandlerTest {
listener.operationComplete(future);
return future;
}).when(future).addListener(any(FutureListener.class));
handler.close(ctx, promise);
handler.close(ctx);
if (future.isDone()) {
when(connection.numActiveStreams()).thenReturn(0);
}
handler.closeStream(stream, future);
// Simulate another stream close call being made after the context should already be closed.
handler.closeStream(stream, future);
verify(ctx, times(1)).close(any(Promise.class));
verify(ctx, times(1)).close();
}
@SuppressWarnings("unchecked")
@ -664,7 +668,7 @@ public class Http2ConnectionHandlerTest {
when(channel.isActive()).thenReturn(false);
handler = newHandler();
when(channel.isActive()).thenReturn(true);
handler.close(ctx, promise);
handler.close(ctx);
verifyZeroInteractions(frameWriter);
}
@ -692,7 +696,7 @@ public class Http2ConnectionHandlerTest {
handler = newHandler();
final long expectedMillis = 1234;
handler.gracefulShutdownTimeoutMillis(expectedMillis);
handler.close(ctx, promise);
handler.close(ctx);
verify(executor, atLeastOnce()).schedule(any(Runnable.class), eq(expectedMillis), eq(TimeUnit.MILLISECONDS));
}
@ -702,7 +706,7 @@ public class Http2ConnectionHandlerTest {
when(connection.numActiveStreams()).thenReturn(0);
final long expectedMillis = 1234;
handler.gracefulShutdownTimeoutMillis(expectedMillis);
handler.close(ctx, promise);
handler.close(ctx);
verify(executor, atLeastOnce()).schedule(any(Runnable.class), eq(expectedMillis), eq(TimeUnit.MILLISECONDS));
}
@ -710,7 +714,7 @@ public class Http2ConnectionHandlerTest {
public void gracefulShutdownIndefiniteTimeoutTest() throws Exception {
handler = newHandler();
handler.gracefulShutdownTimeoutMillis(-1);
handler.close(ctx, promise);
handler.close(ctx);
verify(executor, never()).schedule(any(Runnable.class), anyLong(), any(TimeUnit.class));
}

View File

@ -122,7 +122,7 @@ public class Http2ConnectionRoundtripTest {
@AfterEach
public void teardown() throws Exception {
if (clientChannel != null) {
clientChannel.close().syncUninterruptibly();
clientChannel.close().await();
clientChannel = null;
}
if (serverChannel != null) {
@ -645,18 +645,17 @@ public class Http2ConnectionRoundtripTest {
newPromise());
clientChannel.pipeline().addFirst(new ChannelHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
ReferenceCountUtil.release(msg);
try {
// Ensure we update the window size so we will try to write the rest of the frame while
// processing the flush.
http2Client.encoder().flowController().initialWindowSize(8);
return ctx.newFailedFuture(new IllegalStateException());
} catch (Http2Exception e) {
promise.setFailure(e);
return;
return ctx.newFailedFuture(e);
}
promise.setFailure(new IllegalStateException());
}
});

View File

@ -640,25 +640,24 @@ public class Http2FrameCodecTest {
Http2FrameStream stream1 = frameCodec.newStream();
Http2FrameStream stream2 = frameCodec.newStream();
Promise<Void> promise1 = channel.newPromise();
Promise<Void> promise2 = channel.newPromise();
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1);
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2);
Future<Void> future1 = channel.writeAndFlush(
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1));
Future<Void> future2 = channel.writeAndFlush(
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2));
assertTrue(isStreamIdValid(stream1.id()));
channel.runPendingTasks();
assertTrue(isStreamIdValid(stream2.id()));
assertTrue(promise1.syncUninterruptibly().isSuccess());
assertFalse(promise2.isDone());
assertTrue(future1.syncUninterruptibly().isSuccess());
assertFalse(future2.isDone());
// Increase concurrent streams limit to 2
frameInboundWriter.writeInboundSettings(new Http2Settings().maxConcurrentStreams(2));
channel.flush();
assertTrue(promise2.syncUninterruptibly().isSuccess());
assertTrue(future2.syncUninterruptibly().isSuccess());
}
@Test
@ -671,35 +670,34 @@ public class Http2FrameCodecTest {
Http2FrameStream stream2 = frameCodec.newStream();
Http2FrameStream stream3 = frameCodec.newStream();
Promise<Void> promise1 = channel.newPromise();
Promise<Void> promise2 = channel.newPromise();
Promise<Void> promise3 = channel.newPromise();
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(stream3), promise3);
Future<Void> future1 = channel.writeAndFlush(
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1));
Future<Void> future2 = channel.writeAndFlush(
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2));
Future<Void> future3 = channel.writeAndFlush(
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream3));
assertTrue(isStreamIdValid(stream1.id()));
channel.runPendingTasks();
assertTrue(isStreamIdValid(stream2.id()));
assertTrue(promise1.syncUninterruptibly().isSuccess());
assertFalse(promise2.isDone());
assertFalse(promise3.isDone());
assertTrue(future1.syncUninterruptibly().isSuccess());
assertFalse(future2.isDone());
assertFalse(future3.isDone());
// Increase concurrent streams limit to 2
frameInboundWriter.writeInboundSettings(new Http2Settings().maxConcurrentStreams(2));
channel.flush();
// As we increased the limit to 2 we should have also succeed the second frame.
assertTrue(promise2.syncUninterruptibly().isSuccess());
assertFalse(promise3.isDone());
assertTrue(future2.syncUninterruptibly().isSuccess());
assertFalse(future3.isDone());
frameInboundWriter.writeInboundSettings(new Http2Settings().maxConcurrentStreams(3));
channel.flush();
// With the max streams of 3 all streams should be succeed now.
assertTrue(promise3.syncUninterruptibly().isSuccess());
assertTrue(future3.syncUninterruptibly().isSuccess());
assertFalse(channel.finishAndReleaseAll());
}
@ -707,25 +705,20 @@ public class Http2FrameCodecTest {
@Test
public void doNotLeakOnFailedInitializationForChannels() throws Exception {
setUp(Http2FrameCodecBuilder.forServer(), new Http2Settings().maxConcurrentStreams(2));
Http2FrameStream stream1 = frameCodec.newStream();
Http2FrameStream stream2 = frameCodec.newStream();
Promise<Void> stream1HeaderPromise = channel.newPromise();
Promise<Void> stream2HeaderPromise = channel.newPromise();
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1),
stream1HeaderPromise);
Future<Void> stream1HeaderFuture = channel.writeAndFlush(
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1));
channel.runPendingTasks();
frameInboundWriter.writeInboundGoAway(stream1.id(), 0L, Unpooled.EMPTY_BUFFER);
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2),
stream2HeaderPromise);
Future<Void> stream2HeaderFuture = channel.writeAndFlush(
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2));
channel.runPendingTasks();
assertTrue(stream1HeaderPromise.syncUninterruptibly().isSuccess());
assertTrue(stream2HeaderPromise.isDone());
assertTrue(stream1HeaderFuture.syncUninterruptibly().isSuccess());
assertTrue(stream2HeaderFuture.isDone());
assertEquals(0, frameCodec.numInitializingStreams());
assertFalse(channel.finishAndReleaseAll());
@ -743,15 +736,15 @@ public class Http2FrameCodecTest {
Http2FrameStream stream = frameCodec.newStream();
assertNotNull(stream);
Promise<Void> writePromise = channel.newPromise();
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream), writePromise);
Future<Void> writeFuture = channel.writeAndFlush(
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream));
Http2GoAwayFrame goAwayFrame = inboundHandler.readInbound();
assertNotNull(goAwayFrame);
assertEquals(NO_ERROR.code(), goAwayFrame.errorCode());
assertEquals(Integer.MAX_VALUE, goAwayFrame.lastStreamId());
goAwayFrame.release();
assertThat(writePromise.cause(), instanceOf(Http2NoMoreStreamIdsException.class));
assertThat(writeFuture.cause(), instanceOf(Http2NoMoreStreamIdsException.class));
}
@Test

View File

@ -258,66 +258,20 @@ final class Http2FrameInboundWriter {
return channel.deregister();
}
@Override
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return channel.bind(localAddress, promise);
}
@Override
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return channel.connect(remoteAddress, promise);
}
@Override
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return channel.connect(remoteAddress, localAddress, promise);
}
@Override
public Future<Void> disconnect(Promise<Void> promise) {
return channel.disconnect(promise);
}
@Override
public Future<Void> close(Promise<Void> promise) {
return channel.close(promise);
}
@Override
public Future<Void> register(Promise<Void> promise) {
return channel.register(promise);
}
@Override
public Future<Void> deregister(Promise<Void> promise) {
return channel.deregister(promise);
}
@Override
public Future<Void> write(Object msg) {
return write(msg, newPromise());
}
@Override
public Future<Void> write(Object msg, Promise<Void> promise) {
return writeAndFlush(msg, promise);
}
@Override
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
try {
channel.writeInbound(msg);
channel.runPendingTasks();
promise.setSuccess(null);
} catch (Throwable cause) {
promise.setFailure(cause);
}
return promise;
return writeAndFlush(msg);
}
@Override
public Future<Void> writeAndFlush(Object msg) {
return writeAndFlush(msg, newPromise());
try {
channel.writeInbound(msg);
channel.runPendingTasks();
} catch (Throwable cause) {
return newFailedFuture(cause);
}
return newSucceededFuture();
}
@Override

View File

@ -25,6 +25,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AsciiString;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach;
@ -54,6 +55,7 @@ import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyShort;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
@ -65,7 +67,6 @@ import static org.mockito.Mockito.when;
/**
* Tests encoding/decoding each HTTP2 frame type.
*/
@SuppressWarnings("unchecked")
public class Http2FrameRoundtripTest {
private static final byte[] MESSAGE = "hello world".getBytes(UTF_8);
private static final int STREAM_ID = 0x7FFFFFFF;
@ -98,10 +99,12 @@ public class Http2FrameRoundtripTest {
when(ctx.alloc()).thenReturn(alloc);
when(ctx.executor()).thenReturn(executor);
when(ctx.channel()).thenReturn(channel);
doAnswer((Answer<Future<Void>>) in ->
DefaultPromise.newSuccessfulPromise(executor, null)).when(ctx).write(any());
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<Promise<Void>>) invocation ->
new DefaultPromise<Void>(GlobalEventExecutor.INSTANCE)).when(ctx).newPromise();
new DefaultPromise<>(GlobalEventExecutor.INSTANCE)).when(ctx).newPromise();
writer = new DefaultHttp2FrameWriter(new DefaultHttp2HeadersEncoder(NEVER_SENSITIVE, newTestEncoder()));
reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(false, newTestDecoder()));
@ -433,7 +436,7 @@ public class Http2FrameRoundtripTest {
private ByteBuf captureWrites() {
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
verify(ctx, atLeastOnce()).write(captor.capture(), isA(Promise.class));
verify(ctx, atLeastOnce()).write(captor.capture());
CompositeByteBuf composite = releaseLater(Unpooled.compositeBuffer());
for (ByteBuf buf : captor.getAllValues()) {
buf = releaseLater(buf.retain());

View File

@ -29,6 +29,7 @@ import io.netty.util.AsciiString;
import io.netty.util.AttributeKey;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import org.hamcrest.CoreMatchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@ -738,7 +739,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
channelOpen.set(channel.isOpen());
channelActive.set(channel.isActive());
});
childChannel.close(p).syncUninterruptibly();
childChannel.close().addListener(new PromiseNotifier<>(p)).syncUninterruptibly();
assertFalse(channelOpen.get());
assertFalse(channelActive.get());

View File

@ -45,6 +45,7 @@ import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
@ -454,13 +455,12 @@ public class Http2StreamFrameToHttpObjectCodecTest {
EmbeddedChannel ch = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT),
new ChannelHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof Http2StreamFrame) {
frames.add((Http2StreamFrame) msg);
ctx.write(Unpooled.EMPTY_BUFFER, promise);
} else {
ctx.write(msg, promise);
return ctx.write(Unpooled.EMPTY_BUFFER);
}
return ctx.write(msg);
}
}, new Http2StreamFrameToHttpObjectCodec(false));
@ -884,26 +884,24 @@ public class Http2StreamFrameToHttpObjectCodecTest {
EmbeddedChannel tlsCh = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT),
new ChannelHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof Http2StreamFrame) {
frames.add((Http2StreamFrame) msg);
promise.setSuccess(null);
} else {
ctx.write(msg, promise);
return ctx.newSucceededFuture();
}
return ctx.write(msg);
}
}, sharedHandler);
EmbeddedChannel plaintextCh = new EmbeddedChannel(
new ChannelHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (msg instanceof Http2StreamFrame) {
frames.add((Http2StreamFrame) msg);
promise.setSuccess(null);
} else {
ctx.write(msg, promise);
return ctx.newSucceededFuture();
}
return ctx.write(msg);
}
}, sharedHandler);

View File

@ -145,8 +145,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -168,8 +167,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -189,8 +187,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.add(HttpHeaderNames.COOKIE, "c=d")
.add(HttpHeaderNames.COOKIE, "e=f");
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -205,8 +202,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.path(new AsciiString("/where?q=now&f=then#section1"))
.scheme(new AsciiString("http"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -222,8 +218,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.path(new AsciiString("/where%2B0?q=now%2B0&f=then%2B0#section1%2B0"))
.scheme(new AsciiString("http"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -240,8 +235,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.path(new AsciiString("/pub/WWW/TheProject.html"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("https"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -256,8 +250,7 @@ public class HttpToHttp2ConnectionHandlerTest {
.path(new AsciiString("/pub/WWW/TheProject.html"))
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -270,8 +263,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("CONNECT")).path(new AsciiString("/"))
.scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -286,8 +278,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("OPTIONS")).path(new AsciiString("*"))
.scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -304,8 +295,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
.scheme(new AsciiString("http")).authority(new AsciiString("[::1]:80"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -320,8 +310,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
.scheme(new AsciiString("http")).authority(new AsciiString("localhost:80"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -336,8 +325,7 @@ public class HttpToHttp2ConnectionHandlerTest {
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
.scheme(new AsciiString("http")).authority(new AsciiString("1.2.3.4:80"));
Promise<Void> writePromise = newPromise();
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
verifyHeadersOnly(http2Headers, clientChannel.writeAndFlush(request));
}
@Test
@ -347,12 +335,10 @@ public class HttpToHttp2ConnectionHandlerTest {
final HttpHeaders httpHeaders = request.headers();
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
httpHeaders.set(HttpHeaderNames.HOST, "localhost");
Promise<Void> writePromise = newPromise();
Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isDone());
assertFalse(writePromise.isSuccess());
Future<Void> writeFuture = clientChannel.writeAndFlush(request);
assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writeFuture.isDone());
assertFalse(writeFuture.isSuccess());
}
@ -366,18 +352,13 @@ public class HttpToHttp2ConnectionHandlerTest {
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), -1);
httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http");
httpHeaders.set(HttpHeaderNames.HOST, "localhost");
Promise<Void> writePromise = newPromise();
Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isDone());
assertFalse(writePromise.isSuccess());
Throwable cause = writePromise.cause();
assertThat(cause, instanceOf(Http2NoMoreStreamIdsException.class));
Future<Void> writeFuture = clientChannel.writeAndFlush(request);
assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writeFuture.isDone());
assertFalse(writeFuture.isSuccess());
cause = writeFuture.cause();
Throwable cause = writeFuture.cause();
assertThat(cause, instanceOf(Http2NoMoreStreamIdsException.class));
}
@ -405,11 +386,9 @@ public class HttpToHttp2ConnectionHandlerTest {
.add(new AsciiString("foo"), new AsciiString("goo"))
.add(new AsciiString("foo"), new AsciiString("goo2"))
.add(new AsciiString("foo2"), new AsciiString("goo2"));
Promise<Void> writePromise = newPromise();
Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isSuccess());
Future<Void> writeFuture = clientChannel.writeAndFlush(request);
assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writeFuture.isSuccess());
awaitRequests();
@ -451,11 +430,8 @@ public class HttpToHttp2ConnectionHandlerTest {
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
.add(new AsciiString("trailing"), new AsciiString("bar"));
Promise<Void> writePromise = newPromise();
Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
Future<Void> writeFuture = clientChannel.writeAndFlush(request);
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isSuccess());
assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writeFuture.isSuccess());
awaitRequests();
@ -503,27 +479,18 @@ public class HttpToHttp2ConnectionHandlerTest {
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
.add(new AsciiString("trailing"), new AsciiString("bar"));
Promise<Void> writePromise = newPromise();
Future<Void> writeFuture = clientChannel.write(request, writePromise);
Promise<Void> contentPromise = newPromise();
Future<Void> contentFuture = clientChannel.write(httpContent, contentPromise);
Promise<Void> lastContentPromise = newPromise();
Future<Void> lastContentFuture = clientChannel.write(lastHttpContent, lastContentPromise);
Future<Void> writeFuture = clientChannel.write(request);
Future<Void> contentFuture = clientChannel.write(httpContent);
Future<Void> lastContentFuture = clientChannel.write(lastHttpContent);
clientChannel.flush();
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isSuccess());
assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writeFuture.isSuccess());
assertTrue(contentPromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(contentPromise.isSuccess());
assertTrue(contentFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(contentFuture.isSuccess());
assertTrue(lastContentPromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(lastContentPromise.isSuccess());
assertTrue(lastContentFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(lastContentFuture.isSuccess());
@ -596,11 +563,8 @@ public class HttpToHttp2ConnectionHandlerTest {
assertTrue(prefaceWrittenLatch.await(5, SECONDS));
assertTrue(serverChannelLatch.await(WAIT_TIME_SECONDS, SECONDS));
}
private void verifyHeadersOnly(Http2Headers expected, Promise<Void> writePromise, Future<Void> writeFuture)
private void verifyHeadersOnly(Http2Headers expected, Future<Void> writeFuture)
throws Exception {
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writePromise.isSuccess());
assertTrue(writeFuture.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
assertTrue(writeFuture.isSuccess());
awaitRequests();

View File

@ -254,57 +254,11 @@ public final class BinaryMemcacheClientCodec extends
return ctx.register();
}
@Override
public Future<Void> register(Promise<Void> promise) {
return ctx.register(promise);
}
@Override
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return ctx.bind(localAddress, promise);
}
@Override
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, promise);
}
@Override
public Future<Void> connect(
SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, localAddress, promise);
}
@Override
public Future<Void> disconnect(Promise<Void> promise) {
return ctx.disconnect(promise);
}
@Override
public Future<Void> close(Promise<Void> promise) {
return ctx.close(promise);
}
@Override
public Future<Void> deregister(Promise<Void> promise) {
return ctx.deregister(promise);
}
@Override
public Future<Void> write(Object msg) {
return ctx.write(msg);
}
@Override
public Future<Void> write(Object msg, Promise<Void> promise) {
return ctx.write(msg, promise);
}
@Override
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return ctx.writeAndFlush(msg, promise);
}
@Override
public Future<Void> writeAndFlush(Object msg) {
return ctx.writeAndFlush(msg);

View File

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

View File

@ -599,6 +599,11 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
return this;
}
@Override
public Future<Void> register() {
return ctx.register();
}
@Override
public ChannelHandlerContext read() {
ctx.read();
@ -663,61 +668,11 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
return ctx.deregister();
}
@Override
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return ctx.bind(localAddress, promise);
}
@Override
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, promise);
}
@Override
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, localAddress, promise);
}
@Override
public Future<Void> disconnect(Promise<Void> promise) {
return ctx.disconnect(promise);
}
@Override
public Future<Void> close(Promise<Void> promise) {
return ctx.close(promise);
}
@Override
public Future<Void> register() {
return ctx.register();
}
@Override
public Future<Void> register(Promise<Void> promise) {
return ctx.register(promise);
}
@Override
public Future<Void> deregister(Promise<Void> promise) {
return ctx.deregister(promise);
}
@Override
public Future<Void> write(Object msg) {
return ctx.write(msg);
}
@Override
public Future<Void> write(Object msg, Promise<Void> promise) {
return ctx.write(msg, promise);
}
@Override
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return ctx.writeAndFlush(msg, promise);
}
@Override
public Future<Void> writeAndFlush(Object msg) {
return ctx.writeAndFlush(msg);

View File

@ -21,7 +21,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.protobuf.ProtobufEncoder;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.StringUtil;
import java.net.InetSocketAddress;
@ -91,34 +91,34 @@ public class DatagramPacketEncoder<M> extends MessageToMessageEncoder<AddressedE
}
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
encoder.bind(ctx, localAddress, promise);
public Future<Void> bind(ChannelHandlerContext ctx, SocketAddress localAddress) {
return encoder.bind(ctx, localAddress);
}
@Override
public void connect(
public Future<Void> connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, Promise<Void> promise) {
encoder.connect(ctx, remoteAddress, localAddress, promise);
SocketAddress localAddress) {
return encoder.connect(ctx, remoteAddress, localAddress);
}
@Override
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
encoder.disconnect(ctx, promise);
public Future<Void> disconnect(ChannelHandlerContext ctx) {
return encoder.disconnect(ctx);
}
@Override
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
encoder.close(ctx, promise);
public Future<Void> close(ChannelHandlerContext ctx) {
return encoder.close(ctx);
}
@Override
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
encoder.deregister(ctx, promise);
public Future<Void> deregister(ChannelHandlerContext ctx) {
return encoder.deregister(ctx);
}
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
public void read(ChannelHandlerContext ctx) {
encoder.read(ctx);
}

View File

@ -22,7 +22,7 @@ import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.TypeParameterMatcher;
@ -96,7 +96,7 @@ public abstract class MessageToByteEncoder<I> extends ChannelHandlerAdapter {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
ByteBuf buf = null;
try {
if (acceptOutboundMessage(msg)) {
@ -110,19 +110,17 @@ public abstract class MessageToByteEncoder<I> extends ChannelHandlerAdapter {
}
if (buf.isReadable()) {
ctx.write(buf, promise);
} else {
buf.release();
ctx.write(Unpooled.EMPTY_BUFFER, promise);
Future<Void> f = ctx.write(buf);
buf = null;
return f;
}
buf = null;
} else {
ctx.write(msg, promise);
return ctx.write(Unpooled.EMPTY_BUFFER);
}
return ctx.write(msg);
} catch (EncoderException e) {
promise.setFailure(e);
return ctx.newFailedFuture(e);
} catch (Throwable e) {
promise.setFailure(new EncoderException(e));
return ctx.newFailedFuture(new EncoderException(e));
} finally {
if (buf != null) {
buf.release();

View File

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

View File

@ -21,8 +21,10 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseCombiner;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.TypeParameterMatcher;
@ -78,42 +80,44 @@ public abstract class MessageToMessageEncoder<I> extends ChannelHandlerAdapter {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
CodecOutputList out = null;
try {
if (acceptOutboundMessage(msg)) {
out = CodecOutputList.newInstance();
@SuppressWarnings("unchecked")
I cast = (I) msg;
Promise<Void> promise = ctx.newPromise();
try {
encode(ctx, cast, out);
} finally {
ReferenceCountUtil.release(cast);
}
try {
encode(ctx, cast, out);
} finally {
ReferenceCountUtil.release(cast);
}
if (out.isEmpty()) {
throw new EncoderException(
StringUtil.simpleClassName(this) + " must produce at least one message.");
}
} else {
ctx.write(msg, promise);
}
} catch (EncoderException e) {
promise.setFailure(e);
} catch (Throwable t) {
promise.setFailure(new EncoderException(t));
} finally {
if (out != null) {
try {
final int sizeMinusOne = out.size() - 1;
if (sizeMinusOne == 0) {
ctx.write(out.getUnsafe(0), promise);
} else if (sizeMinusOne > 0) {
writePromiseCombiner(ctx, out, promise);
if (out.isEmpty()) {
throw new EncoderException(
StringUtil.simpleClassName(this) + " must produce at least one message.");
}
} finally {
out.recycle();
final int sizeMinusOne = out.size() - 1;
if (sizeMinusOne == 0) {
PromiseNotifier.cascade(ctx.write(out.getUnsafe(0)), promise);
} else {
writePromiseCombiner(ctx, out, promise);
}
}
return promise;
} else {
return ctx.write(msg);
}
} catch (EncoderException e) {
return ctx.newFailedFuture(e);
} catch (Throwable t) {
return ctx.newFailedFuture(new EncoderException(t));
} finally {
if (out != null) {
out.recycle();
}
}
}

View File

@ -174,22 +174,14 @@ public class Bzip2Encoder extends MessageToByteEncoder<ByteBuf> {
* The returned {@link Future} will be notified once the operation completes.
*/
public Future<Void> close() {
return close(ctx().newPromise());
}
/**
* Close this {@link Bzip2Encoder} and so finish the encoding.
* The given {@link Future} will be notified once the operation
* completes and will also be returned.
*/
public Future<Void> close(Promise<Void> promise) {
ChannelHandlerContext ctx = ctx();
EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) {
return finishEncode(ctx, promise);
return finishEncode(ctx);
} else {
Promise<Void> promise = ctx.newPromise();
executor.execute(() -> {
Future<Void> f = finishEncode(ctx(), promise);
Future<Void> f = finishEncode(ctx());
PromiseNotifier.cascade(f, promise);
});
return promise;
@ -197,22 +189,23 @@ public class Bzip2Encoder extends MessageToByteEncoder<ByteBuf> {
}
@Override
public void close(final ChannelHandlerContext ctx, Promise<Void> promise) {
Future<Void> f = finishEncode(ctx, ctx.newPromise());
f.addListener(f1 -> ctx.close(promise));
if (!f.isDone()) {
// Ensure the channel is closed even if the write operation completes in time.
ctx.executor().schedule(() -> {
ctx.close(promise);
}, 10, TimeUnit.SECONDS); // FIXME: Magic number
public Future<Void> close(final ChannelHandlerContext ctx) {
Future<Void> f = finishEncode(ctx);
if (f.isDone()) {
return ctx.close();
}
Promise<Void> promise = ctx.newPromise();
f.addListener(f1 -> ctx.close().addListener(new PromiseNotifier<>(false, promise)));
// Ensure the channel is closed even if the write operation completes in time.
ctx.executor().schedule(() -> {
ctx.close().addListener(new PromiseNotifier<>(false, promise));
}, 10, TimeUnit.SECONDS); // FIXME: Magic number
return promise;
}
private Future<Void> finishEncode(final ChannelHandlerContext ctx, Promise<Void> promise) {
private Future<Void> finishEncode(final ChannelHandlerContext ctx) {
if (finished) {
promise.setSuccess(null);
return promise;
return ctx.newSucceededFuture();
}
finished = true;
@ -229,7 +222,7 @@ public class Bzip2Encoder extends MessageToByteEncoder<ByteBuf> {
} finally {
blockCompressor = null;
}
return ctx.writeAndFlush(footer, promise);
return ctx.writeAndFlush(footer);
}
private ChannelHandlerContext ctx() {

View File

@ -156,20 +156,15 @@ public class JdkZlibEncoder extends ZlibEncoder {
@Override
public Future<Void> close() {
return close(ctx().newPromise());
}
@Override
public Future<Void> close(Promise<Void> promise) {
ChannelHandlerContext ctx = ctx();
EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) {
return finishEncode(ctx, promise);
return finishEncode(ctx);
} else {
Promise<Void> p = ctx.newPromise();
executor.execute(() -> {
Future<Void> f = finishEncode(ctx(), p);
PromiseNotifier.cascade(f, promise);
Future<Void> f = finishEncode(ctx());
PromiseNotifier.cascade(f, p);
});
return p;
}
@ -261,22 +256,23 @@ public class JdkZlibEncoder extends ZlibEncoder {
}
@Override
public void close(final ChannelHandlerContext ctx, Promise<Void> promise) {
Future<Void> f = finishEncode(ctx, ctx.newPromise());
f.addListener(f1 -> ctx.close(promise));
if (!f.isDone()) {
// Ensure the channel is closed even if the write operation completes in time.
ctx.executor().schedule(() -> {
ctx.close(promise);
}, 10, TimeUnit.SECONDS); // FIXME: Magic number
public Future<Void> close(final ChannelHandlerContext ctx) {
Future<Void> f = finishEncode(ctx);
if (f.isDone()) {
return ctx.close();
}
Promise<Void> promise = ctx.newPromise();
f.addListener(f1 -> ctx.close().addListener(new PromiseNotifier<>(false, promise)));
// Ensure the channel is closed even if the write operation completes in time.
ctx.executor().schedule(() -> {
ctx.close().addListener(new PromiseNotifier<>(false, promise));
}, 10, TimeUnit.SECONDS); // FIXME: Magic number
return promise;
}
private Future<Void> finishEncode(final ChannelHandlerContext ctx, Promise<Void> promise) {
private Future<Void> finishEncode(final ChannelHandlerContext ctx) {
if (finished) {
promise.setSuccess(null);
return promise;
return ctx.newSucceededFuture();
}
finished = true;
@ -310,7 +306,7 @@ public class JdkZlibEncoder extends ZlibEncoder {
footer.writeByte(uncBytes >>> 24);
}
deflater.end();
return ctx.writeAndFlush(footer, promise);
return ctx.writeAndFlush(footer);
}
private void deflate(ByteBuf out) {

View File

@ -304,10 +304,9 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
ctx.flush();
}
private Future<Void> finishEncode(final ChannelHandlerContext ctx, Promise<Void> promise) {
private Future<Void> finishEncode(final ChannelHandlerContext ctx) {
if (finished) {
promise.setSuccess(null);
return promise;
return ctx.newSucceededFuture();
}
finished = true;
@ -325,7 +324,7 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
footer.writerIndex(idx + HEADER_LENGTH);
return ctx.writeAndFlush(footer, promise);
return ctx.writeAndFlush(footer);
}
/**
@ -341,22 +340,14 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
* The returned {@link java.util.concurrent.Future} will be notified once the operation completes.
*/
public Future<Void> close() {
return close(ctx().newPromise());
}
/**
* Close this {@link Lz4FrameEncoder} and so finish the encoding.
* The given {@link java.util.concurrent.Future} will be notified once the operation
* completes and will also be returned.
*/
public Future<Void> close(Promise<Void> promise) {
ChannelHandlerContext ctx = ctx();
EventExecutor executor = ctx.executor();
if (executor.inEventLoop()) {
return finishEncode(ctx, promise);
return finishEncode(ctx);
} else {
Promise<Void> promise = ctx.newPromise();
executor.execute(() -> {
Future<Void> f = finishEncode(ctx(), promise);
Future<Void> f = finishEncode(ctx());
PromiseNotifier.cascade(f, promise);
});
return promise;
@ -364,16 +355,19 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
}
@Override
public void close(final ChannelHandlerContext ctx, Promise<Void> promise) {
Future<Void> f = finishEncode(ctx, ctx.newPromise());
f.addListener(f1 -> ctx.close(promise));
if (!f.isDone()) {
// Ensure the channel is closed even if the write operation completes in time.
ctx.executor().schedule(() -> {
ctx.close(promise);
}, 10, TimeUnit.SECONDS); // FIXME: Magic number
public Future<Void> close(final ChannelHandlerContext ctx) {
Future<Void> f = finishEncode(ctx);
if (f.isDone()) {
return ctx.close();
}
Promise<Void> promise = ctx.newPromise();
f.addListener(f1 ->
ctx.close().addListener(new PromiseNotifier<>(false, promise)));
// Ensure the channel is closed even if the write operation completes in time.
ctx.executor().schedule(() -> {
ctx.close().addListener(new PromiseNotifier<>(false, promise));
}, 10, TimeUnit.SECONDS); // FIXME: Magic number
return promise;
}
private ChannelHandlerContext ctx() {

View File

@ -18,7 +18,6 @@ package io.netty.handler.codec.compression;
import io.netty.buffer.ByteBuf;
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.
@ -46,12 +45,4 @@ public abstract class ZlibEncoder extends MessageToByteEncoder<ByteBuf> {
* operation completes.
*/
public abstract Future<Void> close();
/**
* Close this {@link ZlibEncoder} and so finish the encoding.
* The given {@link Future} will be notified once the operation
* completes and will also be returned.
*/
public abstract Future<Void> close(Promise<Void> promise);
}

View File

@ -37,7 +37,7 @@ public class MessageAggregatorTest {
int value;
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
public void read(ChannelHandlerContext ctx) {
value++;
ctx.read();
}

View File

@ -19,7 +19,6 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
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 java.util.List;
@ -59,12 +58,12 @@ public class MessageToMessageEncoderTest {
ChannelHandler writeThrower = new ChannelHandler() {
private boolean firstWritten;
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (firstWritten) {
ctx.write(msg, promise);
return ctx.write(msg);
} else {
firstWritten = true;
promise.setFailure(firstWriteException);
return ctx.newFailedFuture(firstWriteException);
}
}
};

View File

@ -21,7 +21,6 @@ import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.handler.codec.haproxy.HAProxyMessage;
import io.netty.handler.codec.haproxy.HAProxyMessageEncoder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
public class HAProxyHandler extends ChannelOutboundHandlerAdapter {
@ -32,8 +31,8 @@ public class HAProxyHandler extends ChannelOutboundHandlerAdapter {
}
@Override
public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
Future<Void> future = ctx.write(msg, promise);
public Future<Void> write(final ChannelHandlerContext ctx, Object msg) {
Future<Void> future = ctx.write(msg);
if (msg instanceof HAProxyMessage) {
future.addListener(fut -> {
if (fut.isSuccess()) {
@ -44,5 +43,6 @@ public class HAProxyHandler extends ChannelOutboundHandlerAdapter {
}
});
}
return future;
}
}

View File

@ -25,7 +25,8 @@ import io.netty.handler.codec.memcache.binary.DefaultBinaryMemcacheRequest;
import io.netty.handler.codec.memcache.binary.DefaultFullBinaryMemcacheRequest;
import io.netty.handler.codec.memcache.binary.FullBinaryMemcacheResponse;
import io.netty.util.CharsetUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import io.netty.util.ReferenceCountUtil;
public class MemcacheClientHandler implements ChannelHandler {
@ -33,7 +34,7 @@ public class MemcacheClientHandler implements ChannelHandler {
* Transforms basic string requests to binary memcache requests
*/
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
String command = (String) msg;
if (command.startsWith("get ")) {
String keyString = command.substring("get ".length());
@ -42,8 +43,9 @@ public class MemcacheClientHandler implements ChannelHandler {
BinaryMemcacheRequest req = new DefaultBinaryMemcacheRequest(key);
req.setOpcode(BinaryMemcacheOpcodes.GET);
ctx.write(req, promise);
} else if (command.startsWith("set ")) {
return ctx.write(req);
}
if (command.startsWith("set ")) {
String[] parts = command.split(" ", 3);
if (parts.length < 3) {
throw new IllegalArgumentException("Malformed Command: " + command);
@ -59,9 +61,11 @@ public class MemcacheClientHandler implements ChannelHandler {
BinaryMemcacheRequest req = new DefaultFullBinaryMemcacheRequest(key, extras, content);
req.setOpcode(BinaryMemcacheOpcodes.SET);
ctx.write(req, promise);
return ctx.write(req);
} else {
throw new IllegalStateException("Unknown Message: " + msg);
IllegalStateException ex = new IllegalStateException("Unknown Message: " + msg);
ReferenceCountUtil.release(msg);
return ctx.newFailedFuture(ex);
}
}

View File

@ -28,7 +28,7 @@ import io.netty.handler.codec.redis.RedisMessage;
import io.netty.handler.codec.redis.SimpleStringRedisMessage;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import java.util.ArrayList;
import java.util.List;
@ -39,14 +39,14 @@ import java.util.List;
public class RedisClientHandler implements ChannelHandler {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
String[] commands = ((String) msg).split("\\s+");
List<RedisMessage> children = new ArrayList<>(commands.length);
for (String cmdString : commands) {
children.add(new FullBulkStringRedisMessage(ByteBufUtil.writeUtf8(ctx.alloc(), cmdString)));
}
RedisMessage request = new ArrayRedisMessage(children);
ctx.write(request, promise);
return ctx.write(request);
}
@Override

View File

@ -32,7 +32,7 @@ import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.util.AsciiString;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
@ -285,40 +285,39 @@ public final class HttpProxyHandler extends ProxyHandler {
}
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress,
Promise<Void> promise) {
codec.bind(ctx, localAddress, promise);
public Future<Void> bind(ChannelHandlerContext ctx, SocketAddress localAddress) {
return codec.bind(ctx, localAddress);
}
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
Promise<Void> promise) {
codec.connect(ctx, remoteAddress, localAddress, promise);
public Future<Void> connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress) {
return codec.connect(ctx, remoteAddress, localAddress);
}
@Override
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
codec.disconnect(ctx, promise);
public Future<Void> disconnect(ChannelHandlerContext ctx) {
return codec.disconnect(ctx);
}
@Override
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
codec.close(ctx, promise);
public Future<Void> close(ChannelHandlerContext ctx) {
return codec.close(ctx);
}
@Override
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
codec.deregister(ctx, promise);
public Future<Void> deregister(ChannelHandlerContext ctx) {
return codec.deregister(ctx);
}
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
public void read(ChannelHandlerContext ctx) {
codec.read(ctx);
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
codec.write(ctx, msg, promise);
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
return codec.write(ctx, msg);
}
@Override

View File

@ -164,17 +164,14 @@ public abstract class ProxyHandler implements ChannelHandler {
protected abstract void removeDecoder(ChannelHandlerContext ctx) throws Exception;
@Override
public final void connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
Promise<Void> promise) {
public final Future<Void> connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress) {
if (destinationAddress != null) {
promise.setFailure(new ConnectionPendingException());
return;
return ctx.newFailedFuture(new ConnectionPendingException());
}
destinationAddress = remoteAddress;
ctx.connect(proxyAddress, localAddress, promise);
return ctx.connect(proxyAddress, localAddress);
}
@Override
@ -393,13 +390,14 @@ public abstract class ProxyHandler implements ChannelHandler {
}
@Override
public final void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public final Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (finished) {
writePendingWrites();
ctx.write(msg, promise);
} else {
addPendingWrite(ctx, msg, promise);
return ctx.write(msg);
}
Promise<Void> promise = ctx.newPromise();
addPendingWrite(ctx, msg, promise);
return promise;
}
@Override
@ -420,8 +418,9 @@ public abstract class ProxyHandler implements ChannelHandler {
private void writePendingWrites() {
if (pendingWrites != null) {
pendingWrites.removeAndWriteAll();
PendingWriteQueue queue = pendingWrites;
pendingWrites = null;
queue.removeAndWriteAll();
}
}

View File

@ -256,13 +256,13 @@ public class HttpProxyHandlerTest {
verifyNoMoreInteractions(promise);
ChannelHandlerContext ctx = mock(ChannelHandlerContext.class);
when(ctx.connect(same(proxyAddress), isNull(InetSocketAddress.class), same(promise))).thenReturn(promise);
when(ctx.connect(same(proxyAddress), isNull(InetSocketAddress.class))).thenReturn(promise);
HttpProxyHandler handler = new HttpProxyHandler(
new InetSocketAddress(NetUtil.LOCALHOST, 8080),
headers,
ignoreDefaultPortsInConnectHostHeader);
handler.connect(ctx, socketAddress, null, promise);
handler.connect(ctx, socketAddress, null);
FullHttpRequest request = (FullHttpRequest) handler.newInitialMessage(ctx);
try {
@ -280,7 +280,7 @@ public class HttpProxyHandlerTest {
} finally {
request.release();
}
verify(ctx).connect(proxyAddress, null, promise);
verify(ctx).connect(proxyAddress, null);
}
@Test

View File

@ -17,8 +17,7 @@ package io.netty.handler.address;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import java.net.NetworkInterface;
import java.net.SocketAddress;
@ -32,18 +31,17 @@ import java.net.SocketAddress;
public abstract class DynamicAddressConnectHandler implements ChannelHandler {
@Override
public final void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, Promise<Void> promise) {
public final Future<Void> connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress) {
final SocketAddress remote;
final SocketAddress local;
try {
remote = remoteAddress(remoteAddress, localAddress);
local = localAddress(remoteAddress, localAddress);
} catch (Exception e) {
promise.setFailure(e);
return;
return ctx.newFailedFuture(e);
}
ctx.connect(remote, local, promise).addListener(future -> {
return ctx.connect(remote, local).addListener(future -> {
if (future.isSuccess()) {
// We only remove this handler from the pipeline once the connect was successful as otherwise
// the user may try to connect again.

View File

@ -20,16 +20,18 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.resolver.AddressResolver;
import io.netty.resolver.AddressResolverGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.internal.ObjectUtil;
import java.net.SocketAddress;
/**
* {@link ChannelHandler} which will resolve the {@link SocketAddress} that is passed to
* {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, Promise)} if it is not
* already resolved and the {@link AddressResolver} supports the type of {@link SocketAddress}.
* {@link #connect(ChannelHandlerContext, SocketAddress, SocketAddress)} if it is not already resolved
* and the {@link AddressResolver} supports the type of {@link SocketAddress}.
*/
@Sharable
public class ResolveAddressHandler implements ChannelHandler {
@ -41,22 +43,25 @@ public class ResolveAddressHandler implements ChannelHandler {
}
@Override
public void connect(final ChannelHandlerContext ctx, SocketAddress remoteAddress,
final SocketAddress localAddress, final Promise<Void> promise) {
public Future<Void> connect(final ChannelHandlerContext ctx, SocketAddress remoteAddress,
final SocketAddress localAddress) {
AddressResolver<? extends SocketAddress> resolver = resolverGroup.getResolver(ctx.executor());
if (resolver.isSupported(remoteAddress) && !resolver.isResolved(remoteAddress)) {
Promise<Void> promise = ctx.newPromise();
resolver.resolve(remoteAddress).addListener((FutureListener<SocketAddress>) future -> {
Throwable cause = future.cause();
if (cause != null) {
promise.setFailure(cause);
} else {
ctx.connect(future.getNow(), localAddress, promise);
ctx.connect(future.getNow(), localAddress).addListener(new PromiseNotifier<>(promise));
}
ctx.pipeline().remove(ResolveAddressHandler.this);
});
return promise;
} else {
ctx.connect(remoteAddress, localAddress, promise);
Future<Void> f = ctx.connect(remoteAddress, localAddress);
ctx.pipeline().remove(this);
return f;
}
}
}

View File

@ -132,7 +132,7 @@ public class FlowControlHandler implements ChannelHandler {
}
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
public void read(ChannelHandlerContext ctx) {
if (dequeue(ctx, 1) == 0) {
// It seems no messages were consumed. We need to read() some
// messages from upstream and once one arrives it need to be

View File

@ -20,17 +20,14 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundInvoker;
import io.netty.channel.ChannelPipeline;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.ObjectUtil;
import java.util.concurrent.Future;
/**
* {@link ChannelHandler} which consolidates {@link Channel#flush()} / {@link ChannelHandlerContext#flush()}
* operations (which also includes
* {@link Channel#writeAndFlush(Object)} / {@link Channel#writeAndFlush(Object, Promise)} and
* {@link ChannelOutboundInvoker#writeAndFlush(Object)} /
* {@link ChannelOutboundInvoker#writeAndFlush(Object, Promise)}).
* {@link Channel#writeAndFlush(Object)} and
* {@link ChannelOutboundInvoker#writeAndFlush(Object)}.
* <p>
* Flush operations are generally speaking expensive as these may trigger a syscall on the transport level. Thus it is
* in most cases (where write latency can be traded with throughput) a good idea to try to minimize flush operations
@ -155,17 +152,17 @@ public class FlushConsolidationHandler implements ChannelHandler {
}
@Override
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> disconnect(ChannelHandlerContext ctx) {
// Try to flush one last time if flushes are pending before disconnect the channel.
resetReadAndFlushIfNeeded(ctx);
ctx.disconnect(promise);
return ctx.disconnect();
}
@Override
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> close(ChannelHandlerContext ctx) {
// Try to flush one last time if flushes are pending before close the channel.
resetReadAndFlushIfNeeded(ctx);
ctx.close(promise);
return ctx.close();
}
@Override

View File

@ -21,7 +21,7 @@ import io.netty.buffer.ByteBufHolder;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.logging.InternalLogLevel;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -37,7 +37,7 @@ import static java.util.Objects.requireNonNull;
* By default, all events are logged at <tt>DEBUG</tt> level and full hex dumps are recorded for ByteBufs.
*/
@Sharable
@SuppressWarnings({ "StringConcatenationInsideStringBufferAppend", "StringBufferReplaceableByString" })
@SuppressWarnings({ "StringBufferReplaceableByString" })
public class LoggingHandler implements ChannelHandler {
private static final LogLevel DEFAULT_LEVEL = LogLevel.DEBUG;
@ -222,45 +222,45 @@ public class LoggingHandler implements ChannelHandler {
}
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
public Future<Void> bind(ChannelHandlerContext ctx, SocketAddress localAddress) {
if (logger.isEnabled(internalLevel)) {
logger.log(internalLevel, format(ctx, "BIND", localAddress));
}
ctx.bind(localAddress, promise);
return ctx.bind(localAddress);
}
@Override
public void connect(
public Future<Void> connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
SocketAddress remoteAddress, SocketAddress localAddress) {
if (logger.isEnabled(internalLevel)) {
logger.log(internalLevel, format(ctx, "CONNECT", remoteAddress, localAddress));
}
ctx.connect(remoteAddress, localAddress, promise);
return ctx.connect(remoteAddress, localAddress);
}
@Override
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> disconnect(ChannelHandlerContext ctx) {
if (logger.isEnabled(internalLevel)) {
logger.log(internalLevel, format(ctx, "DISCONNECT"));
}
ctx.disconnect(promise);
return ctx.disconnect();
}
@Override
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> close(ChannelHandlerContext ctx) {
if (logger.isEnabled(internalLevel)) {
logger.log(internalLevel, format(ctx, "CLOSE"));
}
ctx.close(promise);
return ctx.close();
}
@Override
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> deregister(ChannelHandlerContext ctx) {
if (logger.isEnabled(internalLevel)) {
logger.log(internalLevel, format(ctx, "DEREGISTER"));
}
ctx.deregister(promise);
return ctx.deregister();
}
@Override
@ -280,11 +280,11 @@ public class LoggingHandler implements ChannelHandler {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (logger.isEnabled(internalLevel)) {
logger.log(internalLevel, format(ctx, "WRITE", msg));
}
ctx.write(msg, promise);
return ctx.write(msg);
}
@Override
@ -335,7 +335,7 @@ public class LoggingHandler implements ChannelHandler {
/**
* Formats an event and returns the formatted message. This method is currently only used for formatting
* {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, Promise)}.
* {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress)}.
*
* @param eventName the name of the event
* @param firstArg the first argument of the event

View File

@ -27,7 +27,7 @@ import io.netty.channel.socket.DatagramPacket;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.util.NetUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -239,7 +239,7 @@ public final class PcapWriteHandler extends ChannelDuplexHandler implements Clos
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (!isClosed) {
if (ctx.channel() instanceof SocketChannel) {
handleTCP(ctx, msg, true);
@ -249,7 +249,7 @@ public final class PcapWriteHandler extends ChannelDuplexHandler implements Clos
logger.debug("Discarding Pcap Write for Unknown Channel Type: {}", ctx.channel());
}
}
super.write(ctx, msg, promise);
return super.write(ctx, msg);
}
/**

View File

@ -257,7 +257,7 @@ public abstract class SslClientHelloHandler<T> extends ByteToMessageDecoder {
protected abstract void onLookupComplete(ChannelHandlerContext ctx, Future<? extends T> future) throws Exception;
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
public void read(ChannelHandlerContext ctx) {
if (suppressRead) {
readPending = true;
} else {

View File

@ -54,7 +54,6 @@ import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.DatagramChannel;
@ -469,8 +468,7 @@ public class SslHandler extends ByteToMessageDecoder {
* Sets the number of bytes to pass to each {@link SSLEngine#wrap(ByteBuffer[], int, int, ByteBuffer)} call.
* <p>
* This value will partition data which is passed to write
* {@link ChannelHandler#write(ChannelHandlerContext, Object, Promise)}.
* The partitioning will work as follows:
* {@link #write(ChannelHandlerContext, Object)}. The partitioning will work as follows:
* <ul>
* <li>If {@code wrapDataSize <= 0} then we will write each data chunk as is.</li>
* <li>If {@code wrapDataSize > data size} then we will attempt to aggregate multiple data chunks together.</li>
@ -679,40 +677,17 @@ public class SslHandler extends ByteToMessageDecoder {
}
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
ctx.bind(localAddress, promise);
public Future<Void> disconnect(final ChannelHandlerContext ctx) {
return closeOutboundAndChannel(ctx, true);
}
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
Promise<Void> promise) {
ctx.connect(remoteAddress, localAddress, promise);
public Future<Void> close(final ChannelHandlerContext ctx) {
return closeOutboundAndChannel(ctx, false);
}
@Override
public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.register(promise);
}
@Override
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.deregister(promise);
}
@Override
public void disconnect(final ChannelHandlerContext ctx,
final Promise<Void> promise) {
closeOutboundAndChannel(ctx, promise, true);
}
@Override
public void close(final ChannelHandlerContext ctx,
final Promise<Void> promise) {
closeOutboundAndChannel(ctx, promise, false);
}
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
public void read(ChannelHandlerContext ctx) {
if (!handshakePromise.isDone()) {
setState(STATE_READ_DURING_HANDSHAKE);
}
@ -725,16 +700,18 @@ public class SslHandler extends ByteToMessageDecoder {
}
@Override
public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(final ChannelHandlerContext ctx, Object msg) {
if (!(msg instanceof ByteBufConvertible)) {
UnsupportedMessageTypeException exception = new UnsupportedMessageTypeException(msg, ByteBuf.class);
ReferenceCountUtil.safeRelease(msg);
promise.setFailure(exception);
return ctx.newFailedFuture(exception);
} else if (pendingUnencryptedWrites == null) {
ReferenceCountUtil.safeRelease(msg);
promise.setFailure(newPendingWritesNullException());
return ctx.newFailedFuture(newPendingWritesNullException());
} else {
Promise<Void> promise = ctx.newPromise();
pendingUnencryptedWrites.add(((ByteBufConvertible) msg).asByteBuf(), promise);
return promise;
}
}
@ -816,12 +793,12 @@ public class SslHandler extends ByteToMessageDecoder {
final ByteBuf b = out;
out = null;
if (promise != null) {
ctx.write(b, promise);
ctx.write(b).addListener(new PromiseNotifier<>(promise));
} else {
ctx.write(b);
}
} else if (promise != null) {
ctx.write(Unpooled.EMPTY_BUFFER, promise);
ctx.write(Unpooled.EMPTY_BUFFER).addListener(new PromiseNotifier<>(promise));
}
// else out is not readable we can re-use it and so save an extra allocation
@ -1881,20 +1858,18 @@ public class SslHandler extends ByteToMessageDecoder {
}
}
private void closeOutboundAndChannel(
final ChannelHandlerContext ctx, final Promise<Void> promise, boolean disconnect) {
private Future<Void> closeOutboundAndChannel(
final ChannelHandlerContext ctx, boolean disconnect) {
setState(STATE_OUTBOUND_CLOSED);
engine.closeOutbound();
if (!ctx.channel().isActive()) {
if (disconnect) {
ctx.disconnect(promise);
} else {
ctx.close(promise);
return ctx.disconnect();
}
return;
return ctx.close();
}
Promise<Void> promise = ctx.newPromise();
Promise<Void> closeNotifyPromise = ctx.newPromise();
try {
flush(ctx, closeNotifyPromise);
@ -1917,6 +1892,7 @@ public class SslHandler extends ByteToMessageDecoder {
sslClosePromise.addListener(future -> promise.setSuccess(null));
}
}
return promise;
}
private void flush(ChannelHandlerContext ctx, Promise<Void> promise) {
@ -2096,7 +2072,7 @@ public class SslHandler extends ByteToMessageDecoder {
final ChannelHandlerContext ctx, final Future<Void> flushFuture,
final Promise<Void> promise) {
if (!ctx.channel().isActive()) {
ctx.close(promise);
ctx.close().addListener(new PromiseNotifier<>(promise));
return;
}
@ -2110,7 +2086,7 @@ public class SslHandler extends ByteToMessageDecoder {
if (!flushFuture.isDone()) {
logger.warn("{} Last write attempt timed out; force-closing the connection.",
ctx.channel());
addCloseListener(ctx.close(ctx.newPromise()), promise);
addCloseListener(ctx.close(), promise);
}
}, closeNotifyTimeout, TimeUnit.MILLISECONDS);
} else {
@ -2130,7 +2106,7 @@ public class SslHandler extends ByteToMessageDecoder {
if (ctx.channel().isActive()) {
// Trigger the close in all cases to make sure the promise is notified
// See https://github.com/netty/netty/issues/2358
addCloseListener(ctx.close(ctx.newPromise()), promise);
addCloseListener(ctx.close(), promise);
} else {
promise.trySuccess(null);
}
@ -2145,7 +2121,7 @@ public class SslHandler extends ByteToMessageDecoder {
ctx.channel(), closeNotifyReadTimeout);
// Do the close now...
addCloseListener(ctx.close(ctx.newPromise()), promise);
addCloseListener(ctx.close(), promise);
}
}, closeNotifyReadTimeout, TimeUnit.MILLISECONDS);
} else {
@ -2158,7 +2134,7 @@ public class SslHandler extends ByteToMessageDecoder {
closeNotifyReadTimeoutFuture.cancel(false);
}
if (ctx.channel().isActive()) {
addCloseListener(ctx.close(ctx.newPromise()), promise);
addCloseListener(ctx.close(), promise);
} else {
promise.trySuccess(null);
}

View File

@ -24,6 +24,7 @@ import io.netty.channel.ChannelPipeline;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -113,8 +114,10 @@ public class ChunkedWriteHandler implements ChannelHandler {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
Promise<Void> promise = ctx.newPromise();
queue.add(new PendingWrite(msg, promise));
return promise;
}
@Override
@ -280,7 +283,7 @@ public class ChunkedWriteHandler implements ChannelHandler {
requiresFlush = false;
} else {
queue.remove();
ctx.write(pendingMessage, currentWrite.promise);
ctx.write(pendingMessage).addListener(new PromiseNotifier<>(currentWrite.promise));
requiresFlush = true;
}

View File

@ -22,8 +22,8 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOutboundBuffer;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -293,13 +293,13 @@ public class IdleStateHandler implements ChannelHandler {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
Future<Void> future = ctx.write(msg);
// Allow writing with void promise if handler is only configured for read timeout events.
if (writerIdleTimeNanos > 0 || allIdleTimeNanos > 0) {
ctx.write(msg, promise).addListener(writeListener);
} else {
ctx.write(msg, promise);
future.addListener(writeListener);
}
return future;
}
private void initialize(ChannelHandlerContext ctx) {

View File

@ -22,7 +22,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -104,11 +103,12 @@ public class WriteTimeoutHandler implements ChannelHandler {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
Future<Void> f = ctx.write(msg);
if (timeoutNanos > 0) {
scheduleTimeout(ctx, promise);
scheduleTimeout(ctx, f);
}
ctx.write(msg, promise);
return f;
}
@Override
@ -126,16 +126,16 @@ public class WriteTimeoutHandler implements ChannelHandler {
}
}
private void scheduleTimeout(final ChannelHandlerContext ctx, final Promise<Void> promise) {
private void scheduleTimeout(final ChannelHandlerContext ctx, final Future<Void> future) {
// Schedule a timeout.
final WriteTimeoutTask task = new WriteTimeoutTask(ctx, promise);
final WriteTimeoutTask task = new WriteTimeoutTask(ctx, future);
task.scheduledFuture = ctx.executor().schedule(task, timeoutNanos, TimeUnit.NANOSECONDS);
if (!task.scheduledFuture.isDone()) {
addWriteTimeoutTask(task);
// Cancel the scheduled timeout if the flush promise is complete.
promise.addListener(task);
future.addListener(task);
}
}
@ -185,17 +185,16 @@ public class WriteTimeoutHandler implements ChannelHandler {
private final class WriteTimeoutTask implements Runnable, FutureListener<Void> {
private final ChannelHandlerContext ctx;
private final Promise<Void> promise;
private final Future<Void> future;
// WriteTimeoutTask is also a node of a doubly-linked list
WriteTimeoutTask prev;
WriteTimeoutTask next;
ScheduledFuture<?> scheduledFuture;
WriteTimeoutTask(ChannelHandlerContext ctx, Promise<Void> promise) {
WriteTimeoutTask(ChannelHandlerContext ctx, Future<Void> future) {
this.ctx = ctx;
this.promise = promise;
this.future = future;
}
@Override
@ -203,7 +202,7 @@ public class WriteTimeoutHandler implements ChannelHandler {
// Was not written yet so issue a write timeout
// The promise itself will be failed with a ClosedChannelException once the close() was issued
// See https://github.com/netty/netty/issues/2159
if (!promise.isDone()) {
if (!future.isDone()) {
try {
writeTimedOut(ctx);
} catch (Throwable t) {
@ -227,7 +226,7 @@ public class WriteTimeoutHandler implements ChannelHandler {
// from the doubly-linked list. Schedule ourself is fine as the promise itself is done.
//
// This fixes https://github.com/netty/netty/issues/11053
assert promise.isDone();
assert future.isDone();
ctx.executor().execute(this);
}
}

View File

@ -26,6 +26,7 @@ import io.netty.channel.ChannelOutboundBuffer;
import io.netty.channel.FileRegion;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
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.InternalLoggerFactory;
@ -549,9 +550,10 @@ public abstract class AbstractTrafficShapingHandler implements ChannelHandler {
}
@Override
public void write(final ChannelHandlerContext ctx, final Object msg, final Promise<Void> promise) {
public Future<Void> write(final ChannelHandlerContext ctx, final Object msg) {
long size = calculateSize(msg);
long now = TrafficCounter.milliSecondFromNano();
Promise<Void> promise = ctx.newPromise();
if (size > 0) {
// compute the number of ms to wait before continue with the channel
long wait = trafficCounter.writeTimeToWait(size, writeLimit, maxTime, now);
@ -561,11 +563,12 @@ public abstract class AbstractTrafficShapingHandler implements ChannelHandler {
+ isHandlerActive(ctx));
}
submitWrite(ctx, msg, size, wait, now, promise);
return;
return promise;
}
}
// to maintain order of write
submitWrite(ctx, msg, size, 0, now, promise);
return promise;
}
@Deprecated

View File

@ -18,6 +18,7 @@ package io.netty.handler.traffic;
import io.netty.buffer.ByteBufConvertible;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import java.util.ArrayDeque;
import java.util.concurrent.TimeUnit;
@ -146,7 +147,7 @@ public class ChannelTrafficShapingHandler extends AbstractTrafficShapingHandler
long size = calculateSize(toSend.toSend);
trafficCounter.bytesRealWriteFlowControl(size);
queueSize -= size;
ctx.write(toSend.toSend, toSend.promise);
ctx.write(toSend.toSend).addListener(new PromiseNotifier<>(toSend.promise));
}
} else {
for (ToSend toSend : messagesQueue) {
@ -183,7 +184,7 @@ public class ChannelTrafficShapingHandler extends AbstractTrafficShapingHandler
synchronized (this) {
if (delay == 0 && messagesQueue.isEmpty()) {
trafficCounter.bytesRealWriteFlowControl(size);
ctx.write(msg, promise);
ctx.write(msg).addListener(new PromiseNotifier<>(promise));
return;
}
newToSend = new ToSend(delay + now, msg, promise);
@ -204,7 +205,7 @@ public class ChannelTrafficShapingHandler extends AbstractTrafficShapingHandler
long size = calculateSize(newToSend.toSend);
trafficCounter.bytesRealWriteFlowControl(size);
queueSize -= size;
ctx.write(newToSend.toSend, newToSend.promise);
ctx.write(newToSend.toSend).addListener(new PromiseNotifier<>(newToSend.promise));
} else {
messagesQueue.addFirst(newToSend);
break;

View File

@ -22,7 +22,9 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.Attribute;
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.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -493,7 +495,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
perChannel.channelTrafficCounter.bytesRealWriteFlowControl(size);
perChannel.queueSize -= size;
queuesSize.addAndGet(-size);
ctx.write(toSend.toSend, toSend.promise);
ctx.write(toSend.toSend).addListener(new PromiseNotifier<>(toSend.promise));
}
} else {
queuesSize.addAndGet(-perChannel.queueSize);
@ -648,7 +650,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
}
@Override
public void write(final ChannelHandlerContext ctx, final Object msg, final Promise<Void> promise) {
public Future<Void> write(final ChannelHandlerContext ctx, final Object msg) {
long size = calculateSize(msg);
long now = TrafficCounter.milliSecondFromNano();
if (size > 0) {
@ -681,12 +683,15 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
logger.debug("Write suspend: " + wait + ':' + ctx.channel().config().isAutoRead() + ':'
+ isHandlerActive(ctx));
}
Promise<Void> promise = ctx.newPromise();
submitWrite(ctx, msg, size, wait, now, promise);
return;
return promise;
}
}
Promise<Void> promise = ctx.newPromise();
// to maintain order of write
submitWrite(ctx, msg, size, 0, now, promise);
return promise;
}
@Override
@ -709,7 +714,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
if (writedelay == 0 && perChannel.messagesQueue.isEmpty()) {
trafficCounter.bytesRealWriteFlowControl(size);
perChannel.channelTrafficCounter.bytesRealWriteFlowControl(size);
ctx.write(msg, promise);
ctx.write(msg).addListener(new PromiseNotifier<>(promise));
perChannel.lastWriteTimestamp = now;
return;
}
@ -744,7 +749,7 @@ public class GlobalChannelTrafficShapingHandler extends AbstractTrafficShapingHa
perChannel.channelTrafficCounter.bytesRealWriteFlowControl(size);
perChannel.queueSize -= size;
queuesSize.addAndGet(-size);
ctx.write(newToSend.toSend, newToSend.promise);
ctx.write(newToSend.toSend).addListener(new PromiseNotifier<>(newToSend.promise));
perChannel.lastWriteTimestamp = now;
} else {
perChannel.messagesQueue.addFirst(newToSend);

View File

@ -21,6 +21,7 @@ import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.concurrent.EventExecutor;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import java.util.ArrayDeque;
import java.util.concurrent.ConcurrentHashMap;
@ -272,7 +273,7 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
trafficCounter.bytesRealWriteFlowControl(size);
perChannel.queueSize -= size;
queuesSize.addAndGet(-size);
ctx.write(toSend.toSend, toSend.promise);
ctx.write(toSend.toSend).addListener(new PromiseNotifier<>(toSend.promise));
}
} else {
queuesSize.addAndGet(-perChannel.queueSize);
@ -344,7 +345,7 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
synchronized (perChannel) {
if (writedelay == 0 && perChannel.messagesQueue.isEmpty()) {
trafficCounter.bytesRealWriteFlowControl(size);
ctx.write(msg, promise);
ctx.write(msg).addListener(new PromiseNotifier<>(promise));
perChannel.lastWriteTimestamp = now;
return;
}
@ -378,7 +379,7 @@ public class GlobalTrafficShapingHandler extends AbstractTrafficShapingHandler {
trafficCounter.bytesRealWriteFlowControl(size);
perChannel.queueSize -= size;
queuesSize.addAndGet(-size);
ctx.write(newToSend.toSend, newToSend.promise);
ctx.write(newToSend.toSend).addListener(new PromiseNotifier<>(newToSend.promise));
perChannel.lastWriteTimestamp = now;
} else {
perChannel.messagesQueue.addFirst(newToSend);

View File

@ -18,7 +18,7 @@ package io.netty.handler.address;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import org.junit.jupiter.api.Test;
import java.net.SocketAddress;
@ -38,14 +38,14 @@ public class DynamicAddressConnectHandlerTest {
EmbeddedChannel channel = new EmbeddedChannel(new ChannelHandler() {
@Override
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, Promise<Void> promise) {
public Future<Void> connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress) {
try {
assertSame(REMOTE_NEW, remoteAddress);
assertSame(LOCAL_NEW, localAddress);
promise.setSuccess(null);
return ctx.newSucceededFuture();
} catch (Throwable cause) {
promise.setFailure(cause);
return ctx.newFailedFuture(cause);
}
}
}, new DynamicAddressConnectHandler() {

View File

@ -129,7 +129,7 @@ public class LoggingHandlerTest {
// this is used to switch the channel to become unwritable
channel.config().setWriteBufferLowWaterMark(5);
channel.config().setWriteBufferHighWaterMark(10);
channel.write("hello", channel.newPromise());
channel.write("hello");
// This is expected to be called 3 times:
// - Mark the channel unwritable when schedule the write on the EventLoop.

View File

@ -115,22 +115,27 @@ public class SslHandlerTest {
SSLEngine engine = newClientModeSSLEngine();
SslHandler handler = new SslHandler(engine) {
@Override
public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
super.write(ctx, msg, promise);
public Future<Void> write(final ChannelHandlerContext ctx, Object msg) {
Future<Void> future = super.write(ctx, msg);
writeLatch.countDown();
return future;
}
};
EmbeddedChannel ch = new EmbeddedChannel(new ChannelHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (msg instanceof ByteBuf) {
if (((ByteBuf) msg).isReadable()) {
writesToFail.add(promise);
} else {
promise.setSuccess(null);
}
}
ReferenceCountUtil.release(msg);
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
try {
if (msg instanceof ByteBuf) {
if (((ByteBuf) msg).isReadable()) {
Promise<Void> promise = ctx.newPromise();
writesToFail.add(promise);
return promise;
}
}
return ctx.newSucceededFuture();
} finally {
ReferenceCountUtil.release(msg);
}
}
}, handler);
@ -375,13 +380,12 @@ public class SslHandlerTest {
SSLEngine engine = newServerModeSSLEngine();
EmbeddedChannel ch = new EmbeddedChannel(new SslHandler(engine));
Promise<Void> promise = ch.newPromise();
ByteBuf buf = Unpooled.buffer(10).writeZero(10);
ch.writeAndFlush(buf, promise);
assertFalse(promise.isDone());
Future<Void> future = ch.writeAndFlush(buf);
assertFalse(future.isDone());
assertTrue(ch.finishAndReleaseAll());
assertTrue(promise.isDone());
assertThat(promise.cause(), is(instanceOf(SSLException.class)));
assertTrue(future.isDone());
assertThat(future.cause(), is(instanceOf(SSLException.class)));
}
@Test
@ -418,7 +422,7 @@ public class SslHandlerTest {
private volatile boolean readIssued;
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
public void read(ChannelHandlerContext ctx) {
readIssued = true;
ctx.read();
}

View File

@ -25,7 +25,6 @@ import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.PlatformDependent;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.function.Executable;
@ -326,13 +325,12 @@ public class ChunkedWriteHandlerTest {
private int passedWrites;
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
if (++passedWrites < 4) {
ctx.write(msg, promise);
} else {
ReferenceCountUtil.release(msg);
promise.tryFailure(new RuntimeException());
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (++this.passedWrites < 4) {
return ctx.write(msg);
}
ReferenceCountUtil.release(msg);
return ctx.newFailedFuture(new RuntimeException());
}
};
@ -460,9 +458,9 @@ public class ChunkedWriteHandlerTest {
ChannelHandler noOpWrites = new ChannelHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
ReferenceCountUtil.release(msg);
promise.tryFailure(new RuntimeException());
return ctx.newFailedFuture(new RuntimeException());
}
};
@ -616,11 +614,11 @@ public class ChunkedWriteHandlerTest {
EmbeddedChannel ch = new EmbeddedChannel(new ChannelHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
ReferenceCountUtil.release(msg);
// Calling close so we will drop all queued messages in the ChunkedWriteHandler.
ctx.close();
promise.setSuccess(null);
return ctx.newSucceededFuture();
}
}, new ChunkedWriteHandler());
@ -683,9 +681,9 @@ public class ChunkedWriteHandlerTest {
private static void checkFirstFailed(Object input) {
ChannelHandler noOpWrites = new ChannelHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
ReferenceCountUtil.release(msg);
promise.tryFailure(new RuntimeException());
return ctx.newFailedFuture(new RuntimeException());
}
};
@ -702,14 +700,13 @@ public class ChunkedWriteHandlerTest {
private boolean alreadyFailed;
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (alreadyFailed) {
ctx.write(msg, promise);
} else {
alreadyFailed = true;
ReferenceCountUtil.release(msg);
promise.tryFailure(new RuntimeException());
return ctx.write(msg);
}
this.alreadyFailed = true;
ReferenceCountUtil.release(msg);
return ctx.newFailedFuture(new RuntimeException());
}
};

View File

@ -1,48 +0,0 @@
/*
* Copyright 2021 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.handler.timeout;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.concurrent.DefaultEventExecutorGroup;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.EventExecutorGroup;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CountDownLatch;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class WriteTimeoutHandlerTest {
@Test
public void testPromiseUseDifferentExecutor() throws Exception {
EventExecutorGroup group1 = new DefaultEventExecutorGroup(1);
EmbeddedChannel channel = new EmbeddedChannel(false, false);
try {
channel.pipeline().addLast(new WriteTimeoutHandler(10000));
final CountDownLatch latch = new CountDownLatch(1);
channel.register();
channel.writeAndFlush("something", new DefaultPromise<>(group1.next())).addListener(f -> {
latch.countDown();
});
latch.await();
assertTrue(channel.finishAndReleaseAll());
} finally {
group1.shutdownGracefully();
}
}
}

View File

@ -24,6 +24,7 @@ import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext;
import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
@ -145,18 +146,20 @@ public class Http2FrameWriterDataBenchmark extends AbstractMicrobenchmark {
// Only the last frame is not retained. Until then, the outer finally must release.
ByteBuf frameHeader = header.slice(frameDataBytes, framePaddingBytes, lastFrame && endStream);
needToReleaseHeaders = !lastFrame;
ctx.write(lastFrame ? frameHeader : frameHeader.retain(), promiseAggregator.newPromise());
ctx.write(lastFrame ? frameHeader : frameHeader.retain())
.addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write the frame data.
ByteBuf frameData = data.readSlice(frameDataBytes);
// Only the last frame is not retained. Until then, the outer finally must release.
needToReleaseData = !lastFrame;
ctx.write(lastFrame ? frameData : frameData.retain(), promiseAggregator.newPromise());
ctx.write(lastFrame ? frameData : frameData.retain())
.addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
// Write the frame padding.
if (paddingBytes(framePaddingBytes) > 0) {
ctx.write(ZERO_BUFFER.slice(0, paddingBytes(framePaddingBytes)),
promiseAggregator.newPromise());
ctx.write(ZERO_BUFFER.slice(0, paddingBytes(framePaddingBytes)))
.addListener(new PromiseNotifier<>(promiseAggregator.newPromise()));
}
} while (!lastFrame);
} catch (Throwable t) {

View File

@ -137,117 +137,75 @@ public abstract class EmbeddedChannelHandlerContext implements ChannelHandlerCon
}
@Override
public final Future<Void> bind(SocketAddress localAddress) {
return bind(localAddress, newPromise());
}
@Override
public final Future<Void> connect(SocketAddress remoteAddress) {
return connect(remoteAddress, newPromise());
}
@Override
public final Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return connect(remoteAddress, localAddress, newPromise());
}
@Override
public final Future<Void> disconnect() {
return disconnect(newPromise());
}
@Override
public final Future<Void> close() {
return close(newPromise());
}
@Override
public Future<Void> register() {
return register(newPromise());
}
@Override
public Future<Void> register(Promise<Void> promise) {
public final Future<Void> register() {
try {
channel().register(promise);
return channel().register();
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
return channel().newFailedFuture(e);
}
return promise;
}
@Override
public final Future<Void> deregister() {
return deregister(newPromise());
try {
return channel().deregister();
} catch (Exception e) {
handleException(e);
return channel().newFailedFuture(e);
}
}
@Override
public final Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
public final Future<Void> bind(SocketAddress localAddress) {
try {
channel().bind(localAddress, promise);
this.localAddress = localAddress;
return channel().bind(localAddress);
} catch (Exception e) {
promise.setFailure(e);
this.localAddress = null;
handleException(e);
return channel().newFailedFuture(e);
}
return promise;
}
@Override
public final Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
public final Future<Void> connect(SocketAddress remoteAddress) {
try {
channel().connect(remoteAddress, localAddress, promise);
return channel().connect(remoteAddress, localAddress);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
return channel().newFailedFuture(e);
}
return promise;
}
@Override
public final Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress,
Promise<Void> promise) {
public final Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
try {
channel().connect(remoteAddress, localAddress, promise);
return channel().connect(remoteAddress, localAddress);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
return channel().newFailedFuture(e);
}
return promise;
}
@Override
public final Future<Void> disconnect(Promise<Void> promise) {
public final Future<Void> disconnect() {
try {
channel().disconnect(promise);
return channel().disconnect();
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
return channel().newFailedFuture(e);
}
return promise;
}
@Override
public final Future<Void> close(Promise<Void> promise) {
public final Future<Void> close() {
try {
channel().close(promise);
return channel().close();
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
return channel().newFailedFuture(e);
}
return promise;
}
@Override
public final Future<Void> deregister(Promise<Void> promise) {
try {
channel().deregister(promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
@ -266,24 +224,14 @@ public abstract class EmbeddedChannelHandlerContext implements ChannelHandlerCon
}
@Override
public Future<Void> write(Object msg, Promise<Void> promise) {
return channel().write(msg, promise);
}
@Override
public final ChannelHandlerContext flush() {
public ChannelHandlerContext flush() {
channel().flush();
return this;
}
@Override
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return channel().writeAndFlush(msg, promise);
}
@Override
public Future<Void> writeAndFlush(Object msg) {
return writeAndFlush(msg, newPromise());
return channel().writeAndFlush(msg);
}
@Override

View File

@ -20,7 +20,6 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import static java.util.Objects.requireNonNull;
@ -53,11 +52,6 @@ public abstract class EmbeddedChannelWriteAccumulatingHandlerContext extends Emb
@Override
public final Future<Void> write(Object msg) {
return write(msg, newPromise());
}
@Override
public final Future<Void> write(Object msg, Promise<Void> promise) {
try {
if (msg instanceof ByteBuf) {
if (cumulation == null) {
@ -65,19 +59,17 @@ public abstract class EmbeddedChannelWriteAccumulatingHandlerContext extends Emb
} else {
cumulation = cumulator.cumulate(alloc(), cumulation, (ByteBuf) msg);
}
promise.setSuccess(null);
} else {
channel().write(msg, promise);
return channel().newSucceededFuture();
}
return channel().write(msg);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
return channel().newFailedFuture(e);
}
return promise;
}
@Override
public final Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
public final Future<Void> writeAndFlush(Object msg) {
try {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
@ -86,19 +78,13 @@ public abstract class EmbeddedChannelWriteAccumulatingHandlerContext extends Emb
} else {
cumulation = cumulator.cumulate(alloc(), cumulation, buf);
}
promise.setSuccess(null);
return channel().newSucceededFuture();
} else {
channel().writeAndFlush(msg, promise);
return channel().writeAndFlush(msg);
}
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
return channel().newFailedFuture(e);
}
return promise;
}
@Override
public final Future<Void> writeAndFlush(Object msg) {
return writeAndFlush(msg, newPromise());
}
}

View File

@ -19,7 +19,6 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
public abstract class EmbeddedChannelWriteReleaseHandlerContext extends EmbeddedChannelHandlerContext {
protected EmbeddedChannelWriteReleaseHandlerContext(ByteBufAllocator alloc, ChannelHandler handler) {
@ -36,43 +35,29 @@ public abstract class EmbeddedChannelWriteReleaseHandlerContext extends Embedded
@Override
public final Future<Void> write(Object msg) {
return write(msg, newPromise());
}
@Override
public final Future<Void> write(Object msg, Promise<Void> promise) {
try {
if (msg instanceof ReferenceCounted) {
((ReferenceCounted) msg).release();
promise.setSuccess(null);
} else {
channel().write(msg, promise);
return channel().newSucceededFuture();
}
return channel().write(msg);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
return channel().newFailedFuture(e);
}
return promise;
}
@Override
public final Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
try {
if (msg instanceof ReferenceCounted) {
((ReferenceCounted) msg).release();
promise.setSuccess(null);
} else {
channel().writeAndFlush(msg, promise);
}
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final Future<Void> writeAndFlush(Object msg) {
return writeAndFlush(msg, newPromise());
try {
if (msg instanceof ReferenceCounted) {
((ReferenceCounted) msg).release();
return channel().newSucceededFuture();
}
return channel().writeAndFlush(msg);
} catch (Exception e) {
handleException(e);
return channel().newFailedFuture(e);
}
}
}

View File

@ -28,6 +28,7 @@ import io.netty.channel.epoll.EpollHandler;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.ScheduledFuture;
import org.openjdk.jmh.annotations.Benchmark;
@ -104,12 +105,12 @@ public class EpollSocketChannelBenchmark extends AbstractMicrobenchmark {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
if (lastWritePromise != null) {
throw new IllegalStateException();
}
lastWritePromise = promise;
ctx.write(msg);
lastWritePromise = ctx.newPromise();
return ctx.write(msg);
}
});
}

View File

@ -87,7 +87,7 @@ public abstract class AbstractSslHandlerThroughputBenchmark extends AbstractSslH
for (int i = 0; i < numWrites; ++i) {
ByteBuf wrapSrcBuffer = this.wrapSrcBuffer.retainedSlice();
clientSslHandler.write(clientCtx, wrapSrcBuffer, clientCtx.newPromise());
clientSslHandler.write(clientCtx, wrapSrcBuffer);
}
clientSslHandler.flush(clientCtx);
return clientCtx.cumulation().retainedSlice();

View File

@ -36,7 +36,6 @@ import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext;
import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.concurrent.Promise;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
@ -102,22 +101,18 @@ public class HttpObjectEncoderBenchmark extends AbstractMicrobenchmark {
@Benchmark
public void fullMessage() throws Exception {
encoder.write(context, fullRequest, newPromise());
encoder.write(context, fullRequest);
}
@Benchmark
public void contentLength() throws Exception {
encoder.write(context, contentLengthRequest, newPromise());
encoder.write(context, lastContent, newPromise());
encoder.write(context, contentLengthRequest);
encoder.write(context, lastContent);
}
@Benchmark
public void chunked() throws Exception {
encoder.write(context, chunkedRequest, newPromise());
encoder.write(context, lastContent, newPromise());
}
private Promise<Void> newPromise() {
return context.newPromise();
encoder.write(context, chunkedRequest);
encoder.write(context, lastContent);
}
}

View File

@ -26,7 +26,7 @@ import io.netty.handler.codec.redis.RedisEncoder;
import io.netty.handler.codec.redis.RedisMessage;
import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext;
import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
@ -88,11 +88,7 @@ public class RedisEncoderBenchmark extends AbstractMicrobenchmark {
}
@Benchmark
public void writeArray() throws Exception {
encoder.write(context, redisArray.retain(), newPromise());
}
private Promise<Void> newPromise() {
return context.newPromise();
public Future<Void> writeArray() {
return encoder.write(context, redisArray.retain());
}
}

View File

@ -26,7 +26,7 @@ import io.netty.handler.codec.stomp.StompHeadersSubframe;
import io.netty.handler.codec.stomp.StompSubframeEncoder;
import io.netty.microbench.channel.EmbeddedChannelWriteReleaseHandlerContext;
import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
@ -92,15 +92,10 @@ public class StompEncoderBenchmark extends AbstractMicrobenchmark {
}
@Benchmark
public void writeStompFrame() throws Exception {
stompEncoder.write(context, stompFrame.retain(), newPromise());
public Future<Void> writeStompFrame() {
return stompEncoder.write(context, stompFrame.retain());
}
private Promise<Void> newPromise() {
return context.newPromise();
}
@Override
protected ChainedOptionsBuilder newOptionsBuilder() throws Exception {
return super.newOptionsBuilder().addProfiler(GCProfiler.class);
}

View File

@ -136,21 +136,23 @@ abstract class DnsQueryContext implements FutureListener<AddressedEnvelope<DnsRe
}
private void writeQuery(final DnsQuery query, final boolean flush, final Promise<Void> writePromise) {
final Future<Void> writeFuture = flush ? channel().writeAndFlush(query, writePromise) :
channel().write(query, writePromise);
final Future<Void> writeFuture = flush ? channel().writeAndFlush(query) :
channel().write(query);
if (writeFuture.isDone()) {
onQueryWriteCompletion(writeFuture);
onQueryWriteCompletion(writeFuture, writePromise);
} else {
writeFuture.addListener(future -> onQueryWriteCompletion(writeFuture));
writeFuture.addListener(future ->
onQueryWriteCompletion(future, writePromise));
}
}
private void onQueryWriteCompletion(Future<?> writeFuture) {
private void onQueryWriteCompletion(Future<? extends Void> writeFuture, Promise<Void> writePromise) {
if (!writeFuture.isSuccess()) {
writePromise.setFailure(writeFuture.cause());
tryFailure("failed to send a query via " + protocol(), writeFuture.cause(), false);
return;
}
writePromise.setSuccess(null);
// Schedule a query timeout task if necessary.
final long queryTimeoutMillis = parent.queryTimeoutMillis();
if (queryTimeoutMillis > 0) {

View File

@ -26,6 +26,7 @@ import io.netty.util.AttributeKey;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.internal.SocketUtils;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.logging.InternalLogger;
@ -318,7 +319,8 @@ public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C, F>, C
// the pipeline in its channelRegistered() implementation.
channel.eventLoop().execute(() -> {
if (regFuture.isSuccess()) {
channel.bind(localAddress, promise).addListener(channel, ChannelFutureListeners.CLOSE_ON_FAILURE);
PromiseNotifier.cascade(channel.bind(localAddress), promise)
.addListener(channel, ChannelFutureListeners.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}

View File

@ -261,14 +261,14 @@ public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel, ChannelFact
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
channel.eventLoop().execute(() -> {
Promise<Void> connectPromise = channel.newPromise();
connectPromise.addListener(channel, ChannelFutureListeners.CLOSE_ON_FAILURE);
cascade(true, connectPromise, promise, channel);
final Future<Void> future;
if (localAddress == null) {
channel.connect(remoteAddress, connectPromise);
future = channel.connect(remoteAddress);
} else {
channel.connect(remoteAddress, localAddress, connectPromise);
future = channel.connect(remoteAddress, localAddress);
}
future.addListener(channel, ChannelFutureListeners.CLOSE_ON_FAILURE);
cascade(true, future, promise, channel);
});
}

View File

@ -254,7 +254,7 @@ public abstract class AbstractCoalescingBufferQueue {
previousBuf = ((ByteBufConvertible) entry).asByteBuf();
} else if (entry instanceof Promise) {
decrementReadableBytes(previousBuf.readableBytes());
ctx.write(previousBuf, (Promise<Void>) entry);
ctx.write(previousBuf).addListener(new PromiseNotifier<>((Promise<? super Void>) entry));
previousBuf = null;
} else {
decrementReadableBytes(previousBuf.readableBytes());

View File

@ -72,7 +72,7 @@ import java.net.SocketAddress;
*
* <h3>Release resources</h3>
* <p>
* It is important to call {@link #close()} or {@link #close(Promise)} to release all
* It is important to call {@link #close()} to release all
* resources once you are done with the {@link Channel}. This ensures all resources are
* released in a proper way, i.e. filehandles.
*/
@ -245,56 +245,11 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, Comparabl
return pipeline().deregister();
}
@Override
default Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return pipeline().bind(localAddress, promise);
}
@Override
default Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return pipeline().connect(remoteAddress, promise);
}
@Override
default Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return pipeline().connect(remoteAddress, localAddress, promise);
}
@Override
default Future<Void> disconnect(Promise<Void> promise) {
return pipeline().disconnect(promise);
}
@Override
default Future<Void> close(Promise<Void> promise) {
return pipeline().close(promise);
}
@Override
default Future<Void> register(Promise<Void> promise) {
return pipeline().register(promise);
}
@Override
default Future<Void> deregister(Promise<Void> promise) {
return pipeline().deregister(promise);
}
@Override
default Future<Void> write(Object msg) {
return pipeline().write(msg);
}
@Override
default Future<Void> write(Object msg, Promise<Void> promise) {
return pipeline().write(msg, promise);
}
@Override
default Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return pipeline().writeAndFlush(msg, promise);
}
@Override
default Future<Void> writeAndFlush(Object msg) {
return pipeline().writeAndFlush(msg);

View File

@ -18,7 +18,7 @@ package io.netty.channel;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.channel.ChannelHandlerMask.Skip;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
@ -278,11 +278,11 @@ public interface ChannelHandler {
*
* @param ctx the {@link ChannelHandlerContext} for which the bind operation is made
* @param localAddress the {@link SocketAddress} to which it should bound
* @param promise the {@link Promise} to notify once the operation completes
* @return the {@link Future} which will be notified once the operation completes.
*/
@Skip
default void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
ctx.bind(localAddress, promise);
default Future<Void> bind(ChannelHandlerContext ctx, SocketAddress localAddress) {
return ctx.bind(localAddress);
}
/**
@ -291,64 +291,64 @@ public interface ChannelHandler {
* @param ctx the {@link ChannelHandlerContext} for which the connect operation is made
* @param remoteAddress the {@link SocketAddress} to which it should connect
* @param localAddress the {@link SocketAddress} which is used as source on connect
* @param promise the {@link Promise} to notify once the operation completes
* @return the {@link Future} which will be notified once the operation completes.
*/
@Skip
default void connect(
default Future<Void> connect(
ChannelHandlerContext ctx, SocketAddress remoteAddress,
SocketAddress localAddress, Promise<Void> promise) {
ctx.connect(remoteAddress, localAddress, promise);
SocketAddress localAddress) {
return ctx.connect(remoteAddress, localAddress);
}
/**
* Called once a disconnect operation is made.
*
* @param ctx the {@link ChannelHandlerContext} for which the disconnect operation is made
* @param promise the {@link Promise} to notify once the operation completes
* @return the {@link Future} which will be notified once the operation completes.
*/
@Skip
default void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.disconnect(promise);
default Future<Void> disconnect(ChannelHandlerContext ctx) {
return ctx.disconnect();
}
/**
* Called once a close operation is made.
*
* @param ctx the {@link ChannelHandlerContext} for which the close operation is made
* @param promise the {@link Promise} to notify once the operation completes
* @return the {@link Future} which will be notified once the operation completes.
*/
@Skip
default void close(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.close(promise);
default Future<Void> close(ChannelHandlerContext ctx) {
return ctx.close();
}
/**
* Called once a register operation is made to register for IO on the {@link EventLoop}.
*
* @param ctx the {@link ChannelHandlerContext} for which the register operation is made
* @param promise the {@link Promise} to notify once the operation completes
* @return the {@link Future} which will be notified once the operation completes.
*/
@Skip
default void register(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.register(promise);
default Future<Void> register(ChannelHandlerContext ctx) {
return ctx.register();
}
/**
* Called once a deregister operation is made from the current registered {@link EventLoop}.
*
* @param ctx the {@link ChannelHandlerContext} for which the deregister operation is made
* @param promise the {@link Promise} to notify once the operation completes
* @return the {@link Future} which will be notified once the operation completes.
*/
@Skip
default void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
ctx.deregister(promise);
default Future<Void> deregister(ChannelHandlerContext ctx) {
return ctx.deregister();
}
/**
* Intercepts {@link ChannelHandlerContext#read()}.
*/
@Skip
default void read(ChannelHandlerContext ctx) throws Exception {
default void read(ChannelHandlerContext ctx) {
ctx.read();
}
@ -359,11 +359,11 @@ public interface ChannelHandler {
*
* @param ctx the {@link ChannelHandlerContext} for which the write operation is made
* @param msg the message to write
* @param promise the {@link Promise} to notify once the operation completes
* @return the {@link Future} which will be notified once the operation completes.
*/
@Skip
default void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
ctx.write(msg, promise);
default Future<Void> write(ChannelHandlerContext ctx, Object msg) {
return ctx.write(msg);
}
/**

View File

@ -16,7 +16,6 @@
package io.netty.channel;
import io.netty.util.concurrent.FastThreadLocal;
import io.netty.util.concurrent.Promise;
import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
@ -130,31 +129,29 @@ final class ChannelHandlerMask {
if (isSkippable(handlerType, "userEventTriggered", ChannelHandlerContext.class, Object.class)) {
mask &= ~MASK_USER_EVENT_TRIGGERED;
}
if (isSkippable(handlerType, "bind", ChannelHandlerContext.class,
SocketAddress.class, Promise.class)) {
if (isSkippable(handlerType, "bind", ChannelHandlerContext.class, SocketAddress.class)) {
mask &= ~MASK_BIND;
}
if (isSkippable(handlerType, "connect", ChannelHandlerContext.class, SocketAddress.class,
SocketAddress.class, Promise.class)) {
SocketAddress.class)) {
mask &= ~MASK_CONNECT;
}
if (isSkippable(handlerType, "disconnect", ChannelHandlerContext.class, Promise.class)) {
if (isSkippable(handlerType, "disconnect", ChannelHandlerContext.class)) {
mask &= ~MASK_DISCONNECT;
}
if (isSkippable(handlerType, "close", ChannelHandlerContext.class, Promise.class)) {
if (isSkippable(handlerType, "close", ChannelHandlerContext.class)) {
mask &= ~MASK_CLOSE;
}
if (isSkippable(handlerType, "register", ChannelHandlerContext.class, Promise.class)) {
if (isSkippable(handlerType, "register", ChannelHandlerContext.class)) {
mask &= ~MASK_REGISTER;
}
if (isSkippable(handlerType, "deregister", ChannelHandlerContext.class, Promise.class)) {
if (isSkippable(handlerType, "deregister", ChannelHandlerContext.class)) {
mask &= ~MASK_DEREGISTER;
}
if (isSkippable(handlerType, "read", ChannelHandlerContext.class)) {
mask &= ~MASK_READ;
}
if (isSkippable(handlerType, "write", ChannelHandlerContext.class,
Object.class, Promise.class)) {
if (isSkippable(handlerType, "write", ChannelHandlerContext.class, Object.class)) {
mask &= ~MASK_WRITE;
}
if (isSkippable(handlerType, "flush", ChannelHandlerContext.class)) {

View File

@ -30,7 +30,7 @@ public interface ChannelOutboundInvoker {
* completes, either because the operation was successful or because of an error.
* <p>
* This will result in having the
* {@link ChannelHandler#bind(ChannelHandlerContext, SocketAddress, Promise)} method
* {@link ChannelHandler#bind(ChannelHandlerContext, SocketAddress)} method
* called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
@ -45,7 +45,7 @@ public interface ChannelOutboundInvoker {
* will be used.
* <p>
* This will result in having the
* {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, Promise)}
* {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
@ -57,7 +57,7 @@ public interface ChannelOutboundInvoker {
* an error.
* <p>
* This will result in having the
* {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, Promise)}
* {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
@ -68,7 +68,7 @@ public interface ChannelOutboundInvoker {
* either because the operation was successful or because of an error.
* <p>
* This will result in having the
* {@link ChannelHandler#disconnect(ChannelHandlerContext, Promise)}
* {@link ChannelHandler#disconnect(ChannelHandlerContext)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
@ -82,7 +82,7 @@ public interface ChannelOutboundInvoker {
* After it is closed it is not possible to reuse it again.
* <p>
* This will result in having the
* {@link ChannelHandler#close(ChannelHandlerContext, Promise)}
* {@link ChannelHandler#close(ChannelHandlerContext)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
@ -94,10 +94,9 @@ public interface ChannelOutboundInvoker {
* an error.
* <p>
* This will result in having the
* {@link ChannelHandler#register(ChannelHandlerContext, Promise)}
* {@link ChannelHandler#register(ChannelHandlerContext)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*
*/
Future<Void> register();
@ -107,114 +106,13 @@ public interface ChannelOutboundInvoker {
* an error.
* <p>
* This will result in having the
* {@link ChannelHandler#deregister(ChannelHandlerContext, Promise)}
* {@link ChannelHandler#deregister(ChannelHandlerContext)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*
*/
Future<Void> deregister();
/**
* Request to bind to the given {@link SocketAddress} and notify the {@link Future} once the operation
* completes, either because the operation was successful or because of an error.
*
* The given {@link Promise} will be notified.
* <p>
* This will result in having the
* {@link ChannelHandler#bind(ChannelHandlerContext, SocketAddress, Promise)} method
* called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
Future<Void> bind(SocketAddress localAddress, Promise<Void> promise);
/**
* Request to connect to the given {@link SocketAddress} and notify the {@link Future} once the operation
* completes, either because the operation was successful or because of an error.
*
* The given {@link Future} will be notified.
*
* <p>
* If the connection fails because of a connection timeout, the {@link Future} will get failed with
* a {@link ConnectTimeoutException}. If it fails because of connection refused a {@link ConnectException}
* will be used.
* <p>
* This will result in having the
* {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, Promise)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise);
/**
* Request to connect to the given {@link SocketAddress} while bind to the localAddress and notify the
* {@link Future} once the operation completes, either because the operation was successful or because of
* an error.
*
* The given {@link Promise} will be notified and also returned.
* <p>
* This will result in having the
* {@link ChannelHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, Promise)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise);
/**
* Request to disconnect from the remote peer and notify the {@link Future} once the operation completes,
* either because the operation was successful or because of an error.
*
* The given {@link Promise} will be notified.
* <p>
* This will result in having the
* {@link ChannelHandler#disconnect(ChannelHandlerContext, Promise)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
Future<Void> disconnect(Promise<Void> promise);
/**
* Request to close the {@link Channel} and notify the {@link Future} once the operation completes,
* either because the operation was successful or because of
* an error.
*
* After it is closed it is not possible to reuse it again.
* The given {@link Promise} will be notified.
* <p>
* This will result in having the
* {@link ChannelHandler#close(ChannelHandlerContext, Promise)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
Future<Void> close(Promise<Void> promise);
/**
* Request to register on the {@link EventExecutor} for I/O processing.
* {@link Future} once the operation completes, either because the operation was successful or because of
* an error.
*
* The given {@link Promise} will be notified.
* <p>
* This will result in having the
* {@link ChannelHandler#register(ChannelHandlerContext, Promise)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
Future<Void> register(Promise<Void> promise);
/**
* Request to deregister from the previous assigned {@link EventExecutor} and notify the
* {@link Future} once the operation completes, either because the operation was successful or because of
* an error.
*
* The given {@link Promise} will be notified.
* <p>
* This will result in having the
* {@link ChannelHandler#deregister(ChannelHandlerContext, Promise)}
* method called of the next {@link ChannelHandler} contained in the {@link ChannelPipeline} of the
* {@link Channel}.
*/
Future<Void> deregister(Promise<Void> promise);
/**
* Request to Read data from the {@link Channel} into the first inbound buffer, triggers an
* {@link ChannelHandler#channelRead(ChannelHandlerContext, Object)} event if data was
@ -236,23 +134,11 @@ public interface ChannelOutboundInvoker {
*/
Future<Void> write(Object msg);
/**
* Request to write a message via this {@link ChannelHandlerContext} through the {@link ChannelPipeline}.
* This method will not request to actual flush, so be sure to call {@link #flush()}
* once you want to request to flush all pending data to the actual transport.
*/
Future<Void> write(Object msg, Promise<Void> promise);
/**
* Request to flush all pending messages via this ChannelOutboundInvoker.
*/
ChannelOutboundInvoker flush();
/**
* Shortcut for call {@link #write(Object, Promise)} and {@link #flush()}.
*/
Future<Void> writeAndFlush(Object msg, Promise<Void> promise);
/**
* Shortcut for call {@link #write(Object)} and {@link #flush()}.
*/

View File

@ -142,14 +142,14 @@ import java.util.NoSuchElementException;
* </li>
* <li>Outbound event propagation methods:
* <ul>
* <li>{@link ChannelHandlerContext#bind(SocketAddress, Promise)}</li>
* <li>{@link ChannelHandlerContext#connect(SocketAddress, SocketAddress, Promise)}</li>
* <li>{@link ChannelHandlerContext#write(Object, Promise)}</li>
* <li>{@link ChannelHandlerContext#bind(SocketAddress)}</li>
* <li>{@link ChannelHandlerContext#connect(SocketAddress, SocketAddress)}</li>
* <li>{@link ChannelHandlerContext#write(Object)}</li>
* <li>{@link ChannelHandlerContext#flush()}</li>
* <li>{@link ChannelHandlerContext#read()}</li>
* <li>{@link ChannelHandlerContext#disconnect(Promise)}</li>
* <li>{@link ChannelHandlerContext#close(Promise)}</li>
* <li>{@link ChannelHandlerContext#deregister(Promise)}</li>
* <li>{@link ChannelHandlerContext#disconnect()}</li>
* <li>{@link ChannelHandlerContext#close()}</li>
* <li>{@link ChannelHandlerContext#deregister()}</li>
* </ul>
* </li>
* </ul>

View File

@ -243,72 +243,71 @@ public class CombinedChannelDuplexHandler<I extends ChannelHandler, O extends Ch
}
@Override
public void bind(
public Future<Void> bind(
ChannelHandlerContext ctx,
SocketAddress localAddress, Promise<Void> promise) {
SocketAddress localAddress) {
assert ctx == outboundCtx.ctx;
if (!outboundCtx.removed) {
outboundHandler.bind(outboundCtx, localAddress, promise);
return outboundHandler.bind(outboundCtx, localAddress);
} else {
outboundCtx.bind(localAddress, promise);
return outboundCtx.bind(localAddress);
}
}
@Override
public void connect(
public Future<Void> connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
Promise<Void> promise) {
SocketAddress remoteAddress, SocketAddress localAddress) {
assert ctx == outboundCtx.ctx;
if (!outboundCtx.removed) {
outboundHandler.connect(outboundCtx, remoteAddress, localAddress, promise);
return outboundHandler.connect(outboundCtx, remoteAddress, localAddress);
} else {
outboundCtx.connect(remoteAddress, localAddress, promise);
return outboundCtx.connect(remoteAddress, localAddress);
}
}
@Override
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> disconnect(ChannelHandlerContext ctx) {
assert ctx == outboundCtx.ctx;
if (!outboundCtx.removed) {
outboundHandler.disconnect(outboundCtx, promise);
return outboundHandler.disconnect(outboundCtx);
} else {
outboundCtx.disconnect(promise);
return outboundCtx.disconnect();
}
}
@Override
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> close(ChannelHandlerContext ctx) {
assert ctx == outboundCtx.ctx;
if (!outboundCtx.removed) {
outboundHandler.close(outboundCtx, promise);
return outboundHandler.close(outboundCtx);
} else {
outboundCtx.close(promise);
return outboundCtx.close();
}
}
@Override
public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> register(ChannelHandlerContext ctx) {
assert ctx == outboundCtx.ctx;
if (!outboundCtx.removed) {
outboundHandler.register(outboundCtx, promise);
return outboundHandler.register(outboundCtx);
} else {
outboundCtx.register(promise);
return outboundCtx.register();
}
}
@Override
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> deregister(ChannelHandlerContext ctx) {
assert ctx == outboundCtx.ctx;
if (!outboundCtx.removed) {
outboundHandler.deregister(outboundCtx, promise);
return outboundHandler.deregister(outboundCtx);
} else {
outboundCtx.deregister(promise);
return outboundCtx.deregister();
}
}
@Override
public void read(ChannelHandlerContext ctx) throws Exception {
public void read(ChannelHandlerContext ctx) {
assert ctx == outboundCtx.ctx;
if (!outboundCtx.removed) {
outboundHandler.read(outboundCtx);
@ -318,12 +317,12 @@ public class CombinedChannelDuplexHandler<I extends ChannelHandler, O extends Ch
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
assert ctx == outboundCtx.ctx;
if (!outboundCtx.removed) {
outboundHandler.write(outboundCtx, msg, promise);
return outboundHandler.write(outboundCtx, msg);
} else {
outboundCtx.write(msg, promise);
return outboundCtx.write(msg);
}
}
@ -462,42 +461,6 @@ public class CombinedChannelDuplexHandler<I extends ChannelHandler, O extends Ch
return ctx.deregister();
}
@Override
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return ctx.bind(localAddress, promise);
}
@Override
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, promise);
}
@Override
public Future<Void> connect(
SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return ctx.connect(remoteAddress, localAddress, promise);
}
@Override
public Future<Void> disconnect(Promise<Void> promise) {
return ctx.disconnect(promise);
}
@Override
public Future<Void> close(Promise<Void> promise) {
return ctx.close(promise);
}
@Override
public Future<Void> register(Promise<Void> promise) {
return ctx.register(promise);
}
@Override
public Future<Void> deregister(Promise<Void> promise) {
return ctx.deregister(promise);
}
@Override
public ChannelHandlerContext read() {
ctx.read();
@ -509,22 +472,12 @@ public class CombinedChannelDuplexHandler<I extends ChannelHandler, O extends Ch
return ctx.write(msg);
}
@Override
public Future<Void> write(Object msg, Promise<Void> promise) {
return ctx.write(msg, promise);
}
@Override
public ChannelHandlerContext flush() {
ctx.flush();
return this;
}
@Override
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return ctx.writeAndFlush(msg, promise);
}
@Override
public Future<Void> writeAndFlush(Object msg) {
return ctx.writeAndFlush(msg);

View File

@ -18,15 +18,14 @@ package io.netty.channel;
import static java.util.Objects.requireNonNull;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.AbstractChannel.ClosePromise;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.ResourceLeakHint;
import io.netty.util.concurrent.DefaultPromise;
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.internal.ObjectPool;
import io.netty.util.internal.ThrowableUtil;
import io.netty.util.internal.StringUtil;
@ -84,8 +83,8 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
this.handler = handler;
}
private static void failRemoved(DefaultChannelHandlerContext ctx, Promise<Void> promise) {
promise.setFailure(newRemovedException(ctx, null));
private static Future<Void> failRemoved(DefaultChannelHandlerContext ctx) {
return ctx.newFailedFuture(newRemovedException(ctx, null));
}
private void notifyHandlerRemovedAlready() {
@ -423,248 +422,183 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
@Override
public Future<Void> bind(SocketAddress localAddress) {
return bind(localAddress, newPromise());
requireNonNull(localAddress, "localAddress");
EventExecutor executor = executor();
if (executor.inEventLoop()) {
return findAndInvokeBind(localAddress);
}
Promise<Void> promise = newPromise();
safeExecute(executor, () -> PromiseNotifier.cascade(findAndInvokeBind(localAddress), promise), promise, null);
return promise;
}
@Override
public Future<Void> connect(SocketAddress remoteAddress) {
return connect(remoteAddress, newPromise());
}
@Override
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return connect(remoteAddress, localAddress, newPromise());
}
@Override
public Future<Void> disconnect() {
return disconnect(newPromise());
}
@Override
public Future<Void> close() {
return close(newPromise());
}
@Override
public Future<Void> register() {
return register(newPromise());
return connect(remoteAddress, null);
}
@Override
public Future<Void> deregister() {
return deregister(newPromise());
}
@Override
public Future<Void> bind(final SocketAddress localAddress, final Promise<Void> promise) {
requireNonNull(localAddress, "localAddress");
if (isNotValidPromise(promise)) {
// cancelled
return promise;
}
EventExecutor executor = executor();
if (executor.inEventLoop()) {
findAndInvokeBind(localAddress, promise);
} else {
safeExecute(executor, () -> findAndInvokeBind(localAddress, promise), promise, null);
return findAndInvokeDeregister();
}
Promise<Void> promise = newPromise();
safeExecute(executor, () -> PromiseNotifier.cascade(findAndInvokeDeregister(), promise), promise, null);
return promise;
}
private void findAndInvokeBind(SocketAddress localAddress, Promise<Void> promise) {
private Future<Void> findAndInvokeBind(SocketAddress localAddress) {
DefaultChannelHandlerContext ctx = findContextOutbound(MASK_BIND);
if (ctx == null) {
failRemoved(this, promise);
return;
return failRemoved(this);
}
ctx.invokeBind(localAddress, promise);
return ctx.invokeBind(localAddress);
}
private void invokeBind(SocketAddress localAddress, Promise<Void> promise) {
private Future<Void> invokeBind(SocketAddress localAddress) {
try {
handler().bind(this, localAddress, promise);
return handler().bind(this, localAddress);
} catch (Throwable t) {
handleOutboundHandlerException(t, false, promise);
return handleOutboundHandlerException(t, false);
}
}
@Override
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return connect(remoteAddress, null, promise);
}
@Override
public Future<Void> connect(
final SocketAddress remoteAddress, final SocketAddress localAddress, final Promise<Void> promise) {
final SocketAddress remoteAddress, final SocketAddress localAddress) {
requireNonNull(remoteAddress, "remoteAddress");
if (isNotValidPromise(promise)) {
// cancelled
return promise;
}
EventExecutor executor = executor();
if (executor.inEventLoop()) {
findAndInvokeConnect(remoteAddress, localAddress, promise);
} else {
safeExecute(executor, () -> findAndInvokeConnect(remoteAddress, localAddress, promise), promise, null);
return findAndInvokeConnect(remoteAddress, localAddress);
}
Promise<Void> promise = newPromise();
safeExecute(executor, () ->
PromiseNotifier.cascade(findAndInvokeConnect(remoteAddress, localAddress), promise), promise, null);
return promise;
}
private void findAndInvokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
private Future<Void> findAndInvokeConnect(SocketAddress remoteAddress, SocketAddress localAddress) {
DefaultChannelHandlerContext ctx = findContextOutbound(MASK_CONNECT);
if (ctx == null) {
failRemoved(this, promise);
return;
return failRemoved(this);
}
ctx.invokeConnect(remoteAddress, localAddress, promise);
return ctx.invokeConnect(remoteAddress, localAddress);
}
private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
private Future<Void> invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress) {
try {
handler().connect(this, remoteAddress, localAddress, promise);
return handler().connect(this, remoteAddress, localAddress);
} catch (Throwable t) {
handleOutboundHandlerException(t, false, promise);
return handleOutboundHandlerException(t, false);
}
}
@Override
public Future<Void> disconnect(final Promise<Void> promise) {
public Future<Void> disconnect() {
if (!channel().metadata().hasDisconnect()) {
// Translate disconnect to close if the channel has no notion of disconnect-reconnect.
// So far, UDP/IP is the only transport that has such behavior.
return close(promise);
}
if (isNotValidPromise(promise)) {
// cancelled
return promise;
return close();
}
EventExecutor executor = executor();
if (executor.inEventLoop()) {
findAndInvokeDisconnect(promise);
} else {
safeExecute(executor, () -> findAndInvokeDisconnect(promise), promise, null);
return findAndInvokeDisconnect();
}
Promise<Void> promise = newPromise();
safeExecute(executor, () ->
PromiseNotifier.cascade(findAndInvokeDisconnect(), promise), promise, null);
return promise;
}
private void findAndInvokeDisconnect(Promise<Void> promise) {
private Future<Void> findAndInvokeDisconnect() {
DefaultChannelHandlerContext ctx = findContextOutbound(MASK_DISCONNECT);
if (ctx == null) {
failRemoved(this, promise);
return;
return failRemoved(this);
}
ctx.invokeDisconnect(promise);
return ctx.invokeDisconnect();
}
private void invokeDisconnect(Promise<Void> promise) {
private Future<Void> invokeDisconnect() {
try {
handler().disconnect(this, promise);
return handler().disconnect(this);
} catch (Throwable t) {
handleOutboundHandlerException(t, false, promise);
return handleOutboundHandlerException(t, false);
}
}
@Override
public Future<Void> close(final Promise<Void> promise) {
if (isNotValidPromise(promise)) {
// cancelled
return promise;
}
public Future<Void> close() {
EventExecutor executor = executor();
if (executor.inEventLoop()) {
findAndInvokeClose(promise);
} else {
safeExecute(executor, () -> findAndInvokeClose(promise), promise, null);
return findAndInvokeClose();
}
Promise<Void> promise = newPromise();
safeExecute(executor, () ->
PromiseNotifier.cascade(findAndInvokeClose(), promise), promise, null);
return promise;
}
private void findAndInvokeClose(Promise<Void> promise) {
private Future<Void> findAndInvokeClose() {
DefaultChannelHandlerContext ctx = findContextOutbound(MASK_CLOSE);
if (ctx == null) {
failRemoved(this, promise);
return;
return failRemoved(this);
}
ctx.invokeClose(promise);
return ctx.invokeClose();
}
private void invokeClose(Promise<Void> promise) {
private Future<Void> invokeClose() {
try {
handler().close(this, promise);
return handler().close(this);
} catch (Throwable t) {
handleOutboundHandlerException(t, true, promise);
return handleOutboundHandlerException(t, true);
}
}
@Override
public Future<Void> register(final Promise<Void> promise) {
if (isNotValidPromise(promise)) {
// cancelled
return promise;
}
public Future<Void> register() {
EventExecutor executor = executor();
if (executor.inEventLoop()) {
findAndInvokeRegister(promise);
} else {
safeExecute(executor, () -> findAndInvokeRegister(promise), promise, null);
return findAndInvokeRegister();
}
Promise<Void> promise = newPromise();
safeExecute(executor, () ->
PromiseNotifier.cascade(findAndInvokeRegister(), promise), promise, null);
return promise;
}
private void findAndInvokeRegister(Promise<Void> promise) {
private Future<Void> findAndInvokeRegister() {
DefaultChannelHandlerContext ctx = findContextOutbound(MASK_REGISTER);
if (ctx == null) {
failRemoved(this, promise);
return;
return failRemoved(this);
}
ctx.invokeRegister(promise);
return ctx.invokeRegister();
}
private void invokeRegister(Promise<Void> promise) {
private Future<Void> invokeRegister() {
try {
handler().register(this, promise);
return handler().register(this);
} catch (Throwable t) {
handleOutboundHandlerException(t, false, promise);
return handleOutboundHandlerException(t, false);
}
}
@Override
public Future<Void> deregister(final Promise<Void> promise) {
if (isNotValidPromise(promise)) {
// cancelled
return promise;
}
EventExecutor executor = executor();
if (executor.inEventLoop()) {
findAndInvokeDeregister(promise);
} else {
safeExecute(executor, () -> findAndInvokeDeregister(promise), promise, null);
}
return promise;
}
private void findAndInvokeDeregister(Promise<Void> promise) {
private Future<Void> findAndInvokeDeregister() {
DefaultChannelHandlerContext ctx = findContextOutbound(MASK_DEREGISTER);
if (ctx == null) {
failRemoved(this, promise);
return;
return failRemoved(this);
}
ctx.invokeDeregister(promise);
return ctx.invokeDeregister();
}
private void invokeDeregister(Promise<Void> promise) {
private Future<Void> invokeDeregister() {
try {
handler().deregister(this, promise);
return handler().deregister(this);
} catch (Throwable t) {
handleOutboundHandlerException(t, false, promise);
return handleOutboundHandlerException(t, false);
}
}
@ -691,28 +625,21 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
try {
handler().read(this);
} catch (Throwable t) {
handleOutboundHandlerException(t, false, null);
handleOutboundHandlerException(t, false);
}
}
@Override
public Future<Void> write(Object msg) {
return write(msg, newPromise());
return write(msg, false);
}
@Override
public Future<Void> write(final Object msg, final Promise<Void> promise) {
write(msg, false, promise);
return promise;
}
private void invokeWrite(Object msg, Promise<Void> promise) {
private Future<Void> invokeWrite(Object msg) {
final Object m = pipeline.touch(msg, this);
try {
handler().write(this, m, promise);
return handler().write(this, m);
} catch (Throwable t) {
handleOutboundHandlerException(t, false, promise);
return handleOutboundHandlerException(t, false);
}
}
@ -744,33 +671,23 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
try {
handler().flush(this);
} catch (Throwable t) {
handleOutboundHandlerException(t, false, null);
handleOutboundHandlerException(t, false);
}
}
@Override
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
write(msg, true, promise);
return promise;
public Future<Void> writeAndFlush(Object msg) {
return write(msg, true);
}
private void invokeWriteAndFlush(Object msg, Promise<Void> promise) {
invokeWrite(msg, promise);
private Future<Void> invokeWriteAndFlush(Object msg) {
Future<Void> f = invokeWrite(msg);
invokeFlush();
return f;
}
private void write(Object msg, boolean flush, Promise<Void> promise) {
private Future<Void> write(Object msg, boolean flush) {
requireNonNull(msg, "msg");
try {
if (isNotValidPromise(promise)) {
ReferenceCountUtil.release(msg);
// cancelled
return;
}
} catch (RuntimeException e) {
ReferenceCountUtil.release(msg);
throw e;
}
EventExecutor executor = executor();
if (executor.inEventLoop()) {
@ -778,15 +695,14 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
(MASK_WRITE | MASK_FLUSH) : MASK_WRITE);
if (next == null) {
ReferenceCountUtil.release(msg);
failRemoved(this, promise);
return;
return failRemoved(this);
}
if (flush) {
next.invokeWriteAndFlush(msg, promise);
} else {
next.invokeWrite(msg, promise);
return next.invokeWriteAndFlush(msg);
}
return next.invokeWrite(msg);
} else {
Promise<Void> promise = newPromise();
final AbstractWriteTask task;
if (flush) {
task = WriteAndFlushTask.newInstance(this, msg, promise);
@ -800,22 +716,13 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
// See https://github.com/netty/netty/issues/8343.
task.cancel();
}
return promise;
}
}
@Override
public Future<Void> writeAndFlush(Object msg) {
return writeAndFlush(msg, newPromise());
}
private void handleOutboundHandlerException(Throwable cause, boolean closeDidThrow, Promise<Void> promise) {
private Future<Void> handleOutboundHandlerException(Throwable cause, boolean closeDidThrow) {
String msg = handler() + " threw an exception while handling an outbound event. This is most likely a bug";
if (promise != null && !promise.isDone()) {
// This is a "best-effort" approach to at least ensure the promise will be completed somehow.
promise.tryFailure(new IllegalStateException(msg, cause));
}
logger.warn("{}. This is most likely a bug, closing the channel.", msg, cause);
if (closeDidThrow) {
// Close itself did throw, just call close() directly and so have the next handler invoked. If we would
@ -826,6 +733,7 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
// and so give all handlers the chance to do something during close.
channel().close();
}
return newFailedFuture(new IllegalStateException(msg, cause));
}
@Override
@ -843,31 +751,6 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
return pipeline().newFailedFuture(cause);
}
private static boolean isNotValidPromise(Promise<Void> promise) {
requireNonNull(promise, "promise");
if (promise.isDone()) {
// Check if the promise was cancelled and if so signal that the processing of the operation
// should not be performed.
//
// See https://github.com/netty/netty/issues/2349
if (promise.isCancelled()) {
return true;
}
throw new IllegalArgumentException("promise already done: " + promise);
}
if (promise.getClass() == DefaultPromise.class) {
return false;
}
if (promise instanceof ClosePromise) {
throw new IllegalArgumentException(
StringUtil.simpleClassName(ClosePromise.class) + " not allowed in a pipeline");
}
return false;
}
private DefaultChannelHandlerContext findContextInbound(int mask) {
DefaultChannelHandlerContext ctx = this;
if (ctx.next == null) {
@ -1022,10 +905,14 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
public final void run() {
try {
decrementPendingOutboundBytes();
if (promise.isCancelled()) {
ReferenceCountUtil.release(msg);
return;
}
DefaultChannelHandlerContext next = findContext(ctx);
if (next == null) {
ReferenceCountUtil.release(msg);
failRemoved(ctx, promise);
failRemoved(ctx).addListener(new PromiseNotifier<>(promise));
return;
}
write(next, msg, promise);
@ -1057,7 +944,7 @@ final class DefaultChannelHandlerContext implements ChannelHandlerContext, Resou
}
protected void write(DefaultChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
ctx.invokeWrite(msg, promise);
PromiseNotifier.cascade(ctx.invokeWrite(msg), promise);
}
}

View File

@ -872,42 +872,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
return this;
}
@Override
public final Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
return tail.bind(localAddress, promise);
}
@Override
public final Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
return tail.connect(remoteAddress, promise);
}
@Override
public final Future<Void> connect(
SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
return tail.connect(remoteAddress, localAddress, promise);
}
@Override
public final Future<Void> disconnect(Promise<Void> promise) {
return tail.disconnect(promise);
}
@Override
public Future<Void> close(Promise<Void> promise) {
return tail.close(promise);
}
@Override
public final Future<Void> register(final Promise<Void> promise) {
return tail.register(promise);
}
@Override
public final Future<Void> deregister(final Promise<Void> promise) {
return tail.deregister(promise);
}
@Override
public final ChannelPipeline read() {
tail.read();
@ -919,16 +883,6 @@ public class DefaultChannelPipeline implements ChannelPipeline {
return tail.write(msg);
}
@Override
public final Future<Void> write(Object msg, Promise<Void> promise) {
return tail.write(msg, promise);
}
@Override
public final Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
return tail.writeAndFlush(msg, promise);
}
@Override
public final Future<Void> writeAndFlush(Object msg) {
return tail.writeAndFlush(msg);
@ -1087,37 +1041,48 @@ public class DefaultChannelPipeline implements ChannelPipeline {
private static final class HeadHandler implements ChannelHandler {
@Override
public void bind(
ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
public Future<Void> bind(
ChannelHandlerContext ctx, SocketAddress localAddress) {
Promise<Void> promise = ctx.newPromise();
ctx.channel().unsafe().bind(localAddress, promise);
return promise;
}
@Override
public void connect(
public Future<Void> connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
Promise<Void> promise) {
SocketAddress remoteAddress, SocketAddress localAddress) {
Promise<Void> promise = ctx.newPromise();
ctx.channel().unsafe().connect(remoteAddress, localAddress, promise);
return promise;
}
@Override
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> disconnect(ChannelHandlerContext ctx) {
Promise<Void> promise = ctx.newPromise();
ctx.channel().unsafe().disconnect(promise);
return promise;
}
@Override
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> close(ChannelHandlerContext ctx) {
Promise<Void> promise = ctx.newPromise();
ctx.channel().unsafe().close(promise);
return promise;
}
@Override
public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> register(ChannelHandlerContext ctx) {
Promise<Void> promise = ctx.newPromise();
ctx.channel().unsafe().register(promise);
return promise;
}
@Override
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
public Future<Void> deregister(ChannelHandlerContext ctx) {
Promise<Void> promise = ctx.newPromise();
ctx.channel().unsafe().deregister(promise);
return promise;
}
@Override
@ -1126,8 +1091,10 @@ public class DefaultChannelPipeline implements ChannelPipeline {
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
Promise<Void> promise = ctx.newPromise();
ctx.channel().unsafe().write(msg, promise);
return promise;
}
@Override

View File

@ -19,6 +19,7 @@ import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.PromiseCombiner;
import io.netty.util.concurrent.PromiseNotifier;
import io.netty.util.internal.ObjectPool;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
@ -116,7 +117,7 @@ public final class PendingWriteQueue {
/**
* Remove all pending write operation and performs them via
* {@link ChannelHandlerContext#write(Object, Promise)}.
* {@link ChannelHandlerContext#write(Object)}.
*
* @return {@link Future} if something was written and {@code null}
* if the {@link PendingWriteQueue} is empty.
@ -143,8 +144,7 @@ public final class PendingWriteQueue {
Object msg = write.msg;
Promise<Void> promise = write.promise;
recycle(write, false);
combiner.add(promise);
ctx.write(msg, promise);
PromiseNotifier.cascade(ctx.write(msg), promise);
write = next;
}
}
@ -205,7 +205,7 @@ public final class PendingWriteQueue {
/**
* Removes a pending write operation and performs it via
* {@link ChannelHandlerContext#write(Object, Promise)}.
* {@link ChannelHandlerContext#write(Object)}.
*
* @return {@link Future} if something was written and {@code null}
* if the {@link PendingWriteQueue} is empty.
@ -219,7 +219,10 @@ public final class PendingWriteQueue {
Object msg = write.msg;
Promise<Void> promise = write.promise;
recycle(write, true);
return ctx.write(msg, promise);
Future<Void> future = ctx.write(msg);
PromiseNotifier.cascade(future, promise);
return future;
}
/**

View File

@ -225,12 +225,7 @@ public class EmbeddedChannel extends AbstractChannel {
@Override
public Future<Void> register() {
return register(newPromise());
}
@Override
public Future<Void> register(Promise<Void> promise) {
Future<Void> future = super.register(promise);
Future<Void> future = super.register();
assert future.isDone();
Throwable cause = future.cause();
if (cause != null) {
@ -342,7 +337,7 @@ public class EmbeddedChannel extends AbstractChannel {
p.fireChannelRead(m);
}
flushInbound(false, null);
flushInbound(false);
return isNotEmpty(inboundMessages);
}
@ -353,21 +348,10 @@ public class EmbeddedChannel extends AbstractChannel {
* @see #writeOneOutbound(Object)
*/
public Future<Void> writeOneInbound(Object msg) {
return writeOneInbound(msg, newPromise());
}
/**
* Writes one message to the inbound of this {@link Channel} and does not flush it. This
* method is conceptually equivalent to {@link #write(Object, Promise)}.
*
* @see #writeOneOutbound(Object, Promise)
*/
public Future<Void> writeOneInbound(Object msg, Promise<Void> promise) {
if (checkOpen(true)) {
pipeline().fireChannelRead(msg);
}
checkException(promise);
return promise;
return checkException0();
}
/**
@ -376,19 +360,17 @@ public class EmbeddedChannel extends AbstractChannel {
* @see #flushOutbound()
*/
public EmbeddedChannel flushInbound() {
flushInbound(true, null);
flushInbound(true);
return this;
}
private Future<Void> flushInbound(boolean recordException, Promise<Void> promise) {
private void flushInbound(boolean recordException) {
if (checkOpen(recordException)) {
pipeline().fireChannelReadComplete();
readIfIsAutoRead();
runPendingTasks();
}
checkException(promise);
return promise;
checkException();
}
/**
@ -439,21 +421,10 @@ public class EmbeddedChannel extends AbstractChannel {
* @see #writeOneInbound(Object)
*/
public Future<Void> writeOneOutbound(Object msg) {
return writeOneOutbound(msg, newPromise());
}
/**
* Writes one message to the outbound of this {@link Channel} and does not flush it. This
* method is conceptually equivalent to {@link #write(Object, Promise)}.
*
* @see #writeOneInbound(Object, Promise)
*/
public Future<Void> writeOneOutbound(Object msg, Promise<Void> promise) {
if (checkOpen(true)) {
return write(msg, promise);
return write(msg);
}
checkException(promise);
return promise;
return checkException0();
}
/**
@ -465,7 +436,7 @@ public class EmbeddedChannel extends AbstractChannel {
if (checkOpen(true)) {
flushOutbound0();
}
checkException(null);
checkException();
return this;
}
@ -555,20 +526,10 @@ public class EmbeddedChannel extends AbstractChannel {
@Override
public final Future<Void> close() {
return close(newPromise());
}
@Override
public final Future<Void> disconnect() {
return disconnect(newPromise());
}
@Override
public final Future<Void> close(Promise<Void> promise) {
// We need to call runPendingTasks() before calling super.close() as there may be something in the queue
// that needs to be run before the actual close takes place.
runPendingTasks();
Future<Void> future = super.close(promise);
Future<Void> future = super.close();
// Now finish everything else and cancel all scheduled tasks that were not ready set.
finishPendingTasks(true);
@ -576,8 +537,8 @@ public class EmbeddedChannel extends AbstractChannel {
}
@Override
public final Future<Void> disconnect(Promise<Void> promise) {
Future<Void> future = super.disconnect(promise);
public final Future<Void> disconnect() {
Future<Void> future = super.disconnect();
finishPendingTasks(!metadata.hasDisconnect());
return future;
}
@ -643,27 +604,25 @@ public class EmbeddedChannel extends AbstractChannel {
/**
* Checks for the presence of an {@link Exception}.
*/
private void checkException(Promise<Void> promise) {
Throwable t = lastException;
if (t != null) {
lastException = null;
if (promise == null) {
PlatformDependent.throwException(t);
return;
}
promise.setFailure(t);
} else if (promise != null) {
promise.setSuccess(null);
private Future<Void> checkException0() {
try {
checkException();
} catch (Throwable cause) {
return newFailedFuture(cause);
}
return newSucceededFuture();
}
/**
* Check if there was any {@link Throwable} received and if so rethrow it.
*/
public void checkException() {
checkException(null);
Throwable t = lastException;
if (t != null) {
lastException = null;
PlatformDependent.throwException(t);
}
}
/**

View File

@ -253,7 +253,7 @@ public class DefaultChannelGroup extends AbstractSet<Channel> implements Channel
futures.put(c, c.write(safeDuplicate(message)));
}
}
final ChannelGroupFuture future = new DefaultChannelGroupFuture(this, futures, executor);
ChannelGroupFuture future = new DefaultChannelGroupFuture(this, futures, executor);
ReferenceCountUtil.release(message);
return future;
}

View File

@ -58,7 +58,8 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
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.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
@ -225,14 +226,6 @@ public class BootstrapTest {
close();
return newFailedFuture(new SocketException());
}
@Override
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
// Close the Channel to emulate what NIO and others impl do on bind failure
// See https://github.com/netty/netty/issues/2586
close();
return promise.setFailure(new SocketException());
}
});
bootstrap.childHandler(new DummyHandler());
bootstrap.handler(registerHandler);
@ -266,14 +259,31 @@ public class BootstrapTest {
Future<Channel> future = bootstrapA.connect(LocalAddress.ANY);
assertFalse(future.isDone());
registerHandler.registerPromise().setSuccess(null);
CompletionException exception =
assertThrows(CompletionException.class, future::syncUninterruptibly);
assertThat(exception.getCause()).isInstanceOf(ConnectException.class);
CompletionException cause = assertThrows(CompletionException.class, future::syncUninterruptibly);
assertThat(cause.getCause(), instanceOf(ConnectException.class));
} finally {
group.shutdownGracefully();
}
}
@Test
public void testAsyncResolutionSuccess() throws Exception {
final Bootstrap bootstrapA = new Bootstrap();
bootstrapA.group(groupA);
bootstrapA.channel(LocalChannel.class);
bootstrapA.resolver(new TestAddressResolverGroup(true));
bootstrapA.handler(dummyHandler);
final ServerBootstrap bootstrapB = new ServerBootstrap();
bootstrapB.group(groupB);
bootstrapB.channel(LocalServerChannel.class);
bootstrapB.childHandler(dummyHandler);
SocketAddress localAddress = bootstrapB.bind(LocalAddress.ANY).get().localAddress();
// Connect to the server using the asynchronous resolver.
bootstrapA.connect(localAddress).sync();
}
@Test
@Timeout(value = 10000, unit = TimeUnit.MILLISECONDS)
public void testLateRegistrationConnectWithCreateUnregistered() throws Throwable {
@ -298,24 +308,6 @@ public class BootstrapTest {
}
}
@Test
public void testAsyncResolutionSuccess() throws Exception {
final Bootstrap bootstrapA = new Bootstrap();
bootstrapA.group(groupA);
bootstrapA.channel(LocalChannel.class);
bootstrapA.resolver(new TestAddressResolverGroup(true));
bootstrapA.handler(dummyHandler);
final ServerBootstrap bootstrapB = new ServerBootstrap();
bootstrapB.group(groupB);
bootstrapB.channel(LocalServerChannel.class);
bootstrapB.childHandler(dummyHandler);
SocketAddress localAddress = bootstrapB.bind(LocalAddress.ANY).get().localAddress();
// Connect to the server using the asynchronous resolver.
bootstrapA.connect(localAddress).sync();
}
@Test
public void testAsyncResolutionFailure() throws Exception {
final Bootstrap bootstrapA = new Bootstrap();
@ -334,8 +326,8 @@ public class BootstrapTest {
Future<Channel> connectFuture = bootstrapA.connect(localAddress);
// Should fail with the UnknownHostException.
assertThat(connectFuture.await(10000)).isTrue();
assertThat(connectFuture.cause()).isInstanceOf(UnknownHostException.class);
assertTrue(connectFuture.await(10000));
assertThat(connectFuture.cause(), instanceOf(UnknownHostException.class));
}
@Test
@ -352,8 +344,8 @@ public class BootstrapTest {
Future<Channel> connectFuture = bootstrap.connect(LocalAddress.ANY);
// Should fail with the RuntimeException.
assertThat(connectFuture.await(10000)).isTrue();
assertThat(connectFuture.cause()).isSameAs(exception);
assertTrue(connectFuture.await(10000));
assertSame(connectFuture.cause(), exception);
}
@Test
@ -412,16 +404,14 @@ public class BootstrapTest {
private Promise<Void> registerPromise;
@Override
public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
registerPromise = promise;
public Future<Void> register(ChannelHandlerContext ctx) {
registerPromise = ctx.newPromise();
latch.countDown();
Promise<Void> newPromise = ctx.newPromise();
newPromise.addListener(future -> {
return ctx.register().addListener(future -> {
if (!future.isSuccess()) {
registerPromise.tryFailure(future.cause());
}
});
ctx.register(newPromise);
}
Promise<Void> registerPromise() throws InterruptedException {

View File

@ -197,9 +197,7 @@ public class AbstractChannelTest {
}
private static void registerChannel(Channel channel) throws Exception {
Promise<Void> future = channel.newPromise();
channel.register(future);
future.sync(); // Cause any exceptions to be thrown
channel.register().sync(); // Cause any exceptions to be thrown
}
private static class TestChannel extends AbstractChannel {

View File

@ -20,7 +20,7 @@ import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Promise;
import io.netty.util.concurrent.Future;
import org.junit.jupiter.api.Test;
import java.nio.channels.ClosedChannelException;
@ -46,9 +46,9 @@ public class AbstractCoalescingBufferQueueTest {
private static void testDecrementAll(boolean write) {
EmbeddedChannel channel = new EmbeddedChannel(new ChannelHandler() {
@Override
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
public Future<Void> write(ChannelHandlerContext ctx, Object msg) {
ReferenceCountUtil.release(msg);
promise.setSuccess(null);
return ctx.newSucceededFuture();
}
}, new ChannelHandlerAdapter() { });
final AbstractCoalescingBufferQueue queue = new AbstractCoalescingBufferQueue(channel, 128) {

View File

@ -19,6 +19,7 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import io.netty.util.concurrent.Promise;
import org.junit.jupiter.api.AfterEach;
@ -39,7 +40,6 @@ public class CoalescingBufferQueueTest {
private ByteBuf mouse;
private Promise<Void> catPromise, emptyPromise;
private Promise<Void> voidPromise;
private FutureListener<Void> mouseListener;
private boolean mouseDone;
@ -60,7 +60,6 @@ public class CoalescingBufferQueueTest {
mouseSuccess = future.isSuccess();
};
emptyPromise = newPromise();
voidPromise = channel.newPromise();
cat = Unpooled.wrappedBuffer("cat".getBytes(CharsetUtil.US_ASCII));
mouse = Unpooled.wrappedBuffer("mouse".getBytes(CharsetUtil.US_ASCII));
@ -90,29 +89,6 @@ public class CoalescingBufferQueueTest {
assertEquals(0, mouse.refCnt());
}
@Test
public void testAddFirstVoidPromise() {
writeQueue.add(cat, catPromise);
assertQueueSize(3, false);
writeQueue.add(mouse, mouseListener);
assertQueueSize(8, false);
Promise<Void> aggregatePromise = newPromise();
assertEquals("catmous", dequeue(7, aggregatePromise));
ByteBuf remainder = Unpooled.wrappedBuffer("mous".getBytes(CharsetUtil.US_ASCII));
writeQueue.addFirst(remainder, voidPromise);
Promise<Void> aggregatePromise2 = newPromise();
assertEquals("mouse", dequeue(5, aggregatePromise2));
aggregatePromise2.setSuccess(null);
// Because we used a void promise above, we shouldn't complete catPromise until aggregatePromise is completed.
assertFalse(catPromise.isSuccess());
assertTrue(mouseSuccess);
aggregatePromise.setSuccess(null);
assertTrue(catPromise.isSuccess());
assertTrue(mouseSuccess);
assertEquals(0, cat.refCnt());
assertEquals(0, mouse.refCnt());
}
@Test
public void testAggregateWithFullRead() {
writeQueue.add(cat, catPromise);
@ -131,19 +107,6 @@ public class CoalescingBufferQueueTest {
assertEquals(0, mouse.refCnt());
}
@Test
public void testWithVoidPromise() {
writeQueue.add(cat, voidPromise);
writeQueue.add(mouse, voidPromise);
assertQueueSize(8, false);
assertEquals("catm", dequeue(4, newPromise()));
assertQueueSize(4, false);
assertEquals("ouse", dequeue(4, newPromise()));
assertQueueSize(0, true);
assertEquals(0, cat.refCnt());
assertEquals(0, mouse.refCnt());
}
@Test
public void testAggregateWithPartialRead() {
writeQueue.add(cat, catPromise);
@ -276,7 +239,7 @@ public class CoalescingBufferQueueTest {
if (fail) {
writeQueue.releaseAndFailAll(new IllegalStateException());
} else {
ByteBuf buffer = writeQueue.removeFirst(voidPromise);
ByteBuf buffer = writeQueue.removeFirst(channel.newPromise());
assertEquals(1, buffer.readByte());
assertEquals(2, buffer.readByte());
assertEquals(3, buffer.readByte());
@ -284,7 +247,7 @@ public class CoalescingBufferQueueTest {
buffer.release();
assertTrue(channel.isWritable());
buffer = writeQueue.removeFirst(voidPromise);
buffer = writeQueue.removeFirst(channel.newPromise());
assertEquals(4, buffer.readByte());
assertEquals(5, buffer.readByte());
assertFalse(buffer.isReadable());

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