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:
parent
a34b440e22
commit
5c879bc963
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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) {
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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() {
|
||||
|
@ -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.
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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)) {
|
||||
|
@ -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()}.
|
||||
*/
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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) {
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user