Clean up Future/Promises API (#11575)
Motivation: The generics for the existing futures, promises, and listeners are too complicated. This complication comes from the existence of `ChannelPromise` and `ChannelFuture`, which forces listeners to care about the particular _type_ of future being listened on. Modification: * Add a `FutureContextListener` which can take a context object as an additional argument. This allows our listeners to have the channel piped through to them, so they don't need to rely on the `ChannelFuture.channel()` method. * Make the `FutureListener`, along with the `FutureContextListener` sibling, the default listener API, retiring the `GenericFutureListener` since we no longer need to abstract over the type of the future. * Change all uses of `ChannelPromise` to `Promise<Void>`. * Change all uses of `ChannelFuture` to `Future<Void>`. * Change all uses of `GenericFutureListener` to either `FutureListener` or `FutureContextListener` as needed. * Remove `ChannelFutureListener` and `GenericFutureListener`. * Introduce a `ChannelFutureListeners` enum to house the constants that previously lived in `ChannelFutureListener`. These constants now implement `FutureContextListener` and take the `Channel` as a context. * Remove `ChannelPromise` and `ChannelFuture` — all usages now rely on the plain `Future` and `Promise` APIs. * Add static factory methods to `DefaultPromise` that allow us to create promises that are initialised as successful or failed. * Remove `CompleteFuture`, `SucceededFuture`, `FailedFuture`, `CompleteChannelFuture`, `SucceededChannelFuture`, and `FailedChannelFuture`. * Remove `ChannelPromiseNotifier`. Result: Cleaner generics and more straight forward code.
This commit is contained in:
parent
afd812c3a4
commit
7971a252a5
@ -16,19 +16,20 @@
|
|||||||
package io.netty.handler.codec.haproxy;
|
package io.netty.handler.codec.haproxy;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.handler.codec.ProtocolDetectionResult;
|
import io.netty.handler.codec.ProtocolDetectionResult;
|
||||||
import io.netty.handler.codec.ProtocolDetectionState;
|
import io.netty.handler.codec.ProtocolDetectionState;
|
||||||
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily;
|
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.AddressFamily;
|
||||||
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.TransportProtocol;
|
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol.TransportProtocol;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static io.netty.buffer.Unpooled.*;
|
import static io.netty.buffer.Unpooled.buffer;
|
||||||
|
import static io.netty.buffer.Unpooled.copiedBuffer;
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
@ -224,7 +225,7 @@ public class HAProxyMessageDecoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCloseOnInvalid() {
|
public void testCloseOnInvalid() {
|
||||||
ChannelFuture closeFuture = ch.closeFuture();
|
Future<Void> closeFuture = ch.closeFuture();
|
||||||
String header = "GET / HTTP/1.1\r\n";
|
String header = "GET / HTTP/1.1\r\n";
|
||||||
try {
|
try {
|
||||||
ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII));
|
ch.writeInbound(copiedBuffer(header, CharsetUtil.US_ASCII));
|
||||||
|
@ -17,24 +17,24 @@ package io.netty.handler.codec.http;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.util.Attribute;
|
import io.netty.util.Attribute;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
import io.netty.util.internal.ObjectUtil;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
abstract class DelegatingChannelHandlerContext implements ChannelHandlerContext {
|
abstract class DelegatingChannelHandlerContext implements ChannelHandlerContext {
|
||||||
|
|
||||||
private final ChannelHandlerContext ctx;
|
private final ChannelHandlerContext ctx;
|
||||||
|
|
||||||
DelegatingChannelHandlerContext(ChannelHandlerContext ctx) {
|
DelegatingChannelHandlerContext(ChannelHandlerContext ctx) {
|
||||||
this.ctx = ObjectUtil.checkNotNull(ctx, "ctx");
|
this.ctx = Objects.requireNonNull(ctx, "ctx");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -150,108 +150,108 @@ abstract class DelegatingChannelHandlerContext implements ChannelHandlerContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture bind(SocketAddress localAddress) {
|
public Future<Void> bind(SocketAddress localAddress) {
|
||||||
return ctx.bind(localAddress);
|
return ctx.bind(localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress) {
|
public Future<Void> connect(SocketAddress remoteAddress) {
|
||||||
return ctx.connect(remoteAddress);
|
return ctx.connect(remoteAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
||||||
return ctx.connect(remoteAddress, localAddress);
|
return ctx.connect(remoteAddress, localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture disconnect() {
|
public Future<Void> disconnect() {
|
||||||
return ctx.disconnect();
|
return ctx.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close() {
|
public Future<Void> close() {
|
||||||
return ctx.close();
|
return ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture deregister() {
|
public Future<Void> deregister() {
|
||||||
return ctx.deregister();
|
return ctx.deregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register() {
|
public Future<Void> register() {
|
||||||
return ctx.register();
|
return ctx.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register(ChannelPromise promise) {
|
public Future<Void> register(Promise<Void> promise) {
|
||||||
return ctx.register(promise);
|
return ctx.register(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
|
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return ctx.bind(localAddress, promise);
|
return ctx.bind(localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
|
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
|
||||||
return ctx.connect(remoteAddress, promise);
|
return ctx.connect(remoteAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(
|
public Future<Void> connect(
|
||||||
SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
|
SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return ctx.connect(remoteAddress, localAddress, promise);
|
return ctx.connect(remoteAddress, localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture disconnect(ChannelPromise promise) {
|
public Future<Void> disconnect(Promise<Void> promise) {
|
||||||
return ctx.disconnect(promise);
|
return ctx.disconnect(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close(ChannelPromise promise) {
|
public Future<Void> close(Promise<Void> promise) {
|
||||||
return ctx.close(promise);
|
return ctx.close(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture deregister(ChannelPromise promise) {
|
public Future<Void> deregister(Promise<Void> promise) {
|
||||||
return ctx.deregister(promise);
|
return ctx.deregister(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture write(Object msg) {
|
public Future<Void> write(Object msg) {
|
||||||
return ctx.write(msg);
|
return ctx.write(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture write(Object msg, ChannelPromise promise) {
|
public Future<Void> write(Object msg, Promise<Void> promise) {
|
||||||
return ctx.write(msg, promise);
|
return ctx.write(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
|
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
|
||||||
return ctx.writeAndFlush(msg, promise);
|
return ctx.writeAndFlush(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeAndFlush(Object msg) {
|
public Future<Void> writeAndFlush(Object msg) {
|
||||||
return ctx.writeAndFlush(msg);
|
return ctx.writeAndFlush(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelPromise newPromise() {
|
public Promise<Void> newPromise() {
|
||||||
return ctx.newPromise();
|
return ctx.newPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture newSucceededFuture() {
|
public Future<Void> newSucceededFuture() {
|
||||||
return ctx.newSucceededFuture();
|
return ctx.newSucceededFuture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture newFailedFuture(Throwable cause) {
|
public Future<Void> newFailedFuture(Throwable cause) {
|
||||||
return ctx.newFailedFuture(cause);
|
return ctx.newFailedFuture(cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ import io.netty.handler.stream.ChunkedInput;
|
|||||||
*
|
*
|
||||||
* HttpContentChunkedInput httpChunkWriter = new HttpChunkedInput(
|
* HttpContentChunkedInput httpChunkWriter = new HttpChunkedInput(
|
||||||
* new ChunkedFile("/tmp/myfile.txt"));
|
* new ChunkedFile("/tmp/myfile.txt"));
|
||||||
* ChannelFuture sendFileFuture = ctx.write(httpChunkWriter);
|
* Future<Void> sendFileFuture = ctx.write(httpChunkWriter);
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
|
@ -15,8 +15,8 @@
|
|||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -121,33 +121,33 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
|
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
|
||||||
ctx.bind(localAddress, promise);
|
ctx.bind(localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
|
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
ctx.connect(remoteAddress, localAddress, promise);
|
ctx.connect(remoteAddress, localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ctx.disconnect(promise);
|
ctx.disconnect(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ctx.close(promise);
|
ctx.close(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ctx.register(promise);
|
ctx.register(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ctx.deregister(promise);
|
ctx.deregister(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ public class HttpClientUpgradeHandler extends HttpObjectAggregator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
if (!(msg instanceof HttpRequest)) {
|
if (!(msg instanceof HttpRequest)) {
|
||||||
ctx.write(msg, promise);
|
ctx.write(msg, promise);
|
||||||
return;
|
return;
|
||||||
|
@ -17,14 +17,13 @@ package io.netty.handler.codec.http;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.handler.codec.DecoderResult;
|
import io.netty.handler.codec.DecoderResult;
|
||||||
import io.netty.handler.codec.MessageAggregator;
|
import io.netty.handler.codec.MessageAggregator;
|
||||||
import io.netty.handler.codec.TooLongFrameException;
|
import io.netty.handler.codec.TooLongFrameException;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
@ -250,15 +249,15 @@ public class HttpObjectAggregator
|
|||||||
// If keep-alive is off and 'Expect: 100-continue' is missing, no need to leave the connection open.
|
// If keep-alive is off and 'Expect: 100-continue' is missing, no need to leave the connection open.
|
||||||
if (oversized instanceof FullHttpMessage ||
|
if (oversized instanceof FullHttpMessage ||
|
||||||
!HttpUtil.is100ContinueExpected(oversized) && !HttpUtil.isKeepAlive(oversized)) {
|
!HttpUtil.is100ContinueExpected(oversized) && !HttpUtil.isKeepAlive(oversized)) {
|
||||||
ChannelFuture future = ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate());
|
Future<Void> future = ctx.writeAndFlush(TOO_LARGE_CLOSE.retainedDuplicate());
|
||||||
future.addListener((ChannelFutureListener) future1 -> {
|
future.addListener(future1 -> {
|
||||||
if (!future1.isSuccess()) {
|
if (!future1.isSuccess()) {
|
||||||
logger.debug("Failed to send a 413 Request Entity Too Large.", future1.cause());
|
logger.debug("Failed to send a 413 Request Entity Too Large.", future1.cause());
|
||||||
}
|
}
|
||||||
ctx.close();
|
ctx.close();
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener((ChannelFutureListener) future -> {
|
ctx.writeAndFlush(TOO_LARGE.retainedDuplicate()).addListener(future -> {
|
||||||
if (!future.isSuccess()) {
|
if (!future.isSuccess()) {
|
||||||
logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
|
logger.debug("Failed to send a 413 Request Entity Too Large.", future.cause());
|
||||||
ctx.close();
|
ctx.close();
|
||||||
|
@ -108,7 +108,7 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
|
|||||||
|
|
||||||
// Bypass the encoder in case of an empty buffer, so that the following idiom works:
|
// Bypass the encoder in case of an empty buffer, so that the following idiom works:
|
||||||
//
|
//
|
||||||
// ch.write(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
// ch.write(Unpooled.EMPTY_BUFFER).addListener(ch, ChannelFutureListeners.CLOSE);
|
||||||
//
|
//
|
||||||
// See https://github.com/netty/netty/issues/2983 for more information.
|
// See https://github.com/netty/netty/issues/2983 for more information.
|
||||||
if (msg instanceof ByteBufConvertible) {
|
if (msg instanceof ByteBufConvertible) {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
@ -84,11 +84,11 @@ public class HttpServerExpectContinueHandler implements ChannelHandler {
|
|||||||
// the expectation failed so we refuse the request.
|
// the expectation failed so we refuse the request.
|
||||||
HttpResponse rejection = rejectResponse(req);
|
HttpResponse rejection = rejectResponse(req);
|
||||||
ReferenceCountUtil.release(msg);
|
ReferenceCountUtil.release(msg);
|
||||||
ctx.writeAndFlush(rejection).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
|
ctx.writeAndFlush(rejection).addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.writeAndFlush(accept).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
|
ctx.writeAndFlush(accept).addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
|
||||||
req.headers().remove(HttpHeaderNames.EXPECT);
|
req.headers().remove(HttpHeaderNames.EXPECT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpUtil.*;
|
import static io.netty.handler.codec.http.HttpUtil.isContentLengthSet;
|
||||||
|
import static io.netty.handler.codec.http.HttpUtil.isKeepAlive;
|
||||||
|
import static io.netty.handler.codec.http.HttpUtil.isTransferEncodingChunked;
|
||||||
|
import static io.netty.handler.codec.http.HttpUtil.setKeepAlive;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HttpServerKeepAliveHandler helps close persistent connections when appropriate.
|
* HttpServerKeepAliveHandler helps close persistent connections when appropriate.
|
||||||
@ -65,7 +68,7 @@ public class HttpServerKeepAliveHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
// modify message on way out to add headers if needed
|
// modify message on way out to add headers if needed
|
||||||
if (msg instanceof HttpResponse) {
|
if (msg instanceof HttpResponse) {
|
||||||
final HttpResponse response = (HttpResponse) msg;
|
final HttpResponse response = (HttpResponse) msg;
|
||||||
@ -82,7 +85,7 @@ public class HttpServerKeepAliveHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (msg instanceof LastHttpContent && !shouldKeepAlive()) {
|
if (msg instanceof LastHttpContent && !shouldKeepAlive()) {
|
||||||
promise.addListener(ChannelFutureListener.CLOSE);
|
promise.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
|
||||||
}
|
}
|
||||||
ctx.write(msg, promise);
|
ctx.write(msg, promise);
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,11 @@
|
|||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.ReferenceCounted;
|
import io.netty.util.ReferenceCounted;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -360,7 +360,7 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
|
|||||||
// of the write future and receiving data before the pipeline is
|
// of the write future and receiving data before the pipeline is
|
||||||
// restructured.
|
// restructured.
|
||||||
try {
|
try {
|
||||||
final ChannelFuture writeComplete = ctx.writeAndFlush(upgradeResponse);
|
Future<Void> writeComplete = ctx.writeAndFlush(upgradeResponse);
|
||||||
// Perform the upgrade to the new protocol.
|
// Perform the upgrade to the new protocol.
|
||||||
sourceCodec.upgradeFrom(ctx);
|
sourceCodec.upgradeFrom(ctx);
|
||||||
upgradeCodec.upgradeTo(ctx, request);
|
upgradeCodec.upgradeTo(ctx, request);
|
||||||
@ -375,7 +375,7 @@ public class HttpServerUpgradeHandler extends HttpObjectAggregator {
|
|||||||
// Add the listener last to avoid firing upgrade logic after
|
// Add the listener last to avoid firing upgrade logic after
|
||||||
// the channel is already closed since the listener may fire
|
// the channel is already closed since the listener may fire
|
||||||
// immediately if the write failed eagerly.
|
// immediately if the write failed eagerly.
|
||||||
writeComplete.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
|
writeComplete.addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
|
||||||
} finally {
|
} finally {
|
||||||
// Release the event if the upgrade event wasn't fired.
|
// Release the event if the upgrade event wasn't fired.
|
||||||
event.release();
|
event.release();
|
||||||
|
@ -15,11 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.cors;
|
package io.netty.handler.codec.http.cors;
|
||||||
|
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpHeaderValues;
|
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||||
@ -27,6 +25,8 @@ import io.netty.handler.codec.http.HttpHeaders;
|
|||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpUtil;
|
import io.netty.handler.codec.http.HttpUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
@ -69,7 +69,7 @@ public class CorsHandler implements ChannelHandler {
|
|||||||
* config matches a certain origin, the first in the List will be used.
|
* config matches a certain origin, the first in the List will be used.
|
||||||
*
|
*
|
||||||
* @param configList List of {@link CorsConfig}
|
* @param configList List of {@link CorsConfig}
|
||||||
* @param isShortCircuit Same as {@link CorsConfig#shortCircuit} but applicable to all supplied configs.
|
* @param isShortCircuit Same as {@link CorsConfig#isShortCurcuit()} but applicable to all supplied configs.
|
||||||
*/
|
*/
|
||||||
public CorsHandler(final List<CorsConfig> configList, boolean isShortCircuit) {
|
public CorsHandler(final List<CorsConfig> configList, boolean isShortCircuit) {
|
||||||
checkNonEmpty(configList, "configList");
|
checkNonEmpty(configList, "configList");
|
||||||
@ -215,7 +215,7 @@ public class CorsHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) {
|
public void write(final ChannelHandlerContext ctx, final Object msg, Promise<Void> promise) {
|
||||||
if (config != null && config.isCorsSupportEnabled() && msg instanceof HttpResponse) {
|
if (config != null && config.isCorsSupportEnabled() && msg instanceof HttpResponse) {
|
||||||
final HttpResponse response = (HttpResponse) msg;
|
final HttpResponse response = (HttpResponse) msg;
|
||||||
if (setOrigin(response)) {
|
if (setOrigin(response)) {
|
||||||
@ -243,9 +243,9 @@ public class CorsHandler implements ChannelHandler {
|
|||||||
|
|
||||||
HttpUtil.setKeepAlive(response, keepAlive);
|
HttpUtil.setKeepAlive(response, keepAlive);
|
||||||
|
|
||||||
final ChannelFuture future = ctx.writeAndFlush(response);
|
Future<Void> future = ctx.writeAndFlush(response);
|
||||||
if (!keepAlive) {
|
if (!keepAlive) {
|
||||||
future.addListener(ChannelFutureListener.CLOSE);
|
future.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ package io.netty.handler.codec.http.websocketx;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
import io.netty.handler.codec.CorruptedFrameException;
|
||||||
@ -92,7 +92,7 @@ public class Utf8FrameValidator implements ChannelHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
||||||
if (cause instanceof CorruptedFrameException && ctx.channel().isOpen()) {
|
if (cause instanceof CorruptedFrameException && ctx.channel().isOpen()) {
|
||||||
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
|
||||||
}
|
}
|
||||||
ctx.fireExceptionCaught(cause);
|
ctx.fireExceptionCaught(cause);
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ package io.netty.handler.codec.http.websocketx;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
import io.netty.handler.codec.TooLongFrameException;
|
import io.netty.handler.codec.TooLongFrameException;
|
||||||
@ -448,7 +448,7 @@ public class WebSocket08FrameDecoder extends ByteToMessageDecoder
|
|||||||
}
|
}
|
||||||
closeMessage = new CloseWebSocketFrame(closeStatus, reasonText);
|
closeMessage = new CloseWebSocketFrame(closeStatus, reasonText);
|
||||||
}
|
}
|
||||||
ctx.writeAndFlush(closeMessage).addListener(ChannelFutureListener.CLOSE);
|
ctx.writeAndFlush(closeMessage).addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
|
||||||
}
|
}
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
|
@ -15,16 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelOutboundInvoker;
|
import io.netty.channel.ChannelOutboundInvoker;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
@ -39,14 +34,17 @@ import io.netty.handler.codec.http.HttpResponseDecoder;
|
|||||||
import io.netty.handler.codec.http.HttpScheme;
|
import io.netty.handler.codec.http.HttpScheme;
|
||||||
import io.netty.util.NetUtil;
|
import io.netty.util.NetUtil;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.channels.ClosedChannelException;
|
import java.nio.channels.ClosedChannelException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for web socket client handshake implementations
|
* Base class for web socket client handshake implementations
|
||||||
*/
|
*/
|
||||||
@ -236,7 +234,7 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
* @param channel
|
* @param channel
|
||||||
* Channel
|
* Channel
|
||||||
*/
|
*/
|
||||||
public ChannelFuture handshake(Channel channel) {
|
public Future<Void> handshake(Channel channel) {
|
||||||
requireNonNull(channel, "channel");
|
requireNonNull(channel, "channel");
|
||||||
return handshake(channel, channel.newPromise());
|
return handshake(channel, channel.newPromise());
|
||||||
}
|
}
|
||||||
@ -247,9 +245,9 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
* @param channel
|
* @param channel
|
||||||
* Channel
|
* Channel
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the opening handshake is sent
|
* the {@link Promise} to be notified when the opening handshake is sent
|
||||||
*/
|
*/
|
||||||
public final ChannelFuture handshake(Channel channel, final ChannelPromise promise) {
|
public final Future<Void> handshake(Channel channel, final Promise<Void> promise) {
|
||||||
ChannelPipeline pipeline = channel.pipeline();
|
ChannelPipeline pipeline = channel.pipeline();
|
||||||
HttpResponseDecoder decoder = pipeline.get(HttpResponseDecoder.class);
|
HttpResponseDecoder decoder = pipeline.get(HttpResponseDecoder.class);
|
||||||
if (decoder == null) {
|
if (decoder == null) {
|
||||||
@ -263,9 +261,9 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
|
|
||||||
FullHttpRequest request = newHandshakeRequest();
|
FullHttpRequest request = newHandshakeRequest();
|
||||||
|
|
||||||
channel.writeAndFlush(request).addListener((ChannelFutureListener) future -> {
|
channel.writeAndFlush(request).addListener(channel, (ch, future) -> {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
ChannelPipeline p = future.channel().pipeline();
|
ChannelPipeline p = ch.pipeline();
|
||||||
ChannelHandlerContext ctx = p.context(HttpRequestEncoder.class);
|
ChannelHandlerContext ctx = p.context(HttpRequestEncoder.class);
|
||||||
if (ctx == null) {
|
if (ctx == null) {
|
||||||
ctx = p.context(HttpClientCodec.class);
|
ctx = p.context(HttpClientCodec.class);
|
||||||
@ -277,7 +275,7 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
}
|
}
|
||||||
p.addAfter(ctx.name(), "ws-encoder", newWebSocketEncoder());
|
p.addAfter(ctx.name(), "ws-encoder", newWebSocketEncoder());
|
||||||
|
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
promise.setFailure(future.cause());
|
promise.setFailure(future.cause());
|
||||||
}
|
}
|
||||||
@ -384,9 +382,9 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
* @param response
|
* @param response
|
||||||
* HTTP response containing the closing handshake details
|
* HTTP response containing the closing handshake details
|
||||||
* @return future
|
* @return future
|
||||||
* the {@link ChannelFuture} which is notified once the handshake completes.
|
* the {@link Future} which is notified once the handshake completes.
|
||||||
*/
|
*/
|
||||||
public final ChannelFuture processHandshake(final Channel channel, HttpResponse response) {
|
public final Future<Void> processHandshake(final Channel channel, HttpResponse response) {
|
||||||
return processHandshake(channel, response, channel.newPromise());
|
return processHandshake(channel, response, channel.newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -398,16 +396,16 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
* @param response
|
* @param response
|
||||||
* HTTP response containing the closing handshake details
|
* HTTP response containing the closing handshake details
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to notify once the handshake completes.
|
* the {@link Promise} to notify once the handshake completes.
|
||||||
* @return future
|
* @return future
|
||||||
* the {@link ChannelFuture} which is notified once the handshake completes.
|
* the {@link Future} which is notified once the handshake completes.
|
||||||
*/
|
*/
|
||||||
public final ChannelFuture processHandshake(final Channel channel, HttpResponse response,
|
public final Future<Void> processHandshake(final Channel channel, HttpResponse response,
|
||||||
final ChannelPromise promise) {
|
final Promise<Void> promise) {
|
||||||
if (response instanceof FullHttpResponse) {
|
if (response instanceof FullHttpResponse) {
|
||||||
try {
|
try {
|
||||||
finishHandshake(channel, (FullHttpResponse) response);
|
finishHandshake(channel, (FullHttpResponse) response);
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
promise.setFailure(cause);
|
promise.setFailure(cause);
|
||||||
}
|
}
|
||||||
@ -434,7 +432,7 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
ctx.pipeline().remove(this);
|
ctx.pipeline().remove(this);
|
||||||
try {
|
try {
|
||||||
finishHandshake(channel, msg);
|
finishHandshake(channel, msg);
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
promise.setFailure(cause);
|
promise.setFailure(cause);
|
||||||
}
|
}
|
||||||
@ -491,7 +489,7 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received
|
* Closing Frame that was received
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame) {
|
public Future<Void> close(Channel channel, CloseWebSocketFrame frame) {
|
||||||
requireNonNull(channel, "channel");
|
requireNonNull(channel, "channel");
|
||||||
return close(channel, frame, channel.newPromise());
|
return close(channel, frame, channel.newPromise());
|
||||||
}
|
}
|
||||||
@ -500,16 +498,16 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
* Performs the closing handshake
|
* Performs the closing handshake
|
||||||
*
|
*
|
||||||
* When called from within a {@link ChannelHandler} you most likely want to use
|
* When called from within a {@link ChannelHandler} you most likely want to use
|
||||||
* {@link #close(ChannelHandlerContext, CloseWebSocketFrame, ChannelPromise)}.
|
* {@link #close(ChannelHandlerContext, CloseWebSocketFrame, Promise)}.
|
||||||
*
|
*
|
||||||
* @param channel
|
* @param channel
|
||||||
* Channel
|
* Channel
|
||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received
|
* Closing Frame that was received
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the closing handshake is done
|
* the {@link Promise} to be notified when the closing handshake is done
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame, ChannelPromise promise) {
|
public Future<Void> close(Channel channel, CloseWebSocketFrame frame, Promise<Void> promise) {
|
||||||
requireNonNull(channel, "channel");
|
requireNonNull(channel, "channel");
|
||||||
return close0(channel, channel, frame, promise);
|
return close0(channel, channel, frame, promise);
|
||||||
}
|
}
|
||||||
@ -522,7 +520,7 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received
|
* Closing Frame that was received
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
|
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
|
||||||
requireNonNull(ctx, "ctx");
|
requireNonNull(ctx, "ctx");
|
||||||
return close(ctx, frame, ctx.newPromise());
|
return close(ctx, frame, ctx.newPromise());
|
||||||
}
|
}
|
||||||
@ -535,15 +533,15 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received
|
* Closing Frame that was received
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the closing handshake is done
|
* the {@link Promise} to be notified when the closing handshake is done
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, ChannelPromise promise) {
|
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, Promise<Void> promise) {
|
||||||
requireNonNull(ctx, "ctx");
|
requireNonNull(ctx, "ctx");
|
||||||
return close0(ctx, ctx.channel(), frame, promise);
|
return close0(ctx, ctx.channel(), frame, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture close0(final ChannelOutboundInvoker invoker, final Channel channel,
|
private Future<Void> close0(final ChannelOutboundInvoker invoker, final Channel channel,
|
||||||
CloseWebSocketFrame frame, ChannelPromise promise) {
|
CloseWebSocketFrame frame, Promise<Void> promise) {
|
||||||
invoker.writeAndFlush(frame, promise);
|
invoker.writeAndFlush(frame, promise);
|
||||||
final long forceCloseTimeoutMillis = this.forceCloseTimeoutMillis;
|
final long forceCloseTimeoutMillis = this.forceCloseTimeoutMillis;
|
||||||
final WebSocketClientHandshaker handshaker = this;
|
final WebSocketClientHandshaker handshaker = this;
|
||||||
|
@ -15,19 +15,16 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent;
|
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.FutureListener;
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static io.netty.util.internal.ObjectUtil.*;
|
import static io.netty.util.internal.ObjectUtil.checkPositive;
|
||||||
|
|
||||||
class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
|
class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
|
||||||
|
|
||||||
@ -36,7 +33,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
private final WebSocketClientHandshaker handshaker;
|
private final WebSocketClientHandshaker handshaker;
|
||||||
private final long handshakeTimeoutMillis;
|
private final long handshakeTimeoutMillis;
|
||||||
private ChannelHandlerContext ctx;
|
private ChannelHandlerContext ctx;
|
||||||
private ChannelPromise handshakePromise;
|
private Promise<Void> handshakePromise;
|
||||||
|
|
||||||
WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker) {
|
WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker) {
|
||||||
this(handshaker, DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
this(handshaker, DEFAULT_HANDSHAKE_TIMEOUT_MS);
|
||||||
@ -56,7 +53,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
|
public void channelActive(final ChannelHandlerContext ctx) throws Exception {
|
||||||
ctx.fireChannelActive();
|
ctx.fireChannelActive();
|
||||||
handshaker.handshake(ctx.channel()).addListener((ChannelFutureListener) future -> {
|
handshaker.handshake(ctx.channel()).addListener(future -> {
|
||||||
if (!future.isSuccess()) {
|
if (!future.isSuccess()) {
|
||||||
handshakePromise.tryFailure(future.cause());
|
handshakePromise.tryFailure(future.cause());
|
||||||
ctx.fireExceptionCaught(future.cause());
|
ctx.fireExceptionCaught(future.cause());
|
||||||
@ -89,7 +86,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
try {
|
try {
|
||||||
if (!handshaker.isHandshakeComplete()) {
|
if (!handshaker.isHandshakeComplete()) {
|
||||||
handshaker.finishHandshake(ctx.channel(), response);
|
handshaker.finishHandshake(ctx.channel(), response);
|
||||||
handshakePromise.trySuccess();
|
handshakePromise.trySuccess(null);
|
||||||
ctx.fireUserEventTriggered(
|
ctx.fireUserEventTriggered(
|
||||||
WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE);
|
WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE);
|
||||||
ctx.pipeline().remove(this);
|
ctx.pipeline().remove(this);
|
||||||
@ -102,7 +99,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void applyHandshakeTimeout() {
|
private void applyHandshakeTimeout() {
|
||||||
final ChannelPromise localHandshakePromise = handshakePromise;
|
final Promise<Void> localHandshakePromise = handshakePromise;
|
||||||
if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) {
|
if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -120,7 +117,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
|
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
// Cancel the handshake timeout when handshake is finished.
|
// Cancel the handshake timeout when handshake is finished.
|
||||||
localHandshakePromise.addListener((FutureListener<Void>) f -> timeoutFuture.cancel(false));
|
localHandshakePromise.addListener(f -> timeoutFuture.cancel(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,7 +125,7 @@ class WebSocketClientProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
*
|
*
|
||||||
* @return current handshake future
|
* @return current handshake future
|
||||||
*/
|
*/
|
||||||
ChannelFuture getHandshakeFuture() {
|
Future<Void> getHandshakeFuture() {
|
||||||
return handshakePromise;
|
return handshakePromise;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,9 @@ package io.netty.handler.codec.http.websocketx;
|
|||||||
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.MessageToMessageDecoder;
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.PromiseNotifier;
|
import io.netty.util.concurrent.PromiseNotifier;
|
||||||
import io.netty.util.concurrent.ScheduledFuture;
|
import io.netty.util.concurrent.ScheduledFuture;
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
|
|||||||
private final boolean dropPongFrames;
|
private final boolean dropPongFrames;
|
||||||
private final WebSocketCloseStatus closeStatus;
|
private final WebSocketCloseStatus closeStatus;
|
||||||
private final long forceCloseTimeoutMillis;
|
private final long forceCloseTimeoutMillis;
|
||||||
private ChannelPromise closeSent;
|
private Promise<Void> closeSent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@link WebSocketProtocolHandler} that will <i>drop</i> {@link PongWebSocketFrame}s.
|
* Creates a new {@link WebSocketProtocolHandler} that will <i>drop</i> {@link PongWebSocketFrame}s.
|
||||||
@ -82,7 +82,7 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) {
|
public void close(final ChannelHandlerContext ctx, final Promise<Void> promise) {
|
||||||
if (closeStatus == null || !ctx.channel().isActive()) {
|
if (closeStatus == null || !ctx.channel().isActive()) {
|
||||||
ctx.close(promise);
|
ctx.close(promise);
|
||||||
} else {
|
} else {
|
||||||
@ -96,7 +96,7 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
if (closeSent != null) {
|
if (closeSent != null) {
|
||||||
ReferenceCountUtil.release(msg);
|
ReferenceCountUtil.release(msg);
|
||||||
promise.setFailure(new ClosedChannelException());
|
promise.setFailure(new ClosedChannelException());
|
||||||
@ -108,7 +108,7 @@ abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void closeSent(ChannelPromise promise) {
|
void closeSent(Promise<Void> promise) {
|
||||||
closeSent = promise;
|
closeSent = promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,16 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelOutboundInvoker;
|
import io.netty.channel.ChannelOutboundInvoker;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
@ -36,6 +32,8 @@ import io.netty.handler.codec.http.HttpRequestDecoder;
|
|||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
import io.netty.handler.codec.http.HttpServerCodec;
|
import io.netty.handler.codec.http.HttpServerCodec;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.EmptyArrays;
|
import io.netty.util.internal.EmptyArrays;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
@ -46,6 +44,8 @@ import java.util.LinkedHashSet;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for server side web socket opening and closing handshakes
|
* Base class for server side web socket opening and closing handshakes
|
||||||
*/
|
*/
|
||||||
@ -114,7 +114,7 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
} else {
|
} else {
|
||||||
this.subprotocols = EmptyArrays.EMPTY_STRINGS;
|
this.subprotocols = EmptyArrays.EMPTY_STRINGS;
|
||||||
}
|
}
|
||||||
this.decoderConfig = Objects.requireNonNull(decoderConfig, "decoderConfig");
|
this.decoderConfig = requireNonNull(decoderConfig, "decoderConfig");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -167,9 +167,9 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* @param req
|
* @param req
|
||||||
* HTTP Request
|
* HTTP Request
|
||||||
* @return future
|
* @return future
|
||||||
* The {@link ChannelFuture} which is notified once the opening handshake completes
|
* The {@link Future} which is notified once the opening handshake completes
|
||||||
*/
|
*/
|
||||||
public ChannelFuture handshake(Channel channel, FullHttpRequest req) {
|
public Future<Void> handshake(Channel channel, FullHttpRequest req) {
|
||||||
return handshake(channel, req, null, channel.newPromise());
|
return handshake(channel, req, null, channel.newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,12 +185,12 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* @param responseHeaders
|
* @param responseHeaders
|
||||||
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added
|
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the opening handshake is done
|
* the {@link Promise} to be notified when the opening handshake is done
|
||||||
* @return future
|
* @return future
|
||||||
* the {@link ChannelFuture} which is notified when the opening handshake is done
|
* the {@link Future} which is notified when the opening handshake is done
|
||||||
*/
|
*/
|
||||||
public final ChannelFuture handshake(Channel channel, FullHttpRequest req,
|
public final Future<Void> handshake(Channel channel, FullHttpRequest req,
|
||||||
HttpHeaders responseHeaders, final ChannelPromise promise) {
|
HttpHeaders responseHeaders, final Promise<Void> promise) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("{} WebSocket version {} server handshake", channel, version());
|
logger.debug("{} WebSocket version {} server handshake", channel, version());
|
||||||
@ -222,11 +222,11 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
encoderName = p.context(HttpResponseEncoder.class).name();
|
encoderName = p.context(HttpResponseEncoder.class).name();
|
||||||
p.addBefore(encoderName, "wsencoder", newWebSocketEncoder());
|
p.addBefore(encoderName, "wsencoder", newWebSocketEncoder());
|
||||||
}
|
}
|
||||||
channel.writeAndFlush(response).addListener((ChannelFutureListener) future -> {
|
channel.writeAndFlush(response).addListener(channel, (ch, future) -> {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
ChannelPipeline p1 = future.channel().pipeline();
|
ChannelPipeline p1 = ch.pipeline();
|
||||||
p1.remove(encoderName);
|
p1.remove(encoderName);
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
promise.setFailure(future.cause());
|
promise.setFailure(future.cause());
|
||||||
}
|
}
|
||||||
@ -243,9 +243,9 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* @param req
|
* @param req
|
||||||
* HTTP Request
|
* HTTP Request
|
||||||
* @return future
|
* @return future
|
||||||
* The {@link ChannelFuture} which is notified once the opening handshake completes
|
* The {@link Future} which is notified once the opening handshake completes
|
||||||
*/
|
*/
|
||||||
public ChannelFuture handshake(Channel channel, HttpRequest req) {
|
public Future<Void> handshake(Channel channel, HttpRequest req) {
|
||||||
return handshake(channel, req, null, channel.newPromise());
|
return handshake(channel, req, null, channel.newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,12 +261,12 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* @param responseHeaders
|
* @param responseHeaders
|
||||||
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added
|
* Extra headers to add to the handshake response or {@code null} if no extra headers should be added
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the opening handshake is done
|
* the {@link Promise} to be notified when the opening handshake is done
|
||||||
* @return future
|
* @return future
|
||||||
* the {@link ChannelFuture} which is notified when the opening handshake is done
|
* the {@link Future} which is notified when the opening handshake is done
|
||||||
*/
|
*/
|
||||||
public final ChannelFuture handshake(final Channel channel, HttpRequest req,
|
public final Future<Void> handshake(final Channel channel, HttpRequest req,
|
||||||
final HttpHeaders responseHeaders, final ChannelPromise promise) {
|
final HttpHeaders responseHeaders, final Promise<Void> promise) {
|
||||||
|
|
||||||
if (req instanceof FullHttpRequest) {
|
if (req instanceof FullHttpRequest) {
|
||||||
return handshake(channel, (FullHttpRequest) req, responseHeaders, promise);
|
return handshake(channel, (FullHttpRequest) req, responseHeaders, promise);
|
||||||
@ -340,7 +340,7 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received.
|
* Closing Frame that was received.
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame) {
|
public Future<Void> close(Channel channel, CloseWebSocketFrame frame) {
|
||||||
requireNonNull(channel, "channel");
|
requireNonNull(channel, "channel");
|
||||||
return close(channel, frame, channel.newPromise());
|
return close(channel, frame, channel.newPromise());
|
||||||
}
|
}
|
||||||
@ -349,18 +349,18 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* Performs the closing handshake.
|
* Performs the closing handshake.
|
||||||
*
|
*
|
||||||
* When called from within a {@link ChannelHandler} you most likely want to use
|
* When called from within a {@link ChannelHandler} you most likely want to use
|
||||||
* {@link #close(ChannelHandlerContext, CloseWebSocketFrame, ChannelPromise)}.
|
* {@link #close(ChannelHandlerContext, CloseWebSocketFrame, Promise)}.
|
||||||
*
|
*
|
||||||
* @param channel
|
* @param channel
|
||||||
* the {@link Channel} to use.
|
* the {@link Channel} to use.
|
||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received.
|
* Closing Frame that was received.
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the closing handshake is done
|
* the {@link Promise} to be notified when the closing handshake is done
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame, ChannelPromise promise) {
|
public Future<Void> close(Channel channel, CloseWebSocketFrame frame, Promise<Void> promise) {
|
||||||
requireNonNull(channel, "channel");
|
requireNonNull(channel, "channel");
|
||||||
return close0(channel, frame, promise);
|
return close0(channel, channel, frame, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -371,7 +371,7 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received.
|
* Closing Frame that was received.
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
|
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
|
||||||
requireNonNull(ctx, "ctx");
|
requireNonNull(ctx, "ctx");
|
||||||
return close(ctx, frame, ctx.newPromise());
|
return close(ctx, frame, ctx.newPromise());
|
||||||
}
|
}
|
||||||
@ -384,15 +384,16 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received.
|
* Closing Frame that was received.
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the closing handshake is done.
|
* the {@link Promise} to be notified when the closing handshake is done.
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, ChannelPromise promise) {
|
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, Promise<Void> promise) {
|
||||||
requireNonNull(ctx, "ctx");
|
requireNonNull(ctx, "ctx");
|
||||||
return close0(ctx, frame, promise).addListener(ChannelFutureListener.CLOSE);
|
return close0(ctx, ctx.channel(), frame, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture close0(ChannelOutboundInvoker invoker, CloseWebSocketFrame frame, ChannelPromise promise) {
|
private static Future<Void> close0(ChannelOutboundInvoker invoker, Channel channel, CloseWebSocketFrame frame,
|
||||||
return invoker.writeAndFlush(frame, promise).addListener(ChannelFutureListener.CLOSE);
|
Promise<Void> promise) {
|
||||||
|
return invoker.writeAndFlush(frame, promise).addListener(channel, ChannelFutureListeners.CLOSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,9 +18,7 @@ package io.netty.handler.codec.http.websocketx;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
@ -28,6 +26,8 @@ import io.netty.handler.codec.http.HttpHeaderNames;
|
|||||||
import io.netty.handler.codec.http.HttpHeaderValues;
|
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -204,10 +204,10 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
* @param frame
|
* @param frame
|
||||||
* Web Socket frame that was received.
|
* Web Socket frame that was received.
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the closing handshake is done.
|
* the {@link Promise} to be notified when the closing handshake is done.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame, ChannelPromise promise) {
|
public Future<Void> close(Channel channel, CloseWebSocketFrame frame, Promise<Void> promise) {
|
||||||
return channel.writeAndFlush(frame, promise);
|
return channel.writeAndFlush(frame, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,11 +219,10 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received.
|
* Closing Frame that was received.
|
||||||
* @param promise
|
* @param promise
|
||||||
* the {@link ChannelPromise} to be notified when the closing handshake is done.
|
* the {@link Promise} to be notified when the closing handshake is done.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close(ChannelHandlerContext ctx, CloseWebSocketFrame frame,
|
public Future<Void> close(ChannelHandlerContext ctx, CloseWebSocketFrame frame, Promise<Void> promise) {
|
||||||
ChannelPromise promise) {
|
|
||||||
return ctx.writeAndFlush(frame, promise);
|
return ctx.writeAndFlush(frame, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,15 +16,15 @@
|
|||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpUtil;
|
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.handler.codec.http.HttpUtil;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@ -163,14 +163,14 @@ public class WebSocketServerHandshakerFactory {
|
|||||||
/**
|
/**
|
||||||
* Return that we need cannot not support the web socket version
|
* Return that we need cannot not support the web socket version
|
||||||
*/
|
*/
|
||||||
public static ChannelFuture sendUnsupportedVersionResponse(Channel channel) {
|
public static Future<Void> sendUnsupportedVersionResponse(Channel channel) {
|
||||||
return sendUnsupportedVersionResponse(channel, channel.newPromise());
|
return sendUnsupportedVersionResponse(channel, channel.newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return that we need cannot not support the web socket version
|
* Return that we need cannot not support the web socket version
|
||||||
*/
|
*/
|
||||||
public static ChannelFuture sendUnsupportedVersionResponse(Channel channel, ChannelPromise promise) {
|
public static Future<Void> sendUnsupportedVersionResponse(Channel channel, Promise<Void> promise) {
|
||||||
HttpResponse res = new DefaultFullHttpResponse(
|
HttpResponse res = new DefaultFullHttpResponse(
|
||||||
HttpVersion.HTTP_1_1,
|
HttpVersion.HTTP_1_1,
|
||||||
HttpResponseStatus.UPGRADE_REQUIRED, channel.alloc().buffer(0));
|
HttpResponseStatus.UPGRADE_REQUIRED, channel.alloc().buffer(0));
|
||||||
|
@ -17,16 +17,16 @@ package io.netty.handler.codec.http.websocketx;
|
|||||||
|
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@ -237,11 +237,11 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
|
|||||||
WebSocketServerHandshaker handshaker = getHandshaker(ctx.channel());
|
WebSocketServerHandshaker handshaker = getHandshaker(ctx.channel());
|
||||||
if (handshaker != null) {
|
if (handshaker != null) {
|
||||||
frame.retain();
|
frame.retain();
|
||||||
ChannelPromise promise = ctx.newPromise();
|
Promise<Void> promise = ctx.newPromise();
|
||||||
closeSent(promise);
|
closeSent(promise);
|
||||||
handshaker.close(ctx, (CloseWebSocketFrame) frame, promise);
|
handshaker.close(ctx, (CloseWebSocketFrame) frame, promise);
|
||||||
} else {
|
} else {
|
||||||
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
|
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -258,7 +258,7 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
|
|||||||
if (cause instanceof WebSocketHandshakeException) {
|
if (cause instanceof WebSocketHandshakeException) {
|
||||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||||
HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.wrappedBuffer(cause.getMessage().getBytes()));
|
HTTP_1_1, HttpResponseStatus.BAD_REQUEST, Unpooled.wrappedBuffer(cause.getMessage().getBytes()));
|
||||||
ctx.channel().writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
|
ctx.channel().writeAndFlush(response).addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
|
||||||
} else {
|
} else {
|
||||||
ctx.fireExceptionCaught(cause);
|
ctx.fireExceptionCaught(cause);
|
||||||
ctx.close();
|
ctx.close();
|
||||||
|
@ -15,12 +15,10 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
@ -30,14 +28,15 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.Ser
|
|||||||
import io.netty.handler.ssl.SslHandler;
|
import io.netty.handler.ssl.SslHandler;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.FutureListener;
|
import io.netty.util.concurrent.FutureListener;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpMethod.*;
|
import static io.netty.handler.codec.http.HttpMethod.GET;
|
||||||
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
|
||||||
import static io.netty.handler.codec.http.HttpUtil.*;
|
import static io.netty.handler.codec.http.HttpUtil.isKeepAlive;
|
||||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}.
|
* Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}.
|
||||||
@ -45,7 +44,7 @@ import static io.netty.handler.codec.http.HttpVersion.*;
|
|||||||
class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
|
class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
|
||||||
|
|
||||||
private final WebSocketServerProtocolConfig serverConfig;
|
private final WebSocketServerProtocolConfig serverConfig;
|
||||||
private ChannelPromise handshakePromise;
|
private Promise<Void> handshakePromise;
|
||||||
|
|
||||||
WebSocketServerProtocolHandshakeHandler(WebSocketServerProtocolConfig serverConfig) {
|
WebSocketServerProtocolHandshakeHandler(WebSocketServerProtocolConfig serverConfig) {
|
||||||
this.serverConfig = Objects.requireNonNull(serverConfig, "serverConfig");
|
this.serverConfig = Objects.requireNonNull(serverConfig, "serverConfig");
|
||||||
@ -74,7 +73,7 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
getWebSocketLocation(ctx.pipeline(), req, serverConfig.websocketPath()),
|
getWebSocketLocation(ctx.pipeline(), req, serverConfig.websocketPath()),
|
||||||
serverConfig.subprotocols(), serverConfig.decoderConfig());
|
serverConfig.subprotocols(), serverConfig.decoderConfig());
|
||||||
final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
|
final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
|
||||||
final ChannelPromise localHandshakePromise = handshakePromise;
|
Promise<Void> localHandshakePromise = handshakePromise;
|
||||||
if (handshaker == null) {
|
if (handshaker == null) {
|
||||||
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
|
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
|
||||||
} else {
|
} else {
|
||||||
@ -85,13 +84,13 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
// See https://github.com/netty/netty/issues/9471.
|
// See https://github.com/netty/netty/issues/9471.
|
||||||
WebSocketServerProtocolHandler.setHandshaker(ctx.channel(), handshaker);
|
WebSocketServerProtocolHandler.setHandshaker(ctx.channel(), handshaker);
|
||||||
|
|
||||||
final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req);
|
Future<Void> handshakeFuture = handshaker.handshake(ctx.channel(), req);
|
||||||
handshakeFuture.addListener((ChannelFutureListener) future -> {
|
handshakeFuture.addListener(future -> {
|
||||||
if (!future.isSuccess()) {
|
if (!future.isSuccess()) {
|
||||||
localHandshakePromise.tryFailure(future.cause());
|
localHandshakePromise.tryFailure(future.cause());
|
||||||
ctx.fireExceptionCaught(future.cause());
|
ctx.fireExceptionCaught(future.cause());
|
||||||
} else {
|
} else {
|
||||||
localHandshakePromise.trySuccess();
|
localHandshakePromise.trySuccess(null);
|
||||||
// Kept for compatibility
|
// Kept for compatibility
|
||||||
ctx.fireUserEventTriggered(
|
ctx.fireUserEventTriggered(
|
||||||
WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE);
|
WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE);
|
||||||
@ -126,9 +125,9 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
|
private static void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
|
||||||
ChannelFuture f = ctx.channel().writeAndFlush(res);
|
Future<Void> f = ctx.channel().writeAndFlush(res);
|
||||||
if (!isKeepAlive(req) || res.status().code() != 200) {
|
if (!isKeepAlive(req) || res.status().code() != 200) {
|
||||||
f.addListener(ChannelFutureListener.CLOSE);
|
f.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +142,7 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void applyHandshakeTimeout(ChannelHandlerContext ctx) {
|
private void applyHandshakeTimeout(ChannelHandlerContext ctx) {
|
||||||
final ChannelPromise localHandshakePromise = handshakePromise;
|
Promise<Void> localHandshakePromise = handshakePromise;
|
||||||
final long handshakeTimeoutMillis = serverConfig.handshakeTimeoutMillis();
|
final long handshakeTimeoutMillis = serverConfig.handshakeTimeoutMillis();
|
||||||
if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) {
|
if (handshakeTimeoutMillis <= 0 || localHandshakePromise.isDone()) {
|
||||||
return;
|
return;
|
||||||
@ -159,6 +158,6 @@ class WebSocketServerProtocolHandshakeHandler implements ChannelHandler {
|
|||||||
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
|
}, handshakeTimeoutMillis, TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
// Cancel the handshake timeout when handshake is finished.
|
// Cancel the handshake timeout when handshake is finished.
|
||||||
localHandshakePromise.addListener((FutureListener<Void>) f -> timeoutFuture.cancel(false));
|
localHandshakePromise.addListener(f -> timeoutFuture.cancel(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,21 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx.extensions;
|
package io.netty.handler.codec.http.websocketx.extensions;
|
||||||
|
|
||||||
import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.CodecException;
|
import io.netty.handler.codec.CodecException;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handler negotiates and initializes the WebSocket Extensions.
|
* This handler negotiates and initializes the WebSocket Extensions.
|
||||||
*
|
*
|
||||||
@ -56,7 +56,7 @@ public class WebSocketClientExtensionHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade(((HttpRequest) msg).headers())) {
|
if (msg instanceof HttpRequest && WebSocketExtensionUtil.isWebsocketUpgrade(((HttpRequest) msg).headers())) {
|
||||||
HttpRequest request = (HttpRequest) msg;
|
HttpRequest request = (HttpRequest) msg;
|
||||||
String headerValue = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
|
String headerValue = request.headers().getAsString(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS);
|
||||||
|
@ -15,23 +15,22 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx.extensions;
|
package io.netty.handler.codec.http.websocketx.extensions;
|
||||||
|
|
||||||
import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
|
|
||||||
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static io.netty.util.internal.ObjectUtil.checkNonEmpty;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This handler negotiates and initializes the WebSocket Extensions.
|
* This handler negotiates and initializes the WebSocket Extensions.
|
||||||
*
|
*
|
||||||
@ -99,7 +98,7 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(final ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(final ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
if (msg instanceof HttpResponse) {
|
if (msg instanceof HttpResponse) {
|
||||||
HttpResponse httpResponse = (HttpResponse) msg;
|
HttpResponse httpResponse = (HttpResponse) msg;
|
||||||
//checking the status is faster than looking at headers
|
//checking the status is faster than looking at headers
|
||||||
@ -113,7 +112,7 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handlePotentialUpgrade(final ChannelHandlerContext ctx,
|
private void handlePotentialUpgrade(final ChannelHandlerContext ctx,
|
||||||
ChannelPromise promise, HttpResponse httpResponse) {
|
Promise<Void> promise, HttpResponse httpResponse) {
|
||||||
HttpHeaders headers = httpResponse.headers();
|
HttpHeaders headers = httpResponse.headers();
|
||||||
if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) {
|
if (WebSocketExtensionUtil.isWebsocketUpgrade(headers)) {
|
||||||
if (validExtensions != null) {
|
if (validExtensions != null) {
|
||||||
@ -125,7 +124,7 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
|
|||||||
}
|
}
|
||||||
String newHeaderValue = WebSocketExtensionUtil
|
String newHeaderValue = WebSocketExtensionUtil
|
||||||
.computeMergeExtensionsHeaderValue(headerValue, extraExtensions);
|
.computeMergeExtensionsHeaderValue(headerValue, extraExtensions);
|
||||||
promise.addListener((ChannelFutureListener) future -> {
|
promise.addListener(future -> {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
for (WebSocketServerExtension extension : validExtensions) {
|
for (WebSocketServerExtension extension : validExtensions) {
|
||||||
WebSocketExtensionDecoder decoder = extension.newExtensionDecoder();
|
WebSocketExtensionDecoder decoder = extension.newExtensionDecoder();
|
||||||
@ -143,9 +142,9 @@ public class WebSocketServerExtensionHandler implements ChannelHandler {
|
|||||||
headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue);
|
headers.set(HttpHeaderNames.SEC_WEBSOCKET_EXTENSIONS, newHeaderValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
promise.addListener((ChannelFutureListener) future -> {
|
promise.addListener(future -> {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
ctx.pipeline().remove(WebSocketServerExtensionHandler.this);
|
ctx.pipeline().remove(this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ import io.netty.bootstrap.ServerBootstrap;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelOption;
|
import io.netty.channel.ChannelOption;
|
||||||
@ -156,12 +155,12 @@ public class HttpClientCodecTest {
|
|||||||
sChannel.writeAndFlush(Unpooled.wrappedBuffer(("HTTP/1.0 200 OK\r\n" +
|
sChannel.writeAndFlush(Unpooled.wrappedBuffer(("HTTP/1.0 200 OK\r\n" +
|
||||||
"Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" +
|
"Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" +
|
||||||
"Content-Type: text/html\r\n\r\n").getBytes(CharsetUtil.ISO_8859_1)))
|
"Content-Type: text/html\r\n\r\n").getBytes(CharsetUtil.ISO_8859_1)))
|
||||||
.addListener((ChannelFutureListener) future -> {
|
.addListener(future -> {
|
||||||
assertTrue(future.isSuccess());
|
assertTrue(future.isSuccess());
|
||||||
sChannel.writeAndFlush(Unpooled.wrappedBuffer(
|
sChannel.writeAndFlush(Unpooled.wrappedBuffer(
|
||||||
"<html><body>hello half closed!</body></html>\r\n"
|
"<html><body>hello half closed!</body></html>\r\n"
|
||||||
.getBytes(CharsetUtil.ISO_8859_1)))
|
.getBytes(CharsetUtil.ISO_8859_1)))
|
||||||
.addListener((ChannelFutureListener) future1 -> {
|
.addListener(future1 -> {
|
||||||
assertTrue(future1.isSuccess());
|
assertTrue(future1.isSuccess());
|
||||||
sChannel.shutdownOutput();
|
sChannel.shutdownOutput();
|
||||||
});
|
});
|
||||||
|
@ -15,22 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http;
|
package io.netty.handler.codec.http;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec;
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodec;
|
||||||
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeCodecFactory;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
@ -94,13 +93,13 @@ public class HttpServerUpgradeHandlerTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(final ChannelHandlerContext ctx, final Object msg, final ChannelPromise promise) {
|
public void write(final ChannelHandlerContext ctx, final Object msg, final Promise<Void> promise) {
|
||||||
// We ensure that we're in the read call and defer the write so we can
|
// We ensure that we're in the read call and defer the write so we can
|
||||||
// make sure the pipeline was reformed irrespective of the flush completing.
|
// make sure the pipeline was reformed irrespective of the flush completing.
|
||||||
assertTrue(inReadCall);
|
assertTrue(inReadCall);
|
||||||
writeUpgradeMessage = true;
|
writeUpgradeMessage = true;
|
||||||
ctx.channel().eventLoop().execute(() -> ctx.write(msg, promise));
|
ctx.channel().eventLoop().execute(() -> ctx.write(msg, promise));
|
||||||
promise.addListener((ChannelFutureListener) future -> writeFlushed = true);
|
promise.addListener(future -> writeFlushed = true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ package io.netty.handler.codec.http.websocketx;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
@ -28,13 +27,13 @@ import io.netty.handler.codec.http.HttpObjectAggregator;
|
|||||||
import io.netty.handler.codec.http.HttpServerCodec;
|
import io.netty.handler.codec.http.HttpServerCodec;
|
||||||
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent;
|
import io.netty.handler.codec.http.websocketx.WebSocketClientProtocolHandler.ClientHandshakeStateEvent;
|
||||||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.ServerHandshakeStateEvent;
|
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.ServerHandshakeStateEvent;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.Timeout;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import org.junit.jupiter.api.Timeout;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
@ -244,7 +243,7 @@ public class WebSocketHandshakeHandOverTest {
|
|||||||
@Override
|
@Override
|
||||||
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) {
|
||||||
if (evt == ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) {
|
if (evt == ClientHandshakeStateEvent.HANDSHAKE_COMPLETE) {
|
||||||
ctx.channel().closeFuture().addListener((ChannelFutureListener) future -> clientForceClosed = true);
|
ctx.channel().closeFuture().addListener(future -> clientForceClosed = true);
|
||||||
handshaker.close(ctx.channel(), new CloseWebSocketFrame());
|
handshaker.close(ctx.channel(), new CloseWebSocketFrame());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,13 +18,13 @@ package io.netty.handler.codec.http.websocketx;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
import io.netty.channel.ChannelOutboundHandlerAdapter;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.handler.flow.FlowControlHandler;
|
import io.netty.handler.flow.FlowControlHandler;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
@ -153,18 +153,18 @@ public class WebSocketProtocolHandlerTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTimeout() throws Exception {
|
public void testTimeout() throws Exception {
|
||||||
final AtomicReference<ChannelPromise> ref = new AtomicReference<ChannelPromise>();
|
final AtomicReference<Promise<Void>> ref = new AtomicReference<>();
|
||||||
WebSocketProtocolHandler handler = new WebSocketProtocolHandler(
|
WebSocketProtocolHandler handler = new WebSocketProtocolHandler(
|
||||||
false, WebSocketCloseStatus.NORMAL_CLOSURE, 1) { };
|
false, WebSocketCloseStatus.NORMAL_CLOSURE, 1) { };
|
||||||
EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter() {
|
EmbeddedChannel channel = new EmbeddedChannel(new ChannelOutboundHandlerAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
ref.set(promise);
|
ref.set(promise);
|
||||||
ReferenceCountUtil.release(msg);
|
ReferenceCountUtil.release(msg);
|
||||||
}
|
}
|
||||||
}, handler);
|
}, handler);
|
||||||
|
|
||||||
ChannelFuture future = channel.writeAndFlush(new CloseWebSocketFrame());
|
Future<Void> future = channel.writeAndFlush(new CloseWebSocketFrame());
|
||||||
ChannelHandlerContext ctx = channel.pipeline().context(WebSocketProtocolHandler.class);
|
ChannelHandlerContext ctx = channel.pipeline().context(WebSocketProtocolHandler.class);
|
||||||
handler.close(ctx, ctx.newPromise());
|
handler.close(ctx, ctx.newPromise());
|
||||||
|
|
||||||
|
@ -18,30 +18,30 @@ package io.netty.handler.codec.http.websocketx;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
|
|
||||||
import io.netty.handler.codec.http.HttpClientCodec;
|
|
||||||
import io.netty.handler.codec.http.HttpHeaderValues;
|
|
||||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
|
||||||
import io.netty.handler.codec.http.FullHttpRequest;
|
import io.netty.handler.codec.http.FullHttpRequest;
|
||||||
import io.netty.handler.codec.http.FullHttpResponse;
|
import io.netty.handler.codec.http.FullHttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpMethod;
|
import io.netty.handler.codec.http.HttpClientCodec;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
|
||||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
|
||||||
import io.netty.handler.codec.http.HttpServerCodec;
|
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
|
import io.netty.handler.codec.http.HttpHeaderValues;
|
||||||
|
import io.netty.handler.codec.http.HttpMethod;
|
||||||
|
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||||
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
|
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
|
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
|
import io.netty.handler.codec.http.HttpServerCodec;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http.HttpResponseStatus.*;
|
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
|
||||||
import static io.netty.handler.codec.http.HttpVersion.*;
|
import static io.netty.handler.codec.http.HttpResponseStatus.SWITCHING_PROTOCOLS;
|
||||||
|
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
@ -354,7 +354,7 @@ public class WebSocketServerProtocolHandlerTest {
|
|||||||
assertFalse(client.isOpen());
|
assertFalse(client.isOpen());
|
||||||
assertFalse(server.isOpen());
|
assertFalse(server.isOpen());
|
||||||
|
|
||||||
CloseWebSocketFrame closeMessage = decode(server.<ByteBuf>readOutbound(), CloseWebSocketFrame.class);
|
CloseWebSocketFrame closeMessage = decode(server.readOutbound(), CloseWebSocketFrame.class);
|
||||||
assertEquals(closeMessage.statusCode(), closeStatus.code());
|
assertEquals(closeMessage.statusCode(), closeStatus.code());
|
||||||
closeMessage.release();
|
closeMessage.release();
|
||||||
|
|
||||||
@ -378,7 +378,7 @@ public class WebSocketServerProtocolHandlerTest {
|
|||||||
assertFalse(client.isOpen());
|
assertFalse(client.isOpen());
|
||||||
assertFalse(server.isOpen());
|
assertFalse(server.isOpen());
|
||||||
|
|
||||||
CloseWebSocketFrame closeMessage = decode(server.<ByteBuf>readOutbound(), CloseWebSocketFrame.class);
|
CloseWebSocketFrame closeMessage = decode(server.readOutbound(), CloseWebSocketFrame.class);
|
||||||
assertEquals(closeMessage, new CloseWebSocketFrame(WebSocketCloseStatus.NORMAL_CLOSURE));
|
assertEquals(closeMessage, new CloseWebSocketFrame(WebSocketCloseStatus.NORMAL_CLOSURE));
|
||||||
closeMessage.release();
|
closeMessage.release();
|
||||||
|
|
||||||
@ -459,9 +459,9 @@ public class WebSocketServerProtocolHandlerTest {
|
|||||||
private class MockOutboundHandler implements ChannelHandler {
|
private class MockOutboundHandler implements ChannelHandler {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
responses.add((FullHttpResponse) msg);
|
responses.add((FullHttpResponse) msg);
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -15,25 +15,34 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx.extensions;
|
package io.netty.handler.codec.http.websocketx.extensions;
|
||||||
|
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpRequest;
|
import io.netty.handler.codec.http.HttpRequest;
|
||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.Dummy2Decoder;
|
||||||
|
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.Dummy2Encoder;
|
||||||
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.*;
|
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.DummyDecoder;
|
||||||
|
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.DummyEncoder;
|
||||||
|
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.newUpgradeRequest;
|
||||||
|
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.newUpgradeResponse;
|
||||||
|
import static io.netty.handler.codec.http.websocketx.extensions.WebSocketExtensionTestUtil.webSocketExtensionDataMatcher;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class WebSocketServerExtensionHandlerTest {
|
public class WebSocketServerExtensionHandlerTest {
|
||||||
|
|
||||||
@ -209,7 +218,7 @@ public class WebSocketServerExtensionHandlerTest {
|
|||||||
when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("main")))
|
when(mainHandshakerMock.handshakeExtension(webSocketExtensionDataMatcher("main")))
|
||||||
.thenReturn(mainExtensionMock);
|
.thenReturn(mainExtensionMock);
|
||||||
when(mainExtensionMock.newResponseData()).thenReturn(
|
when(mainExtensionMock.newResponseData()).thenReturn(
|
||||||
new WebSocketExtensionData("main", Collections.<String, String>emptyMap()));
|
new WebSocketExtensionData("main", Collections.emptyMap()));
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
WebSocketServerExtensionHandler extensionHandler =
|
WebSocketServerExtensionHandler extensionHandler =
|
||||||
@ -220,7 +229,7 @@ public class WebSocketServerExtensionHandlerTest {
|
|||||||
ch.writeInbound(req);
|
ch.writeInbound(req);
|
||||||
|
|
||||||
HttpResponse res = newUpgradeResponse(null);
|
HttpResponse res = newUpgradeResponse(null);
|
||||||
ChannelPromise failurePromise = ch.newPromise();
|
Promise<Void> failurePromise = ch.newPromise();
|
||||||
ch.writeOneOutbound(res, failurePromise);
|
ch.writeOneOutbound(res, failurePromise);
|
||||||
failurePromise.setFailure(new IOException("Cannot write response"));
|
failurePromise.setFailure(new IOException("Cannot write response"));
|
||||||
|
|
||||||
|
@ -18,15 +18,12 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelConfig;
|
import io.netty.channel.ChannelConfig;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelId;
|
import io.netty.channel.ChannelId;
|
||||||
import io.netty.channel.ChannelMetadata;
|
import io.netty.channel.ChannelMetadata;
|
||||||
import io.netty.channel.ChannelOutboundBuffer;
|
import io.netty.channel.ChannelOutboundBuffer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.DefaultChannelConfig;
|
import io.netty.channel.DefaultChannelConfig;
|
||||||
import io.netty.channel.DefaultChannelPipeline;
|
import io.netty.channel.DefaultChannelPipeline;
|
||||||
import io.netty.channel.EventLoop;
|
import io.netty.channel.EventLoop;
|
||||||
@ -36,6 +33,8 @@ import io.netty.channel.WriteBufferWaterMark;
|
|||||||
import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream;
|
import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream;
|
||||||
import io.netty.util.DefaultAttributeMap;
|
import io.netty.util.DefaultAttributeMap;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
@ -97,7 +96,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
private static final AtomicIntegerFieldUpdater<AbstractHttp2StreamChannel> UNWRITABLE_UPDATER =
|
private static final AtomicIntegerFieldUpdater<AbstractHttp2StreamChannel> UNWRITABLE_UPDATER =
|
||||||
AtomicIntegerFieldUpdater.newUpdater(AbstractHttp2StreamChannel.class, "unwritable");
|
AtomicIntegerFieldUpdater.newUpdater(AbstractHttp2StreamChannel.class, "unwritable");
|
||||||
|
|
||||||
private static void windowUpdateFrameWriteComplete(ChannelFuture future, Channel streamChannel) {
|
private static void windowUpdateFrameWriteComplete(Channel streamChannel, Future<?> future) {
|
||||||
Throwable cause = future.cause();
|
Throwable cause = future.cause();
|
||||||
if (cause != null) {
|
if (cause != null) {
|
||||||
Throwable unwrappedCause;
|
Throwable unwrappedCause;
|
||||||
@ -112,9 +111,6 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ChannelFutureListener windowUpdateFrameWriteListener = future ->
|
|
||||||
windowUpdateFrameWriteComplete(future, AbstractHttp2StreamChannel.this);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current status of the read-processing for a {@link AbstractHttp2StreamChannel}.
|
* The current status of the read-processing for a {@link AbstractHttp2StreamChannel}.
|
||||||
*/
|
*/
|
||||||
@ -140,7 +136,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
private final ChannelId channelId;
|
private final ChannelId channelId;
|
||||||
private final ChannelPipeline pipeline;
|
private final ChannelPipeline pipeline;
|
||||||
private final DefaultHttp2FrameStream stream;
|
private final DefaultHttp2FrameStream stream;
|
||||||
private final ChannelPromise closePromise;
|
private final Promise<Void> closePromise;
|
||||||
|
|
||||||
private volatile boolean registered;
|
private volatile boolean registered;
|
||||||
|
|
||||||
@ -338,7 +334,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture closeFuture() {
|
public Future<Void> closeFuture() {
|
||||||
return closePromise;
|
return closePromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -394,107 +390,107 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture bind(SocketAddress localAddress) {
|
public Future<Void> bind(SocketAddress localAddress) {
|
||||||
return pipeline().bind(localAddress);
|
return pipeline().bind(localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress) {
|
public Future<Void> connect(SocketAddress remoteAddress) {
|
||||||
return pipeline().connect(remoteAddress);
|
return pipeline().connect(remoteAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
||||||
return pipeline().connect(remoteAddress, localAddress);
|
return pipeline().connect(remoteAddress, localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture disconnect() {
|
public Future<Void> disconnect() {
|
||||||
return pipeline().disconnect();
|
return pipeline().disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close() {
|
public Future<Void> close() {
|
||||||
return pipeline().close();
|
return pipeline().close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register() {
|
public Future<Void> register() {
|
||||||
return pipeline().register();
|
return pipeline().register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture deregister() {
|
public Future<Void> deregister() {
|
||||||
return pipeline().deregister();
|
return pipeline().deregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
|
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return pipeline().bind(localAddress, promise);
|
return pipeline().bind(localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
|
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
|
||||||
return pipeline().connect(remoteAddress, promise);
|
return pipeline().connect(remoteAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
|
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return pipeline().connect(remoteAddress, localAddress, promise);
|
return pipeline().connect(remoteAddress, localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture disconnect(ChannelPromise promise) {
|
public Future<Void> disconnect(Promise<Void> promise) {
|
||||||
return pipeline().disconnect(promise);
|
return pipeline().disconnect(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close(ChannelPromise promise) {
|
public Future<Void> close(Promise<Void> promise) {
|
||||||
return pipeline().close(promise);
|
return pipeline().close(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register(ChannelPromise promise) {
|
public Future<Void> register(Promise<Void> promise) {
|
||||||
return pipeline().register(promise);
|
return pipeline().register(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture deregister(ChannelPromise promise) {
|
public Future<Void> deregister(Promise<Void> promise) {
|
||||||
return pipeline().deregister(promise);
|
return pipeline().deregister(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture write(Object msg) {
|
public Future<Void> write(Object msg) {
|
||||||
return pipeline().write(msg);
|
return pipeline().write(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture write(Object msg, ChannelPromise promise) {
|
public Future<Void> write(Object msg, Promise<Void> promise) {
|
||||||
return pipeline().write(msg, promise);
|
return pipeline().write(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
|
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
|
||||||
return pipeline().writeAndFlush(msg, promise);
|
return pipeline().writeAndFlush(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeAndFlush(Object msg) {
|
public Future<Void> writeAndFlush(Object msg) {
|
||||||
return pipeline().writeAndFlush(msg);
|
return pipeline().writeAndFlush(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelPromise newPromise() {
|
public Promise<Void> newPromise() {
|
||||||
return pipeline().newPromise();
|
return pipeline().newPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture newSucceededFuture() {
|
public Future<Void> newSucceededFuture() {
|
||||||
return pipeline().newSucceededFuture();
|
return pipeline().newSucceededFuture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture newFailedFuture(Throwable cause) {
|
public Future<Void> newFailedFuture(Throwable cause) {
|
||||||
return pipeline().newFailedFuture(cause);
|
return pipeline().newFailedFuture(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -568,7 +564,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(final SocketAddress remoteAddress,
|
public void connect(final SocketAddress remoteAddress,
|
||||||
SocketAddress localAddress, final ChannelPromise promise) {
|
SocketAddress localAddress, Promise<Void> promise) {
|
||||||
if (!promise.setUncancellable()) {
|
if (!promise.setUncancellable()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -595,7 +591,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(ChannelPromise promise) {
|
public void register(Promise<Void> promise) {
|
||||||
if (!promise.setUncancellable()) {
|
if (!promise.setUncancellable()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -606,7 +602,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
|
|
||||||
registered = true;
|
registered = true;
|
||||||
|
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
|
|
||||||
pipeline().fireChannelRegistered();
|
pipeline().fireChannelRegistered();
|
||||||
if (isActive()) {
|
if (isActive()) {
|
||||||
@ -618,7 +614,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind(SocketAddress localAddress, ChannelPromise promise) {
|
public void bind(SocketAddress localAddress, Promise<Void> promise) {
|
||||||
if (!promise.setUncancellable()) {
|
if (!promise.setUncancellable()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -626,22 +622,22 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect(ChannelPromise promise) {
|
public void disconnect(Promise<Void> promise) {
|
||||||
close(promise);
|
close(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close(final ChannelPromise promise) {
|
public void close(final Promise<Void> promise) {
|
||||||
if (!promise.setUncancellable()) {
|
if (!promise.setUncancellable()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (closeInitiated) {
|
if (closeInitiated) {
|
||||||
if (closePromise.isDone()) {
|
if (closePromise.isDone()) {
|
||||||
// Closed already.
|
// Closed already.
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
// This means close() was called before so we just register a listener and return
|
// This means close() was called before so we just register a listener and return
|
||||||
closePromise.addListener((ChannelFutureListener) future -> promise.setSuccess());
|
closePromise.addListener(promise, (p, future) -> p.setSuccess(null));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -675,8 +671,8 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
|
|
||||||
// The promise should be notified before we call fireChannelInactive().
|
// The promise should be notified before we call fireChannelInactive().
|
||||||
outboundClosed = true;
|
outboundClosed = true;
|
||||||
closePromise.setSuccess();
|
closePromise.setSuccess(null);
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
|
|
||||||
fireChannelInactiveAndDeregister(newPromise(), wasActive);
|
fireChannelInactiveAndDeregister(newPromise(), wasActive);
|
||||||
}
|
}
|
||||||
@ -687,18 +683,18 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deregister(ChannelPromise promise) {
|
public void deregister(Promise<Void> promise) {
|
||||||
fireChannelInactiveAndDeregister(promise, false);
|
fireChannelInactiveAndDeregister(promise, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireChannelInactiveAndDeregister(final ChannelPromise promise,
|
private void fireChannelInactiveAndDeregister(Promise<Void> promise,
|
||||||
final boolean fireChannelInactive) {
|
final boolean fireChannelInactive) {
|
||||||
if (!promise.setUncancellable()) {
|
if (!promise.setUncancellable()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!registered) {
|
if (!registered) {
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -723,8 +719,8 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void safeSetSuccess(ChannelPromise promise) {
|
private void safeSetSuccess(Promise<Void> promise) {
|
||||||
if (!promise.trySuccess()) {
|
if (!promise.trySuccess(null)) {
|
||||||
logger.warn("Failed to mark a promise as success because it is done already: {}", promise);
|
logger.warn("Failed to mark a promise as success because it is done already: {}", promise);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -813,7 +809,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
if (flowControlledBytes != 0) {
|
if (flowControlledBytes != 0) {
|
||||||
int bytes = flowControlledBytes;
|
int bytes = flowControlledBytes;
|
||||||
flowControlledBytes = 0;
|
flowControlledBytes = 0;
|
||||||
ChannelFuture future = write0(parentContext(), new DefaultHttp2WindowUpdateFrame(bytes).stream(stream));
|
Future<Void> future = write0(parentContext(), new DefaultHttp2WindowUpdateFrame(bytes).stream(stream));
|
||||||
// window update frames are commonly swallowed by the Http2FrameCodec and the promise is synchronously
|
// window update frames are commonly swallowed by the Http2FrameCodec and the promise is synchronously
|
||||||
// completed but the flow controller _may_ have generated a wire level WINDOW_UPDATE. Therefore we need,
|
// completed but the flow controller _may_ have generated a wire level WINDOW_UPDATE. Therefore we need,
|
||||||
// to assume there was a write done that needs to be flushed or we risk flow control starvation.
|
// to assume there was a write done that needs to be flushed or we risk flow control starvation.
|
||||||
@ -823,9 +819,10 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
// already.
|
// already.
|
||||||
// See https://github.com/netty/netty/issues/9663
|
// See https://github.com/netty/netty/issues/9663
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
windowUpdateFrameWriteComplete(future, AbstractHttp2StreamChannel.this);
|
windowUpdateFrameWriteComplete(AbstractHttp2StreamChannel.this, future);
|
||||||
} else {
|
} else {
|
||||||
future.addListener(windowUpdateFrameWriteListener);
|
future.addListener(AbstractHttp2StreamChannel.this,
|
||||||
|
AbstractHttp2StreamChannel::windowUpdateFrameWriteComplete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -882,7 +879,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(Object msg, final ChannelPromise promise) {
|
public void write(Object msg, Promise<Void> promise) {
|
||||||
// After this point its not possible to cancel a write anymore.
|
// After this point its not possible to cancel a write anymore.
|
||||||
if (!promise.setUncancellable()) {
|
if (!promise.setUncancellable()) {
|
||||||
ReferenceCountUtil.release(msg);
|
ReferenceCountUtil.release(msg);
|
||||||
@ -913,7 +910,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeHttp2StreamFrame(Http2StreamFrame frame, final ChannelPromise promise) {
|
private void writeHttp2StreamFrame(Http2StreamFrame frame, Promise<Void> promise) {
|
||||||
if (!firstFrameWritten && !isStreamIdValid(stream().id()) && !(frame instanceof Http2HeadersFrame)) {
|
if (!firstFrameWritten && !isStreamIdValid(stream().id()) && !(frame instanceof Http2HeadersFrame)) {
|
||||||
ReferenceCountUtil.release(frame);
|
ReferenceCountUtil.release(frame);
|
||||||
promise.setFailure(
|
promise.setFailure(
|
||||||
@ -929,7 +926,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
firstWrite = firstFrameWritten = true;
|
firstWrite = firstFrameWritten = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelFuture f = write0(parentContext(), frame);
|
Future<Void> f = write0(parentContext(), frame);
|
||||||
if (f.isDone()) {
|
if (f.isDone()) {
|
||||||
if (firstWrite) {
|
if (firstWrite) {
|
||||||
firstWriteComplete(f, promise);
|
firstWriteComplete(f, promise);
|
||||||
@ -939,7 +936,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
} else {
|
} else {
|
||||||
final long bytes = FlowControlledFrameSizeEstimator.HANDLE_INSTANCE.size(frame);
|
final long bytes = FlowControlledFrameSizeEstimator.HANDLE_INSTANCE.size(frame);
|
||||||
incrementPendingOutboundBytes(bytes, false);
|
incrementPendingOutboundBytes(bytes, false);
|
||||||
f.addListener((ChannelFutureListener) future -> {
|
f.addListener(future -> {
|
||||||
if (firstWrite) {
|
if (firstWrite) {
|
||||||
firstWriteComplete(future, promise);
|
firstWriteComplete(future, promise);
|
||||||
} else {
|
} else {
|
||||||
@ -951,10 +948,10 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void firstWriteComplete(ChannelFuture future, ChannelPromise promise) {
|
private void firstWriteComplete(Future<?> future, Promise<Void> promise) {
|
||||||
Throwable cause = future.cause();
|
Throwable cause = future.cause();
|
||||||
if (cause == null) {
|
if (cause == null) {
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
// If the first write fails there is not much we can do, just close
|
// If the first write fails there is not much we can do, just close
|
||||||
closeForcibly();
|
closeForcibly();
|
||||||
@ -962,10 +959,10 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeComplete(ChannelFuture future, ChannelPromise promise) {
|
private void writeComplete(Future<?> future, Promise<Void> promise) {
|
||||||
Throwable cause = future.cause();
|
Throwable cause = future.cause();
|
||||||
if (cause == null) {
|
if (cause == null) {
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
Throwable error = wrapStreamClosedError(cause);
|
Throwable error = wrapStreamClosedError(cause);
|
||||||
// To make it more consistent with AbstractChannel we handle all IOExceptions here.
|
// To make it more consistent with AbstractChannel we handle all IOExceptions here.
|
||||||
@ -1011,7 +1008,7 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
// There is nothing to flush so this is a NOOP.
|
// There is nothing to flush so this is a NOOP.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// We need to set this to false before we call flush0(...) as ChannelFutureListener may produce more data
|
// We need to set this to false before we call flush0(...) as FutureListener may produce more data
|
||||||
// that are explicit flushed.
|
// that are explicit flushed.
|
||||||
writeDoneAndNoFlush = false;
|
writeDoneAndNoFlush = false;
|
||||||
flush0(parentContext());
|
flush0(parentContext());
|
||||||
@ -1066,8 +1063,8 @@ abstract class AbstractHttp2StreamChannel extends DefaultAttributeMap implements
|
|||||||
ctx.flush();
|
ctx.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected ChannelFuture write0(ChannelHandlerContext ctx, Object msg) {
|
protected Future<Void> write0(ChannelHandlerContext ctx, Object msg) {
|
||||||
ChannelPromise promise = ctx.newPromise();
|
Promise<Void> promise = ctx.newPromise();
|
||||||
ctx.write(msg, promise);
|
ctx.write(msg, promise);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
@ -16,21 +16,21 @@ package io.netty.handler.codec.http2;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
import io.netty.handler.codec.compression.BrotliEncoder;
|
import io.netty.handler.codec.compression.BrotliEncoder;
|
||||||
import io.netty.handler.codec.compression.ZlibCodecFactory;
|
|
||||||
import io.netty.handler.codec.compression.ZlibWrapper;
|
|
||||||
import io.netty.handler.codec.compression.BrotliOptions;
|
import io.netty.handler.codec.compression.BrotliOptions;
|
||||||
import io.netty.handler.codec.compression.CompressionOptions;
|
import io.netty.handler.codec.compression.CompressionOptions;
|
||||||
import io.netty.handler.codec.compression.DeflateOptions;
|
import io.netty.handler.codec.compression.DeflateOptions;
|
||||||
import io.netty.handler.codec.compression.GzipOptions;
|
import io.netty.handler.codec.compression.GzipOptions;
|
||||||
import io.netty.handler.codec.compression.StandardCompressionOptions;
|
import io.netty.handler.codec.compression.StandardCompressionOptions;
|
||||||
|
import io.netty.handler.codec.compression.ZlibCodecFactory;
|
||||||
|
import io.netty.handler.codec.compression.ZlibWrapper;
|
||||||
import io.netty.handler.codec.compression.ZstdEncoder;
|
import io.netty.handler.codec.compression.ZstdEncoder;
|
||||||
import io.netty.handler.codec.compression.ZstdOptions;
|
import io.netty.handler.codec.compression.ZstdOptions;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.PromiseCombiner;
|
import io.netty.util.concurrent.PromiseCombiner;
|
||||||
import io.netty.util.internal.ObjectUtil;
|
import io.netty.util.internal.ObjectUtil;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
@ -142,8 +142,8 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
|
public Future<Void> writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
|
||||||
final boolean endOfStream, ChannelPromise promise) {
|
final boolean endOfStream, Promise<Void> promise) {
|
||||||
final Http2Stream stream = connection().stream(streamId);
|
final Http2Stream stream = connection().stream(streamId);
|
||||||
final EmbeddedChannel channel = stream == null ? null : (EmbeddedChannel) stream.getProperty(propertyKey);
|
final EmbeddedChannel channel = stream == null ? null : (EmbeddedChannel) stream.getProperty(propertyKey);
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
@ -164,7 +164,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
|||||||
true, promise);
|
true, promise);
|
||||||
}
|
}
|
||||||
// END_STREAM is not set and the assumption is data is still forthcoming.
|
// END_STREAM is not set and the assumption is data is still forthcoming.
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +177,7 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
|||||||
compressedEndOfStream = nextBuf == null;
|
compressedEndOfStream = nextBuf == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelPromise bufPromise = ctx.newPromise();
|
Promise<Void> bufPromise = ctx.newPromise();
|
||||||
combiner.add(bufPromise);
|
combiner.add(bufPromise);
|
||||||
super.writeData(ctx, streamId, buf, padding, compressedEndOfStream, bufPromise);
|
super.writeData(ctx, streamId, buf, padding, compressedEndOfStream, bufPromise);
|
||||||
if (nextBuf == null) {
|
if (nextBuf == null) {
|
||||||
@ -199,14 +199,14 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||||
boolean endStream, ChannelPromise promise) {
|
boolean endStream, Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
// Determine if compression is required and sanitize the headers.
|
// Determine if compression is required and sanitize the headers.
|
||||||
EmbeddedChannel compressor = newCompressor(ctx, headers, endStream);
|
EmbeddedChannel compressor = newCompressor(ctx, headers, endStream);
|
||||||
|
|
||||||
// Write the headers and create the stream object.
|
// Write the headers and create the stream object.
|
||||||
ChannelFuture future = super.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
Future<Void> future = super.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
||||||
|
|
||||||
// After the stream object has been created, then attach the compressor as a property for data compression.
|
// After the stream object has been created, then attach the compressor as a property for data compression.
|
||||||
bindCompressorToStream(compressor, streamId);
|
bindCompressorToStream(compressor, streamId);
|
||||||
@ -219,15 +219,15 @@ public class CompressorHttp2ConnectionEncoder extends DecoratingHttp2ConnectionE
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(final ChannelHandlerContext ctx, final int streamId, final Http2Headers headers,
|
public Future<Void> writeHeaders(final ChannelHandlerContext ctx, final int streamId, final Http2Headers headers,
|
||||||
final int streamDependency, final short weight, final boolean exclusive, final int padding,
|
final int streamDependency, final short weight, final boolean exclusive, final int padding,
|
||||||
final boolean endOfStream, final ChannelPromise promise) {
|
final boolean endOfStream, final Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
// Determine if compression is required and sanitize the headers.
|
// Determine if compression is required and sanitize the headers.
|
||||||
EmbeddedChannel compressor = newCompressor(ctx, headers, endOfStream);
|
EmbeddedChannel compressor = newCompressor(ctx, headers, endOfStream);
|
||||||
|
|
||||||
// Write the headers and create the stream object.
|
// Write the headers and create the stream object.
|
||||||
ChannelFuture future = super.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive,
|
Future<Void> future = super.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive,
|
||||||
padding, endOfStream, promise);
|
padding, endOfStream, promise);
|
||||||
|
|
||||||
// After the stream object has been created, then attach the compressor as a property for data compression.
|
// After the stream object has been created, then attach the compressor as a property for data compression.
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
@ -34,73 +34,73 @@ public class DecoratingHttp2FrameWriter implements Http2FrameWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
|
||||||
boolean endStream, ChannelPromise promise) {
|
boolean endStream, Promise<Void> promise) {
|
||||||
return delegate.writeData(ctx, streamId, data, padding, endStream, promise);
|
return delegate.writeData(ctx, streamId, data, padding, endStream, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||||
boolean endStream, ChannelPromise promise) {
|
boolean endStream, Promise<Void> promise) {
|
||||||
return delegate.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
return delegate.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int streamDependency, short weight, boolean exclusive, int padding,
|
int streamDependency, short weight, boolean exclusive, int padding,
|
||||||
boolean endStream, ChannelPromise promise) {
|
boolean endStream, Promise<Void> promise) {
|
||||||
return delegate
|
return delegate
|
||||||
.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream, promise);
|
.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive, padding, endStream, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
||||||
boolean exclusive, ChannelPromise promise) {
|
boolean exclusive, Promise<Void> promise) {
|
||||||
return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
return delegate.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
return delegate.writeRstStream(ctx, streamId, errorCode, promise);
|
return delegate.writeRstStream(ctx, streamId, errorCode, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings, ChannelPromise promise) {
|
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings, Promise<Void> promise) {
|
||||||
return delegate.writeSettings(ctx, settings, promise);
|
return delegate.writeSettings(ctx, settings, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
return delegate.writeSettingsAck(ctx, promise);
|
return delegate.writeSettingsAck(ctx, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) {
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
||||||
return delegate.writePing(ctx, ack, data, promise);
|
return delegate.writePing(ctx, ack, data, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||||
Http2Headers headers, int padding, ChannelPromise promise) {
|
Http2Headers headers, int padding, Promise<Void> promise) {
|
||||||
return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise);
|
return delegate.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
|
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
return delegate.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
|
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
|
return delegate.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
||||||
ByteBuf payload, ChannelPromise promise) {
|
ByteBuf payload, Promise<Void> promise) {
|
||||||
return delegate.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
return delegate.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,13 +15,14 @@
|
|||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.CoalescingBufferQueue;
|
import io.netty.channel.CoalescingBufferQueue;
|
||||||
import io.netty.handler.codec.http.HttpStatusClass;
|
import io.netty.handler.codec.http.HttpStatusClass;
|
||||||
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
|
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.FutureListener;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
@ -118,8 +119,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
|
public Future<Void> writeData(final ChannelHandlerContext ctx, final int streamId, ByteBuf data, int padding,
|
||||||
final boolean endOfStream, ChannelPromise promise) {
|
final boolean endOfStream, Promise<Void> promise) {
|
||||||
final Http2Stream stream;
|
final Http2Stream stream;
|
||||||
try {
|
try {
|
||||||
stream = requireStream(streamId);
|
stream = requireStream(streamId);
|
||||||
@ -140,13 +141,13 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
|
|
||||||
// Hand control of the frame to the flow controller.
|
// Hand control of the frame to the flow controller.
|
||||||
flowController().addFlowControlled(stream,
|
flowController().addFlowControlled(stream,
|
||||||
new FlowControlledData(stream, data, padding, endOfStream, promise));
|
new FlowControlledData(stream, data, padding, endOfStream, promise, ctx.channel()));
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers, int padding,
|
||||||
boolean endStream, ChannelPromise promise) {
|
boolean endStream, Promise<Void> promise) {
|
||||||
return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream, promise);
|
return writeHeaders0(ctx, streamId, headers, false, 0, (short) 0, false, padding, endStream, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,9 +161,10 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(final ChannelHandlerContext ctx, final int streamId,
|
public Future<Void> writeHeaders(final ChannelHandlerContext ctx, final int streamId,
|
||||||
final Http2Headers headers, final int streamDependency, final short weight,
|
final Http2Headers headers, final int streamDependency, final short weight,
|
||||||
final boolean exclusive, final int padding, final boolean endOfStream, ChannelPromise promise) {
|
final boolean exclusive, final int padding, final boolean endOfStream,
|
||||||
|
Promise<Void> promise) {
|
||||||
return writeHeaders0(ctx, streamId, headers, true, streamDependency,
|
return writeHeaders0(ctx, streamId, headers, true, streamDependency,
|
||||||
weight, exclusive, padding, endOfStream, promise);
|
weight, exclusive, padding, endOfStream, promise);
|
||||||
}
|
}
|
||||||
@ -171,11 +173,11 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
* Write headers via {@link Http2FrameWriter}. If {@code hasPriority} is {@code false} it will ignore the
|
* Write headers via {@link Http2FrameWriter}. If {@code hasPriority} is {@code false} it will ignore the
|
||||||
* {@code streamDependency}, {@code weight} and {@code exclusive} parameters.
|
* {@code streamDependency}, {@code weight} and {@code exclusive} parameters.
|
||||||
*/
|
*/
|
||||||
private static ChannelFuture sendHeaders(Http2FrameWriter frameWriter, ChannelHandlerContext ctx, int streamId,
|
private static Future<Void> sendHeaders(Http2FrameWriter frameWriter, ChannelHandlerContext ctx, int streamId,
|
||||||
Http2Headers headers, final boolean hasPriority,
|
Http2Headers headers, final boolean hasPriority,
|
||||||
int streamDependency, final short weight,
|
int streamDependency, final short weight,
|
||||||
boolean exclusive, final int padding,
|
boolean exclusive, final int padding,
|
||||||
boolean endOfStream, ChannelPromise promise) {
|
boolean endOfStream, Promise<Void> promise) {
|
||||||
if (hasPriority) {
|
if (hasPriority) {
|
||||||
return frameWriter.writeHeaders(ctx, streamId, headers, streamDependency,
|
return frameWriter.writeHeaders(ctx, streamId, headers, streamDependency,
|
||||||
weight, exclusive, padding, endOfStream, promise);
|
weight, exclusive, padding, endOfStream, promise);
|
||||||
@ -183,11 +185,11 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream, promise);
|
return frameWriter.writeHeaders(ctx, streamId, headers, padding, endOfStream, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture writeHeaders0(final ChannelHandlerContext ctx, final int streamId,
|
private Future<Void> writeHeaders0(final ChannelHandlerContext ctx, final int streamId,
|
||||||
final Http2Headers headers, final boolean hasPriority,
|
final Http2Headers headers, final boolean hasPriority,
|
||||||
final int streamDependency, final short weight,
|
final int streamDependency, final short weight,
|
||||||
final boolean exclusive, final int padding,
|
final boolean exclusive, final int padding,
|
||||||
final boolean endOfStream, ChannelPromise promise) {
|
final boolean endOfStream, Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
Http2Stream stream = connection.stream(streamId);
|
Http2Stream stream = connection.stream(streamId);
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
@ -228,7 +230,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
|
|
||||||
boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream);
|
boolean isInformational = validateHeadersSentState(stream, headers, connection.isServer(), endOfStream);
|
||||||
|
|
||||||
ChannelFuture future = sendHeaders(frameWriter, ctx, streamId, headers, hasPriority, streamDependency,
|
Future<Void> future = sendHeaders(frameWriter, ctx, streamId, headers, hasPriority, streamDependency,
|
||||||
weight, exclusive, padding, endOfStream, promise);
|
weight, exclusive, padding, endOfStream, promise);
|
||||||
|
|
||||||
// Writing headers may fail during the encode state if they violate HPACK limits.
|
// Writing headers may fail during the encode state if they violate HPACK limits.
|
||||||
@ -272,21 +274,21 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency, short weight,
|
||||||
boolean exclusive, ChannelPromise promise) {
|
boolean exclusive, Promise<Void> promise) {
|
||||||
return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
return frameWriter.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
// Delegate to the lifecycle manager for proper updating of connection state.
|
// Delegate to the lifecycle manager for proper updating of connection state.
|
||||||
return lifecycleManager.resetStream(ctx, streamId, errorCode, promise);
|
return lifecycleManager.resetStream(ctx, streamId, errorCode, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
outstandingLocalSettingsQueue.add(settings);
|
outstandingLocalSettingsQueue.add(settings);
|
||||||
try {
|
try {
|
||||||
Boolean pushEnabled = settings.pushEnabled();
|
Boolean pushEnabled = settings.pushEnabled();
|
||||||
@ -301,17 +303,16 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
if (outstandingRemoteSettingsQueue == null) {
|
if (outstandingRemoteSettingsQueue == null) {
|
||||||
return frameWriter.writeSettingsAck(ctx, promise);
|
return frameWriter.writeSettingsAck(ctx, promise);
|
||||||
}
|
}
|
||||||
Http2Settings settings = outstandingRemoteSettingsQueue.poll();
|
Http2Settings settings = outstandingRemoteSettingsQueue.poll();
|
||||||
if (settings == null) {
|
if (settings == null) {
|
||||||
return promise.setFailure(new Http2Exception(INTERNAL_ERROR, "attempted to write a SETTINGS ACK with no " +
|
return promise.setFailure(new Http2Exception(INTERNAL_ERROR, "attempted to write a SETTINGS ACK with no " +
|
||||||
" pending SETTINGS"));
|
" pending SETTINGS"));
|
||||||
}
|
}
|
||||||
SimpleChannelPromiseAggregator aggregator = new SimpleChannelPromiseAggregator(promise, ctx.channel(),
|
SimpleChannelPromiseAggregator aggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||||
ctx.executor());
|
|
||||||
// Acknowledge receipt of the settings. We should do this before we process the settings to ensure our
|
// Acknowledge receipt of the settings. We should do this before we process the settings to ensure our
|
||||||
// remote peer applies these settings before any subsequent frames that we may send which depend upon
|
// remote peer applies these settings before any subsequent frames that we may send which depend upon
|
||||||
// these new settings. See https://github.com/netty/netty/issues/6520.
|
// these new settings. See https://github.com/netty/netty/issues/6520.
|
||||||
@ -319,10 +320,10 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
|
|
||||||
// We create a "new promise" to make sure that status from both the write and the application are taken into
|
// We create a "new promise" to make sure that status from both the write and the application are taken into
|
||||||
// account independently.
|
// account independently.
|
||||||
ChannelPromise applySettingsPromise = aggregator.newPromise();
|
Promise<Void> applySettingsPromise = aggregator.newPromise();
|
||||||
try {
|
try {
|
||||||
remoteSettings(settings);
|
remoteSettings(settings);
|
||||||
applySettingsPromise.setSuccess();
|
applySettingsPromise.setSuccess(null);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
applySettingsPromise.setFailure(e);
|
applySettingsPromise.setFailure(e);
|
||||||
lifecycleManager.onError(ctx, true, e);
|
lifecycleManager.onError(ctx, true, e);
|
||||||
@ -331,13 +332,13 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) {
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
||||||
return frameWriter.writePing(ctx, ack, data, promise);
|
return frameWriter.writePing(ctx, ack, data, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||||
Http2Headers headers, int padding, ChannelPromise promise) {
|
Http2Headers headers, int padding, Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
if (connection.goAwayReceived()) {
|
if (connection.goAwayReceived()) {
|
||||||
throw connectionError(PROTOCOL_ERROR, "Sending PUSH_PROMISE after GO_AWAY received.");
|
throw connectionError(PROTOCOL_ERROR, "Sending PUSH_PROMISE after GO_AWAY received.");
|
||||||
@ -347,7 +348,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
// Reserve the promised stream.
|
// Reserve the promised stream.
|
||||||
connection.local().reservePushStream(promisedStreamId, stream);
|
connection.local().reservePushStream(promisedStreamId, stream);
|
||||||
|
|
||||||
ChannelFuture future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding,
|
Future<Void> future = frameWriter.writePushPromise(ctx, streamId, promisedStreamId, headers, padding,
|
||||||
promise);
|
promise);
|
||||||
// Writing headers may fail during the encode state if they violate HPACK limits.
|
// Writing headers may fail during the encode state if they violate HPACK limits.
|
||||||
Throwable failureCause = future.cause();
|
Throwable failureCause = future.cause();
|
||||||
@ -372,21 +373,21 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
|
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode, ByteBuf debugData,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData, promise);
|
return lifecycleManager.goAway(ctx, lastStreamId, errorCode, debugData, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
|
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId, int windowSizeIncrement,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
return promise.setFailure(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" +
|
return promise.setFailure(new UnsupportedOperationException("Use the Http2[Inbound|Outbound]FlowController" +
|
||||||
" objects to control window sizes"));
|
" objects to control window sizes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId, Http2Flags flags,
|
||||||
ByteBuf payload, ChannelPromise promise) {
|
ByteBuf payload, Promise<Void> promise) {
|
||||||
return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
return frameWriter.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,9 +442,9 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
private int dataSize;
|
private int dataSize;
|
||||||
|
|
||||||
FlowControlledData(Http2Stream stream, ByteBuf buf, int padding, boolean endOfStream,
|
FlowControlledData(Http2Stream stream, ByteBuf buf, int padding, boolean endOfStream,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise, Channel channel) {
|
||||||
super(stream, padding, endOfStream, promise);
|
super(stream, padding, endOfStream, promise);
|
||||||
queue = new CoalescingBufferQueue(promise.channel());
|
queue = new CoalescingBufferQueue(channel);
|
||||||
queue.add(buf, promise);
|
queue.add(buf, promise);
|
||||||
dataSize = queue.readableBytes();
|
dataSize = queue.readableBytes();
|
||||||
}
|
}
|
||||||
@ -481,7 +482,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
// There's no need to write any data frames because there are only empty data frames in the
|
// There's no need to write any data frames because there are only empty data frames in the
|
||||||
// queue and it is not end of stream yet. Just complete their promises by getting the buffer
|
// queue and it is not end of stream yet. Just complete their promises by getting the buffer
|
||||||
// corresponding to 0 bytes and writing it to the channel (to preserve notification order).
|
// corresponding to 0 bytes and writing it to the channel (to preserve notification order).
|
||||||
ChannelPromise writePromise = ctx.newPromise().addListener(this);
|
Promise<Void> writePromise = ctx.newPromise();
|
||||||
|
writePromise.addListener(this);
|
||||||
ctx.write(queue.remove(0, writePromise), writePromise);
|
ctx.write(queue.remove(0, writePromise), writePromise);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -494,7 +496,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
|
|
||||||
// Determine how much data to write.
|
// Determine how much data to write.
|
||||||
int writableData = min(queuedData, allowedBytes);
|
int writableData = min(queuedData, allowedBytes);
|
||||||
ChannelPromise writePromise = ctx.newPromise().addListener(this);
|
Promise<Void> writePromise = ctx.newPromise();
|
||||||
|
writePromise.addListener(this);
|
||||||
ByteBuf toWrite = queue.remove(writableData, writePromise);
|
ByteBuf toWrite = queue.remove(writableData, writePromise);
|
||||||
dataSize = queue.readableBytes();
|
dataSize = queue.readableBytes();
|
||||||
|
|
||||||
@ -523,8 +526,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void notifyLifecycleManagerOnError(ChannelFuture future, final ChannelHandlerContext ctx) {
|
private void notifyLifecycleManagerOnError(Future<Void> future, final ChannelHandlerContext ctx) {
|
||||||
future.addListener((ChannelFutureListener) future1 -> {
|
future.addListener(future1 -> {
|
||||||
Throwable cause = future1.cause();
|
Throwable cause = future1.cause();
|
||||||
if (cause != null) {
|
if (cause != null) {
|
||||||
lifecycleManager.onError(ctx, true, cause);
|
lifecycleManager.onError(ctx, true, cause);
|
||||||
@ -546,7 +549,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
|
|
||||||
FlowControlledHeaders(Http2Stream stream, Http2Headers headers, boolean hasPriority,
|
FlowControlledHeaders(Http2Stream stream, Http2Headers headers, boolean hasPriority,
|
||||||
int streamDependency, short weight, boolean exclusive,
|
int streamDependency, short weight, boolean exclusive,
|
||||||
int padding, boolean endOfStream, ChannelPromise promise) {
|
int padding, boolean endOfStream, Promise<Void> promise) {
|
||||||
super(stream, padding, endOfStream, promise);
|
super(stream, padding, endOfStream, promise);
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
this.hasPriority = hasPriority;
|
this.hasPriority = hasPriority;
|
||||||
@ -575,8 +578,8 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
// closeStreamLocal().
|
// closeStreamLocal().
|
||||||
promise.addListener(this);
|
promise.addListener(this);
|
||||||
|
|
||||||
ChannelFuture f = sendHeaders(frameWriter, ctx, stream.id(), headers, hasPriority, streamDependency,
|
Future<Void> f = sendHeaders(frameWriter, ctx, stream.id(), headers, hasPriority, streamDependency,
|
||||||
weight, exclusive, padding, endOfStream, promise);
|
weight, exclusive, padding, endOfStream, promise);
|
||||||
// Writing headers may fail during the encode state if they violate HPACK limits.
|
// Writing headers may fail during the encode state if they violate HPACK limits.
|
||||||
Throwable failureCause = f.cause();
|
Throwable failureCause = f.cause();
|
||||||
if (failureCause == null) {
|
if (failureCause == null) {
|
||||||
@ -595,15 +598,14 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
/**
|
/**
|
||||||
* Common base type for payloads to deliver via flow-control.
|
* Common base type for payloads to deliver via flow-control.
|
||||||
*/
|
*/
|
||||||
public abstract class FlowControlledBase implements Http2RemoteFlowController.FlowControlled,
|
public abstract class FlowControlledBase implements Http2RemoteFlowController.FlowControlled, FutureListener<Void> {
|
||||||
ChannelFutureListener {
|
|
||||||
protected final Http2Stream stream;
|
protected final Http2Stream stream;
|
||||||
protected ChannelPromise promise;
|
protected Promise<Void> promise;
|
||||||
protected boolean endOfStream;
|
protected boolean endOfStream;
|
||||||
protected int padding;
|
protected int padding;
|
||||||
|
|
||||||
FlowControlledBase(final Http2Stream stream, int padding, boolean endOfStream,
|
FlowControlledBase(final Http2Stream stream, int padding, boolean endOfStream,
|
||||||
final ChannelPromise promise) {
|
final Promise<Void> promise) {
|
||||||
checkPositiveOrZero(padding, "padding");
|
checkPositiveOrZero(padding, "padding");
|
||||||
this.padding = padding;
|
this.padding = padding;
|
||||||
this.endOfStream = endOfStream;
|
this.endOfStream = endOfStream;
|
||||||
@ -619,7 +621,7 @@ public class DefaultHttp2ConnectionEncoder implements Http2ConnectionEncoder, Ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(ChannelFuture future) throws Exception {
|
public void operationComplete(Future<? extends Void> future) throws Exception {
|
||||||
if (!future.isSuccess()) {
|
if (!future.isSuccess()) {
|
||||||
error(flowController().channelHandlerContext(), future.cause());
|
error(flowController().channelHandlerContext(), future.cause());
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,12 @@
|
|||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
|
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
|
||||||
import io.netty.handler.codec.http2.Http2FrameWriter.Configuration;
|
import io.netty.handler.codec.http2.Http2FrameWriter.Configuration;
|
||||||
import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
|
import io.netty.handler.codec.http2.Http2HeadersEncoder.SensitivityDetector;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import static io.netty.buffer.Unpooled.directBuffer;
|
import static io.netty.buffer.Unpooled.directBuffer;
|
||||||
@ -132,10 +132,10 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
public void close() { }
|
public void close() { }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
||||||
int padding, boolean endStream, ChannelPromise promise) {
|
int padding, boolean endStream, Promise<Void> promise) {
|
||||||
final SimpleChannelPromiseAggregator promiseAggregator =
|
final SimpleChannelPromiseAggregator promiseAggregator =
|
||||||
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
|
new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||||
ByteBuf frameHeader = null;
|
ByteBuf frameHeader = null;
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
@ -254,23 +254,23 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||||
Http2Headers headers, int padding, boolean endStream, ChannelPromise promise) {
|
Http2Headers headers, int padding, boolean endStream, Promise<Void> promise) {
|
||||||
return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
|
return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
|
||||||
false, 0, (short) 0, false, promise);
|
false, 0, (short) 0, false, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||||
Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
||||||
int padding, boolean endStream, ChannelPromise promise) {
|
int padding, boolean endStream, Promise<Void> promise) {
|
||||||
return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
|
return writeHeadersInternal(ctx, streamId, headers, padding, endStream,
|
||||||
true, streamDependency, weight, exclusive, promise);
|
true, streamDependency, weight, exclusive, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId,
|
||||||
int streamDependency, short weight, boolean exclusive, ChannelPromise promise) {
|
int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY);
|
verifyStreamOrConnectionId(streamDependency, STREAM_DEPENDENCY);
|
||||||
@ -288,8 +288,8 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
verifyErrorCode(errorCode);
|
verifyErrorCode(errorCode);
|
||||||
@ -304,8 +304,8 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
public Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
requireNonNull(settings, "settings");
|
requireNonNull(settings, "settings");
|
||||||
int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
|
int payloadLength = SETTING_ENTRY_LENGTH * settings.size();
|
||||||
@ -322,7 +322,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
||||||
writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0);
|
writeFrameHeaderInternal(buf, 0, SETTINGS, new Http2Flags().ack(true), 0);
|
||||||
@ -333,7 +333,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) {
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
||||||
Http2Flags flags = ack ? new Http2Flags().ack(true) : new Http2Flags();
|
Http2Flags flags = ack ? new Http2Flags().ack(true) : new Http2Flags();
|
||||||
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + PING_FRAME_PAYLOAD_LENGTH);
|
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH + PING_FRAME_PAYLOAD_LENGTH);
|
||||||
// Assume nothing below will throw until buf is written. That way we don't have to take care of ownership
|
// Assume nothing below will throw until buf is written. That way we don't have to take care of ownership
|
||||||
@ -344,11 +344,10 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||||
int promisedStreamId, Http2Headers headers, int padding, ChannelPromise promise) {
|
Http2Headers headers, int padding, Promise<Void> promise) {
|
||||||
ByteBuf headerBlock = null;
|
ByteBuf headerBlock = null;
|
||||||
SimpleChannelPromiseAggregator promiseAggregator =
|
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||||
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
|
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
verifyStreamId(promisedStreamId, "Promised Stream ID");
|
verifyStreamId(promisedStreamId, "Promised Stream ID");
|
||||||
@ -402,10 +401,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||||
ByteBuf debugData, ChannelPromise promise) {
|
ByteBuf debugData, Promise<Void> promise) {
|
||||||
SimpleChannelPromiseAggregator promiseAggregator =
|
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||||
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
|
|
||||||
try {
|
try {
|
||||||
verifyStreamOrConnectionId(lastStreamId, "Last Stream ID");
|
verifyStreamOrConnectionId(lastStreamId, "Last Stream ID");
|
||||||
verifyErrorCode(errorCode);
|
verifyErrorCode(errorCode);
|
||||||
@ -437,8 +435,8 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
|
||||||
int windowSizeIncrement, ChannelPromise promise) {
|
int windowSizeIncrement, Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
||||||
verifyWindowSizeIncrement(windowSizeIncrement);
|
verifyWindowSizeIncrement(windowSizeIncrement);
|
||||||
@ -453,10 +451,9 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||||
Http2Flags flags, ByteBuf payload, ChannelPromise promise) {
|
Http2Flags flags, ByteBuf payload, Promise<Void> promise) {
|
||||||
SimpleChannelPromiseAggregator promiseAggregator =
|
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||||
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
|
|
||||||
try {
|
try {
|
||||||
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
verifyStreamOrConnectionId(streamId, STREAM_ID);
|
||||||
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
ByteBuf buf = ctx.alloc().buffer(FRAME_HEADER_LENGTH);
|
||||||
@ -481,12 +478,11 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
return promiseAggregator.doneAllocatingPromises();
|
return promiseAggregator.doneAllocatingPromises();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture writeHeadersInternal(ChannelHandlerContext ctx,
|
private Future<Void> writeHeadersInternal(ChannelHandlerContext ctx,
|
||||||
int streamId, Http2Headers headers, int padding, boolean endStream,
|
int streamId, Http2Headers headers, int padding, boolean endStream,
|
||||||
boolean hasPriority, int streamDependency, short weight, boolean exclusive, ChannelPromise promise) {
|
boolean hasPriority, int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
|
||||||
ByteBuf headerBlock = null;
|
ByteBuf headerBlock = null;
|
||||||
SimpleChannelPromiseAggregator promiseAggregator =
|
SimpleChannelPromiseAggregator promiseAggregator = new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||||
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
|
|
||||||
try {
|
try {
|
||||||
verifyStreamId(streamId, STREAM_ID);
|
verifyStreamId(streamId, STREAM_ID);
|
||||||
if (hasPriority) {
|
if (hasPriority) {
|
||||||
@ -551,7 +547,7 @@ public class DefaultHttp2FrameWriter implements Http2FrameWriter, Http2FrameSize
|
|||||||
/**
|
/**
|
||||||
* Writes as many continuation frames as needed until {@code padding} and {@code headerBlock} are consumed.
|
* Writes as many continuation frames as needed until {@code padding} and {@code headerBlock} are consumed.
|
||||||
*/
|
*/
|
||||||
private ChannelFuture writeContinuationFrames(ChannelHandlerContext ctx, int streamId,
|
private Future<Void> writeContinuationFrames(ChannelHandlerContext ctx, int streamId,
|
||||||
ByteBuf headerBlock, SimpleChannelPromiseAggregator promiseAggregator) {
|
ByteBuf headerBlock, SimpleChannelPromiseAggregator promiseAggregator) {
|
||||||
Http2Flags flags = new Http2Flags();
|
Http2Flags flags = new Http2Flags();
|
||||||
|
|
||||||
|
@ -18,13 +18,12 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.DefaultChannelPromise;
|
|
||||||
import io.netty.handler.ssl.ApplicationProtocolNames;
|
import io.netty.handler.ssl.ApplicationProtocolNames;
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
|
import io.netty.util.concurrent.DefaultPromise;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import static io.netty.buffer.Unpooled.directBuffer;
|
import static io.netty.buffer.Unpooled.directBuffer;
|
||||||
@ -255,18 +254,18 @@ public final class Http2CodecUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides the ability to associate the outcome of multiple {@link ChannelPromise}
|
* Provides the ability to associate the outcome of multiple {@link Promise}
|
||||||
* objects into a single {@link ChannelPromise} object.
|
* objects into a single {@link Promise} object.
|
||||||
*/
|
*/
|
||||||
static final class SimpleChannelPromiseAggregator extends DefaultChannelPromise {
|
static final class SimpleChannelPromiseAggregator extends DefaultPromise<Void> {
|
||||||
private final ChannelPromise promise;
|
private final Promise<Void> promise;
|
||||||
private int expectedCount;
|
private int expectedCount;
|
||||||
private int doneCount;
|
private int doneCount;
|
||||||
private Throwable aggregateFailure;
|
private Throwable aggregateFailure;
|
||||||
private boolean doneAllocating;
|
private boolean doneAllocating;
|
||||||
|
|
||||||
SimpleChannelPromiseAggregator(ChannelPromise promise, Channel c, EventExecutor e) {
|
SimpleChannelPromiseAggregator(Promise<Void> promise, EventExecutor e) {
|
||||||
super(c, e);
|
super(e);
|
||||||
assert promise != null && !promise.isDone();
|
assert promise != null && !promise.isDone();
|
||||||
this.promise = promise;
|
this.promise = promise;
|
||||||
}
|
}
|
||||||
@ -276,7 +275,7 @@ public final class Http2CodecUtil {
|
|||||||
* @return A new promise which will be aggregated.
|
* @return A new promise which will be aggregated.
|
||||||
* {@code null} if {@link #doneAllocatingPromises()} was previously called.
|
* {@code null} if {@link #doneAllocatingPromises()} was previously called.
|
||||||
*/
|
*/
|
||||||
public ChannelPromise newPromise() {
|
public Promise<Void> newPromise() {
|
||||||
assert !doneAllocating : "Done allocating. No more promises can be allocated.";
|
assert !doneAllocating : "Done allocating. No more promises can be allocated.";
|
||||||
++expectedCount;
|
++expectedCount;
|
||||||
return this;
|
return this;
|
||||||
@ -287,7 +286,7 @@ public final class Http2CodecUtil {
|
|||||||
* The aggregation can not be successful until this method is called.
|
* The aggregation can not be successful until this method is called.
|
||||||
* @return The promise that is the aggregation of all promises allocated with {@link #newPromise()}.
|
* @return The promise that is the aggregation of all promises allocated with {@link #newPromise()}.
|
||||||
*/
|
*/
|
||||||
public ChannelPromise doneAllocatingPromises() {
|
public Promise<Void> doneAllocatingPromises() {
|
||||||
if (!doneAllocating) {
|
if (!doneAllocating) {
|
||||||
doneAllocating = true;
|
doneAllocating = true;
|
||||||
if (doneCount == expectedCount || expectedCount == 0) {
|
if (doneCount == expectedCount || expectedCount == 0) {
|
||||||
@ -319,7 +318,7 @@ public final class Http2CodecUtil {
|
|||||||
* because that may be expected.
|
* because that may be expected.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public ChannelPromise setFailure(Throwable cause) {
|
public Promise<Void> setFailure(Throwable cause) {
|
||||||
if (allowFailure()) {
|
if (allowFailure()) {
|
||||||
++doneCount;
|
++doneCount;
|
||||||
setAggregateFailure(cause);
|
setAggregateFailure(cause);
|
||||||
@ -331,7 +330,7 @@ public final class Http2CodecUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelPromise setSuccess(Void result) {
|
public Promise<Void> setSuccess(Void result) {
|
||||||
if (awaitingPromises()) {
|
if (awaitingPromises()) {
|
||||||
++doneCount;
|
++doneCount;
|
||||||
if (allPromisesDone()) {
|
if (allPromisesDone()) {
|
||||||
@ -367,9 +366,9 @@ public final class Http2CodecUtil {
|
|||||||
return doneCount == expectedCount && doneAllocating;
|
return doneCount == expectedCount && doneAllocating;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise setPromise() {
|
private Promise<Void> setPromise() {
|
||||||
if (aggregateFailure == null) {
|
if (aggregateFailure == null) {
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
return super.setSuccess(null);
|
return super.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
promise.setFailure(aggregateFailure);
|
promise.setFailure(aggregateFailure);
|
||||||
@ -379,7 +378,7 @@ public final class Http2CodecUtil {
|
|||||||
|
|
||||||
private boolean tryPromise() {
|
private boolean tryPromise() {
|
||||||
if (aggregateFailure == null) {
|
if (aggregateFailure == null) {
|
||||||
promise.trySuccess();
|
promise.trySuccess(null);
|
||||||
return super.trySuccess(null);
|
return super.trySuccess(null);
|
||||||
} else {
|
} else {
|
||||||
promise.tryFailure(aggregateFailure);
|
promise.tryFailure(aggregateFailure);
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
|
|
||||||
@ -61,8 +61,9 @@ public interface Http2ConnectionEncoder extends Http2FrameWriter {
|
|||||||
/**
|
/**
|
||||||
* Writes the given data to the internal {@link Http2FrameWriter} without performing any
|
* Writes the given data to the internal {@link Http2FrameWriter} without performing any
|
||||||
* state checks on the connection/stream.
|
* state checks on the connection/stream.
|
||||||
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||||
Http2Flags flags, ByteBuf payload, ChannelPromise promise);
|
Http2Flags flags, ByteBuf payload, Promise<Void> promise);
|
||||||
}
|
}
|
||||||
|
@ -17,15 +17,16 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http2.Http2Exception.CompositeStreamException;
|
import io.netty.handler.codec.http2.Http2Exception.CompositeStreamException;
|
||||||
import io.netty.handler.codec.http2.Http2Exception.StreamException;
|
import io.netty.handler.codec.http2.Http2Exception.StreamException;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.FutureListener;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.ScheduledFuture;
|
import io.netty.util.concurrent.ScheduledFuture;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
@ -74,7 +75,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
private final Http2ConnectionEncoder encoder;
|
private final Http2ConnectionEncoder encoder;
|
||||||
private final Http2Settings initialSettings;
|
private final Http2Settings initialSettings;
|
||||||
private final boolean decoupleCloseAndGoAway;
|
private final boolean decoupleCloseAndGoAway;
|
||||||
private ChannelFutureListener closeListener;
|
private FutureListener<Object> closeListener;
|
||||||
private BaseDecoder byteDecoder;
|
private BaseDecoder byteDecoder;
|
||||||
private long gracefulShutdownTimeoutMillis;
|
private long gracefulShutdownTimeoutMillis;
|
||||||
|
|
||||||
@ -352,12 +353,12 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
final boolean isClient = !connection().isServer();
|
final boolean isClient = !connection().isServer();
|
||||||
if (isClient) {
|
if (isClient) {
|
||||||
// Clients must send the preface string as the first bytes on the connection.
|
// Clients must send the preface string as the first bytes on the connection.
|
||||||
ctx.write(connectionPrefaceBuf()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
|
ctx.write(connectionPrefaceBuf()).addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Both client and server must send their initial settings.
|
// Both client and server must send their initial settings.
|
||||||
encoder.writeSettings(ctx, initialSettings, ctx.newPromise()).addListener(
|
encoder.writeSettings(ctx, initialSettings, ctx.newPromise())
|
||||||
ChannelFutureListener.CLOSE_ON_FAILURE);
|
.addListener(ctx.channel(), ChannelFutureListeners.CLOSE_ON_FAILURE);
|
||||||
|
|
||||||
if (isClient) {
|
if (isClient) {
|
||||||
// If this handler is extended by the user and we directly fire the userEvent from this context then
|
// If this handler is extended by the user and we directly fire the userEvent from this context then
|
||||||
@ -436,23 +437,23 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
|
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
|
||||||
ctx.bind(localAddress, promise);
|
ctx.bind(localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
|
public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
ctx.connect(remoteAddress, localAddress, promise);
|
ctx.connect(remoteAddress, localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ctx.disconnect(promise);
|
ctx.disconnect(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
if (decoupleCloseAndGoAway) {
|
if (decoupleCloseAndGoAway) {
|
||||||
ctx.close(promise);
|
ctx.close(promise);
|
||||||
return;
|
return;
|
||||||
@ -468,21 +469,21 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
// a GO_AWAY has been sent we send a empty buffer just so we can wait to close until all other data has been
|
// a GO_AWAY has been sent we send a empty buffer just so we can wait to close until all other data has been
|
||||||
// flushed to the OS.
|
// flushed to the OS.
|
||||||
// https://github.com/netty/netty/issues/5307
|
// https://github.com/netty/netty/issues/5307
|
||||||
ChannelFuture f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise());
|
Future<Void> f = connection().goAwaySent() ? ctx.write(EMPTY_BUFFER) : goAway(ctx, null, ctx.newPromise());
|
||||||
ctx.flush();
|
ctx.flush();
|
||||||
doGracefulShutdown(ctx, f, promise);
|
doGracefulShutdown(ctx, f, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFutureListener newClosingChannelFutureListener(
|
private FutureListener<Object> newClosingChannelFutureListener(
|
||||||
ChannelHandlerContext ctx, ChannelPromise promise) {
|
ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
long gracefulShutdownTimeoutMillis = this.gracefulShutdownTimeoutMillis;
|
long gracefulShutdownTimeoutMillis = this.gracefulShutdownTimeoutMillis;
|
||||||
return gracefulShutdownTimeoutMillis < 0 ?
|
return gracefulShutdownTimeoutMillis < 0 ?
|
||||||
new ClosingChannelFutureListener(ctx, promise) :
|
new ClosingChannelFutureListener(ctx, promise) :
|
||||||
new ClosingChannelFutureListener(ctx, promise, gracefulShutdownTimeoutMillis, MILLISECONDS);
|
new ClosingChannelFutureListener(ctx, promise, gracefulShutdownTimeoutMillis, MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doGracefulShutdown(ChannelHandlerContext ctx, ChannelFuture future, final ChannelPromise promise) {
|
private void doGracefulShutdown(ChannelHandlerContext ctx, Future<Void> future, final Promise<Void> promise) {
|
||||||
final ChannelFutureListener listener = newClosingChannelFutureListener(ctx, promise);
|
FutureListener<Object> listener = newClosingChannelFutureListener(ctx, promise);
|
||||||
if (isGracefulShutdownComplete()) {
|
if (isGracefulShutdownComplete()) {
|
||||||
// If there are no active streams, close immediately after the GO_AWAY write completes or the timeout
|
// If there are no active streams, close immediately after the GO_AWAY write completes or the timeout
|
||||||
// elapsed.
|
// elapsed.
|
||||||
@ -495,7 +496,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
if (closeListener == null) {
|
if (closeListener == null) {
|
||||||
closeListener = listener;
|
closeListener = listener;
|
||||||
} else if (promise != null) {
|
} else if (promise != null) {
|
||||||
final ChannelFutureListener oldCloseListener = closeListener;
|
FutureListener<Object> oldCloseListener = closeListener;
|
||||||
closeListener = future1 -> {
|
closeListener = future1 -> {
|
||||||
try {
|
try {
|
||||||
oldCloseListener.operationComplete(future1);
|
oldCloseListener.operationComplete(future1);
|
||||||
@ -508,12 +509,12 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void register(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void register(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ctx.register(promise);
|
ctx.register(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ctx.deregister(promise);
|
ctx.deregister(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -523,7 +524,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
ctx.write(msg, promise);
|
ctx.write(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -574,7 +575,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
* @param future If closing, the future after which to close the channel.
|
* @param future If closing, the future after which to close the channel.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void closeStreamLocal(Http2Stream stream, ChannelFuture future) {
|
public void closeStreamLocal(Http2Stream stream, Future<Void> future) {
|
||||||
switch (stream.state()) {
|
switch (stream.state()) {
|
||||||
case HALF_CLOSED_LOCAL:
|
case HALF_CLOSED_LOCAL:
|
||||||
case OPEN:
|
case OPEN:
|
||||||
@ -594,7 +595,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
* @param future If closing, the future after which to close the channel.
|
* @param future If closing, the future after which to close the channel.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void closeStreamRemote(Http2Stream stream, ChannelFuture future) {
|
public void closeStreamRemote(Http2Stream stream, Future<Void> future) {
|
||||||
switch (stream.state()) {
|
switch (stream.state()) {
|
||||||
case HALF_CLOSED_REMOTE:
|
case HALF_CLOSED_REMOTE:
|
||||||
case OPEN:
|
case OPEN:
|
||||||
@ -607,13 +608,13 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void closeStream(final Http2Stream stream, ChannelFuture future) {
|
public void closeStream(final Http2Stream stream, Future<Void> future) {
|
||||||
stream.close();
|
stream.close();
|
||||||
|
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
checkCloseConnection(future);
|
checkCloseConnection(future);
|
||||||
} else {
|
} else {
|
||||||
future.addListener((ChannelFutureListener) this::checkCloseConnection);
|
future.addListener(this::checkCloseConnection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,8 +662,8 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
http2Ex = new Http2Exception(INTERNAL_ERROR, cause.getMessage(), cause);
|
http2Ex = new Http2Exception(INTERNAL_ERROR, cause.getMessage(), cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelPromise promise = ctx.newPromise();
|
Promise<Void> promise = ctx.newPromise();
|
||||||
ChannelFuture future = goAway(ctx, http2Ex, ctx.newPromise());
|
Future<Void> future = goAway(ctx, http2Ex, ctx.newPromise());
|
||||||
if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) {
|
if (http2Ex.shutdownHint() == Http2Exception.ShutdownHint.GRACEFUL_SHUTDOWN) {
|
||||||
doGracefulShutdown(ctx, future, promise);
|
doGracefulShutdown(ctx, future, promise);
|
||||||
} else {
|
} else {
|
||||||
@ -743,20 +744,20 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
* triggered by the first frame of a stream being invalid. That is, there was an error reading the frame before
|
* triggered by the first frame of a stream being invalid. That is, there was an error reading the frame before
|
||||||
* we could create a new stream.
|
* we could create a new stream.
|
||||||
*/
|
*/
|
||||||
private ChannelFuture resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
|
private Future<Void> resetUnknownStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
ChannelFuture future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise);
|
Future<Void> future = frameWriter().writeRstStream(ctx, streamId, errorCode, promise);
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
closeConnectionOnError(ctx, future);
|
closeConnectionOnError(ctx, future);
|
||||||
} else {
|
} else {
|
||||||
future.addListener((ChannelFutureListener) future1 -> closeConnectionOnError(ctx, future1));
|
future.addListener(ctx, this::closeConnectionOnError);
|
||||||
}
|
}
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> resetStream(final ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
final Http2Stream stream = connection().stream(streamId);
|
final Http2Stream stream = connection().stream(streamId);
|
||||||
if (stream == null) {
|
if (stream == null) {
|
||||||
return resetUnknownStream(ctx, streamId, errorCode, promise);
|
return resetUnknownStream(ctx, streamId, errorCode, promise);
|
||||||
@ -765,11 +766,11 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
return resetStream(ctx, stream, errorCode, promise);
|
return resetStream(ctx, stream, errorCode, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture resetStream(final ChannelHandlerContext ctx, final Http2Stream stream,
|
private Future<Void> resetStream(final ChannelHandlerContext ctx, final Http2Stream stream,
|
||||||
long errorCode, ChannelPromise promise) {
|
long errorCode, Promise<Void> promise) {
|
||||||
if (stream.isResetSent()) {
|
if (stream.isResetSent()) {
|
||||||
// Don't write a RST_STREAM frame if we have already written one.
|
// Don't write a RST_STREAM frame if we have already written one.
|
||||||
return promise.setSuccess();
|
return promise.setSuccess(null);
|
||||||
}
|
}
|
||||||
// Synchronously set the resetSent flag to prevent any subsequent calls
|
// Synchronously set the resetSent flag to prevent any subsequent calls
|
||||||
// from resulting in multiple reset frames being sent.
|
// from resulting in multiple reset frames being sent.
|
||||||
@ -778,32 +779,32 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
// call resetStream(...) again.
|
// call resetStream(...) again.
|
||||||
stream.resetSent();
|
stream.resetSent();
|
||||||
|
|
||||||
final ChannelFuture future;
|
final Future<Void> future;
|
||||||
// If the remote peer is not aware of the steam, then we are not allowed to send a RST_STREAM
|
// If the remote peer is not aware of the steam, then we are not allowed to send a RST_STREAM
|
||||||
// https://tools.ietf.org/html/rfc7540#section-6.4.
|
// https://tools.ietf.org/html/rfc7540#section-6.4.
|
||||||
if (stream.state() == IDLE ||
|
if (stream.state() == IDLE ||
|
||||||
connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) {
|
connection().local().created(stream) && !stream.isHeadersSent() && !stream.isPushPromiseSent()) {
|
||||||
future = promise.setSuccess();
|
future = promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise);
|
future = frameWriter().writeRstStream(ctx, stream.id(), errorCode, promise);
|
||||||
}
|
}
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
processRstStreamWriteResult(ctx, stream, future);
|
processRstStreamWriteResult(ctx, stream, future);
|
||||||
} else {
|
} else {
|
||||||
future.addListener((ChannelFutureListener) future1 -> processRstStreamWriteResult(ctx, stream, future1));
|
future.addListener(future1 -> processRstStreamWriteResult(ctx, stream, future1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode,
|
public Future<Void> goAway(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode,
|
||||||
final ByteBuf debugData, ChannelPromise promise) {
|
final ByteBuf debugData, Promise<Void> promise) {
|
||||||
final Http2Connection connection = connection();
|
final Http2Connection connection = connection();
|
||||||
try {
|
try {
|
||||||
if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) {
|
if (!connection.goAwaySent(lastStreamId, errorCode, debugData)) {
|
||||||
debugData.release();
|
debugData.release();
|
||||||
promise.trySuccess();
|
promise.trySuccess(null);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
@ -815,12 +816,12 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
// Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and
|
// Need to retain before we write the buffer because if we do it after the refCnt could already be 0 and
|
||||||
// result in an IllegalRefCountException.
|
// result in an IllegalRefCountException.
|
||||||
debugData.retain();
|
debugData.retain();
|
||||||
ChannelFuture future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
Future<Void> future = frameWriter().writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
||||||
|
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future);
|
processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future);
|
||||||
} else {
|
} else {
|
||||||
future.addListener((ChannelFutureListener) future1 ->
|
future.addListener(future1 ->
|
||||||
processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future1));
|
processGoAwayWriteResult(ctx, lastStreamId, errorCode, debugData, future1));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -831,11 +832,11 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
* Closes the connection if the graceful shutdown process has completed.
|
* Closes the connection if the graceful shutdown process has completed.
|
||||||
* @param future Represents the status that will be passed to the {@link #closeListener}.
|
* @param future Represents the status that will be passed to the {@link #closeListener}.
|
||||||
*/
|
*/
|
||||||
private void checkCloseConnection(ChannelFuture future) {
|
private void checkCloseConnection(Future<?> future) {
|
||||||
// If this connection is closing and the graceful shutdown has completed, close the connection
|
// If this connection is closing and the graceful shutdown has completed, close the connection
|
||||||
// once this operation completes.
|
// once this operation completes.
|
||||||
if (closeListener != null && isGracefulShutdownComplete()) {
|
if (closeListener != null && isGracefulShutdownComplete()) {
|
||||||
ChannelFutureListener closeListener = this.closeListener;
|
FutureListener<Object> closeListener = this.closeListener;
|
||||||
// This method could be called multiple times
|
// This method could be called multiple times
|
||||||
// and we don't want to notify the closeListener multiple times.
|
// and we don't want to notify the closeListener multiple times.
|
||||||
this.closeListener = null;
|
this.closeListener = null;
|
||||||
@ -851,7 +852,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
* Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush
|
* Close the remote endpoint with with a {@code GO_AWAY} frame. Does <strong>not</strong> flush
|
||||||
* immediately, this is the responsibility of the caller.
|
* immediately, this is the responsibility of the caller.
|
||||||
*/
|
*/
|
||||||
private ChannelFuture goAway(ChannelHandlerContext ctx, Http2Exception cause, ChannelPromise promise) {
|
private Future<Void> goAway(ChannelHandlerContext ctx, Http2Exception cause, Promise<Void> promise) {
|
||||||
long errorCode = cause != null ? cause.error().code() : NO_ERROR.code();
|
long errorCode = cause != null ? cause.error().code() : NO_ERROR.code();
|
||||||
int lastKnownStream;
|
int lastKnownStream;
|
||||||
if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) {
|
if (cause != null && cause.shutdownHint() == Http2Exception.ShutdownHint.HARD_SHUTDOWN) {
|
||||||
@ -866,16 +867,17 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), promise);
|
return goAway(ctx, lastKnownStream, errorCode, Http2CodecUtil.toByteBuf(ctx, cause), promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processRstStreamWriteResult(ChannelHandlerContext ctx, Http2Stream stream, ChannelFuture future) {
|
@SuppressWarnings("unchecked")
|
||||||
|
private void processRstStreamWriteResult(ChannelHandlerContext ctx, Http2Stream stream, Future<?> future) {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
closeStream(stream, future);
|
closeStream(stream, (Future<Void>) future);
|
||||||
} else {
|
} else {
|
||||||
// The connection will be closed and so no need to change the resetSent flag to false.
|
// The connection will be closed and so no need to change the resetSent flag to false.
|
||||||
onConnectionError(ctx, true, future.cause(), null);
|
onConnectionError(ctx, true, future.cause(), null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void closeConnectionOnError(ChannelHandlerContext ctx, ChannelFuture future) {
|
private void closeConnectionOnError(ChannelHandlerContext ctx, Future<?> future) {
|
||||||
if (!future.isSuccess()) {
|
if (!future.isSuccess()) {
|
||||||
onConnectionError(ctx, true, future.cause(), null);
|
onConnectionError(ctx, true, future.cause(), null);
|
||||||
}
|
}
|
||||||
@ -889,7 +891,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void processGoAwayWriteResult(final ChannelHandlerContext ctx, final int lastStreamId,
|
private static void processGoAwayWriteResult(final ChannelHandlerContext ctx, final int lastStreamId,
|
||||||
final long errorCode, final ByteBuf debugData, ChannelFuture future) {
|
final long errorCode, final ByteBuf debugData, Future<?> future) {
|
||||||
try {
|
try {
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
if (errorCode != NO_ERROR.code()) {
|
if (errorCode != NO_ERROR.code()) {
|
||||||
@ -917,19 +919,19 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
/**
|
/**
|
||||||
* Closes the channel when the future completes.
|
* Closes the channel when the future completes.
|
||||||
*/
|
*/
|
||||||
private static final class ClosingChannelFutureListener implements ChannelFutureListener {
|
private static final class ClosingChannelFutureListener implements FutureListener<Object> {
|
||||||
private final ChannelHandlerContext ctx;
|
private final ChannelHandlerContext ctx;
|
||||||
private final ChannelPromise promise;
|
private final Promise<Void> promise;
|
||||||
private final ScheduledFuture<?> timeoutTask;
|
private final ScheduledFuture<?> timeoutTask;
|
||||||
private boolean closed;
|
private boolean closed;
|
||||||
|
|
||||||
ClosingChannelFutureListener(ChannelHandlerContext ctx, ChannelPromise promise) {
|
ClosingChannelFutureListener(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.promise = promise;
|
this.promise = promise;
|
||||||
timeoutTask = null;
|
timeoutTask = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ClosingChannelFutureListener(final ChannelHandlerContext ctx, final ChannelPromise promise,
|
ClosingChannelFutureListener(final ChannelHandlerContext ctx, final Promise<Void> promise,
|
||||||
long timeout, TimeUnit unit) {
|
long timeout, TimeUnit unit) {
|
||||||
this.ctx = ctx;
|
this.ctx = ctx;
|
||||||
this.promise = promise;
|
this.promise = promise;
|
||||||
@ -937,7 +939,7 @@ public class Http2ConnectionHandler extends ByteToMessageDecoder implements Http
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(ChannelFuture sentGoAwayFuture) {
|
public void operationComplete(Future<?> sentGoAwayFuture) {
|
||||||
if (timeoutTask != null) {
|
if (timeoutTask != null) {
|
||||||
timeoutTask.cancel(false);
|
timeoutTask.cancel(false);
|
||||||
}
|
}
|
||||||
|
@ -14,10 +14,10 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.FutureListener;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.ObjectUtil;
|
import io.netty.util.internal.ObjectUtil;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
import io.netty.util.internal.logging.InternalLoggerFactory;
|
import io.netty.util.internal.logging.InternalLoggerFactory;
|
||||||
@ -31,14 +31,9 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
|
|||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ControlFrameLimitEncoder.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2ControlFrameLimitEncoder.class);
|
||||||
|
|
||||||
private final int maxOutstandingControlFrames;
|
private final int maxOutstandingControlFrames;
|
||||||
private final ChannelFutureListener outstandingControlFramesListener = new ChannelFutureListener() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(ChannelFuture future) {
|
|
||||||
outstandingControlFrames--;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
private Http2LifecycleManager lifecycleManager;
|
private Http2LifecycleManager lifecycleManager;
|
||||||
private int outstandingControlFrames;
|
private int outstandingControlFrames;
|
||||||
|
private final FutureListener<Void> outstandingControlFramesListener = future -> outstandingControlFrames--;
|
||||||
private boolean limitReached;
|
private boolean limitReached;
|
||||||
|
|
||||||
Http2ControlFrameLimitEncoder(Http2ConnectionEncoder delegate, int maxOutstandingControlFrames) {
|
Http2ControlFrameLimitEncoder(Http2ConnectionEncoder delegate, int maxOutstandingControlFrames) {
|
||||||
@ -54,8 +49,8 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise);
|
Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
|
||||||
if (newPromise == null) {
|
if (newPromise == null) {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
@ -63,10 +58,10 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) {
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data, Promise<Void> promise) {
|
||||||
// Only apply the limit to ping acks.
|
// Only apply the limit to ping acks.
|
||||||
if (ack) {
|
if (ack) {
|
||||||
ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise);
|
Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
|
||||||
if (newPromise == null) {
|
if (newPromise == null) {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
@ -76,16 +71,16 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeRstStream(
|
public Future<Void> writeRstStream(
|
||||||
ChannelHandlerContext ctx, int streamId, long errorCode, ChannelPromise promise) {
|
ChannelHandlerContext ctx, int streamId, long errorCode, Promise<Void> promise) {
|
||||||
ChannelPromise newPromise = handleOutstandingControlFrames(ctx, promise);
|
Promise<Void> newPromise = handleOutstandingControlFrames(ctx, promise);
|
||||||
if (newPromise == null) {
|
if (newPromise == null) {
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
return super.writeRstStream(ctx, streamId, errorCode, newPromise);
|
return super.writeRstStream(ctx, streamId, errorCode, newPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise handleOutstandingControlFrames(ChannelHandlerContext ctx, ChannelPromise promise) {
|
private Promise<Void> handleOutstandingControlFrames(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
if (!limitReached) {
|
if (!limitReached) {
|
||||||
if (outstandingControlFrames == maxOutstandingControlFrames) {
|
if (outstandingControlFrames == maxOutstandingControlFrames) {
|
||||||
// Let's try to flush once as we may be able to flush some of the control frames.
|
// Let's try to flush once as we may be able to flush some of the control frames.
|
||||||
@ -106,7 +101,7 @@ final class Http2ControlFrameLimitEncoder extends DecoratingHttp2ConnectionEncod
|
|||||||
|
|
||||||
// We did not reach the limit yet, add the listener to decrement the number of outstanding control frames
|
// We did not reach the limit yet, add the listener to decrement the number of outstanding control frames
|
||||||
// once the promise was completed
|
// once the promise was completed
|
||||||
return promise.addListener(outstandingControlFramesListener);
|
promise.addListener(outstandingControlFramesListener);
|
||||||
}
|
}
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -40,6 +40,6 @@ public interface Http2DataWriter {
|
|||||||
* @param promise the promise for the write.
|
* @param promise the promise for the write.
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writeData(ChannelHandlerContext ctx, int streamId,
|
Future<Void> writeData(ChannelHandlerContext ctx, int streamId,
|
||||||
ByteBuf data, int padding, boolean endStream, ChannelPromise promise);
|
ByteBuf data, int padding, boolean endStream, Promise<Void> promise);
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,8 @@ package io.netty.handler.codec.http2;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
||||||
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeEvent;
|
import io.netty.handler.codec.http.HttpServerUpgradeHandler.UpgradeEvent;
|
||||||
import io.netty.handler.codec.http2.Http2Connection.PropertyKey;
|
import io.netty.handler.codec.http2.Http2Connection.PropertyKey;
|
||||||
@ -32,6 +30,7 @@ import io.netty.util.ReferenceCounted;
|
|||||||
import io.netty.util.collection.IntObjectHashMap;
|
import io.netty.util.collection.IntObjectHashMap;
|
||||||
import io.netty.util.collection.IntObjectMap;
|
import io.netty.util.collection.IntObjectMap;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
import io.netty.util.internal.logging.InternalLogLevel;
|
import io.netty.util.internal.logging.InternalLogLevel;
|
||||||
import io.netty.util.internal.logging.InternalLogger;
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
@ -91,10 +90,10 @@ import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
|
|||||||
*
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* final Http2Stream2 stream = handler.newStream();
|
* final Http2Stream2 stream = handler.newStream();
|
||||||
* ctx.write(headersFrame.stream(stream)).addListener(new ChannelFutureListener() {
|
* ctx.write(headersFrame.stream(stream)).addListener(new FutureListener<Void>() {
|
||||||
*
|
*
|
||||||
* @Override
|
* @Override
|
||||||
* public void operationComplete(ChannelFuture f) {
|
* public void operationComplete(Future<Void> f) {
|
||||||
* if (f.isSuccess()) {
|
* if (f.isSuccess()) {
|
||||||
* // Stream is active and stream.id() returns a valid stream identifier.
|
* // Stream is active and stream.id() returns a valid stream identifier.
|
||||||
* System.out.println("New stream with id " + stream.id() + " created.");
|
* System.out.println("New stream with id " + stream.id() + " created.");
|
||||||
@ -112,7 +111,7 @@ import static io.netty.handler.codec.http2.Http2Error.NO_ERROR;
|
|||||||
* }
|
* }
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* <p>If a new stream cannot be created due to stream id exhaustion of the endpoint, the {@link ChannelPromise} of the
|
* <p>If a new stream cannot be created due to stream id exhaustion of the endpoint, the {@link Promise} of the
|
||||||
* HEADERS frame will fail with a {@link Http2NoMoreStreamIdsException}.
|
* HEADERS frame will fail with a {@link Http2NoMoreStreamIdsException}.
|
||||||
*
|
*
|
||||||
* <p>The HTTP/2 standard allows for an endpoint to limit the maximum number of concurrently active streams via the
|
* <p>The HTTP/2 standard allows for an endpoint to limit the maximum number of concurrently active streams via the
|
||||||
@ -193,7 +192,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
if (connection().numActiveStreams() > 0) {
|
if (connection().numActiveStreams() > 0) {
|
||||||
connection().forEachActiveStream(stream -> {
|
connection().forEachActiveStream(stream -> {
|
||||||
try {
|
try {
|
||||||
return streamVisitor.visit((Http2FrameStream) stream.getProperty(streamKey));
|
return streamVisitor.visit(stream.getProperty(streamKey));
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
onError(ctx, false, cause);
|
onError(ctx, false, cause);
|
||||||
return false;
|
return false;
|
||||||
@ -282,7 +281,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
* streams.
|
* streams.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
if (msg instanceof Http2DataFrame) {
|
if (msg instanceof Http2DataFrame) {
|
||||||
Http2DataFrame dataFrame = (Http2DataFrame) msg;
|
Http2DataFrame dataFrame = (Http2DataFrame) msg;
|
||||||
encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(),
|
encoder().writeData(ctx, dataFrame.stream().id(), dataFrame.content(),
|
||||||
@ -300,7 +299,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
} else {
|
} else {
|
||||||
consumeBytes(frameStream.id(), frame.windowSizeIncrement());
|
consumeBytes(frameStream.id(), frame.windowSizeIncrement());
|
||||||
}
|
}
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
promise.setFailure(t);
|
promise.setFailure(t);
|
||||||
}
|
}
|
||||||
@ -355,7 +354,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
Http2Stream stream = connection().stream(streamId);
|
Http2Stream stream = connection().stream(streamId);
|
||||||
// Upgraded requests are ineligible for stream control. We add the null check
|
// Upgraded requests are ineligible for stream control. We add the null check
|
||||||
// in case the stream has been deregistered.
|
// in case the stream has been deregistered.
|
||||||
if (stream != null && streamId == Http2CodecUtil.HTTP_UPGRADE_STREAM_ID) {
|
if (stream != null && streamId == HTTP_UPGRADE_STREAM_ID) {
|
||||||
Boolean upgraded = stream.getProperty(upgradeKey);
|
Boolean upgraded = stream.getProperty(upgradeKey);
|
||||||
if (Boolean.TRUE.equals(upgraded)) {
|
if (Boolean.TRUE.equals(upgraded)) {
|
||||||
return false;
|
return false;
|
||||||
@ -365,7 +364,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
return connection().local().flowController().consumeBytes(stream, bytes);
|
return connection().local().flowController().consumeBytes(stream, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame, ChannelPromise promise) {
|
private void writeGoAwayFrame(ChannelHandlerContext ctx, Http2GoAwayFrame frame, Promise<Void> promise) {
|
||||||
if (frame.lastStreamId() > -1) {
|
if (frame.lastStreamId() > -1) {
|
||||||
frame.release();
|
frame.release();
|
||||||
throw new IllegalArgumentException("Last stream id must not be set on GOAWAY frame");
|
throw new IllegalArgumentException("Last stream id must not be set on GOAWAY frame");
|
||||||
@ -381,7 +380,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame,
|
private void writeHeadersFrame(final ChannelHandlerContext ctx, Http2HeadersFrame headersFrame,
|
||||||
final ChannelPromise promise) {
|
final Promise<Void> promise) {
|
||||||
|
|
||||||
if (isStreamIdValid(headersFrame.stream().id())) {
|
if (isStreamIdValid(headersFrame.stream().id())) {
|
||||||
encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(),
|
encoder().writeHeaders(ctx, headersFrame.stream().id(), headersFrame.headers(), headersFrame.padding(),
|
||||||
@ -408,7 +407,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame,
|
private void writePushPromise(final ChannelHandlerContext ctx, Http2PushPromiseFrame pushPromiseFrame,
|
||||||
final ChannelPromise promise) {
|
final Promise<Void> promise) {
|
||||||
if (isStreamIdValid(pushPromiseFrame.pushStream().id())) {
|
if (isStreamIdValid(pushPromiseFrame.pushStream().id())) {
|
||||||
encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(),
|
encoder().writePushPromise(ctx, pushPromiseFrame.stream().id(), pushPromiseFrame.pushStream().id(),
|
||||||
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise);
|
pushPromiseFrame.http2Headers(), pushPromiseFrame.padding(), promise);
|
||||||
@ -423,7 +422,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
numBufferedStreams++;
|
numBufferedStreams++;
|
||||||
// Clean up the stream being initialized if writing the headers fails and also
|
// Clean up the stream being initialized if writing the headers fails and also
|
||||||
// decrement the number of buffered streams.
|
// decrement the number of buffered streams.
|
||||||
promise.addListener((ChannelFuture f) -> {
|
promise.addListener(f -> {
|
||||||
numBufferedStreams--;
|
numBufferedStreams--;
|
||||||
handleHeaderFuture(f, streamId);
|
handleHeaderFuture(f, streamId);
|
||||||
});
|
});
|
||||||
@ -432,7 +431,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream,
|
private boolean initializeNewStream(ChannelHandlerContext ctx, DefaultHttp2FrameStream http2FrameStream,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
final Http2Connection connection = connection();
|
final Http2Connection connection = connection();
|
||||||
final int streamId = connection.local().incrementAndGetNextStreamId();
|
final int streamId = connection.local().incrementAndGetNextStreamId();
|
||||||
if (streamId < 0) {
|
if (streamId < 0) {
|
||||||
@ -467,7 +466,7 @@ public class Http2FrameCodec extends Http2ConnectionHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onStreamActive0(Http2Stream stream) {
|
private void onStreamActive0(Http2Stream stream) {
|
||||||
if (stream.id() != Http2CodecUtil.HTTP_UPGRADE_STREAM_ID &&
|
if (stream.id() != HTTP_UPGRADE_STREAM_ID &&
|
||||||
connection().local().isValidStreamId(stream.id())) {
|
connection().local().isValidStreamId(stream.id())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
@ -64,8 +64,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* <p>
|
* <p>
|
||||||
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int padding, boolean endStream, ChannelPromise promise);
|
int padding, boolean endStream, Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a HEADERS frame with priority specified to the remote endpoint.
|
* Writes a HEADERS frame with priority specified to the remote endpoint.
|
||||||
@ -90,9 +90,9 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* <p>
|
* <p>
|
||||||
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream,
|
int streamDependency, short weight, boolean exclusive, int padding, boolean endStream,
|
||||||
ChannelPromise promise);
|
Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a PRIORITY frame to the remote endpoint.
|
* Writes a PRIORITY frame to the remote endpoint.
|
||||||
@ -106,8 +106,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param promise the promise for the write.
|
* @param promise the promise for the write.
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency,
|
Future<Void> writePriority(ChannelHandlerContext ctx, int streamId, int streamDependency,
|
||||||
short weight, boolean exclusive, ChannelPromise promise);
|
short weight, boolean exclusive, Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a RST_STREAM frame to the remote endpoint.
|
* Writes a RST_STREAM frame to the remote endpoint.
|
||||||
@ -118,8 +118,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param promise the promise for the write.
|
* @param promise the promise for the write.
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||||
ChannelPromise promise);
|
Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a SETTINGS frame to the remote endpoint.
|
* Writes a SETTINGS frame to the remote endpoint.
|
||||||
@ -129,8 +129,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param promise the promise for the write.
|
* @param promise the promise for the write.
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
Future<Void> writeSettings(ChannelHandlerContext ctx, Http2Settings settings,
|
||||||
ChannelPromise promise);
|
Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a SETTINGS acknowledgment to the remote endpoint.
|
* Writes a SETTINGS acknowledgment to the remote endpoint.
|
||||||
@ -139,7 +139,7 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param promise the promise for the write.
|
* @param promise the promise for the write.
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise);
|
Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a PING frame to the remote endpoint.
|
* Writes a PING frame to the remote endpoint.
|
||||||
@ -151,8 +151,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param promise the promise for the write.
|
* @param promise the promise for the write.
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack, long data,
|
Future<Void> writePing(ChannelHandlerContext ctx, boolean ack, long data,
|
||||||
ChannelPromise promise);
|
Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a PUSH_PROMISE frame to the remote endpoint.
|
* Writes a PUSH_PROMISE frame to the remote endpoint.
|
||||||
@ -173,8 +173,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* <p>
|
* <p>
|
||||||
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
* If this call has <strong>NOT</strong> modified the HPACK header state you are free to throw a stream error.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId, int promisedStreamId,
|
||||||
Http2Headers headers, int padding, ChannelPromise promise);
|
Http2Headers headers, int padding, Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a GO_AWAY frame to the remote endpoint.
|
* Writes a GO_AWAY frame to the remote endpoint.
|
||||||
@ -186,8 +186,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param promise the promise for the write.
|
* @param promise the promise for the write.
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||||
ByteBuf debugData, ChannelPromise promise);
|
ByteBuf debugData, Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes a WINDOW_UPDATE frame to the remote endpoint.
|
* Writes a WINDOW_UPDATE frame to the remote endpoint.
|
||||||
@ -199,8 +199,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param promise the promise for the write.
|
* @param promise the promise for the write.
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
|
Future<Void> writeWindowUpdate(ChannelHandlerContext ctx, int streamId,
|
||||||
int windowSizeIncrement, ChannelPromise promise);
|
int windowSizeIncrement, Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic write method for any HTTP/2 frame. This allows writing of non-standard frames.
|
* Generic write method for any HTTP/2 frame. This allows writing of non-standard frames.
|
||||||
@ -213,8 +213,8 @@ public interface Http2FrameWriter extends Http2DataWriter, Closeable {
|
|||||||
* @param promise the promise for the write.
|
* @param promise the promise for the write.
|
||||||
* @return the future for the write.
|
* @return the future for the write.
|
||||||
*/
|
*/
|
||||||
ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||||
Http2Flags flags, ByteBuf payload, ChannelPromise promise);
|
Http2Flags flags, ByteBuf payload, Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the configuration related elements for this {@link Http2FrameWriter}
|
* Get the configuration related elements for this {@link Http2FrameWriter}
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -29,19 +29,19 @@ public interface Http2LifecycleManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the local side of the {@code stream}. Depending on the {@code stream} state this may result in
|
* Closes the local side of the {@code stream}. Depending on the {@code stream} state this may result in
|
||||||
* {@code stream} being closed. See {@link #closeStream(Http2Stream, ChannelFuture)}.
|
* {@code stream} being closed. See {@link #closeStream(Http2Stream, Future)}.
|
||||||
* @param stream the stream to be half closed.
|
* @param stream the stream to be half closed.
|
||||||
* @param future See {@link #closeStream(Http2Stream, ChannelFuture)}.
|
* @param future See {@link #closeStream(Http2Stream, Future)}.
|
||||||
*/
|
*/
|
||||||
void closeStreamLocal(Http2Stream stream, ChannelFuture future);
|
void closeStreamLocal(Http2Stream stream, Future<Void> future);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes the remote side of the {@code stream}. Depending on the {@code stream} state this may result in
|
* Closes the remote side of the {@code stream}. Depending on the {@code stream} state this may result in
|
||||||
* {@code stream} being closed. See {@link #closeStream(Http2Stream, ChannelFuture)}.
|
* {@code stream} being closed. See {@link #closeStream(Http2Stream, Future)}.
|
||||||
* @param stream the stream to be half closed.
|
* @param stream the stream to be half closed.
|
||||||
* @param future See {@link #closeStream(Http2Stream, ChannelFuture)}.
|
* @param future See {@link #closeStream(Http2Stream, Future)}.
|
||||||
*/
|
*/
|
||||||
void closeStreamRemote(Http2Stream stream, ChannelFuture future);
|
void closeStreamRemote(Http2Stream stream, Future<Void> future);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Closes and deactivates the given {@code stream}. A listener is also attached to {@code future} and upon
|
* Closes and deactivates the given {@code stream}. A listener is also attached to {@code future} and upon
|
||||||
@ -50,7 +50,7 @@ public interface Http2LifecycleManager {
|
|||||||
* @param future when completed if {@link Http2Connection#numActiveStreams()} is 0 then the underlying channel
|
* @param future when completed if {@link Http2Connection#numActiveStreams()} is 0 then the underlying channel
|
||||||
* will be closed.
|
* will be closed.
|
||||||
*/
|
*/
|
||||||
void closeStream(Http2Stream stream, ChannelFuture future);
|
void closeStream(Http2Stream stream, Future<Void> future);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ensure the stream identified by {@code streamId} is reset. If our local state does not indicate the stream has
|
* Ensure the stream identified by {@code streamId} is reset. If our local state does not indicate the stream has
|
||||||
@ -64,8 +64,8 @@ public interface Http2LifecycleManager {
|
|||||||
* {@code RST_STREAM} frame has been sent to the peer. If the stream state has already been updated and a
|
* {@code RST_STREAM} frame has been sent to the peer. If the stream state has already been updated and a
|
||||||
* {@code RST_STREAM} frame has been sent then the return status may indicate success immediately.
|
* {@code RST_STREAM} frame has been sent then the return status may indicate success immediately.
|
||||||
*/
|
*/
|
||||||
ChannelFuture resetStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
Future<Void> resetStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||||
ChannelPromise promise);
|
Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prevents the peer from creating streams and close the connection if {@code errorCode} is not
|
* Prevents the peer from creating streams and close the connection if {@code errorCode} is not
|
||||||
@ -83,15 +83,15 @@ public interface Http2LifecycleManager {
|
|||||||
* {@code GO_AWAY} frame has been sent to the peer. If the stream state has already been updated and a
|
* {@code GO_AWAY} frame has been sent to the peer. If the stream state has already been updated and a
|
||||||
* {@code GO_AWAY} frame has been sent then the return status may indicate success immediately.
|
* {@code GO_AWAY} frame has been sent then the return status may indicate success immediately.
|
||||||
*/
|
*/
|
||||||
ChannelFuture goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
Future<Void> goAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||||
ByteBuf debugData, ChannelPromise promise);
|
ByteBuf debugData, Promise<Void> promise);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Processes the given error.
|
* Processes the given error.
|
||||||
*
|
*
|
||||||
* @param ctx The context used for communication and buffer allocation if necessary.
|
* @param ctx The context used for communication and buffer allocation if necessary.
|
||||||
* @param outbound {@code true} if the error was caused by an outbound operation and so the corresponding
|
* @param outbound {@code true} if the error was caused by an outbound operation and so the corresponding
|
||||||
* {@link ChannelPromise} was failed as well.
|
* {@link Promise} was failed as well.
|
||||||
* @param cause the error.
|
* @param cause the error.
|
||||||
*/
|
*/
|
||||||
void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause);
|
void onError(ChannelHandlerContext ctx, boolean outbound, Throwable cause);
|
||||||
|
@ -18,13 +18,12 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelConfig;
|
import io.netty.channel.ChannelConfig;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.EventLoop;
|
import io.netty.channel.EventLoop;
|
||||||
import io.netty.util.ReferenceCounted;
|
import io.netty.util.ReferenceCounted;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
@ -178,11 +177,11 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
|
|||||||
streamChannel = new Http2MultiplexCodecStreamChannel(stream, inboundStreamHandler);
|
streamChannel = new Http2MultiplexCodecStreamChannel(stream, inboundStreamHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelFuture future = streamChannel.register();
|
Future<Void> future = streamChannel.register();
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
Http2MultiplexHandler.registerDone(future);
|
Http2MultiplexHandler.registerDone(streamChannel, future);
|
||||||
} else {
|
} else {
|
||||||
future.addListener(Http2MultiplexHandler.CHILD_CHANNEL_REGISTRATION_LISTENER);
|
future.addListener(streamChannel, Http2MultiplexHandler.CHILD_CHANNEL_REGISTRATION_LISTENER);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CLOSED:
|
case CLOSED:
|
||||||
@ -312,8 +311,8 @@ public class Http2MultiplexCodec extends Http2FrameCodec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected ChannelFuture write0(ChannelHandlerContext ctx, Object msg) {
|
protected Future<Void> write0(ChannelHandlerContext ctx, Object msg) {
|
||||||
ChannelPromise promise = ctx.newPromise();
|
Promise<Void> promise = ctx.newPromise();
|
||||||
Http2MultiplexCodec.this.write(ctx, msg, promise);
|
Http2MultiplexCodec.this.write(ctx, msg, promise);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,6 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelConfig;
|
import io.netty.channel.ChannelConfig;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
@ -27,6 +25,8 @@ import io.netty.channel.EventLoop;
|
|||||||
import io.netty.channel.ServerChannel;
|
import io.netty.channel.ServerChannel;
|
||||||
import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream;
|
import io.netty.handler.codec.http2.Http2FrameCodec.DefaultHttp2FrameStream;
|
||||||
import io.netty.util.ReferenceCounted;
|
import io.netty.util.ReferenceCounted;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.FutureContextListener;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import javax.net.ssl.SSLException;
|
import javax.net.ssl.SSLException;
|
||||||
@ -86,7 +86,8 @@ import static io.netty.handler.codec.http2.Http2Exception.connectionError;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler {
|
public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler {
|
||||||
|
|
||||||
static final ChannelFutureListener CHILD_CHANNEL_REGISTRATION_LISTENER = Http2MultiplexHandler::registerDone;
|
static final FutureContextListener<Channel, Void> CHILD_CHANNEL_REGISTRATION_LISTENER =
|
||||||
|
Http2MultiplexHandler::registerDone;
|
||||||
|
|
||||||
private final ChannelHandler inboundStreamHandler;
|
private final ChannelHandler inboundStreamHandler;
|
||||||
private final ChannelHandler upgradeStreamHandler;
|
private final ChannelHandler upgradeStreamHandler;
|
||||||
@ -124,12 +125,11 @@ public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler {
|
|||||||
this.upgradeStreamHandler = upgradeStreamHandler;
|
this.upgradeStreamHandler = upgradeStreamHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registerDone(ChannelFuture future) {
|
static void registerDone(Channel childChannel, Future<?> future) {
|
||||||
// Handle any errors that occurred on the local thread while registering. Even though
|
// Handle any errors that occurred on the local thread while registering. Even though
|
||||||
// failures can happen after this point, they will be handled by the channel by closing the
|
// failures can happen after this point, they will be handled by the channel by closing the
|
||||||
// childChannel.
|
// childChannel.
|
||||||
if (!future.isSuccess()) {
|
if (!future.isSuccess()) {
|
||||||
Channel childChannel = future.channel();
|
|
||||||
if (childChannel.isRegistered()) {
|
if (childChannel.isRegistered()) {
|
||||||
childChannel.close();
|
childChannel.close();
|
||||||
} else {
|
} else {
|
||||||
@ -231,11 +231,11 @@ public final class Http2MultiplexHandler extends Http2ChannelDuplexHandler {
|
|||||||
} else {
|
} else {
|
||||||
ch = new Http2MultiplexHandlerStreamChannel(stream, inboundStreamHandler);
|
ch = new Http2MultiplexHandlerStreamChannel(stream, inboundStreamHandler);
|
||||||
}
|
}
|
||||||
ChannelFuture future = ch.register();
|
Future<Void> future = ch.register();
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
registerDone(future);
|
registerDone(ch, future);
|
||||||
} else {
|
} else {
|
||||||
future.addListener(CHILD_CHANNEL_REGISTRATION_LISTENER);
|
future.addListener(ch, CHILD_CHANNEL_REGISTRATION_LISTENER);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CLOSED:
|
case CLOSED:
|
||||||
|
@ -15,15 +15,15 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http2.Http2FrameLogger.Direction.OUTBOUND;
|
import static io.netty.handler.codec.http2.Http2FrameLogger.Direction.OUTBOUND;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.util.internal.UnstableApi;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorator around a {@link Http2FrameWriter} that logs all outbound frames before calling the
|
* Decorator around a {@link Http2FrameWriter} that logs all outbound frames before calling the
|
||||||
* writer.
|
* writer.
|
||||||
@ -39,23 +39,23 @@ public class Http2OutboundFrameLogger implements Http2FrameWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
||||||
int padding, boolean endStream, ChannelPromise promise) {
|
int padding, boolean endStream, Promise<Void> promise) {
|
||||||
logger.logData(OUTBOUND, ctx, streamId, data, padding, endStream);
|
logger.logData(OUTBOUND, ctx, streamId, data, padding, endStream);
|
||||||
return writer.writeData(ctx, streamId, data, padding, endStream, promise);
|
return writer.writeData(ctx, streamId, data, padding, endStream, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||||
Http2Headers headers, int padding, boolean endStream, ChannelPromise promise) {
|
Http2Headers headers, int padding, boolean endStream, Promise<Void> promise) {
|
||||||
logger.logHeaders(OUTBOUND, ctx, streamId, headers, padding, endStream);
|
logger.logHeaders(OUTBOUND, ctx, streamId, headers, padding, endStream);
|
||||||
return writer.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
return writer.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId,
|
||||||
Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
||||||
int padding, boolean endStream, ChannelPromise promise) {
|
int padding, boolean endStream, Promise<Void> promise) {
|
||||||
logger.logHeaders(OUTBOUND, ctx, streamId, headers, streamDependency, weight, exclusive,
|
logger.logHeaders(OUTBOUND, ctx, streamId, headers, streamDependency, weight, exclusive,
|
||||||
padding, endStream);
|
padding, endStream);
|
||||||
return writer.writeHeaders(ctx, streamId, headers, streamDependency, weight,
|
return writer.writeHeaders(ctx, streamId, headers, streamDependency, weight,
|
||||||
@ -63,35 +63,35 @@ public class Http2OutboundFrameLogger implements Http2FrameWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePriority(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writePriority(ChannelHandlerContext ctx, int streamId,
|
||||||
int streamDependency, short weight, boolean exclusive, ChannelPromise promise) {
|
int streamDependency, short weight, boolean exclusive, Promise<Void> promise) {
|
||||||
logger.logPriority(OUTBOUND, ctx, streamId, streamDependency, weight, exclusive);
|
logger.logPriority(OUTBOUND, ctx, streamId, streamDependency, weight, exclusive);
|
||||||
return writer.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
return writer.writePriority(ctx, streamId, streamDependency, weight, exclusive, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeRstStream(ChannelHandlerContext ctx,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx,
|
||||||
int streamId, long errorCode, ChannelPromise promise) {
|
int streamId, long errorCode, Promise<Void> promise) {
|
||||||
logger.logRstStream(OUTBOUND, ctx, streamId, errorCode);
|
logger.logRstStream(OUTBOUND, ctx, streamId, errorCode);
|
||||||
return writer.writeRstStream(ctx, streamId, errorCode, promise);
|
return writer.writeRstStream(ctx, streamId, errorCode, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeSettings(ChannelHandlerContext ctx,
|
public Future<Void> writeSettings(ChannelHandlerContext ctx,
|
||||||
Http2Settings settings, ChannelPromise promise) {
|
Http2Settings settings, Promise<Void> promise) {
|
||||||
logger.logSettings(OUTBOUND, ctx, settings);
|
logger.logSettings(OUTBOUND, ctx, settings);
|
||||||
return writer.writeSettings(ctx, settings, promise);
|
return writer.writeSettings(ctx, settings, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeSettingsAck(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public Future<Void> writeSettingsAck(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
logger.logSettingsAck(OUTBOUND, ctx);
|
logger.logSettingsAck(OUTBOUND, ctx);
|
||||||
return writer.writeSettingsAck(ctx, promise);
|
return writer.writeSettingsAck(ctx, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePing(ChannelHandlerContext ctx, boolean ack,
|
public Future<Void> writePing(ChannelHandlerContext ctx, boolean ack,
|
||||||
long data, ChannelPromise promise) {
|
long data, Promise<Void> promise) {
|
||||||
if (ack) {
|
if (ack) {
|
||||||
logger.logPingAck(OUTBOUND, ctx, data);
|
logger.logPingAck(OUTBOUND, ctx, data);
|
||||||
} else {
|
} else {
|
||||||
@ -101,29 +101,30 @@ public class Http2OutboundFrameLogger implements Http2FrameWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writePushPromise(ChannelHandlerContext ctx, int streamId,
|
public Future<Void> writePushPromise(ChannelHandlerContext ctx, int streamId,
|
||||||
int promisedStreamId, Http2Headers headers, int padding, ChannelPromise promise) {
|
int promisedStreamId, Http2Headers headers, int padding,
|
||||||
|
Promise<Void> promise) {
|
||||||
logger.logPushPromise(OUTBOUND, ctx, streamId, promisedStreamId, headers, padding);
|
logger.logPushPromise(OUTBOUND, ctx, streamId, promisedStreamId, headers, padding);
|
||||||
return writer.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise);
|
return writer.writePushPromise(ctx, streamId, promisedStreamId, headers, padding, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
public Future<Void> writeGoAway(ChannelHandlerContext ctx, int lastStreamId, long errorCode,
|
||||||
ByteBuf debugData, ChannelPromise promise) {
|
ByteBuf debugData, Promise<Void> promise) {
|
||||||
logger.logGoAway(OUTBOUND, ctx, lastStreamId, errorCode, debugData);
|
logger.logGoAway(OUTBOUND, ctx, lastStreamId, errorCode, debugData);
|
||||||
return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
return writer.writeGoAway(ctx, lastStreamId, errorCode, debugData, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeWindowUpdate(ChannelHandlerContext ctx,
|
public Future<Void> writeWindowUpdate(ChannelHandlerContext ctx,
|
||||||
int streamId, int windowSizeIncrement, ChannelPromise promise) {
|
int streamId, int windowSizeIncrement, Promise<Void> promise) {
|
||||||
logger.logWindowsUpdate(OUTBOUND, ctx, streamId, windowSizeIncrement);
|
logger.logWindowsUpdate(OUTBOUND, ctx, streamId, windowSizeIncrement);
|
||||||
return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
|
return writer.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
public Future<Void> writeFrame(ChannelHandlerContext ctx, byte frameType, int streamId,
|
||||||
Http2Flags flags, ByteBuf payload, ChannelPromise promise) {
|
Http2Flags flags, ByteBuf payload, Promise<Void> promise) {
|
||||||
logger.logUnknownFrame(OUTBOUND, ctx, frameType, streamId, flags, payload);
|
logger.logUnknownFrame(OUTBOUND, ctx, frameType, streamId, flags, payload);
|
||||||
return writer.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
return writer.writeFrame(ctx, frameType, streamId, flags, payload, promise);
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelOption;
|
import io.netty.channel.ChannelOption;
|
||||||
@ -38,6 +34,8 @@ import java.util.LinkedHashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class Http2StreamChannelBootstrap {
|
public final class Http2StreamChannelBootstrap {
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2StreamChannelBootstrap.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(Http2StreamChannelBootstrap.class);
|
||||||
@ -186,8 +184,8 @@ public final class Http2StreamChannelBootstrap {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelFuture future = streamChannel.register();
|
Future<Void> future = streamChannel.register();
|
||||||
future.addListener((ChannelFutureListener) future1 -> {
|
future.addListener(future1 -> {
|
||||||
if (future1.isSuccess()) {
|
if (future1.isSuccess()) {
|
||||||
promise.setSuccess(streamChannel);
|
promise.setSuccess(streamChannel);
|
||||||
} else if (future1.isCancelled()) {
|
} else if (future1.isCancelled()) {
|
||||||
|
@ -17,7 +17,6 @@ package io.netty.handler.codec.http2;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.EmptyHttpHeaders;
|
import io.netty.handler.codec.http.EmptyHttpHeaders;
|
||||||
import io.netty.handler.codec.http.FullHttpMessage;
|
import io.netty.handler.codec.http.FullHttpMessage;
|
||||||
import io.netty.handler.codec.http.HttpContent;
|
import io.netty.handler.codec.http.HttpContent;
|
||||||
@ -27,6 +26,7 @@ import io.netty.handler.codec.http.HttpScheme;
|
|||||||
import io.netty.handler.codec.http.LastHttpContent;
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
|
import io.netty.handler.codec.http2.Http2CodecUtil.SimpleChannelPromiseAggregator;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,7 +77,7 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
|
|||||||
* Handles conversion of {@link HttpMessage} and {@link HttpContent} to HTTP/2 frames.
|
* Handles conversion of {@link HttpMessage} and {@link HttpContent} to HTTP/2 frames.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
|
|
||||||
if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) {
|
if (!(msg instanceof HttpMessage || msg instanceof HttpContent)) {
|
||||||
ctx.write(msg, promise);
|
ctx.write(msg, promise);
|
||||||
@ -86,7 +86,7 @@ public class HttpToHttp2ConnectionHandler extends Http2ConnectionHandler {
|
|||||||
|
|
||||||
boolean release = true;
|
boolean release = true;
|
||||||
SimpleChannelPromiseAggregator promiseAggregator =
|
SimpleChannelPromiseAggregator promiseAggregator =
|
||||||
new SimpleChannelPromiseAggregator(promise, ctx.channel(), ctx.executor());
|
new SimpleChannelPromiseAggregator(promise, ctx.executor());
|
||||||
try {
|
try {
|
||||||
Http2ConnectionEncoder encoder = encoder();
|
Http2ConnectionEncoder encoder = encoder();
|
||||||
boolean endStream = false;
|
boolean endStream = false;
|
||||||
|
@ -17,10 +17,10 @@ package io.netty.handler.codec.http2;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
@ -153,16 +153,16 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int padding, boolean endStream, ChannelPromise promise) {
|
int padding, boolean endStream, Promise<Void> promise) {
|
||||||
return writeHeaders(ctx, streamId, headers, 0, Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT,
|
return writeHeaders(ctx, streamId, headers, 0, Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT,
|
||||||
false, padding, endStream, promise);
|
false, padding, endStream, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
public Future<Void> writeHeaders(ChannelHandlerContext ctx, int streamId, Http2Headers headers,
|
||||||
int streamDependency, short weight, boolean exclusive,
|
int streamDependency, short weight, boolean exclusive,
|
||||||
int padding, boolean endOfStream, ChannelPromise promise) {
|
int padding, boolean endOfStream, Promise<Void> promise) {
|
||||||
if (closed) {
|
if (closed) {
|
||||||
return promise.setFailure(new Http2ChannelClosedException());
|
return promise.setFailure(new Http2ChannelClosedException());
|
||||||
}
|
}
|
||||||
@ -184,8 +184,8 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
public Future<Void> writeRstStream(ChannelHandlerContext ctx, int streamId, long errorCode,
|
||||||
ChannelPromise promise) {
|
Promise<Void> promise) {
|
||||||
if (isExistingStream(streamId)) {
|
if (isExistingStream(streamId)) {
|
||||||
return super.writeRstStream(ctx, streamId, errorCode, promise);
|
return super.writeRstStream(ctx, streamId, errorCode, promise);
|
||||||
}
|
}
|
||||||
@ -198,7 +198,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
// about the stream anymore and thus there is not point in failing the promises and invoking
|
// about the stream anymore and thus there is not point in failing the promises and invoking
|
||||||
// error handling routines.
|
// error handling routines.
|
||||||
stream.close(null);
|
stream.close(null);
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
promise.setFailure(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
|
promise.setFailure(connectionError(PROTOCOL_ERROR, "Stream does not exist %d", streamId));
|
||||||
}
|
}
|
||||||
@ -206,8 +206,8 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
public Future<Void> writeData(ChannelHandlerContext ctx, int streamId, ByteBuf data,
|
||||||
int padding, boolean endOfStream, ChannelPromise promise) {
|
int padding, boolean endOfStream, Promise<Void> promise) {
|
||||||
if (isExistingStream(streamId)) {
|
if (isExistingStream(streamId)) {
|
||||||
return super.writeData(ctx, streamId, data, padding, endOfStream, promise);
|
return super.writeData(ctx, streamId, data, padding, endOfStream, promise);
|
||||||
}
|
}
|
||||||
@ -311,9 +311,9 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private abstract static class Frame {
|
private abstract static class Frame {
|
||||||
final ChannelPromise promise;
|
final Promise<Void> promise;
|
||||||
|
|
||||||
Frame(ChannelPromise promise) {
|
Frame(Promise<Void> promise) {
|
||||||
this.promise = promise;
|
this.promise = promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -322,7 +322,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
*/
|
*/
|
||||||
void release(Throwable t) {
|
void release(Throwable t) {
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
promise.setFailure(t);
|
promise.setFailure(t);
|
||||||
}
|
}
|
||||||
@ -340,7 +340,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
final boolean endOfStream;
|
final boolean endOfStream;
|
||||||
|
|
||||||
HeadersFrame(Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
HeadersFrame(Http2Headers headers, int streamDependency, short weight, boolean exclusive,
|
||||||
int padding, boolean endOfStream, ChannelPromise promise) {
|
int padding, boolean endOfStream, Promise<Void> promise) {
|
||||||
super(promise);
|
super(promise);
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
this.streamDependency = streamDependency;
|
this.streamDependency = streamDependency;
|
||||||
@ -361,7 +361,7 @@ public class StreamBufferingEncoder extends DecoratingHttp2ConnectionEncoder {
|
|||||||
final int padding;
|
final int padding;
|
||||||
final boolean endOfStream;
|
final boolean endOfStream;
|
||||||
|
|
||||||
DataFrame(ByteBuf data, int padding, boolean endOfStream, ChannelPromise promise) {
|
DataFrame(ByteBuf data, int padding, boolean endOfStream, Promise<Void> promise) {
|
||||||
super(promise);
|
super(promise);
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.padding = padding;
|
this.padding = padding;
|
||||||
|
@ -23,7 +23,6 @@ import io.netty.channel.ChannelHandler;
|
|||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.MultithreadEventLoopGroup;
|
import io.netty.channel.MultithreadEventLoopGroup;
|
||||||
import io.netty.channel.nio.NioHandler;
|
import io.netty.channel.nio.NioHandler;
|
||||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||||
@ -34,6 +33,7 @@ import io.netty.util.AsciiString;
|
|||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.NetUtil;
|
import io.netty.util.NetUtil;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -432,7 +432,7 @@ public class DataCompressionHttp2Test {
|
|||||||
return clientChannel.pipeline().firstContext();
|
return clientChannel.pipeline().firstContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise newPromiseClient() {
|
private Promise<Void> newPromiseClient() {
|
||||||
return ctxClient().newPromise();
|
return ctxClient().newPromise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,13 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.DefaultChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
|
import io.netty.util.concurrent.DefaultPromise;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import junit.framework.AssertionFailedError;
|
import junit.framework.AssertionFailedError;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -35,7 +35,6 @@ import org.mockito.MockitoAnnotations;
|
|||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
@ -48,11 +47,9 @@ import static io.netty.handler.codec.http2.Http2Stream.State.IDLE;
|
|||||||
import static io.netty.handler.codec.http2.Http2Stream.State.OPEN;
|
import static io.netty.handler.codec.http2.Http2Stream.State.OPEN;
|
||||||
import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_REMOTE;
|
import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_REMOTE;
|
||||||
import static io.netty.util.CharsetUtil.UTF_8;
|
import static io.netty.util.CharsetUtil.UTF_8;
|
||||||
|
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.instanceOf;
|
import static org.hamcrest.Matchers.instanceOf;
|
||||||
import static org.hamcrest.Matchers.not;
|
import static org.hamcrest.Matchers.not;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
@ -82,7 +79,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
|||||||
private static final int STATE_RECV_TRAILERS = 1 << 1;
|
private static final int STATE_RECV_TRAILERS = 1 << 1;
|
||||||
|
|
||||||
private Http2ConnectionDecoder decoder;
|
private Http2ConnectionDecoder decoder;
|
||||||
private ChannelPromise promise;
|
private Promise<Void> promise;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Http2Connection connection;
|
private Http2Connection connection;
|
||||||
@ -106,7 +103,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
|||||||
private Channel channel;
|
private Channel channel;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ChannelFuture future;
|
private Future<Void> future;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Http2Stream stream;
|
private Http2Stream stream;
|
||||||
@ -133,7 +130,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
|||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
|
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
|
|
||||||
final AtomicInteger headersReceivedState = new AtomicInteger();
|
final AtomicInteger headersReceivedState = new AtomicInteger();
|
||||||
when(channel.isActive()).thenReturn(true);
|
when(channel.isActive()).thenReturn(true);
|
||||||
@ -623,6 +620,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
|||||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true));
|
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0), eq(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
@Test
|
@Test
|
||||||
public void headersDependencyNotCreatedShouldCreateAndSucceed() throws Exception {
|
public void headersDependencyNotCreatedShouldCreateAndSucceed() throws Exception {
|
||||||
final short weight = 1;
|
final short weight = 1;
|
||||||
@ -631,7 +629,7 @@ public class DefaultHttp2ConnectionDecoderTest {
|
|||||||
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(STREAM_DEPENDENCY_ID),
|
verify(listener).onHeadersRead(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(STREAM_DEPENDENCY_ID),
|
||||||
eq(weight), eq(true), eq(0), eq(true));
|
eq(weight), eq(true), eq(0), eq(true));
|
||||||
verify(remoteFlow).updateDependencyTree(eq(STREAM_ID), eq(STREAM_DEPENDENCY_ID), eq(weight), eq(true));
|
verify(remoteFlow).updateDependencyTree(eq(STREAM_ID), eq(STREAM_DEPENDENCY_ID), eq(weight), eq(true));
|
||||||
verify(lifecycleManager).closeStreamRemote(eq(stream), any(ChannelFuture.class));
|
verify(lifecycleManager).closeStreamRemote(eq(stream), any(Future.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -20,16 +20,16 @@ import io.netty.buffer.Unpooled;
|
|||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelConfig;
|
import io.netty.channel.ChannelConfig;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelMetadata;
|
import io.netty.channel.ChannelMetadata;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.DefaultChannelConfig;
|
import io.netty.channel.DefaultChannelConfig;
|
||||||
import io.netty.channel.DefaultChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http2.Http2RemoteFlowController.FlowControlled;
|
import io.netty.handler.codec.http2.Http2RemoteFlowController.FlowControlled;
|
||||||
|
import io.netty.util.concurrent.DefaultPromise;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import junit.framework.AssertionFailedError;
|
import junit.framework.AssertionFailedError;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -49,8 +49,8 @@ import static io.netty.handler.codec.http2.Http2Error.PROTOCOL_ERROR;
|
|||||||
import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE;
|
import static io.netty.handler.codec.http2.Http2Stream.State.HALF_CLOSED_REMOTE;
|
||||||
import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_LOCAL;
|
import static io.netty.handler.codec.http2.Http2Stream.State.RESERVED_LOCAL;
|
||||||
import static io.netty.util.CharsetUtil.UTF_8;
|
import static io.netty.util.CharsetUtil.UTF_8;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
@ -76,6 +76,7 @@ import static org.mockito.Mockito.when;
|
|||||||
/**
|
/**
|
||||||
* Tests for {@link DefaultHttp2ConnectionEncoder}
|
* Tests for {@link DefaultHttp2ConnectionEncoder}
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class DefaultHttp2ConnectionEncoderTest {
|
public class DefaultHttp2ConnectionEncoderTest {
|
||||||
private static final int STREAM_ID = 2;
|
private static final int STREAM_ID = 2;
|
||||||
private static final int PUSH_STREAM_ID = 4;
|
private static final int PUSH_STREAM_ID = 4;
|
||||||
@ -131,19 +132,19 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
when(writer.configuration()).thenReturn(writerConfig);
|
when(writer.configuration()).thenReturn(writerConfig);
|
||||||
when(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy);
|
when(writerConfig.frameSizePolicy()).thenReturn(frameSizePolicy);
|
||||||
when(frameSizePolicy.maxFrameSize()).thenReturn(64);
|
when(frameSizePolicy.maxFrameSize()).thenReturn(64);
|
||||||
doAnswer((Answer<ChannelFuture>) in -> ((ChannelPromise) in.getArguments()[2])
|
doAnswer((Answer<Future<Void>>) in -> ((Promise<Void>) in.getArguments()[2])
|
||||||
.setSuccess()).when(writer).writeSettings(eq(ctx), any(Http2Settings.class), any(ChannelPromise.class));
|
.setSuccess(null)).when(writer).writeSettings(eq(ctx), any(Http2Settings.class), any(Promise.class));
|
||||||
doAnswer((Answer<ChannelFuture>) in -> {
|
doAnswer((Answer<Future<Void>>) in -> {
|
||||||
((ByteBuf) in.getArguments()[3]).release();
|
((ByteBuf) in.getArguments()[3]).release();
|
||||||
return ((ChannelPromise) in.getArguments()[4]).setSuccess();
|
return ((Promise<Void>) in.getArguments()[4]).setSuccess(null);
|
||||||
}).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class), any(ChannelPromise.class));
|
}).when(writer).writeGoAway(eq(ctx), anyInt(), anyInt(), any(ByteBuf.class), any(Promise.class));
|
||||||
writtenData = new ArrayList<>();
|
writtenData = new ArrayList<>();
|
||||||
writtenPadding = new ArrayList<>();
|
writtenPadding = new ArrayList<>();
|
||||||
when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
|
when(writer.writeData(eq(ctx), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
|
||||||
any(ChannelPromise.class))).then((Answer<ChannelFuture>) in -> {
|
any(Promise.class))).then((Answer<Future<Void>>) in -> {
|
||||||
// Make sure we only receive stream closure on the last frame and that void promises
|
// Make sure we only receive stream closure on the last frame and that void promises
|
||||||
// are used for all writes except the last one.
|
// are used for all writes except the last one.
|
||||||
ChannelPromise promise = (ChannelPromise) in.getArguments()[5];
|
Promise<Void> promise = (Promise<Void>) in.getArguments()[5];
|
||||||
if (streamClosed) {
|
if (streamClosed) {
|
||||||
fail("Stream already closed");
|
fail("Stream already closed");
|
||||||
} else {
|
} else {
|
||||||
@ -155,36 +156,36 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
// Release the buffer just as DefaultHttp2FrameWriter does
|
// Release the buffer just as DefaultHttp2FrameWriter does
|
||||||
data.release();
|
data.release();
|
||||||
// Let the promise succeed to trigger listeners.
|
// Let the promise succeed to trigger listeners.
|
||||||
return promise.setSuccess();
|
return promise.setSuccess(null);
|
||||||
});
|
});
|
||||||
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(),
|
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(),
|
||||||
anyInt(), anyBoolean(), any(ChannelPromise.class)))
|
anyInt(), anyBoolean(), any(Promise.class)))
|
||||||
.then((Answer<ChannelFuture>) invocationOnMock -> {
|
.then((Answer<Future<Void>>) invocationOnMock -> {
|
||||||
ChannelPromise promise = invocationOnMock.getArgument(8);
|
Promise<Void> promise = invocationOnMock.getArgument(8);
|
||||||
if (streamClosed) {
|
if (streamClosed) {
|
||||||
fail("Stream already closed");
|
fail("Stream already closed");
|
||||||
} else {
|
} else {
|
||||||
streamClosed = (Boolean) invocationOnMock.getArguments()[5];
|
streamClosed = (Boolean) invocationOnMock.getArguments()[5];
|
||||||
}
|
}
|
||||||
return promise.setSuccess();
|
return promise.setSuccess(null);
|
||||||
});
|
});
|
||||||
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class),
|
when(writer.writeHeaders(eq(ctx), anyInt(), any(Http2Headers.class),
|
||||||
anyInt(), anyBoolean(), any(ChannelPromise.class)))
|
anyInt(), anyBoolean(), any(Promise.class)))
|
||||||
.then((Answer<ChannelFuture>) invocationOnMock -> {
|
.then((Answer<Future<Void>>) invocationOnMock -> {
|
||||||
ChannelPromise promise = invocationOnMock.getArgument(5);
|
Promise<Void> promise = invocationOnMock.getArgument(5);
|
||||||
if (streamClosed) {
|
if (streamClosed) {
|
||||||
fail("Stream already closed");
|
fail("Stream already closed");
|
||||||
} else {
|
} else {
|
||||||
streamClosed = invocationOnMock.getArgument(4);
|
streamClosed = invocationOnMock.getArgument(4);
|
||||||
}
|
}
|
||||||
return promise.setSuccess();
|
return promise.setSuccess(null);
|
||||||
});
|
});
|
||||||
payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class);
|
payloadCaptor = ArgumentCaptor.forClass(Http2RemoteFlowController.FlowControlled.class);
|
||||||
doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture());
|
doNothing().when(remoteFlow).addFlowControlled(any(Http2Stream.class), payloadCaptor.capture());
|
||||||
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(ctx.channel()).thenReturn(channel);
|
when(ctx.channel()).thenReturn(channel);
|
||||||
doAnswer((Answer<ChannelPromise>) in -> newPromise()).when(ctx).newPromise();
|
doAnswer((Answer<Promise<Void>>) in -> newPromise()).when(ctx).newPromise();
|
||||||
doAnswer((Answer<ChannelFuture>) in -> newSucceededFuture()).when(ctx).newSucceededFuture();
|
doAnswer((Answer<Future<Void>>) in -> newSucceededFuture()).when(ctx).newSucceededFuture();
|
||||||
when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden"));
|
when(ctx.flush()).thenThrow(new AssertionFailedError("forbidden"));
|
||||||
when(channel.alloc()).thenReturn(PooledByteBufAllocator.DEFAULT);
|
when(channel.alloc()).thenReturn(PooledByteBufAllocator.DEFAULT);
|
||||||
|
|
||||||
@ -209,7 +210,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
private void dataWriteShouldSignalThatFrameWasConsumedOnError0(boolean endOfStream) throws Exception {
|
private void dataWriteShouldSignalThatFrameWasConsumedOnError0(boolean endOfStream) throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
final ByteBuf data = dummyData();
|
final ByteBuf data = dummyData();
|
||||||
ChannelPromise p = newPromise();
|
Promise<Void> p = newPromise();
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream, p);
|
encoder.writeData(ctx, STREAM_ID, data, 0, endOfStream, p);
|
||||||
|
|
||||||
FlowControlled controlled = payloadCaptor.getValue();
|
FlowControlled controlled = payloadCaptor.getValue();
|
||||||
@ -230,7 +231,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void dataWriteShouldSucceed() throws Exception {
|
public void dataWriteShouldSucceed() throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
final ByteBuf data = dummyData();
|
final ByteBuf data = dummyData();
|
||||||
ChannelPromise p = newPromise();
|
Promise<Void> p = newPromise();
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, true, p);
|
encoder.writeData(ctx, STREAM_ID, data, 0, true, p);
|
||||||
assertEquals(8, payloadCaptor.getValue().size());
|
assertEquals(8, payloadCaptor.getValue().size());
|
||||||
payloadCaptor.getValue().write(ctx, 8);
|
payloadCaptor.getValue().write(ctx, 8);
|
||||||
@ -245,9 +246,9 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
final ByteBuf data = dummyData().retain();
|
final ByteBuf data = dummyData().retain();
|
||||||
|
|
||||||
ChannelPromise promise1 = newPromise();
|
Promise<Void> promise1 = newPromise();
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise1);
|
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise1);
|
||||||
ChannelPromise promise2 = newPromise();
|
Promise<Void> promise2 = newPromise();
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise2);
|
encoder.writeData(ctx, STREAM_ID, data, 0, true, promise2);
|
||||||
|
|
||||||
// Now merge the two payloads.
|
// Now merge the two payloads.
|
||||||
@ -288,7 +289,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
private void assertSplitPaddingOnEmptyBuffer(ByteBuf data) throws Exception {
|
private void assertSplitPaddingOnEmptyBuffer(ByteBuf data) throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
when(frameSizePolicy.maxFrameSize()).thenReturn(5);
|
when(frameSizePolicy.maxFrameSize()).thenReturn(5);
|
||||||
ChannelPromise p = newPromise();
|
Promise<Void> p = newPromise();
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 10, true, p);
|
encoder.writeData(ctx, STREAM_ID, data, 10, true, p);
|
||||||
assertEquals(10, payloadCaptor.getValue().size());
|
assertEquals(10, payloadCaptor.getValue().size());
|
||||||
payloadCaptor.getValue().write(ctx, 10);
|
payloadCaptor.getValue().write(ctx, 10);
|
||||||
@ -304,7 +305,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void headersWriteForUnknownStreamShouldCreateStream() throws Exception {
|
public void headersWriteForUnknownStreamShouldCreateStream() throws Exception {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
|
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(0),
|
||||||
eq(false), eq(promise));
|
eq(false), eq(promise));
|
||||||
@ -317,7 +318,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
Http2Stream parent = createStream(STREAM_ID, false);
|
Http2Stream parent = createStream(STREAM_ID, false);
|
||||||
reservePushStream(PUSH_STREAM_ID, parent);
|
reservePushStream(PUSH_STREAM_ID, parent);
|
||||||
|
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||||
assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state());
|
assertEquals(HALF_CLOSED_REMOTE, stream(PUSH_STREAM_ID).state());
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer).writeHeaders(eq(ctx), eq(PUSH_STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
@ -328,11 +329,11 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void trailersDoNotEndStreamThrows() {
|
public void trailersDoNotEndStreamThrows() {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||||
|
|
||||||
ChannelPromise promise2 = newPromise();
|
Promise<Void> promise2 = newPromise();
|
||||||
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
|
|
||||||
@ -344,14 +345,14 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void trailersDoNotEndStreamWithDataThrows() {
|
public void trailersDoNotEndStreamWithDataThrows() {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||||
|
|
||||||
Http2Stream stream = connection.stream(streamId);
|
Http2Stream stream = connection.stream(streamId);
|
||||||
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
||||||
|
|
||||||
ChannelPromise promise2 = newPromise();
|
Promise<Void> promise2 = newPromise();
|
||||||
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
|
|
||||||
@ -372,13 +373,13 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
private void tooManyHeadersThrows(boolean eos) {
|
private void tooManyHeadersThrows(boolean eos) {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||||
ChannelPromise promise2 = newPromise();
|
Promise<Void> promise2 = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
|
||||||
|
|
||||||
ChannelPromise promise3 = newPromise();
|
Promise<Void> promise3 = newPromise();
|
||||||
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
|
|
||||||
@ -415,16 +416,16 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
for (int i = 0; i < infoHeaderCount; ++i) {
|
for (int i = 0; i < infoHeaderCount; ++i) {
|
||||||
encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false, newPromise());
|
encoder.writeHeaders(ctx, streamId, infoHeaders, 0, false, newPromise());
|
||||||
}
|
}
|
||||||
ChannelPromise promise2 = newPromise();
|
Promise<Void> promise2 = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
||||||
|
|
||||||
ChannelPromise promise3 = newPromise();
|
Promise<Void> promise3 = newPromise();
|
||||||
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertEquals(eos, future.isSuccess());
|
assertEquals(eos, future.isSuccess());
|
||||||
|
|
||||||
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
|
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
|
||||||
eq(0), eq(false), any(ChannelPromise.class));
|
eq(0), eq(false), any(Promise.class));
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise2));
|
eq(0), eq(false), eq(promise2));
|
||||||
if (eos) {
|
if (eos) {
|
||||||
@ -452,17 +453,17 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
private void tooManyHeadersWithDataThrows(boolean eos) {
|
private void tooManyHeadersWithDataThrows(boolean eos) {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||||
|
|
||||||
Http2Stream stream = connection.stream(streamId);
|
Http2Stream stream = connection.stream(streamId);
|
||||||
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
||||||
|
|
||||||
ChannelPromise promise2 = newPromise();
|
Promise<Void> promise2 = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, true, promise2);
|
||||||
|
|
||||||
ChannelPromise promise3 = newPromise();
|
Promise<Void> promise3 = newPromise();
|
||||||
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
|
|
||||||
@ -503,16 +504,16 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
Http2Stream stream = connection.stream(streamId);
|
Http2Stream stream = connection.stream(streamId);
|
||||||
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
when(remoteFlow.hasFlowControlled(eq(stream))).thenReturn(true);
|
||||||
|
|
||||||
ChannelPromise promise2 = newPromise();
|
Promise<Void> promise2 = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise2);
|
||||||
|
|
||||||
ChannelPromise promise3 = newPromise();
|
Promise<Void> promise3 = newPromise();
|
||||||
ChannelFuture future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
Future<Void> future = encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, eos, promise3);
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertEquals(eos, future.isSuccess());
|
assertEquals(eos, future.isSuccess());
|
||||||
|
|
||||||
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
|
verify(writer, times(infoHeaderCount)).writeHeaders(eq(ctx), eq(streamId), eq(infoHeaders),
|
||||||
eq(0), eq(false), any(ChannelPromise.class));
|
eq(0), eq(false), any(Promise.class));
|
||||||
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer, times(1)).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise2));
|
eq(0), eq(false), eq(promise2));
|
||||||
if (eos) {
|
if (eos) {
|
||||||
@ -525,7 +526,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void pushPromiseWriteAfterGoAwayReceivedShouldFail() throws Exception {
|
public void pushPromiseWriteAfterGoAwayReceivedShouldFail() throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
goAwayReceived(0);
|
goAwayReceived(0);
|
||||||
ChannelFuture future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0,
|
Future<Void> future = encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0,
|
||||||
newPromise());
|
newPromise());
|
||||||
assertTrue(future.isDone());
|
assertTrue(future.isDone());
|
||||||
assertFalse(future.isSuccess());
|
assertFalse(future.isSuccess());
|
||||||
@ -534,7 +535,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void pushPromiseWriteShouldReserveStream() throws Exception {
|
public void pushPromiseWriteShouldReserveStream() throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, promise);
|
encoder.writePushPromise(ctx, STREAM_ID, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, promise);
|
||||||
assertEquals(RESERVED_LOCAL, stream(PUSH_STREAM_ID).state());
|
assertEquals(RESERVED_LOCAL, stream(PUSH_STREAM_ID).state());
|
||||||
verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID),
|
verify(writer).writePushPromise(eq(ctx), eq(STREAM_ID), eq(PUSH_STREAM_ID),
|
||||||
@ -545,14 +546,14 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void priorityWriteAfterGoAwayShouldSucceed() throws Exception {
|
public void priorityWriteAfterGoAwayShouldSucceed() throws Exception {
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
goAwayReceived(Integer.MAX_VALUE);
|
goAwayReceived(Integer.MAX_VALUE);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writePriority(ctx, STREAM_ID, 0, (short) 255, true, promise);
|
encoder.writePriority(ctx, STREAM_ID, 0, (short) 255, true, promise);
|
||||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true), eq(promise));
|
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq((short) 255), eq(true), eq(promise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void priorityWriteShouldSetPriorityForStream() throws Exception {
|
public void priorityWriteShouldSetPriorityForStream() throws Exception {
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
short weight = 255;
|
short weight = 255;
|
||||||
encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
|
encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
|
||||||
|
|
||||||
@ -566,7 +567,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void priorityWriteOnPreviouslyExistingStreamShouldSucceed() throws Exception {
|
public void priorityWriteOnPreviouslyExistingStreamShouldSucceed() throws Exception {
|
||||||
createStream(STREAM_ID, false).close();
|
createStream(STREAM_ID, false).close();
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
short weight = 255;
|
short weight = 255;
|
||||||
encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
|
encoder.writePriority(ctx, STREAM_ID, 0, weight, true, promise);
|
||||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true), eq(promise));
|
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(0), eq(weight), eq(true), eq(promise));
|
||||||
@ -578,7 +579,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
createStream(parentStreamId, false).close();
|
createStream(parentStreamId, false).close();
|
||||||
|
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
short weight = 255;
|
short weight = 255;
|
||||||
encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true, promise);
|
encoder.writePriority(ctx, STREAM_ID, parentStreamId, weight, true, promise);
|
||||||
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true), eq(promise));
|
verify(writer).writePriority(eq(ctx), eq(STREAM_ID), eq(parentStreamId), eq(weight), eq(true), eq(promise));
|
||||||
@ -586,7 +587,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void rstStreamWriteForUnknownStreamShouldIgnore() throws Exception {
|
public void rstStreamWriteForUnknownStreamShouldIgnore() throws Exception {
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code(), promise);
|
encoder.writeRstStream(ctx, 5, PROTOCOL_ERROR.code(), promise);
|
||||||
verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong(), eq(promise));
|
verify(writer, never()).writeRstStream(eq(ctx), anyInt(), anyLong(), eq(promise));
|
||||||
}
|
}
|
||||||
@ -599,14 +600,14 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
|
|
||||||
// Now verify that a stream reset is performed.
|
// Now verify that a stream reset is performed.
|
||||||
stream(STREAM_ID);
|
stream(STREAM_ID);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
encoder.writeRstStream(ctx, STREAM_ID, PROTOCOL_ERROR.code(), promise);
|
||||||
verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong(), eq(promise));
|
verify(lifecycleManager).resetStream(eq(ctx), eq(STREAM_ID), anyLong(), eq(promise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pingWriteAfterGoAwayShouldSucceed() throws Exception {
|
public void pingWriteAfterGoAwayShouldSucceed() throws Exception {
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
goAwayReceived(0);
|
goAwayReceived(0);
|
||||||
encoder.writePing(ctx, false, 0L, promise);
|
encoder.writePing(ctx, false, 0L, promise);
|
||||||
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
|
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
|
||||||
@ -614,7 +615,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pingWriteShouldSucceed() throws Exception {
|
public void pingWriteShouldSucceed() throws Exception {
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writePing(ctx, false, 0L, promise);
|
encoder.writePing(ctx, false, 0L, promise);
|
||||||
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
|
verify(writer).writePing(eq(ctx), eq(false), eq(0L), eq(promise));
|
||||||
}
|
}
|
||||||
@ -622,7 +623,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
@Test
|
@Test
|
||||||
public void settingsWriteAfterGoAwayShouldSucceed() throws Exception {
|
public void settingsWriteAfterGoAwayShouldSucceed() throws Exception {
|
||||||
goAwayReceived(0);
|
goAwayReceived(0);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeSettings(ctx, new Http2Settings(), promise);
|
encoder.writeSettings(ctx, new Http2Settings(), promise);
|
||||||
verify(writer).writeSettings(eq(ctx), any(Http2Settings.class), eq(promise));
|
verify(writer).writeSettings(eq(ctx), any(Http2Settings.class), eq(promise));
|
||||||
}
|
}
|
||||||
@ -634,7 +635,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
settings.maxConcurrentStreams(1000);
|
settings.maxConcurrentStreams(1000);
|
||||||
settings.headerTableSize(2000);
|
settings.headerTableSize(2000);
|
||||||
|
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeSettings(ctx, settings, promise);
|
encoder.writeSettings(ctx, settings, promise);
|
||||||
verify(writer).writeSettings(eq(ctx), eq(settings), eq(promise));
|
verify(writer).writeSettings(eq(ctx), eq(settings), eq(promise));
|
||||||
}
|
}
|
||||||
@ -645,7 +646,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
|
|
||||||
Http2Stream stream = createStream(STREAM_ID, false);
|
Http2Stream stream = createStream(STREAM_ID, false);
|
||||||
ByteBuf data = dummyData();
|
ByteBuf data = dummyData();
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true, promise);
|
encoder.writeData(ctx, STREAM_ID, data.retain(), 0, true, promise);
|
||||||
assertTrue(promise.isSuccess());
|
assertTrue(promise.isSuccess());
|
||||||
verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
|
verify(remoteFlow).addFlowControlled(eq(stream), any(FlowControlled.class));
|
||||||
@ -658,7 +659,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void headersWriteShouldHalfCloseStream() throws Exception {
|
public void headersWriteShouldHalfCloseStream() throws Exception {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
||||||
|
|
||||||
assertTrue(promise.isSuccess());
|
assertTrue(promise.isSuccess());
|
||||||
@ -670,7 +671,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
Http2Stream parent = createStream(STREAM_ID, false);
|
Http2Stream parent = createStream(STREAM_ID, false);
|
||||||
Http2Stream stream = reservePushStream(PUSH_STREAM_ID, parent);
|
Http2Stream stream = reservePushStream(PUSH_STREAM_ID, parent);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
encoder.writeHeaders(ctx, PUSH_STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, true, promise);
|
||||||
assertEquals(HALF_CLOSED_REMOTE, stream.state());
|
assertEquals(HALF_CLOSED_REMOTE, stream.state());
|
||||||
assertTrue(promise.isSuccess());
|
assertTrue(promise.isSuccess());
|
||||||
@ -679,11 +680,11 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception {
|
public void headersWriteShouldHalfCloseAfterOnErrorForPreCreatedStream() throws Exception {
|
||||||
final ChannelPromise promise = newPromise();
|
final Promise<Void> promise = newPromise();
|
||||||
final Throwable ex = new RuntimeException();
|
final Throwable ex = new RuntimeException();
|
||||||
// Fake an encoding error, like HPACK's HeaderListSizeException
|
// Fake an encoding error, like HPACK's HeaderListSizeException
|
||||||
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
|
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
|
||||||
.thenAnswer((Answer<ChannelFuture>) invocation -> {
|
.thenAnswer((Answer<Future<Void>>) invocation -> {
|
||||||
promise.setFailure(ex);
|
promise.setFailure(ex);
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
@ -702,11 +703,11 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception {
|
public void headersWriteShouldHalfCloseAfterOnErrorForImplicitlyCreatedStream() throws Exception {
|
||||||
final ChannelPromise promise = newPromise();
|
final Promise<Void> promise = newPromise();
|
||||||
final Throwable ex = new RuntimeException();
|
final Throwable ex = new RuntimeException();
|
||||||
// Fake an encoding error, like HPACK's HeaderListSizeException
|
// Fake an encoding error, like HPACK's HeaderListSizeException
|
||||||
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
|
when(writer.writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE), eq(0), eq(true), eq(promise)))
|
||||||
.thenAnswer((Answer<ChannelFuture>) invocation -> {
|
.thenAnswer((Answer<Future<Void>>) invocation -> {
|
||||||
promise.setFailure(ex);
|
promise.setFailure(ex);
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
@ -724,7 +725,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void encoderDelegatesGoAwayToLifeCycleManager() {
|
public void encoderDelegatesGoAwayToLifeCycleManager() {
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null, promise);
|
encoder.writeGoAway(ctx, STREAM_ID, Http2Error.INTERNAL_ERROR.code(), null, promise);
|
||||||
verify(lifecycleManager).goAway(eq(ctx), eq(STREAM_ID), eq(Http2Error.INTERNAL_ERROR.code()),
|
verify(lifecycleManager).goAway(eq(ctx), eq(STREAM_ID), eq(Http2Error.INTERNAL_ERROR.code()),
|
||||||
eq((ByteBuf) null), eq(promise));
|
eq((ByteBuf) null), eq(promise));
|
||||||
@ -735,7 +736,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void dataWriteToClosedStreamShouldFail() throws Exception {
|
public void dataWriteToClosedStreamShouldFail() throws Exception {
|
||||||
createStream(STREAM_ID, false).close();
|
createStream(STREAM_ID, false).close();
|
||||||
ByteBuf data = mock(ByteBuf.class);
|
ByteBuf data = mock(ByteBuf.class);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
||||||
assertTrue(promise.isDone());
|
assertTrue(promise.isDone());
|
||||||
assertFalse(promise.isSuccess());
|
assertFalse(promise.isSuccess());
|
||||||
@ -747,7 +748,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void dataWriteToHalfClosedLocalStreamShouldFail() throws Exception {
|
public void dataWriteToHalfClosedLocalStreamShouldFail() throws Exception {
|
||||||
createStream(STREAM_ID, true);
|
createStream(STREAM_ID, true);
|
||||||
ByteBuf data = mock(ByteBuf.class);
|
ByteBuf data = mock(ByteBuf.class);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
encoder.writeData(ctx, STREAM_ID, data, 0, false, promise);
|
||||||
assertTrue(promise.isDone());
|
assertTrue(promise.isDone());
|
||||||
assertFalse(promise.isSuccess());
|
assertFalse(promise.isSuccess());
|
||||||
@ -769,7 +770,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
createStream(STREAM_ID, false);
|
createStream(STREAM_ID, false);
|
||||||
goAwaySent(0);
|
goAwaySent(0);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false), eq(promise));
|
||||||
@ -788,7 +789,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception {
|
public void canWriteHeaderFrameAfterGoAwayReceived() throws Http2Exception {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
goAwayReceived(STREAM_ID);
|
goAwayReceived(STREAM_ID);
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, STREAM_ID, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer).writeHeaders(eq(ctx), eq(STREAM_ID), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false), eq(promise));
|
||||||
@ -798,7 +799,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void headersWithNoPriority() {
|
public void headersWithNoPriority() {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 0, false, promise);
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE),
|
||||||
eq(0), eq(false), eq(promise));
|
eq(0), eq(false), eq(promise));
|
||||||
@ -808,7 +809,7 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
public void headersWithPriority() {
|
public void headersWithPriority() {
|
||||||
writeAllFlowControlledFrames();
|
writeAllFlowControlledFrames();
|
||||||
final int streamId = 6;
|
final int streamId = 6;
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 10, DEFAULT_PRIORITY_WEIGHT,
|
encoder.writeHeaders(ctx, streamId, EmptyHttp2Headers.INSTANCE, 10, DEFAULT_PRIORITY_WEIGHT,
|
||||||
true, 1, false, promise);
|
true, 1, false, promise);
|
||||||
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(10),
|
verify(writer).writeHeaders(eq(ctx), eq(streamId), eq(EmptyHttp2Headers.INSTANCE), eq(10),
|
||||||
@ -844,12 +845,12 @@ public class DefaultHttp2ConnectionEncoderTest {
|
|||||||
connection.goAwaySent(lastStreamId, 0, EMPTY_BUFFER);
|
connection.goAwaySent(lastStreamId, 0, EMPTY_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise newPromise() {
|
private static Promise<Void> newPromise() {
|
||||||
return new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
|
return new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture newSucceededFuture() {
|
private static Future<Void> newSucceededFuture() {
|
||||||
return newPromise().setSuccess();
|
return newPromise().setSuccess(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf dummyData() {
|
private static ByteBuf dummyData() {
|
||||||
|
@ -18,18 +18,17 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.DefaultChannelPromise;
|
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.DefaultPromise;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@ -38,11 +37,13 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link DefaultHttp2FrameWriter}.
|
* Tests for {@link DefaultHttp2FrameWriter}.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class DefaultHttp2FrameWriterTest {
|
public class DefaultHttp2FrameWriterTest {
|
||||||
private DefaultHttp2FrameWriter frameWriter;
|
private DefaultHttp2FrameWriter frameWriter;
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
|
|
||||||
private ByteBuf expectedOutbound;
|
private ByteBuf expectedOutbound;
|
||||||
|
|
||||||
private ChannelPromise promise;
|
private Promise<Void> promise;
|
||||||
|
|
||||||
private Http2HeadersEncoder http2HeadersEncoder;
|
private Http2HeadersEncoder http2HeadersEncoder;
|
||||||
|
|
||||||
@ -58,7 +59,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
private Channel channel;
|
private Channel channel;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ChannelFuture future;
|
private Future<Void> future;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ChannelHandlerContext ctx;
|
private ChannelHandlerContext ctx;
|
||||||
@ -76,7 +77,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
|
|
||||||
expectedOutbound = Unpooled.EMPTY_BUFFER;
|
expectedOutbound = Unpooled.EMPTY_BUFFER;
|
||||||
|
|
||||||
promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
|
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
|
|
||||||
Answer<Object> answer = var1 -> {
|
Answer<Object> answer = var1 -> {
|
||||||
Object msg = var1.getArgument(0);
|
Object msg = var1.getArgument(0);
|
||||||
@ -87,7 +88,7 @@ public class DefaultHttp2FrameWriterTest {
|
|||||||
return future;
|
return future;
|
||||||
};
|
};
|
||||||
when(ctx.write(any())).then(answer);
|
when(ctx.write(any())).then(answer);
|
||||||
when(ctx.write(any(), any(ChannelPromise.class))).then(answer);
|
when(ctx.write(any(), any(Promise.class))).then(answer);
|
||||||
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(ctx.channel()).thenReturn(channel);
|
when(ctx.channel()).thenReturn(channel);
|
||||||
when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE);
|
when(ctx.executor()).thenReturn(ImmediateEventExecutor.INSTANCE);
|
||||||
|
@ -15,6 +15,20 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.handler.codec.http2.Http2Stream.State;
|
||||||
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
import junit.framework.AssertionFailedError;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.function.Executable;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http2.DefaultHttp2LocalFlowController.DEFAULT_WINDOW_UPDATE_RATIO;
|
import static io.netty.handler.codec.http2.DefaultHttp2LocalFlowController.DEFAULT_WINDOW_UPDATE_RATIO;
|
||||||
import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID;
|
import static io.netty.handler.codec.http2.Http2CodecUtil.CONNECTION_STREAM_ID;
|
||||||
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_WINDOW_SIZE;
|
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_WINDOW_SIZE;
|
||||||
@ -32,20 +46,6 @@ import static org.mockito.Mockito.verify;
|
|||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.http2.Http2Stream.State;
|
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
|
||||||
import junit.framework.AssertionFailedError;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.junit.jupiter.api.function.Executable;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link DefaultHttp2LocalFlowController}.
|
* Tests for {@link DefaultHttp2LocalFlowController}.
|
||||||
*/
|
*/
|
||||||
@ -64,7 +64,7 @@ public class DefaultHttp2LocalFlowControllerTest {
|
|||||||
private EventExecutor executor;
|
private EventExecutor executor;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ChannelPromise promise;
|
private Promise<Void> promise;
|
||||||
|
|
||||||
private DefaultHttp2Connection connection;
|
private DefaultHttp2Connection connection;
|
||||||
|
|
||||||
@ -424,9 +424,10 @@ public class DefaultHttp2LocalFlowControllerTest {
|
|||||||
verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt(), eq(promise));
|
verify(frameWriter, never()).writeWindowUpdate(eq(ctx), eq(streamId), anyInt(), eq(promise));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private void verifyWindowUpdateNotSent() {
|
private void verifyWindowUpdateNotSent() {
|
||||||
verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
|
verify(frameWriter, never()).writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
|
||||||
any(ChannelPromise.class));
|
any(Promise.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int window(int streamId) {
|
private int window(int streamId) {
|
||||||
|
@ -19,8 +19,6 @@ import io.netty.bootstrap.Bootstrap;
|
|||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
@ -33,7 +31,6 @@ import io.netty.channel.socket.nio.NioSocketChannel;
|
|||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -97,9 +94,7 @@ public class DefaultHttp2PushPromiseFrameTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void send() {
|
public void send() {
|
||||||
connectionFuture.addListener((GenericFutureListener<Future<Channel>>) future -> {
|
connectionFuture.addListener(future -> clientHandler.write());
|
||||||
clientHandler.write();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
@ -126,30 +121,24 @@ public class DefaultHttp2PushPromiseFrameTest {
|
|||||||
Http2PushPromiseFrame pushPromiseFrame = new DefaultHttp2PushPromiseFrame(pushRequestHeaders);
|
Http2PushPromiseFrame pushPromiseFrame = new DefaultHttp2PushPromiseFrame(pushRequestHeaders);
|
||||||
pushPromiseFrame.stream(receivedFrame.stream());
|
pushPromiseFrame.stream(receivedFrame.stream());
|
||||||
pushPromiseFrame.pushStream(newPushFrameStream);
|
pushPromiseFrame.pushStream(newPushFrameStream);
|
||||||
ctx.writeAndFlush(pushPromiseFrame).addListener(new ChannelFutureListener() {
|
ctx.writeAndFlush(pushPromiseFrame).addListener(future -> {
|
||||||
@Override
|
contentMap.put(newPushFrameStream.id(), "Meow, I am Pushed via HTTP/2");
|
||||||
public void operationComplete(ChannelFuture future) {
|
|
||||||
contentMap.put(newPushFrameStream.id(), "Meow, I am Pushed via HTTP/2");
|
|
||||||
|
|
||||||
// Write headers for actual request
|
// Write headers for actual request
|
||||||
Http2Headers http2Headers = new DefaultHttp2Headers();
|
Http2Headers http2Headers = new DefaultHttp2Headers();
|
||||||
http2Headers.status("200");
|
http2Headers.status("200");
|
||||||
http2Headers.add("push", "false");
|
http2Headers.add("push", "false");
|
||||||
Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(http2Headers, false);
|
Http2HeadersFrame headersFrame = new DefaultHttp2HeadersFrame(http2Headers, false);
|
||||||
headersFrame.stream(receivedFrame.stream());
|
headersFrame.stream(receivedFrame.stream());
|
||||||
ChannelFuture channelFuture = ctx.writeAndFlush(headersFrame);
|
Future<Void> channelFuture = ctx.writeAndFlush(headersFrame);
|
||||||
|
|
||||||
// Write Data of actual request
|
// Write Data of actual request
|
||||||
channelFuture.addListener(new ChannelFutureListener() {
|
channelFuture.addListener(fut -> {
|
||||||
@Override
|
Http2DataFrame dataFrame = new DefaultHttp2DataFrame(
|
||||||
public void operationComplete(ChannelFuture future) throws Exception {
|
Unpooled.wrappedBuffer("Meow".getBytes()), true);
|
||||||
Http2DataFrame dataFrame = new DefaultHttp2DataFrame(
|
dataFrame.stream(receivedFrame.stream());
|
||||||
Unpooled.wrappedBuffer("Meow".getBytes()), true);
|
ctx.writeAndFlush(dataFrame);
|
||||||
dataFrame.stream(receivedFrame.stream());
|
});
|
||||||
ctx.writeAndFlush(dataFrame);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
} else if (msg instanceof Http2PriorityFrame) {
|
} else if (msg instanceof Http2PriorityFrame) {
|
||||||
Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg;
|
Http2PriorityFrame priorityFrame = (Http2PriorityFrame) msg;
|
||||||
@ -177,7 +166,7 @@ public class DefaultHttp2PushPromiseFrameTest {
|
|||||||
|
|
||||||
private static final class ClientHandler extends Http2ChannelDuplexHandler {
|
private static final class ClientHandler extends Http2ChannelDuplexHandler {
|
||||||
|
|
||||||
private ChannelHandlerContext ctx;
|
private volatile ChannelHandlerContext ctx;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void channelActive(ChannelHandlerContext ctx) throws InterruptedException {
|
public void channelActive(ChannelHandlerContext ctx) throws InterruptedException {
|
||||||
|
@ -18,14 +18,13 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelConfig;
|
import io.netty.channel.ChannelConfig;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.function.Executable;
|
import org.junit.jupiter.api.function.Executable;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
import org.opentest4j.AssertionFailedError;
|
import org.opentest4j.AssertionFailedError;
|
||||||
|
|
||||||
@ -80,7 +79,7 @@ public abstract class DefaultHttp2RemoteFlowControllerTest {
|
|||||||
private EventExecutor executor;
|
private EventExecutor executor;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ChannelPromise promise;
|
private Promise<Void> promise;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Http2RemoteFlowController.Listener listener;
|
private Http2RemoteFlowController.Listener listener;
|
||||||
|
@ -71,7 +71,10 @@ public class Http2ClientUpgradeCodecTest {
|
|||||||
// Flush the channel to ensure we write out all buffered data
|
// Flush the channel to ensure we write out all buffered data
|
||||||
channel.flush();
|
channel.flush();
|
||||||
|
|
||||||
codec.upgradeTo(ctx, null);
|
channel.eventLoop().submit(() -> {
|
||||||
|
codec.upgradeTo(ctx, null);
|
||||||
|
return null;
|
||||||
|
}).sync();
|
||||||
assertNotNull(channel.pipeline().get("connectionHandler"));
|
assertNotNull(channel.pipeline().get("connectionHandler"));
|
||||||
|
|
||||||
if (multiplexer != null) {
|
if (multiplexer != null) {
|
||||||
|
@ -19,19 +19,17 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelMetadata;
|
import io.netty.channel.ChannelMetadata;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.DefaultChannelConfig;
|
import io.netty.channel.DefaultChannelConfig;
|
||||||
import io.netty.channel.DefaultChannelPromise;
|
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http2.Http2Exception.ShutdownHint;
|
import io.netty.handler.codec.http2.Http2Exception.ShutdownHint;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.DefaultPromise;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.FutureListener;
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
@ -78,12 +76,13 @@ import static org.mockito.Mockito.when;
|
|||||||
/**
|
/**
|
||||||
* Tests for {@link Http2ConnectionHandler}
|
* Tests for {@link Http2ConnectionHandler}
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class Http2ConnectionHandlerTest {
|
public class Http2ConnectionHandlerTest {
|
||||||
private static final int STREAM_ID = 1;
|
private static final int STREAM_ID = 1;
|
||||||
private static final int NON_EXISTANT_STREAM_ID = 13;
|
private static final int NON_EXISTANT_STREAM_ID = 13;
|
||||||
|
|
||||||
private Http2ConnectionHandler handler;
|
private Http2ConnectionHandler handler;
|
||||||
private ChannelPromise promise;
|
private Promise<Void> promise;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Http2Connection connection;
|
private Http2Connection connection;
|
||||||
@ -119,7 +118,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
private ChannelPipeline pipeline;
|
private ChannelPipeline pipeline;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ChannelFuture future;
|
private Future<Void> future;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Http2Stream stream;
|
private Http2Stream stream;
|
||||||
@ -140,7 +139,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
|
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
|
|
||||||
when(channel.metadata()).thenReturn(new ChannelMetadata(false));
|
when(channel.metadata()).thenReturn(new ChannelMetadata(false));
|
||||||
DefaultChannelConfig config = new DefaultChannelConfig(channel);
|
DefaultChannelConfig config = new DefaultChannelConfig(channel);
|
||||||
@ -152,22 +151,21 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(encoder.frameWriter()).thenReturn(frameWriter);
|
when(encoder.frameWriter()).thenReturn(frameWriter);
|
||||||
when(encoder.flowController()).thenReturn(remoteFlow);
|
when(encoder.flowController()).thenReturn(remoteFlow);
|
||||||
when(decoder.flowController()).thenReturn(localFlow);
|
when(decoder.flowController()).thenReturn(localFlow);
|
||||||
doAnswer((Answer<ChannelFuture>) invocation -> {
|
doAnswer((Answer<Future<Void>>) invocation -> {
|
||||||
ByteBuf buf = invocation.getArgument(3);
|
ByteBuf buf = invocation.getArgument(3);
|
||||||
goAwayDebugCap = buf.toString(UTF_8);
|
goAwayDebugCap = buf.toString(UTF_8);
|
||||||
buf.release();
|
buf.release();
|
||||||
return future;
|
return future;
|
||||||
}).when(frameWriter).writeGoAway(
|
}).when(frameWriter).writeGoAway(
|
||||||
any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class), any(ChannelPromise.class));
|
any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class), any(Promise.class));
|
||||||
doAnswer((Answer<ChannelFuture>) invocation -> {
|
doAnswer((Answer<Future<Void>>) invocation -> {
|
||||||
Object o = invocation.getArguments()[0];
|
Object o = invocation.getArguments()[0];
|
||||||
if (o instanceof ChannelFutureListener) {
|
if (o instanceof FutureListener) {
|
||||||
((ChannelFutureListener) o).operationComplete(future);
|
((FutureListener<Void>) o).operationComplete(future);
|
||||||
}
|
}
|
||||||
return future;
|
return future;
|
||||||
}).when(future).addListener(any(GenericFutureListener.class));
|
}).when(future).addListener(any(FutureListener.class));
|
||||||
when(future.cause()).thenReturn(fakeException);
|
when(future.cause()).thenReturn(fakeException);
|
||||||
when(future.channel()).thenReturn(channel);
|
|
||||||
when(channel.isActive()).thenReturn(true);
|
when(channel.isActive()).thenReturn(true);
|
||||||
when(channel.pipeline()).thenReturn(pipeline);
|
when(channel.pipeline()).thenReturn(pipeline);
|
||||||
when(connection.remote()).thenReturn(remote);
|
when(connection.remote()).thenReturn(remote);
|
||||||
@ -252,7 +250,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
final Answer<Object> verifier = in -> {
|
final Answer<Object> verifier = in -> {
|
||||||
assertEquals(in.getArgument(0), evt); // sanity check...
|
assertEquals(in.getArgument(0), evt); // sanity check...
|
||||||
verify(ctx).write(eq(connectionPrefaceBuf()));
|
verify(ctx).write(eq(connectionPrefaceBuf()));
|
||||||
verify(encoder).writeSettings(eq(ctx), any(Http2Settings.class), any(ChannelPromise.class));
|
verify(encoder).writeSettings(eq(ctx), any(Http2Settings.class), any(Promise.class));
|
||||||
verified.set(true);
|
verified.set(true);
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
@ -526,13 +524,13 @@ public class Http2ConnectionHandlerTest {
|
|||||||
handler = newHandler();
|
handler = newHandler();
|
||||||
when(stream.id()).thenReturn(STREAM_ID);
|
when(stream.id()).thenReturn(STREAM_ID);
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID),
|
||||||
anyLong(), any(ChannelPromise.class))).thenReturn(future);
|
anyLong(), any(Promise.class))).thenReturn(future);
|
||||||
when(stream.state()).thenReturn(CLOSED);
|
when(stream.state()).thenReturn(CLOSED);
|
||||||
when(stream.isHeadersSent()).thenReturn(true);
|
when(stream.isHeadersSent()).thenReturn(true);
|
||||||
// The stream is "closed" but is still known about by the connection (connection().stream(..)
|
// The stream is "closed" but is still known about by the connection (connection().stream(..)
|
||||||
// will return the stream). We should still write a RST_STREAM frame in this scenario.
|
// will return the stream). We should still write a RST_STREAM frame in this scenario.
|
||||||
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
|
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
|
||||||
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(ChannelPromise.class));
|
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -540,7 +538,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
handler = newHandler();
|
handler = newHandler();
|
||||||
when(stream.state()).thenReturn(IDLE);
|
when(stream.state()).thenReturn(IDLE);
|
||||||
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
|
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise);
|
||||||
verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(ChannelPromise.class));
|
verify(frameWriter, never()).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
|
||||||
verify(stream).close();
|
verify(stream).close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -550,9 +548,9 @@ public class Http2ConnectionHandlerTest {
|
|||||||
handler = newHandler();
|
handler = newHandler();
|
||||||
when(future.isDone()).thenReturn(true);
|
when(future.isDone()).thenReturn(true);
|
||||||
when(future.isSuccess()).thenReturn(true);
|
when(future.isSuccess()).thenReturn(true);
|
||||||
doAnswer((Answer<ChannelFuture>) invocation -> {
|
doAnswer((Answer<Future<Void>>) invocation -> {
|
||||||
Object[] args = invocation.getArguments();
|
Object[] args = invocation.getArguments();
|
||||||
GenericFutureListener<ChannelFuture> listener = (GenericFutureListener<ChannelFuture>) args[0];
|
FutureListener<Void> listener = (FutureListener<Void>) args[0];
|
||||||
// Simulate that all streams have become inactive by the time the future completes.
|
// Simulate that all streams have become inactive by the time the future completes.
|
||||||
doAnswer((Answer<Http2Stream>) in -> null).when(connection).forEachActiveStream(
|
doAnswer((Answer<Http2Stream>) in -> null).when(connection).forEachActiveStream(
|
||||||
any(Http2StreamVisitor.class));
|
any(Http2StreamVisitor.class));
|
||||||
@ -560,7 +558,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
// Simulate the future being completed.
|
// Simulate the future being completed.
|
||||||
listener.operationComplete(future);
|
listener.operationComplete(future);
|
||||||
return future;
|
return future;
|
||||||
}).when(future).addListener(any(GenericFutureListener.class));
|
}).when(future).addListener(any(FutureListener.class));
|
||||||
handler.close(ctx, promise);
|
handler.close(ctx, promise);
|
||||||
if (future.isDone()) {
|
if (future.isDone()) {
|
||||||
when(connection.numActiveStreams()).thenReturn(0);
|
when(connection.numActiveStreams()).thenReturn(0);
|
||||||
@ -568,7 +566,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
handler.closeStream(stream, future);
|
handler.closeStream(stream, future);
|
||||||
// Simulate another stream close call being made after the context should already be closed.
|
// Simulate another stream close call being made after the context should already be closed.
|
||||||
handler.closeStream(stream, future);
|
handler.closeStream(stream, future);
|
||||||
verify(ctx, times(1)).close(any(ChannelPromise.class));
|
verify(ctx, times(1)).close(any(Promise.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
@ -579,9 +577,9 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(future.isDone()).thenReturn(true);
|
when(future.isDone()).thenReturn(true);
|
||||||
when(future.isSuccess()).thenReturn(true);
|
when(future.isSuccess()).thenReturn(true);
|
||||||
doAnswer((Answer<Void>) invocation -> {
|
doAnswer((Answer<Void>) invocation -> {
|
||||||
((GenericFutureListener) invocation.getArgument(0)).operationComplete(future);
|
((FutureListener<Void>) invocation.getArgument(0)).operationComplete(future);
|
||||||
return null;
|
return null;
|
||||||
}).when(future).addListener(any(GenericFutureListener.class));
|
}).when(future).addListener(any(FutureListener.class));
|
||||||
handler = newHandler();
|
handler = newHandler();
|
||||||
handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
|
handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
|
||||||
|
|
||||||
@ -602,7 +600,7 @@ public class Http2ConnectionHandlerTest {
|
|||||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data),
|
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID + 2), eq(errorCode), eq(data),
|
||||||
eq(promise));
|
eq(promise));
|
||||||
verify(connection).goAwaySent(eq(STREAM_ID + 2), eq(errorCode), eq(data));
|
verify(connection).goAwaySent(eq(STREAM_ID + 2), eq(errorCode), eq(data));
|
||||||
promise = new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
|
promise = new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
|
handler.goAway(ctx, STREAM_ID, errorCode, data, promise);
|
||||||
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data), eq(promise));
|
verify(frameWriter).writeGoAway(eq(ctx), eq(STREAM_ID), eq(errorCode), eq(data), eq(promise));
|
||||||
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
|
verify(connection).goAwaySent(eq(STREAM_ID), eq(errorCode), eq(data));
|
||||||
@ -655,9 +653,9 @@ public class Http2ConnectionHandlerTest {
|
|||||||
when(channel.isActive()).thenReturn(false);
|
when(channel.isActive()).thenReturn(false);
|
||||||
handler.channelInactive(ctx);
|
handler.channelInactive(ctx);
|
||||||
verify(frameWriter, never()).writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(),
|
verify(frameWriter, never()).writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(),
|
||||||
any(ByteBuf.class), any(ChannelPromise.class));
|
any(ByteBuf.class), any(Promise.class));
|
||||||
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong(),
|
verify(frameWriter, never()).writeRstStream(any(ChannelHandlerContext.class), anyInt(), anyLong(),
|
||||||
any(ChannelPromise.class));
|
any(Promise.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -727,21 +725,20 @@ public class Http2ConnectionHandlerTest {
|
|||||||
return stream;
|
return stream;
|
||||||
});
|
});
|
||||||
when(stream.isResetSent()).then((Answer<Boolean>) invocationOnMock -> resetSent.get());
|
when(stream.isResetSent()).then((Answer<Boolean>) invocationOnMock -> resetSent.get());
|
||||||
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(ChannelPromise.class)))
|
when(frameWriter.writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class)))
|
||||||
.then((Answer<ChannelFuture>) invocationOnMock -> {
|
.then((Answer<Future<Void>>) invocationOnMock -> {
|
||||||
ChannelPromise promise = invocationOnMock.getArgument(3);
|
Promise<Void> promise = invocationOnMock.getArgument(3);
|
||||||
return promise.setSuccess();
|
return promise.setSuccess(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
ChannelPromise promise =
|
Promise<Void> promise =
|
||||||
new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
|
new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
final ChannelPromise promise2 =
|
final Promise<Void> promise2 =
|
||||||
new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
|
new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
promise.addListener((ChannelFutureListener) future ->
|
promise.addListener(future -> handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise2));
|
||||||
handler.resetStream(ctx, STREAM_ID, STREAM_CLOSED.code(), promise2));
|
|
||||||
|
|
||||||
handler.resetStream(ctx, STREAM_ID, CANCEL.code(), promise);
|
handler.resetStream(ctx, STREAM_ID, CANCEL.code(), promise);
|
||||||
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(ChannelPromise.class));
|
verify(frameWriter).writeRstStream(eq(ctx), eq(STREAM_ID), anyLong(), any(Promise.class));
|
||||||
assertTrue(promise.isSuccess());
|
assertTrue(promise.isSuccess());
|
||||||
assertTrue(promise2.isSuccess());
|
assertTrue(promise2.isSuccess());
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,10 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.buffer.ByteBufUtil;
|
import io.netty.buffer.ByteBufUtil;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.MultithreadEventLoopGroup;
|
import io.netty.channel.MultithreadEventLoopGroup;
|
||||||
import io.netty.channel.local.LocalAddress;
|
import io.netty.channel.local.LocalAddress;
|
||||||
import io.netty.channel.local.LocalChannel;
|
import io.netty.channel.local.LocalChannel;
|
||||||
@ -38,6 +35,7 @@ import io.netty.util.AsciiString;
|
|||||||
import io.netty.util.IllegalReferenceCountException;
|
import io.netty.util.IllegalReferenceCountException;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -261,10 +259,10 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, false, newPromise())
|
http2Client.encoder().writeHeaders(ctx(), 3, headers, 0, false, newPromise())
|
||||||
.addListener((ChannelFutureListener) future -> clientHeadersWriteException.set(future.cause()));
|
.addListener(future -> clientHeadersWriteException.set(future.cause()));
|
||||||
// It is expected that this write should fail locally and the remote peer will never see this.
|
// It is expected that this write should fail locally and the remote peer will never see this.
|
||||||
http2Client.encoder().writeData(ctx(), 3, Unpooled.buffer(), 0, true, newPromise())
|
http2Client.encoder().writeData(ctx(), 3, Unpooled.buffer(), 0, true, newPromise())
|
||||||
.addListener((ChannelFutureListener) future -> {
|
.addListener(future -> {
|
||||||
clientDataWriteException.set(future.cause());
|
clientDataWriteException.set(future.cause());
|
||||||
clientDataWrite.countDown();
|
clientDataWrite.countDown();
|
||||||
});
|
});
|
||||||
@ -289,7 +287,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true,
|
http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, true,
|
||||||
newPromise()).addListener((ChannelFutureListener) future -> {
|
newPromise()).addListener(future -> {
|
||||||
clientHeadersWriteException2.set(future.cause());
|
clientHeadersWriteException2.set(future.cause());
|
||||||
clientHeadersLatch.countDown();
|
clientHeadersLatch.countDown();
|
||||||
});
|
});
|
||||||
@ -481,7 +479,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
// Now have the server attempt to send a headers frame simulating some asynchronous work.
|
// Now have the server attempt to send a headers frame simulating some asynchronous work.
|
||||||
runInChannel(serverConnectedChannel, () -> {
|
runInChannel(serverConnectedChannel, () -> {
|
||||||
http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true, serverNewPromise())
|
http2Server.encoder().writeHeaders(serverCtx(), streamId, headers, 0, true, serverNewPromise())
|
||||||
.addListener((ChannelFutureListener) future -> {
|
.addListener(future -> {
|
||||||
serverWriteHeadersCauseRef.set(future.cause());
|
serverWriteHeadersCauseRef.set(future.cause());
|
||||||
serverWriteHeadersLatch.countDown();
|
serverWriteHeadersLatch.countDown();
|
||||||
});
|
});
|
||||||
@ -507,7 +505,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
// Create a latch to track when the close occurs.
|
// Create a latch to track when the close occurs.
|
||||||
final CountDownLatch closeLatch = new CountDownLatch(1);
|
final CountDownLatch closeLatch = new CountDownLatch(1);
|
||||||
clientChannel.closeFuture().addListener((ChannelFutureListener) future -> closeLatch.countDown());
|
clientChannel.closeFuture().addListener(future -> closeLatch.countDown());
|
||||||
|
|
||||||
// Create a single stream by sending a HEADERS frame to the server.
|
// Create a single stream by sending a HEADERS frame to the server.
|
||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
@ -545,7 +543,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
// Create a latch to track when the close occurs.
|
// Create a latch to track when the close occurs.
|
||||||
final CountDownLatch closeLatch = new CountDownLatch(1);
|
final CountDownLatch closeLatch = new CountDownLatch(1);
|
||||||
clientChannel.closeFuture().addListener((ChannelFutureListener) future -> closeLatch.countDown());
|
clientChannel.closeFuture().addListener(future -> closeLatch.countDown());
|
||||||
|
|
||||||
// Create a single stream by sending a HEADERS frame to the server.
|
// Create a single stream by sending a HEADERS frame to the server.
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
@ -594,7 +592,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
bootstrapEnv(1, 1, 2, 1);
|
bootstrapEnv(1, 1, 2, 1);
|
||||||
|
|
||||||
final ChannelPromise emptyDataPromise = newPromise();
|
final Promise<Void> emptyDataPromise = newPromise();
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
|
||||||
newPromise());
|
newPromise());
|
||||||
@ -639,15 +637,15 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
bootstrapEnv(1, 1, 2, 1);
|
bootstrapEnv(1, 1, 2, 1);
|
||||||
|
|
||||||
final ChannelPromise dataPromise = newPromise();
|
final Promise<Void> dataPromise = newPromise();
|
||||||
final ChannelPromise assertPromise = newPromise();
|
final Promise<Void> assertPromise = newPromise();
|
||||||
|
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
|
http2Client.encoder().writeHeaders(ctx(), 3, EmptyHttp2Headers.INSTANCE, 0, (short) 16, false, 0, false,
|
||||||
newPromise());
|
newPromise());
|
||||||
clientChannel.pipeline().addFirst(new ChannelHandler() {
|
clientChannel.pipeline().addFirst(new ChannelHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
ReferenceCountUtil.release(msg);
|
ReferenceCountUtil.release(msg);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -673,7 +671,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
// The Frame should have been removed after the write failed.
|
// The Frame should have been removed after the write failed.
|
||||||
assertFalse(http2Client.encoder().flowController()
|
assertFalse(http2Client.encoder().flowController()
|
||||||
.hasFlowControlled(http2Client.connection().stream(3)));
|
.hasFlowControlled(http2Client.connection().stream(3)));
|
||||||
assertPromise.setSuccess();
|
assertPromise.setSuccess(null);
|
||||||
} catch (Throwable error) {
|
} catch (Throwable error) {
|
||||||
assertPromise.setFailure(error);
|
assertPromise.setFailure(error);
|
||||||
}
|
}
|
||||||
@ -695,7 +693,7 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
|
|
||||||
// Create a latch to track when the close occurs.
|
// Create a latch to track when the close occurs.
|
||||||
final CountDownLatch closeLatch = new CountDownLatch(1);
|
final CountDownLatch closeLatch = new CountDownLatch(1);
|
||||||
clientChannel.closeFuture().addListener((ChannelFutureListener) future -> closeLatch.countDown());
|
clientChannel.closeFuture().addListener(future -> closeLatch.countDown());
|
||||||
|
|
||||||
// Create a single stream by sending a HEADERS frame to the server.
|
// Create a single stream by sending a HEADERS frame to the server.
|
||||||
final Http2Headers headers = dummyHeaders();
|
final Http2Headers headers = dummyHeaders();
|
||||||
@ -790,20 +788,20 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
verify(clientListener).onGoAwayRead(any(ChannelHandlerContext.class), eq(3), eq(NO_ERROR.code()),
|
verify(clientListener).onGoAwayRead(any(ChannelHandlerContext.class), eq(3), eq(NO_ERROR.code()),
|
||||||
any(ByteBuf.class));
|
any(ByteBuf.class));
|
||||||
|
|
||||||
final AtomicReference<ChannelFuture> clientWriteAfterGoAwayFutureRef = new AtomicReference<>();
|
final AtomicReference<Future<Void>> clientWriteAfterGoAwayFutureRef = new AtomicReference<>();
|
||||||
final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1);
|
final CountDownLatch clientWriteAfterGoAwayLatch = new CountDownLatch(1);
|
||||||
runInChannel(clientChannel, () -> {
|
runInChannel(clientChannel, () -> {
|
||||||
ChannelFuture f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0,
|
Future<Void> f = http2Client.encoder().writeHeaders(ctx(), 5, headers, 0, (short) 16, false, 0,
|
||||||
true, newPromise());
|
true, newPromise());
|
||||||
clientWriteAfterGoAwayFutureRef.set(f);
|
clientWriteAfterGoAwayFutureRef.set(f);
|
||||||
http2Client.flush(ctx());
|
http2Client.flush(ctx());
|
||||||
f.addListener((ChannelFutureListener) future -> clientWriteAfterGoAwayLatch.countDown());
|
f.addListener(future -> clientWriteAfterGoAwayLatch.countDown());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait for the client's write operation to complete.
|
// Wait for the client's write operation to complete.
|
||||||
assertTrue(clientWriteAfterGoAwayLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
assertTrue(clientWriteAfterGoAwayLatch.await(DEFAULT_AWAIT_TIMEOUT_SECONDS, SECONDS));
|
||||||
|
|
||||||
ChannelFuture clientWriteAfterGoAwayFuture = clientWriteAfterGoAwayFutureRef.get();
|
Future<Void> clientWriteAfterGoAwayFuture = clientWriteAfterGoAwayFutureRef.get();
|
||||||
assertNotNull(clientWriteAfterGoAwayFuture);
|
assertNotNull(clientWriteAfterGoAwayFuture);
|
||||||
Throwable clientCause = clientWriteAfterGoAwayFuture.cause();
|
Throwable clientCause = clientWriteAfterGoAwayFuture.cause();
|
||||||
assertThat(clientCause, is(instanceOf(Http2Exception.StreamException.class)));
|
assertThat(clientCause, is(instanceOf(Http2Exception.StreamException.class)));
|
||||||
@ -1106,11 +1104,11 @@ public class Http2ConnectionRoundtripTest {
|
|||||||
return serverConnectedChannel.pipeline().firstContext();
|
return serverConnectedChannel.pipeline().firstContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise newPromise() {
|
private Promise<Void> newPromise() {
|
||||||
return ctx().newPromise();
|
return ctx().newPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise serverNewPromise() {
|
private Promise<Void> serverNewPromise() {
|
||||||
return serverCtx().newPromise();
|
return serverCtx().newPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,15 +19,15 @@ import io.netty.buffer.ByteBuf;
|
|||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelConfig;
|
import io.netty.channel.ChannelConfig;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelMetadata;
|
import io.netty.channel.ChannelMetadata;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.DefaultChannelPromise;
|
|
||||||
import io.netty.channel.DefaultMessageSizeEstimator;
|
import io.netty.channel.DefaultMessageSizeEstimator;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.DefaultPromise;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -36,20 +36,30 @@ import org.mockito.MockitoAnnotations;
|
|||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
import java.util.ArrayDeque;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
|
||||||
import static io.netty.handler.codec.http2.Http2CodecUtil.*;
|
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
|
||||||
import static io.netty.handler.codec.http2.Http2Error.CANCEL;
|
import static io.netty.handler.codec.http2.Http2Error.CANCEL;
|
||||||
import static io.netty.handler.codec.http2.Http2Error.ENHANCE_YOUR_CALM;
|
import static io.netty.handler.codec.http2.Http2Error.ENHANCE_YOUR_CALM;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.mockito.Mockito.*;
|
import static org.mockito.Mockito.any;
|
||||||
|
import static org.mockito.Mockito.anyBoolean;
|
||||||
|
import static org.mockito.Mockito.anyInt;
|
||||||
|
import static org.mockito.Mockito.anyLong;
|
||||||
|
import static org.mockito.Mockito.atLeast;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link Http2ControlFrameLimitEncoder}.
|
* Tests for {@link Http2ControlFrameLimitEncoder}.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class Http2ControlFrameLimitEncoderTest {
|
public class Http2ControlFrameLimitEncoderTest {
|
||||||
|
|
||||||
private Http2ControlFrameLimitEncoder encoder;
|
private Http2ControlFrameLimitEncoder encoder;
|
||||||
@ -74,7 +84,7 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
|
|
||||||
private int numWrites;
|
private int numWrites;
|
||||||
|
|
||||||
private final Queue<ChannelPromise> goAwayPromises = new ArrayDeque<ChannelPromise>();
|
private final Queue<Promise<Void>> goAwayPromises = new ArrayDeque<Promise<Void>>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init fields and do mocking.
|
* Init fields and do mocking.
|
||||||
@ -91,22 +101,22 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
||||||
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
||||||
|
|
||||||
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(ChannelPromise.class)))
|
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class)))
|
||||||
.thenAnswer((Answer<ChannelFuture>) invocationOnMock -> handlePromise(invocationOnMock, 3));
|
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise(invocationOnMock, 3));
|
||||||
when(writer.writeSettingsAck(any(ChannelHandlerContext.class), any(ChannelPromise.class)))
|
when(writer.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
|
||||||
.thenAnswer((Answer<ChannelFuture>) invocationOnMock -> handlePromise(invocationOnMock, 1));
|
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> handlePromise(invocationOnMock, 1));
|
||||||
when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong(), any(ChannelPromise.class)))
|
when(writer.writePing(any(ChannelHandlerContext.class), anyBoolean(), anyLong(), any(Promise.class)))
|
||||||
.thenAnswer((Answer<ChannelFuture>) invocationOnMock -> {
|
.thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
|
||||||
ChannelPromise promise = handlePromise(invocationOnMock, 3);
|
Promise<Void> promise = handlePromise(invocationOnMock, 3);
|
||||||
if (invocationOnMock.getArgument(1) == Boolean.FALSE) {
|
if (invocationOnMock.getArgument(1) == Boolean.FALSE) {
|
||||||
promise.trySuccess();
|
promise.trySuccess(null);
|
||||||
}
|
}
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
|
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
|
||||||
any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock -> {
|
any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock -> {
|
||||||
ReferenceCountUtil.release(invocationOnMock.getArgument(3));
|
ReferenceCountUtil.release(invocationOnMock.getArgument(3));
|
||||||
ChannelPromise promise = invocationOnMock.getArgument(4);
|
Promise<Void> promise = invocationOnMock.getArgument(4);
|
||||||
goAwayPromises.offer(promise);
|
goAwayPromises.offer(promise);
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
@ -128,7 +138,7 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(executor.inEventLoop()).thenReturn(true);
|
when(executor.inEventLoop()).thenReturn(true);
|
||||||
doAnswer((Answer<ChannelPromise>) invocation -> newPromise()).when(ctx).newPromise();
|
doAnswer((Answer<Promise>) invocation -> newPromise()).when(ctx).newPromise();
|
||||||
when(ctx.executor()).thenReturn(executor);
|
when(ctx.executor()).thenReturn(executor);
|
||||||
when(channel.isActive()).thenReturn(false);
|
when(channel.isActive()).thenReturn(false);
|
||||||
when(channel.config()).thenReturn(config);
|
when(channel.config()).thenReturn(config);
|
||||||
@ -142,10 +152,10 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
handler.handlerAdded(ctx);
|
handler.handlerAdded(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise handlePromise(InvocationOnMock invocationOnMock, int promiseIdx) {
|
private Promise<Void> handlePromise(InvocationOnMock invocationOnMock, int promiseIdx) {
|
||||||
ChannelPromise promise = invocationOnMock.getArgument(promiseIdx);
|
Promise<Void> promise = invocationOnMock.getArgument(promiseIdx);
|
||||||
if (++numWrites == 2) {
|
if (++numWrites == 2) {
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
}
|
}
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
@ -155,14 +165,14 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
// Close and release any buffered frames.
|
// Close and release any buffered frames.
|
||||||
encoder.close();
|
encoder.close();
|
||||||
|
|
||||||
// Notify all goAway ChannelPromise instances now as these will also release the retained ByteBuf for the
|
// Notify all goAway Promise instances now as these will also release the retained ByteBuf for the
|
||||||
// debugData.
|
// debugData.
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ChannelPromise promise = goAwayPromises.poll();
|
Promise<Void> promise = goAwayPromises.poll();
|
||||||
if (promise == null) {
|
if (promise == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,11 +256,11 @@ public class Http2ControlFrameLimitEncoderTest {
|
|||||||
verify(ctx, times(invocations)).close();
|
verify(ctx, times(invocations)).close();
|
||||||
if (failed) {
|
if (failed) {
|
||||||
verify(writer, times(1)).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(ENHANCE_YOUR_CALM.code()),
|
verify(writer, times(1)).writeGoAway(eq(ctx), eq(Integer.MAX_VALUE), eq(ENHANCE_YOUR_CALM.code()),
|
||||||
any(ByteBuf.class), any(ChannelPromise.class));
|
any(ByteBuf.class), any(Promise.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise newPromise() {
|
private static Promise<Void> newPromise() {
|
||||||
return new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
|
return new DefaultPromise<>(ImmediateEventExecutor.INSTANCE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,8 @@ package io.netty.handler.codec.http2;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
import io.netty.handler.codec.UnsupportedMessageTypeException;
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
@ -39,6 +36,7 @@ import io.netty.util.AsciiString;
|
|||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.ReferenceCounted;
|
import io.netty.util.ReferenceCounted;
|
||||||
import io.netty.util.concurrent.DefaultPromise;
|
import io.netty.util.concurrent.DefaultPromise;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||||
import io.netty.util.concurrent.Promise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.ReflectionUtil;
|
import io.netty.util.internal.ReflectionUtil;
|
||||||
@ -84,6 +82,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|||||||
/**
|
/**
|
||||||
* Unit tests for {@link Http2FrameCodec}.
|
* Unit tests for {@link Http2FrameCodec}.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class Http2FrameCodecTest {
|
public class Http2FrameCodecTest {
|
||||||
|
|
||||||
// For verifying outbound frames
|
// For verifying outbound frames
|
||||||
@ -384,7 +383,7 @@ public class Http2FrameCodecTest {
|
|||||||
UnknownHttp2Frame frame = new UnknownHttp2Frame();
|
UnknownHttp2Frame frame = new UnknownHttp2Frame();
|
||||||
assertEquals(1, frame.refCnt());
|
assertEquals(1, frame.refCnt());
|
||||||
|
|
||||||
ChannelFuture f = channel.write(frame);
|
Future<Void> f = channel.write(frame);
|
||||||
f.await();
|
f.await();
|
||||||
assertTrue(f.isDone());
|
assertTrue(f.isDone());
|
||||||
assertFalse(f.isSuccess());
|
assertFalse(f.isSuccess());
|
||||||
@ -493,7 +492,7 @@ public class Http2FrameCodecTest {
|
|||||||
Http2FrameStream stream2 = inboundHeaders.stream();
|
Http2FrameStream stream2 = inboundHeaders.stream();
|
||||||
|
|
||||||
int before = connection.local().flowController().unconsumedBytes(stream);
|
int before = connection.local().flowController().unconsumedBytes(stream);
|
||||||
ChannelFuture f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2));
|
Future<Void> f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2));
|
||||||
int after = connection.local().flowController().unconsumedBytes(stream);
|
int after = connection.local().flowController().unconsumedBytes(stream);
|
||||||
assertEquals(100, before - after);
|
assertEquals(100, before - after);
|
||||||
assertTrue(f.isSuccess());
|
assertTrue(f.isSuccess());
|
||||||
@ -512,7 +511,7 @@ public class Http2FrameCodecTest {
|
|||||||
Http2FrameStream stream2 = inboundHeaders.stream();
|
Http2FrameStream stream2 = inboundHeaders.stream();
|
||||||
|
|
||||||
// Fails, cause trying to return too many bytes to the flow controller
|
// Fails, cause trying to return too many bytes to the flow controller
|
||||||
ChannelFuture f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2));
|
Future<Void> f = channel.write(new DefaultHttp2WindowUpdateFrame(100).stream(stream2));
|
||||||
assertTrue(f.isDone());
|
assertTrue(f.isDone());
|
||||||
assertFalse(f.isSuccess());
|
assertFalse(f.isSuccess());
|
||||||
assertThat(f.cause(), instanceOf(Http2Exception.class));
|
assertThat(f.cause(), instanceOf(Http2Exception.class));
|
||||||
@ -597,7 +596,7 @@ public class Http2FrameCodecTest {
|
|||||||
channel.write(unknownFrame);
|
channel.write(unknownFrame);
|
||||||
|
|
||||||
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq(unknownFrame.frameType()),
|
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq(unknownFrame.frameType()),
|
||||||
eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer), any(ChannelPromise.class));
|
eq(unknownFrame.stream().id()), eq(unknownFrame.flags()), eq(buffer), any(Promise.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -605,7 +604,7 @@ public class Http2FrameCodecTest {
|
|||||||
Http2Settings settings = new Http2Settings();
|
Http2Settings settings = new Http2Settings();
|
||||||
channel.write(new DefaultHttp2SettingsFrame(settings));
|
channel.write(new DefaultHttp2SettingsFrame(settings));
|
||||||
|
|
||||||
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), same(settings), any(ChannelPromise.class));
|
verify(frameWriter).writeSettings(any(ChannelHandlerContext.class), same(settings), any(Promise.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -619,14 +618,14 @@ public class Http2FrameCodecTest {
|
|||||||
final Promise<Void> listenerExecuted = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
|
final Promise<Void> listenerExecuted = new DefaultPromise<>(GlobalEventExecutor.INSTANCE);
|
||||||
|
|
||||||
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers(), false).stream(stream))
|
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers(), false).stream(stream))
|
||||||
.addListener((ChannelFutureListener) future -> {
|
.addListener(future -> {
|
||||||
assertTrue(future.isSuccess());
|
assertTrue(future.isSuccess());
|
||||||
assertTrue(isStreamIdValid(stream.id()));
|
assertTrue(isStreamIdValid(stream.id()));
|
||||||
listenerExecuted.setSuccess(null);
|
listenerExecuted.setSuccess(null);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
ByteBuf data = Unpooled.buffer().writeZero(100);
|
ByteBuf data = Unpooled.buffer().writeZero(100);
|
||||||
ChannelFuture f = channel.writeAndFlush(new DefaultHttp2DataFrame(data).stream(stream));
|
Future<Void> f = channel.writeAndFlush(new DefaultHttp2DataFrame(data).stream(stream));
|
||||||
assertTrue(f.isSuccess());
|
assertTrue(f.isSuccess());
|
||||||
|
|
||||||
listenerExecuted.syncUninterruptibly();
|
listenerExecuted.syncUninterruptibly();
|
||||||
@ -641,8 +640,8 @@ public class Http2FrameCodecTest {
|
|||||||
Http2FrameStream stream1 = frameCodec.newStream();
|
Http2FrameStream stream1 = frameCodec.newStream();
|
||||||
Http2FrameStream stream2 = frameCodec.newStream();
|
Http2FrameStream stream2 = frameCodec.newStream();
|
||||||
|
|
||||||
ChannelPromise promise1 = channel.newPromise();
|
Promise<Void> promise1 = channel.newPromise();
|
||||||
ChannelPromise promise2 = channel.newPromise();
|
Promise<Void> promise2 = channel.newPromise();
|
||||||
|
|
||||||
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1);
|
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1);
|
||||||
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2);
|
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2);
|
||||||
@ -672,9 +671,9 @@ public class Http2FrameCodecTest {
|
|||||||
Http2FrameStream stream2 = frameCodec.newStream();
|
Http2FrameStream stream2 = frameCodec.newStream();
|
||||||
Http2FrameStream stream3 = frameCodec.newStream();
|
Http2FrameStream stream3 = frameCodec.newStream();
|
||||||
|
|
||||||
ChannelPromise promise1 = channel.newPromise();
|
Promise<Void> promise1 = channel.newPromise();
|
||||||
ChannelPromise promise2 = channel.newPromise();
|
Promise<Void> promise2 = channel.newPromise();
|
||||||
ChannelPromise promise3 = channel.newPromise();
|
Promise<Void> promise3 = channel.newPromise();
|
||||||
|
|
||||||
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1);
|
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1), promise1);
|
||||||
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2);
|
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2), promise2);
|
||||||
@ -712,8 +711,8 @@ public class Http2FrameCodecTest {
|
|||||||
Http2FrameStream stream1 = frameCodec.newStream();
|
Http2FrameStream stream1 = frameCodec.newStream();
|
||||||
Http2FrameStream stream2 = frameCodec.newStream();
|
Http2FrameStream stream2 = frameCodec.newStream();
|
||||||
|
|
||||||
ChannelPromise stream1HeaderPromise = channel.newPromise();
|
Promise<Void> stream1HeaderPromise = channel.newPromise();
|
||||||
ChannelPromise stream2HeaderPromise = channel.newPromise();
|
Promise<Void> stream2HeaderPromise = channel.newPromise();
|
||||||
|
|
||||||
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1),
|
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream1),
|
||||||
stream1HeaderPromise);
|
stream1HeaderPromise);
|
||||||
@ -733,15 +732,18 @@ public class Http2FrameCodecTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void streamIdentifiersExhausted() throws Http2Exception {
|
public void streamIdentifiersExhausted() throws Exception {
|
||||||
int maxServerStreamId = Integer.MAX_VALUE - 1;
|
int maxServerStreamId = Integer.MAX_VALUE - 1;
|
||||||
|
|
||||||
assertNotNull(frameCodec.connection().local().createStream(maxServerStreamId, false));
|
channel.eventLoop().submit(() -> {
|
||||||
|
assertNotNull(frameCodec.connection().local().createStream(maxServerStreamId, false));
|
||||||
|
return null;
|
||||||
|
}).sync();
|
||||||
|
|
||||||
Http2FrameStream stream = frameCodec.newStream();
|
Http2FrameStream stream = frameCodec.newStream();
|
||||||
assertNotNull(stream);
|
assertNotNull(stream);
|
||||||
|
|
||||||
ChannelPromise writePromise = channel.newPromise();
|
Promise<Void> writePromise = channel.newPromise();
|
||||||
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream), writePromise);
|
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream), writePromise);
|
||||||
|
|
||||||
Http2GoAwayFrame goAwayFrame = inboundHandler.readInbound();
|
Http2GoAwayFrame goAwayFrame = inboundHandler.readInbound();
|
||||||
@ -866,7 +868,7 @@ public class Http2FrameCodecTest {
|
|||||||
|
|
||||||
final AtomicBoolean listenerExecuted = new AtomicBoolean();
|
final AtomicBoolean listenerExecuted = new AtomicBoolean();
|
||||||
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2))
|
channel.writeAndFlush(new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()).stream(stream2))
|
||||||
.addListener((ChannelFutureListener) future -> {
|
.addListener(future -> {
|
||||||
assertTrue(future.isSuccess());
|
assertTrue(future.isSuccess());
|
||||||
assertEquals(State.OPEN, stream2.state());
|
assertEquals(State.OPEN, stream2.state());
|
||||||
listenerExecuted.set(true);
|
listenerExecuted.set(true);
|
||||||
@ -900,12 +902,12 @@ public class Http2FrameCodecTest {
|
|||||||
// Simulate consuming the frame and update the flow-controller.
|
// Simulate consuming the frame and update the flow-controller.
|
||||||
Http2DataFrame data = (Http2DataFrame) msg;
|
Http2DataFrame data = (Http2DataFrame) msg;
|
||||||
ctx.writeAndFlush(new DefaultHttp2WindowUpdateFrame(data.initialFlowControlledBytes())
|
ctx.writeAndFlush(new DefaultHttp2WindowUpdateFrame(data.initialFlowControlledBytes())
|
||||||
.stream(data.stream())).addListener((ChannelFutureListener) future -> {
|
.stream(data.stream())).addListener(future -> {
|
||||||
Throwable cause = future.cause();
|
Throwable cause = future.cause();
|
||||||
if (cause != null) {
|
if (cause != null) {
|
||||||
ctx.fireExceptionCaught(cause);
|
ctx.fireExceptionCaught(cause);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ReferenceCountUtil.release(msg);
|
ReferenceCountUtil.release(msg);
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,15 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.util.Attribute;
|
import io.netty.util.Attribute;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
@ -224,91 +224,91 @@ final class Http2FrameInboundWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture bind(SocketAddress localAddress) {
|
public Future<Void> bind(SocketAddress localAddress) {
|
||||||
return channel.bind(localAddress);
|
return channel.bind(localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress) {
|
public Future<Void> connect(SocketAddress remoteAddress) {
|
||||||
return channel.connect(remoteAddress);
|
return channel.connect(remoteAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
||||||
return channel.connect(remoteAddress, localAddress);
|
return channel.connect(remoteAddress, localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture disconnect() {
|
public Future<Void> disconnect() {
|
||||||
return channel.disconnect();
|
return channel.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close() {
|
public Future<Void> close() {
|
||||||
return channel.close();
|
return channel.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register() {
|
public Future<Void> register() {
|
||||||
return channel.register();
|
return channel.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture deregister() {
|
public Future<Void> deregister() {
|
||||||
return channel.deregister();
|
return channel.deregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
|
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return channel.bind(localAddress, promise);
|
return channel.bind(localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
|
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
|
||||||
return channel.connect(remoteAddress, promise);
|
return channel.connect(remoteAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
|
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return channel.connect(remoteAddress, localAddress, promise);
|
return channel.connect(remoteAddress, localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture disconnect(ChannelPromise promise) {
|
public Future<Void> disconnect(Promise<Void> promise) {
|
||||||
return channel.disconnect(promise);
|
return channel.disconnect(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close(ChannelPromise promise) {
|
public Future<Void> close(Promise<Void> promise) {
|
||||||
return channel.close(promise);
|
return channel.close(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register(ChannelPromise promise) {
|
public Future<Void> register(Promise<Void> promise) {
|
||||||
return channel.register(promise);
|
return channel.register(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture deregister(ChannelPromise promise) {
|
public Future<Void> deregister(Promise<Void> promise) {
|
||||||
return channel.deregister(promise);
|
return channel.deregister(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture write(Object msg) {
|
public Future<Void> write(Object msg) {
|
||||||
return write(msg, newPromise());
|
return write(msg, newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture write(Object msg, ChannelPromise promise) {
|
public Future<Void> write(Object msg, Promise<Void> promise) {
|
||||||
return writeAndFlush(msg, promise);
|
return writeAndFlush(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
|
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
|
||||||
try {
|
try {
|
||||||
channel.writeInbound(msg);
|
channel.writeInbound(msg);
|
||||||
channel.runPendingTasks();
|
channel.runPendingTasks();
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} catch (Throwable cause) {
|
} catch (Throwable cause) {
|
||||||
promise.setFailure(cause);
|
promise.setFailure(cause);
|
||||||
}
|
}
|
||||||
@ -316,22 +316,22 @@ final class Http2FrameInboundWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeAndFlush(Object msg) {
|
public Future<Void> writeAndFlush(Object msg) {
|
||||||
return writeAndFlush(msg, newPromise());
|
return writeAndFlush(msg, newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelPromise newPromise() {
|
public Promise<Void> newPromise() {
|
||||||
return channel.newPromise();
|
return channel.newPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture newSucceededFuture() {
|
public Future<Void> newSucceededFuture() {
|
||||||
return channel.newSucceededFuture();
|
return channel.newSucceededFuture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture newFailedFuture(Throwable cause) {
|
public Future<Void> newFailedFuture(Throwable cause) {
|
||||||
return channel.newFailedFuture(cause);
|
return channel.newFailedFuture(cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,11 +22,11 @@ import io.netty.buffer.EmptyByteBuf;
|
|||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.DefaultChannelPromise;
|
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
|
import io.netty.util.concurrent.DefaultPromise;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
import io.netty.util.concurrent.GlobalEventExecutor;
|
import io.netty.util.concurrent.GlobalEventExecutor;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -34,7 +34,6 @@ import org.junit.jupiter.api.function.Executable;
|
|||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockitoAnnotations;
|
import org.mockito.MockitoAnnotations;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -55,10 +54,10 @@ import static org.mockito.Mockito.any;
|
|||||||
import static org.mockito.Mockito.anyBoolean;
|
import static org.mockito.Mockito.anyBoolean;
|
||||||
import static org.mockito.Mockito.anyInt;
|
import static org.mockito.Mockito.anyInt;
|
||||||
import static org.mockito.Mockito.anyShort;
|
import static org.mockito.Mockito.anyShort;
|
||||||
import static org.mockito.Mockito.eq;
|
|
||||||
import static org.mockito.Mockito.isA;
|
|
||||||
import static org.mockito.Mockito.atLeastOnce;
|
import static org.mockito.Mockito.atLeastOnce;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
|
import static org.mockito.Mockito.isA;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@ -66,6 +65,7 @@ import static org.mockito.Mockito.when;
|
|||||||
/**
|
/**
|
||||||
* Tests encoding/decoding each HTTP2 frame type.
|
* Tests encoding/decoding each HTTP2 frame type.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class Http2FrameRoundtripTest {
|
public class Http2FrameRoundtripTest {
|
||||||
private static final byte[] MESSAGE = "hello world".getBytes(UTF_8);
|
private static final byte[] MESSAGE = "hello world".getBytes(UTF_8);
|
||||||
private static final int STREAM_ID = 0x7FFFFFFF;
|
private static final int STREAM_ID = 0x7FFFFFFF;
|
||||||
@ -100,8 +100,8 @@ public class Http2FrameRoundtripTest {
|
|||||||
when(ctx.channel()).thenReturn(channel);
|
when(ctx.channel()).thenReturn(channel);
|
||||||
doAnswer((Answer<ByteBuf>) in -> Unpooled.buffer()).when(alloc).buffer();
|
doAnswer((Answer<ByteBuf>) in -> Unpooled.buffer()).when(alloc).buffer();
|
||||||
doAnswer((Answer<ByteBuf>) in -> Unpooled.buffer((Integer) in.getArguments()[0])).when(alloc).buffer(anyInt());
|
doAnswer((Answer<ByteBuf>) in -> Unpooled.buffer((Integer) in.getArguments()[0])).when(alloc).buffer(anyInt());
|
||||||
doAnswer((Answer<ChannelPromise>) invocation ->
|
doAnswer((Answer<Promise<Void>>) invocation ->
|
||||||
new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE)).when(ctx).newPromise();
|
new DefaultPromise<Void>(GlobalEventExecutor.INSTANCE)).when(ctx).newPromise();
|
||||||
|
|
||||||
writer = new DefaultHttp2FrameWriter(new DefaultHttp2HeadersEncoder(NEVER_SENSITIVE, newTestEncoder()));
|
writer = new DefaultHttp2FrameWriter(new DefaultHttp2HeadersEncoder(NEVER_SENSITIVE, newTestEncoder()));
|
||||||
reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(false, newTestDecoder()));
|
reader = new DefaultHttp2FrameReader(new DefaultHttp2HeadersDecoder(false, newTestDecoder()));
|
||||||
@ -433,7 +433,7 @@ public class Http2FrameRoundtripTest {
|
|||||||
|
|
||||||
private ByteBuf captureWrites() {
|
private ByteBuf captureWrites() {
|
||||||
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
ArgumentCaptor<ByteBuf> captor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||||
verify(ctx, atLeastOnce()).write(captor.capture(), isA(ChannelPromise.class));
|
verify(ctx, atLeastOnce()).write(captor.capture(), isA(Promise.class));
|
||||||
CompositeByteBuf composite = releaseLater(Unpooled.compositeBuffer());
|
CompositeByteBuf composite = releaseLater(Unpooled.compositeBuffer());
|
||||||
for (ByteBuf buf : captor.getAllValues()) {
|
for (ByteBuf buf : captor.getAllValues()) {
|
||||||
buf = releaseLater(buf.retain());
|
buf = releaseLater(buf.retain());
|
||||||
|
@ -66,7 +66,10 @@ public abstract class Http2MultiplexClientUpgradeTest<C extends Http2FrameCodec>
|
|||||||
C codec = newCodec(upgradeHandler);
|
C codec = newCodec(upgradeHandler);
|
||||||
EmbeddedChannel ch = new EmbeddedChannel(codec, newMultiplexer(upgradeHandler));
|
EmbeddedChannel ch = new EmbeddedChannel(codec, newMultiplexer(upgradeHandler));
|
||||||
|
|
||||||
codec.onHttpClientUpgrade();
|
ch.eventLoop().submit(() -> {
|
||||||
|
codec.onHttpClientUpgrade();
|
||||||
|
return null;
|
||||||
|
}).sync();
|
||||||
|
|
||||||
assertFalse(upgradeHandler.stateOnActive.localSideOpen());
|
assertFalse(upgradeHandler.stateOnActive.localSideOpen());
|
||||||
assertTrue(upgradeHandler.stateOnActive.remoteSideOpen());
|
assertTrue(upgradeHandler.stateOnActive.remoteSideOpen());
|
||||||
|
@ -17,11 +17,8 @@ package io.netty.handler.codec.http2;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.WriteBufferWaterMark;
|
import io.netty.channel.WriteBufferWaterMark;
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.handler.codec.http.HttpHeaderNames;
|
import io.netty.handler.codec.http.HttpHeaderNames;
|
||||||
@ -31,12 +28,12 @@ import io.netty.handler.codec.http2.Http2Exception.StreamException;
|
|||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.hamcrest.CoreMatchers;
|
import org.hamcrest.CoreMatchers;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.function.Executable;
|
import org.junit.jupiter.api.function.Executable;
|
||||||
import org.mockito.ArgumentMatcher;
|
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
@ -75,6 +72,7 @@ import static org.mockito.Mockito.never;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
||||||
private final Http2Headers request = new DefaultHttp2Headers()
|
private final Http2Headers request = new DefaultHttp2Headers()
|
||||||
.method(HttpMethod.GET.asciiName()).scheme(HttpScheme.HTTPS.name())
|
.method(HttpMethod.GET.asciiName()).scheme(HttpScheme.HTTPS.name())
|
||||||
@ -153,7 +151,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
assertTrue(childChannel.isActive());
|
assertTrue(childChannel.isActive());
|
||||||
|
|
||||||
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq((byte) 99), eqStreamId(childChannel),
|
verify(frameWriter).writeFrame(any(ChannelHandlerContext.class), eq((byte) 99), eqStreamId(childChannel),
|
||||||
any(Http2Flags.class), any(ByteBuf.class), any(ChannelPromise.class));
|
any(Http2Flags.class), any(ByteBuf.class), any(Promise.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Http2StreamChannel newInboundStream(int streamId, boolean endStream, final ChannelHandler childHandler) {
|
private Http2StreamChannel newInboundStream(int streamId, boolean endStream, final ChannelHandler childHandler) {
|
||||||
@ -561,18 +559,18 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() {
|
public void outboundStreamShouldNotWriteResetFrameOnClose_IfStreamDidntExist() {
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
any(Http2Headers.class), anyInt(), anyBoolean(),
|
any(Http2Headers.class), anyInt(), anyBoolean(),
|
||||||
any(ChannelPromise.class))).thenAnswer(new Answer<ChannelFuture>() {
|
any(Promise.class))).thenAnswer(new Answer<Future<Void>>() {
|
||||||
|
|
||||||
private boolean headersWritten;
|
private boolean headersWritten;
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture answer(InvocationOnMock invocationOnMock) {
|
public Future<Void> answer(InvocationOnMock invocationOnMock) {
|
||||||
// We want to fail to write the first headers frame. This is what happens if the connection
|
// We want to fail to write the first headers frame. This is what happens if the connection
|
||||||
// refuses to allocate a new stream due to having received a GOAWAY.
|
// refuses to allocate a new stream due to having received a GOAWAY.
|
||||||
if (!headersWritten) {
|
if (!headersWritten) {
|
||||||
headersWritten = true;
|
headersWritten = true;
|
||||||
return ((ChannelPromise) invocationOnMock.getArgument(5)).setFailure(new Exception("boom"));
|
return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(new Exception("boom"));
|
||||||
}
|
}
|
||||||
return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess();
|
return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -636,11 +634,11 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
Http2Headers headers = new DefaultHttp2Headers();
|
Http2Headers headers = new DefaultHttp2Headers();
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
eq(headers), anyInt(), anyBoolean(),
|
eq(headers), anyInt(), anyBoolean(),
|
||||||
any(ChannelPromise.class))).thenAnswer(invocationOnMock -> {
|
any(Promise.class))).thenAnswer(invocationOnMock -> {
|
||||||
return ((ChannelPromise) invocationOnMock.getArgument(5)).setFailure(
|
return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(
|
||||||
new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed"));
|
new StreamException(childChannel.stream().id(), Http2Error.STREAM_CLOSED, "Stream Closed"));
|
||||||
});
|
});
|
||||||
final ChannelFuture future = childChannel.writeAndFlush(
|
final Future<Void> future = childChannel.writeAndFlush(
|
||||||
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()));
|
new DefaultHttp2HeadersFrame(new DefaultHttp2Headers()));
|
||||||
|
|
||||||
parentChannel.flush();
|
parentChannel.flush();
|
||||||
@ -700,12 +698,12 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
Http2Headers headers = new DefaultHttp2Headers();
|
Http2Headers headers = new DefaultHttp2Headers();
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
eq(headers), anyInt(), anyBoolean(), any(ChannelPromise.class))).thenAnswer(invocationOnMock -> {
|
eq(headers), anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(invocationOnMock -> {
|
||||||
return ((ChannelPromise) invocationOnMock.getArgument(5)).setFailure(
|
return ((Promise<Void>) invocationOnMock.getArgument(5)).setFailure(
|
||||||
new Http2NoMoreStreamIdsException());
|
new Http2NoMoreStreamIdsException());
|
||||||
});
|
});
|
||||||
|
|
||||||
final ChannelFuture future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
|
final Future<Void> future = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
|
||||||
parentChannel.flush();
|
parentChannel.flush();
|
||||||
|
|
||||||
assertFalse(childChannel.isActive());
|
assertFalse(childChannel.isActive());
|
||||||
@ -735,10 +733,10 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
// Create a promise before actually doing the close, because otherwise we would be adding a listener to a future
|
// Create a promise before actually doing the close, because otherwise we would be adding a listener to a future
|
||||||
// that is already completed because we are using EmbeddedChannel which executes code in the JUnit thread.
|
// that is already completed because we are using EmbeddedChannel which executes code in the JUnit thread.
|
||||||
ChannelPromise p = childChannel.newPromise();
|
Promise<Void> p = childChannel.newPromise();
|
||||||
p.addListener((ChannelFutureListener) future -> {
|
p.addListener(childChannel, (channel, future) -> {
|
||||||
channelOpen.set(future.channel().isOpen());
|
channelOpen.set(channel.isOpen());
|
||||||
channelActive.set(future.channel().isActive());
|
channelActive.set(channel.isActive());
|
||||||
});
|
});
|
||||||
childChannel.close(p).syncUninterruptibly();
|
childChannel.close(p).syncUninterruptibly();
|
||||||
|
|
||||||
@ -758,9 +756,9 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
final AtomicBoolean channelOpen = new AtomicBoolean(true);
|
final AtomicBoolean channelOpen = new AtomicBoolean(true);
|
||||||
final AtomicBoolean channelActive = new AtomicBoolean(true);
|
final AtomicBoolean channelActive = new AtomicBoolean(true);
|
||||||
|
|
||||||
childChannel.closeFuture().addListener((ChannelFutureListener) future -> {
|
childChannel.closeFuture().addListener(childChannel, (channel, future) -> {
|
||||||
channelOpen.set(future.channel().isOpen());
|
channelOpen.set(channel.isOpen());
|
||||||
channelActive.set(future.channel().isActive());
|
channelActive.set(channel.isActive());
|
||||||
});
|
});
|
||||||
childChannel.close().syncUninterruptibly();
|
childChannel.close().syncUninterruptibly();
|
||||||
|
|
||||||
@ -771,7 +769,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void channelClosedWhenWriteFutureFails() {
|
public void channelClosedWhenWriteFutureFails() {
|
||||||
final Queue<ChannelPromise> writePromises = new ArrayDeque<ChannelPromise>();
|
final Queue<Promise<Void>> writePromises = new ArrayDeque<>();
|
||||||
|
|
||||||
LastInboundHandler inboundHandler = new LastInboundHandler();
|
LastInboundHandler inboundHandler = new LastInboundHandler();
|
||||||
Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler);
|
Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler);
|
||||||
@ -785,20 +783,20 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
Http2Headers headers = new DefaultHttp2Headers();
|
Http2Headers headers = new DefaultHttp2Headers();
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
eq(headers), anyInt(), anyBoolean(),
|
eq(headers), anyInt(), anyBoolean(),
|
||||||
any(ChannelPromise.class))).thenAnswer(invocationOnMock -> {
|
any(Promise.class))).thenAnswer(invocationOnMock -> {
|
||||||
ChannelPromise promise = invocationOnMock.getArgument(5);
|
Promise<Void> promise = invocationOnMock.getArgument(5);
|
||||||
writePromises.offer(promise);
|
writePromises.offer(promise);
|
||||||
return promise;
|
return promise;
|
||||||
});
|
});
|
||||||
|
|
||||||
ChannelFuture f = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
|
Future<Void> f = childChannel.writeAndFlush(new DefaultHttp2HeadersFrame(headers));
|
||||||
assertFalse(f.isDone());
|
assertFalse(f.isDone());
|
||||||
f.addListener((ChannelFutureListener) future-> {
|
f.addListener(childChannel, (channel, future)-> {
|
||||||
channelOpen.set(future.channel().isOpen());
|
channelOpen.set(channel.isOpen());
|
||||||
channelActive.set(future.channel().isActive());
|
channelActive.set(channel.isActive());
|
||||||
});
|
});
|
||||||
|
|
||||||
ChannelPromise first = writePromises.poll();
|
Promise<Void> first = writePromises.poll();
|
||||||
first.setFailure(new ClosedChannelException());
|
first.setFailure(new ClosedChannelException());
|
||||||
f.awaitUninterruptibly();
|
f.awaitUninterruptibly();
|
||||||
|
|
||||||
@ -885,7 +883,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
assertTrue(childChannel.isWritable());
|
assertTrue(childChannel.isWritable());
|
||||||
assertEquals(bytesBeforeUnwritable, childChannel.bytesBeforeUnwritable());
|
assertEquals(bytesBeforeUnwritable, childChannel.bytesBeforeUnwritable());
|
||||||
|
|
||||||
ChannelFuture future = childChannel.writeAndFlush(new DefaultHttp2DataFrame(
|
Future<Void> future = childChannel.writeAndFlush(new DefaultHttp2DataFrame(
|
||||||
Unpooled.buffer().writeZero((int) bytesBeforeUnwritable)));
|
Unpooled.buffer().writeZero((int) bytesBeforeUnwritable)));
|
||||||
assertFalse(childChannel.isWritable());
|
assertFalse(childChannel.isWritable());
|
||||||
assertTrue(parentChannel.isWritable());
|
assertTrue(parentChannel.isWritable());
|
||||||
@ -978,7 +976,7 @@ public abstract class Http2MultiplexTest<C extends Http2FrameCodec> {
|
|||||||
Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler);
|
Http2StreamChannel childChannel = newInboundStream(3, false, inboundHandler);
|
||||||
childChannel.unsafe().close(childChannel.newPromise());
|
childChannel.unsafe().close(childChannel.newPromise());
|
||||||
|
|
||||||
ChannelPromise promise = childChannel.newPromise();
|
Promise<Void> promise = childChannel.newPromise();
|
||||||
childChannel.unsafe().close(promise);
|
childChannel.unsafe().close(promise);
|
||||||
promise.syncUninterruptibly();
|
promise.syncUninterruptibly();
|
||||||
childChannel.closeFuture().syncUninterruptibly();
|
childChannel.closeFuture().syncUninterruptibly();
|
||||||
|
@ -19,7 +19,6 @@ import io.netty.bootstrap.Bootstrap;
|
|||||||
import io.netty.bootstrap.ServerBootstrap;
|
import io.netty.bootstrap.ServerBootstrap;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerAdapter;
|
import io.netty.channel.ChannelHandlerAdapter;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
@ -44,8 +43,6 @@ import io.netty.handler.ssl.util.SelfSignedCertificate;
|
|||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.NetUtil;
|
import io.netty.util.NetUtil;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.concurrent.Future;
|
|
||||||
import io.netty.util.concurrent.FutureListener;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Disabled;
|
import org.junit.jupiter.api.Disabled;
|
||||||
@ -221,13 +218,12 @@ public class Http2MultiplexTransportTest {
|
|||||||
if (msg instanceof Http2HeadersFrame && ((Http2HeadersFrame) msg).isEndStream()) {
|
if (msg instanceof Http2HeadersFrame && ((Http2HeadersFrame) msg).isEndStream()) {
|
||||||
executorService.schedule(() -> {
|
executorService.schedule(() -> {
|
||||||
ctx.writeAndFlush(new DefaultHttp2HeadersFrame(
|
ctx.writeAndFlush(new DefaultHttp2HeadersFrame(
|
||||||
new DefaultHttp2Headers(), false)).addListener(
|
new DefaultHttp2Headers(), false)).addListener(future -> {
|
||||||
(ChannelFutureListener) future -> {
|
ctx.write(new DefaultHttp2DataFrame(
|
||||||
ctx.write(new DefaultHttp2DataFrame(
|
Unpooled.copiedBuffer("Hello World",
|
||||||
Unpooled.copiedBuffer("Hello World",
|
CharsetUtil.US_ASCII), true));
|
||||||
CharsetUtil.US_ASCII), true));
|
ctx.channel().eventLoop().execute(ctx::flush);
|
||||||
ctx.channel().eventLoop().execute(ctx::flush);
|
});
|
||||||
});
|
|
||||||
}, 500, MILLISECONDS);
|
}, 500, MILLISECONDS);
|
||||||
}
|
}
|
||||||
ReferenceCountUtil.release(msg);
|
ReferenceCountUtil.release(msg);
|
||||||
@ -406,15 +402,12 @@ public class Http2MultiplexTransportTest {
|
|||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
h2Bootstrap.open().addListener(new FutureListener<Channel>() {
|
h2Bootstrap.open().addListener(future -> {
|
||||||
@Override
|
if (future.isSuccess()) {
|
||||||
public void operationComplete(Future<Channel> future) {
|
future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame(
|
||||||
if (future.isSuccess()) {
|
new DefaultHttp2Headers(), false));
|
||||||
future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame(
|
}
|
||||||
new DefaultHttp2Headers(), false));
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
} else if (handshakeCompletionEvent.cause() instanceof SSLException) {
|
} else if (handshakeCompletionEvent.cause() instanceof SSLException) {
|
||||||
// In case of TLSv1.2 we should never see the handshake succeed as the alert for
|
// In case of TLSv1.2 we should never see the handshake succeed as the alert for
|
||||||
@ -548,13 +541,10 @@ public class Http2MultiplexTransportTest {
|
|||||||
ReferenceCountUtil.release(msg);
|
ReferenceCountUtil.release(msg);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
h2Bootstrap.open().addListener(new FutureListener<Channel>() {
|
h2Bootstrap.open().addListener(future -> {
|
||||||
@Override
|
if (future.isSuccess()) {
|
||||||
public void operationComplete(Future<Channel> future) {
|
future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame(
|
||||||
if (future.isSuccess()) {
|
new DefaultHttp2Headers(), true));
|
||||||
future.getNow().writeAndFlush(new DefaultHttp2HeadersFrame(
|
|
||||||
new DefaultHttp2Headers(), true));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import io.netty.buffer.ByteBufAllocator;
|
|||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
import io.netty.handler.codec.EncoderException;
|
import io.netty.handler.codec.EncoderException;
|
||||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||||
@ -39,13 +38,14 @@ import io.netty.handler.codec.http.HttpRequest;
|
|||||||
import io.netty.handler.codec.http.HttpResponse;
|
import io.netty.handler.codec.http.HttpResponse;
|
||||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import io.netty.handler.codec.http.HttpScheme;
|
import io.netty.handler.codec.http.HttpScheme;
|
||||||
import io.netty.handler.codec.http.HttpVersion;
|
|
||||||
import io.netty.handler.codec.http.HttpUtil;
|
import io.netty.handler.codec.http.HttpUtil;
|
||||||
|
import io.netty.handler.codec.http.HttpVersion;
|
||||||
import io.netty.handler.codec.http.LastHttpContent;
|
import io.netty.handler.codec.http.LastHttpContent;
|
||||||
import io.netty.handler.ssl.SslContext;
|
import io.netty.handler.ssl.SslContext;
|
||||||
import io.netty.handler.ssl.SslContextBuilder;
|
import io.netty.handler.ssl.SslContextBuilder;
|
||||||
import io.netty.handler.ssl.SslProvider;
|
import io.netty.handler.ssl.SslProvider;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.function.Executable;
|
import org.junit.jupiter.api.function.Executable;
|
||||||
|
|
||||||
@ -56,10 +56,10 @@ import static org.hamcrest.CoreMatchers.is;
|
|||||||
import static org.hamcrest.CoreMatchers.nullValue;
|
import static org.hamcrest.CoreMatchers.nullValue;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
|
|
||||||
public class Http2StreamFrameToHttpObjectCodecTest {
|
public class Http2StreamFrameToHttpObjectCodecTest {
|
||||||
|
|
||||||
@ -454,7 +454,7 @@ public class Http2StreamFrameToHttpObjectCodecTest {
|
|||||||
EmbeddedChannel ch = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT),
|
EmbeddedChannel ch = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT),
|
||||||
new ChannelHandler() {
|
new ChannelHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
if (msg instanceof Http2StreamFrame) {
|
if (msg instanceof Http2StreamFrame) {
|
||||||
frames.add((Http2StreamFrame) msg);
|
frames.add((Http2StreamFrame) msg);
|
||||||
ctx.write(Unpooled.EMPTY_BUFFER, promise);
|
ctx.write(Unpooled.EMPTY_BUFFER, promise);
|
||||||
@ -884,10 +884,10 @@ public class Http2StreamFrameToHttpObjectCodecTest {
|
|||||||
EmbeddedChannel tlsCh = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT),
|
EmbeddedChannel tlsCh = new EmbeddedChannel(ctx.newHandler(ByteBufAllocator.DEFAULT),
|
||||||
new ChannelHandler() {
|
new ChannelHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
if (msg instanceof Http2StreamFrame) {
|
if (msg instanceof Http2StreamFrame) {
|
||||||
frames.add((Http2StreamFrame) msg);
|
frames.add((Http2StreamFrame) msg);
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
ctx.write(msg, promise);
|
ctx.write(msg, promise);
|
||||||
}
|
}
|
||||||
@ -897,10 +897,10 @@ public class Http2StreamFrameToHttpObjectCodecTest {
|
|||||||
EmbeddedChannel plaintextCh = new EmbeddedChannel(
|
EmbeddedChannel plaintextCh = new EmbeddedChannel(
|
||||||
new ChannelHandler() {
|
new ChannelHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
if (msg instanceof Http2StreamFrame) {
|
if (msg instanceof Http2StreamFrame) {
|
||||||
frames.add((Http2StreamFrame) msg);
|
frames.add((Http2StreamFrame) msg);
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
} else {
|
} else {
|
||||||
ctx.write(msg, promise);
|
ctx.write(msg, promise);
|
||||||
}
|
}
|
||||||
|
@ -19,19 +19,13 @@ import io.netty.buffer.ByteBufUtil;
|
|||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
import io.netty.channel.DefaultChannelPromise;
|
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
import io.netty.util.concurrent.GenericFutureListener;
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
|
||||||
import junit.framework.AssertionFailedError;
|
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
import org.mockito.stubbing.Answer;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
@ -56,6 +50,7 @@ import static org.mockito.Mockito.when;
|
|||||||
/**
|
/**
|
||||||
* Utilities for the integration tests.
|
* Utilities for the integration tests.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public final class Http2TestUtil {
|
public final class Http2TestUtil {
|
||||||
/**
|
/**
|
||||||
* Interface that allows for running a operation that throws a {@link Http2Exception}.
|
* Interface that allows for running a operation that throws a {@link Http2Exception}.
|
||||||
@ -367,55 +362,55 @@ public final class Http2TestUtil {
|
|||||||
|
|
||||||
when(frameWriter.configuration()).thenReturn(configuration);
|
when(frameWriter.configuration()).thenReturn(configuration);
|
||||||
when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class),
|
when(frameWriter.writeSettings(any(ChannelHandlerContext.class), any(Http2Settings.class),
|
||||||
any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock ->
|
any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||||
((ChannelPromise) invocationOnMock.getArgument(2)).setSuccess());
|
((Promise<Void>) invocationOnMock.getArgument(2)).setSuccess(null));
|
||||||
|
|
||||||
when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class), any(ChannelPromise.class)))
|
when(frameWriter.writeSettingsAck(any(ChannelHandlerContext.class), any(Promise.class)))
|
||||||
.thenAnswer((Answer<ChannelFuture>) invocationOnMock ->
|
.thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||||
((ChannelPromise) invocationOnMock.getArgument(1)).setSuccess());
|
((Promise<Void>) invocationOnMock.getArgument(1)).setSuccess(null));
|
||||||
|
|
||||||
when(frameWriter.writeGoAway(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeGoAway(any(ChannelHandlerContext.class), anyInt(),
|
||||||
anyLong(), any(ByteBuf.class), any(ChannelPromise.class))).thenAnswer(invocationOnMock -> {
|
anyLong(), any(ByteBuf.class), any(Promise.class))).thenAnswer(invocationOnMock -> {
|
||||||
buffers.offer((ByteBuf) invocationOnMock.getArgument(3));
|
buffers.offer((ByteBuf) invocationOnMock.getArgument(3));
|
||||||
return ((ChannelPromise) invocationOnMock.getArgument(4)).setSuccess();
|
return ((Promise<Void>) invocationOnMock.getArgument(4)).setSuccess(null);
|
||||||
});
|
});
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
||||||
anyBoolean(), any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock ->
|
anyBoolean(), any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||||
((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess());
|
((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null));
|
||||||
|
|
||||||
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeHeaders(any(ChannelHandlerContext.class), anyInt(),
|
||||||
any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
|
any(Http2Headers.class), anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(),
|
||||||
any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock ->
|
any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||||
((ChannelPromise) invocationOnMock.getArgument(8)).setSuccess());
|
((Promise<Void>) invocationOnMock.getArgument(8)).setSuccess(null));
|
||||||
|
|
||||||
when(frameWriter.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(),
|
when(frameWriter.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(),
|
||||||
anyBoolean(), any(ChannelPromise.class))).thenAnswer(invocationOnMock -> {
|
anyBoolean(), any(Promise.class))).thenAnswer(invocationOnMock -> {
|
||||||
buffers.offer((ByteBuf) invocationOnMock.getArgument(2));
|
buffers.offer((ByteBuf) invocationOnMock.getArgument(2));
|
||||||
return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess();
|
return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(),
|
when(frameWriter.writeRstStream(any(ChannelHandlerContext.class), anyInt(),
|
||||||
anyLong(), any(ChannelPromise.class))).thenAnswer((Answer<ChannelFuture>) invocationOnMock ->
|
anyLong(), any(Promise.class))).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||||
((ChannelPromise) invocationOnMock.getArgument(3)).setSuccess());
|
((Promise<Void>) invocationOnMock.getArgument(3)).setSuccess(null));
|
||||||
|
|
||||||
when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
|
when(frameWriter.writeWindowUpdate(any(ChannelHandlerContext.class), anyInt(), anyInt(),
|
||||||
any(ChannelPromise.class))).then((Answer<ChannelFuture>) invocationOnMock ->
|
any(Promise.class))).then((Answer<Future<Void>>) invocationOnMock ->
|
||||||
((ChannelPromise) invocationOnMock.getArgument(3)).setSuccess());
|
((Promise<Void>) invocationOnMock.getArgument(3)).setSuccess(null));
|
||||||
|
|
||||||
when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class),
|
when(frameWriter.writePushPromise(any(ChannelHandlerContext.class), anyInt(), anyInt(), any(Http2Headers.class),
|
||||||
anyInt(), anyChannelPromise())).thenAnswer((Answer<ChannelFuture>) invocationOnMock ->
|
anyInt(), anyChannelPromise())).thenAnswer((Answer<Future<Void>>) invocationOnMock ->
|
||||||
((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess());
|
((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null));
|
||||||
|
|
||||||
when(frameWriter.writeFrame(any(ChannelHandlerContext.class), anyByte(), anyInt(), any(Http2Flags.class),
|
when(frameWriter.writeFrame(any(ChannelHandlerContext.class), anyByte(), anyInt(), any(Http2Flags.class),
|
||||||
any(ByteBuf.class), anyChannelPromise())).thenAnswer(invocationOnMock -> {
|
any(ByteBuf.class), anyChannelPromise())).thenAnswer(invocationOnMock -> {
|
||||||
buffers.offer((ByteBuf) invocationOnMock.getArgument(4));
|
buffers.offer((ByteBuf) invocationOnMock.getArgument(4));
|
||||||
return ((ChannelPromise) invocationOnMock.getArgument(5)).setSuccess();
|
return ((Promise<Void>) invocationOnMock.getArgument(5)).setSuccess(null);
|
||||||
});
|
});
|
||||||
return frameWriter;
|
return frameWriter;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ChannelPromise anyChannelPromise() {
|
static Promise<Void> anyChannelPromise() {
|
||||||
return any(ChannelPromise.class);
|
return any(Promise.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Http2Settings anyHttp2Settings() {
|
static Http2Settings anyHttp2Settings() {
|
||||||
|
@ -19,12 +19,10 @@ import io.netty.bootstrap.ServerBootstrap;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.MultithreadEventLoopGroup;
|
import io.netty.channel.MultithreadEventLoopGroup;
|
||||||
import io.netty.channel.local.LocalAddress;
|
import io.netty.channel.local.LocalAddress;
|
||||||
import io.netty.channel.local.LocalChannel;
|
import io.netty.channel.local.LocalChannel;
|
||||||
@ -43,6 +41,7 @@ import io.netty.handler.codec.http.LastHttpContent;
|
|||||||
import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown;
|
import io.netty.handler.codec.http2.Http2TestUtil.FrameCountDown;
|
||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -146,7 +145,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
.add(new AsciiString("foo"), new AsciiString("goo2"))
|
.add(new AsciiString("foo"), new AsciiString("goo2"))
|
||||||
.add(new AsciiString("foo2"), new AsciiString("goo2"));
|
.add(new AsciiString("foo2"), new AsciiString("goo2"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +168,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
.add(new AsciiString("foo"), new AsciiString("goo2"))
|
.add(new AsciiString("foo"), new AsciiString("goo2"))
|
||||||
.add(new AsciiString("foo2"), new AsciiString("goo2"));
|
.add(new AsciiString("foo2"), new AsciiString("goo2"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +189,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
.add(HttpHeaderNames.COOKIE, "c=d")
|
.add(HttpHeaderNames.COOKIE, "c=d")
|
||||||
.add(HttpHeaderNames.COOKIE, "e=f");
|
.add(HttpHeaderNames.COOKIE, "e=f");
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,7 +205,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
.path(new AsciiString("/where?q=now&f=then#section1"))
|
.path(new AsciiString("/where?q=now&f=then#section1"))
|
||||||
.scheme(new AsciiString("http"));
|
.scheme(new AsciiString("http"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,7 +222,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
.path(new AsciiString("/where%2B0?q=now%2B0&f=then%2B0#section1%2B0"))
|
.path(new AsciiString("/where%2B0?q=now%2B0&f=then%2B0#section1%2B0"))
|
||||||
.scheme(new AsciiString("http"));
|
.scheme(new AsciiString("http"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +240,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
.path(new AsciiString("/pub/WWW/TheProject.html"))
|
.path(new AsciiString("/pub/WWW/TheProject.html"))
|
||||||
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("https"));
|
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("https"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +256,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
.path(new AsciiString("/pub/WWW/TheProject.html"))
|
.path(new AsciiString("/pub/WWW/TheProject.html"))
|
||||||
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"));
|
.authority(new AsciiString("www.example.org:5555")).scheme(new AsciiString("http"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +270,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
new DefaultHttp2Headers().method(new AsciiString("CONNECT")).path(new AsciiString("/"))
|
new DefaultHttp2Headers().method(new AsciiString("CONNECT")).path(new AsciiString("/"))
|
||||||
.scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80"));
|
.scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,7 +286,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
new DefaultHttp2Headers().method(new AsciiString("OPTIONS")).path(new AsciiString("*"))
|
new DefaultHttp2Headers().method(new AsciiString("OPTIONS")).path(new AsciiString("*"))
|
||||||
.scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80"));
|
.scheme(new AsciiString("http")).authority(new AsciiString("www.example.com:80"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +304,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
|
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
|
||||||
.scheme(new AsciiString("http")).authority(new AsciiString("[::1]:80"));
|
.scheme(new AsciiString("http")).authority(new AsciiString("[::1]:80"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +320,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
|
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
|
||||||
.scheme(new AsciiString("http")).authority(new AsciiString("localhost:80"));
|
.scheme(new AsciiString("http")).authority(new AsciiString("localhost:80"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -337,7 +336,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
|
new DefaultHttp2Headers().method(new AsciiString("GET")).path(new AsciiString("/"))
|
||||||
.scheme(new AsciiString("http")).authority(new AsciiString("1.2.3.4:80"));
|
.scheme(new AsciiString("http")).authority(new AsciiString("1.2.3.4:80"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
verifyHeadersOnly(http2Headers, writePromise, clientChannel.writeAndFlush(request, writePromise));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,8 +347,8 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
final HttpHeaders httpHeaders = request.headers();
|
final HttpHeaders httpHeaders = request.headers();
|
||||||
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), 5);
|
||||||
httpHeaders.set(HttpHeaderNames.HOST, "localhost");
|
httpHeaders.set(HttpHeaderNames.HOST, "localhost");
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
||||||
|
|
||||||
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
||||||
assertTrue(writePromise.isDone());
|
assertTrue(writePromise.isDone());
|
||||||
@ -367,8 +366,8 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), -1);
|
httpHeaders.setInt(HttpConversionUtil.ExtensionHeaderNames.STREAM_ID.text(), -1);
|
||||||
httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http");
|
httpHeaders.set(HttpConversionUtil.ExtensionHeaderNames.SCHEME.text(), "http");
|
||||||
httpHeaders.set(HttpHeaderNames.HOST, "localhost");
|
httpHeaders.set(HttpHeaderNames.HOST, "localhost");
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
||||||
|
|
||||||
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
||||||
assertTrue(writePromise.isDone());
|
assertTrue(writePromise.isDone());
|
||||||
@ -406,8 +405,8 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
.add(new AsciiString("foo"), new AsciiString("goo"))
|
.add(new AsciiString("foo"), new AsciiString("goo"))
|
||||||
.add(new AsciiString("foo"), new AsciiString("goo2"))
|
.add(new AsciiString("foo"), new AsciiString("goo2"))
|
||||||
.add(new AsciiString("foo2"), new AsciiString("goo2"));
|
.add(new AsciiString("foo2"), new AsciiString("goo2"));
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
||||||
|
|
||||||
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
||||||
assertTrue(writePromise.isSuccess());
|
assertTrue(writePromise.isSuccess());
|
||||||
@ -452,8 +451,8 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
|
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
|
||||||
.add(new AsciiString("trailing"), new AsciiString("bar"));
|
.add(new AsciiString("trailing"), new AsciiString("bar"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
ChannelFuture writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
Future<Void> writeFuture = clientChannel.writeAndFlush(request, writePromise);
|
||||||
|
|
||||||
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
||||||
assertTrue(writePromise.isSuccess());
|
assertTrue(writePromise.isSuccess());
|
||||||
@ -504,12 +503,12 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
|
final Http2Headers http2TrailingHeaders = new DefaultHttp2Headers()
|
||||||
.add(new AsciiString("trailing"), new AsciiString("bar"));
|
.add(new AsciiString("trailing"), new AsciiString("bar"));
|
||||||
|
|
||||||
ChannelPromise writePromise = newPromise();
|
Promise<Void> writePromise = newPromise();
|
||||||
ChannelFuture writeFuture = clientChannel.write(request, writePromise);
|
Future<Void> writeFuture = clientChannel.write(request, writePromise);
|
||||||
ChannelPromise contentPromise = newPromise();
|
Promise<Void> contentPromise = newPromise();
|
||||||
ChannelFuture contentFuture = clientChannel.write(httpContent, contentPromise);
|
Future<Void> contentFuture = clientChannel.write(httpContent, contentPromise);
|
||||||
ChannelPromise lastContentPromise = newPromise();
|
Promise<Void> lastContentPromise = newPromise();
|
||||||
ChannelFuture lastContentFuture = clientChannel.write(lastHttpContent, lastContentPromise);
|
Future<Void> lastContentFuture = clientChannel.write(lastHttpContent, lastContentPromise);
|
||||||
|
|
||||||
clientChannel.flush();
|
clientChannel.flush();
|
||||||
|
|
||||||
@ -598,7 +597,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
assertTrue(serverChannelLatch.await(WAIT_TIME_SECONDS, SECONDS));
|
assertTrue(serverChannelLatch.await(WAIT_TIME_SECONDS, SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void verifyHeadersOnly(Http2Headers expected, ChannelPromise writePromise, ChannelFuture writeFuture)
|
private void verifyHeadersOnly(Http2Headers expected, Promise<Void> writePromise, Future<Void> writeFuture)
|
||||||
throws Exception {
|
throws Exception {
|
||||||
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
assertTrue(writePromise.awaitUninterruptibly(WAIT_TIME_SECONDS, SECONDS));
|
||||||
assertTrue(writePromise.isSuccess());
|
assertTrue(writePromise.isSuccess());
|
||||||
@ -623,7 +622,7 @@ public class HttpToHttp2ConnectionHandlerTest {
|
|||||||
return clientChannel.pipeline().firstContext();
|
return clientChannel.pipeline().firstContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise newPromise() {
|
private Promise<Void> newPromise() {
|
||||||
return ctx().newPromise();
|
return ctx().newPromise();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import io.netty.channel.ChannelHandler;
|
|||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.MultithreadEventLoopGroup;
|
import io.netty.channel.MultithreadEventLoopGroup;
|
||||||
import io.netty.channel.SimpleChannelInboundHandler;
|
import io.netty.channel.SimpleChannelInboundHandler;
|
||||||
import io.netty.channel.local.LocalAddress;
|
import io.netty.channel.local.LocalAddress;
|
||||||
@ -44,6 +43,7 @@ import io.netty.handler.codec.http.HttpVersion;
|
|||||||
import io.netty.util.AsciiString;
|
import io.netty.util.AsciiString;
|
||||||
import io.netty.util.CharsetUtil;
|
import io.netty.util.CharsetUtil;
|
||||||
import io.netty.util.concurrent.Future;
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -732,7 +732,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
return clientChannel.pipeline().firstContext();
|
return clientChannel.pipeline().firstContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise newPromiseClient() {
|
private Promise<Void> newPromiseClient() {
|
||||||
return ctxClient().newPromise();
|
return ctxClient().newPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -740,7 +740,7 @@ public class InboundHttp2ToHttpAdapterTest {
|
|||||||
return serverConnectedChannel.pipeline().firstContext();
|
return serverConnectedChannel.pipeline().firstContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise newPromiseServer() {
|
private Promise<Void> newPromiseServer() {
|
||||||
return ctxServer().newPromise();
|
return ctxServer().newPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,34 @@
|
|||||||
|
|
||||||
package io.netty.handler.codec.http2;
|
package io.netty.handler.codec.http2;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.buffer.UnpooledByteBufAllocator;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelConfig;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelMetadata;
|
||||||
|
import io.netty.channel.DefaultMessageSizeEstimator;
|
||||||
|
import io.netty.handler.codec.http2.StreamBufferingEncoder.Http2GoAwayException;
|
||||||
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.DefaultPromise;
|
||||||
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.ImmediateEventExecutor;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
import org.mockito.verification.VerificationMode;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
|
import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
|
||||||
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
|
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_MAX_FRAME_SIZE;
|
||||||
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT;
|
import static io.netty.handler.codec.http2.Http2CodecUtil.DEFAULT_PRIORITY_WEIGHT;
|
||||||
@ -30,45 +58,18 @@ import static org.mockito.Mockito.anyBoolean;
|
|||||||
import static org.mockito.Mockito.anyInt;
|
import static org.mockito.Mockito.anyInt;
|
||||||
import static org.mockito.Mockito.anyLong;
|
import static org.mockito.Mockito.anyLong;
|
||||||
import static org.mockito.Mockito.anyShort;
|
import static org.mockito.Mockito.anyShort;
|
||||||
import static org.mockito.Mockito.eq;
|
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.buffer.UnpooledByteBufAllocator;
|
|
||||||
import io.netty.channel.Channel;
|
|
||||||
import io.netty.channel.ChannelConfig;
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelMetadata;
|
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.DefaultChannelPromise;
|
|
||||||
import io.netty.channel.DefaultMessageSizeEstimator;
|
|
||||||
import io.netty.handler.codec.http2.StreamBufferingEncoder.Http2GoAwayException;
|
|
||||||
import io.netty.util.ReferenceCountUtil;
|
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
|
||||||
import io.netty.util.concurrent.ImmediateEventExecutor;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.MockitoAnnotations;
|
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
import org.mockito.verification.VerificationMode;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link StreamBufferingEncoder}.
|
* Tests for {@link StreamBufferingEncoder}.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public class StreamBufferingEncoderTest {
|
public class StreamBufferingEncoderTest {
|
||||||
|
|
||||||
private StreamBufferingEncoder encoder;
|
private StreamBufferingEncoder encoder;
|
||||||
@ -106,16 +107,16 @@ public class StreamBufferingEncoderTest {
|
|||||||
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
when(configuration.frameSizePolicy()).thenReturn(frameSizePolicy);
|
||||||
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
when(frameSizePolicy.maxFrameSize()).thenReturn(DEFAULT_MAX_FRAME_SIZE);
|
||||||
when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
|
when(writer.writeData(any(ChannelHandlerContext.class), anyInt(), any(ByteBuf.class), anyInt(), anyBoolean(),
|
||||||
any(ChannelPromise.class))).thenAnswer(successAnswer());
|
any(Promise.class))).thenAnswer(successAnswer());
|
||||||
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(ChannelPromise.class))).thenAnswer(
|
when(writer.writeRstStream(eq(ctx), anyInt(), anyLong(), any(Promise.class))).thenAnswer(
|
||||||
successAnswer());
|
successAnswer());
|
||||||
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
|
when(writer.writeGoAway(any(ChannelHandlerContext.class), anyInt(), anyLong(), any(ByteBuf.class),
|
||||||
any(ChannelPromise.class)))
|
any(Promise.class)))
|
||||||
.thenAnswer(successAnswer());
|
.thenAnswer(successAnswer());
|
||||||
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
||||||
anyInt(), anyBoolean(), any(ChannelPromise.class))).thenAnswer(noopAnswer());
|
anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(noopAnswer());
|
||||||
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class),
|
||||||
anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(ChannelPromise.class)))
|
anyInt(), anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Promise.class)))
|
||||||
.thenAnswer(noopAnswer());
|
.thenAnswer(noopAnswer());
|
||||||
|
|
||||||
connection = new DefaultHttp2Connection(false);
|
connection = new DefaultHttp2Connection(false);
|
||||||
@ -136,7 +137,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(ctx.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
when(channel.alloc()).thenReturn(UnpooledByteBufAllocator.DEFAULT);
|
||||||
when(executor.inEventLoop()).thenReturn(true);
|
when(executor.inEventLoop()).thenReturn(true);
|
||||||
doAnswer((Answer<ChannelPromise>) invocation -> newPromise()).when(ctx).newPromise();
|
doAnswer((Answer<Promise<Void>>) invocation -> newPromise()).when(ctx).newPromise();
|
||||||
when(ctx.executor()).thenReturn(executor);
|
when(ctx.executor()).thenReturn(executor);
|
||||||
when(channel.isActive()).thenReturn(false);
|
when(channel.isActive()).thenReturn(false);
|
||||||
when(channel.config()).thenReturn(config);
|
when(channel.config()).thenReturn(config);
|
||||||
@ -172,7 +173,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
// Contiguous data writes are coalesced
|
// Contiguous data writes are coalesced
|
||||||
ArgumentCaptor<ByteBuf> bufCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
ArgumentCaptor<ByteBuf> bufCaptor = ArgumentCaptor.forClass(ByteBuf.class);
|
||||||
verify(writer, times(1)).writeData(any(ChannelHandlerContext.class), eq(3),
|
verify(writer, times(1)).writeData(any(ChannelHandlerContext.class), eq(3),
|
||||||
bufCaptor.capture(), eq(0), eq(false), any(ChannelPromise.class));
|
bufCaptor.capture(), eq(0), eq(false), any(Promise.class));
|
||||||
assertEquals(expectedBytes, bufCaptor.getValue().readableBytes());
|
assertEquals(expectedBytes, bufCaptor.getValue().readableBytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -228,7 +229,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
setMaxConcurrentStreams(0);
|
setMaxConcurrentStreams(0);
|
||||||
connection.goAwayReceived(1, 8, EMPTY_BUFFER);
|
connection.goAwayReceived(1, 8, EMPTY_BUFFER);
|
||||||
|
|
||||||
ChannelPromise promise = newPromise();
|
Promise<Void> promise = newPromise();
|
||||||
encoderWriteHeaders(3, promise);
|
encoderWriteHeaders(3, promise);
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
assertTrue(promise.isDone());
|
assertTrue(promise.isDone());
|
||||||
@ -241,7 +242,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
setMaxConcurrentStreams(5);
|
setMaxConcurrentStreams(5);
|
||||||
|
|
||||||
int streamId = 3;
|
int streamId = 3;
|
||||||
List<ChannelFuture> futures = new ArrayList<>();
|
List<Future<Void>> futures = new ArrayList<>();
|
||||||
for (int i = 0; i < 9; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
futures.add(encoderWriteHeaders(streamId, newPromise()));
|
futures.add(encoderWriteHeaders(streamId, newPromise()));
|
||||||
streamId += 2;
|
streamId += 2;
|
||||||
@ -254,7 +255,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
assertEquals(5, connection.numActiveStreams());
|
assertEquals(5, connection.numActiveStreams());
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
int failCount = 0;
|
int failCount = 0;
|
||||||
for (ChannelFuture f : futures) {
|
for (Future<Void> f : futures) {
|
||||||
if (f.cause() != null) {
|
if (f.cause() != null) {
|
||||||
assertTrue(f.cause() instanceof Http2GoAwayException);
|
assertTrue(f.cause() instanceof Http2GoAwayException);
|
||||||
failCount++;
|
failCount++;
|
||||||
@ -269,7 +270,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
setMaxConcurrentStreams(1);
|
setMaxConcurrentStreams(1);
|
||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3, newPromise());
|
||||||
connection.goAwayReceived(11, 8, EMPTY_BUFFER);
|
connection.goAwayReceived(11, 8, EMPTY_BUFFER);
|
||||||
ChannelFuture f = encoderWriteHeaders(5, newPromise());
|
Future<Void> f = encoderWriteHeaders(5, newPromise());
|
||||||
|
|
||||||
assertTrue(f.cause() instanceof Http2GoAwayException);
|
assertTrue(f.cause() instanceof Http2GoAwayException);
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
@ -281,15 +282,15 @@ public class StreamBufferingEncoderTest {
|
|||||||
setMaxConcurrentStreams(1);
|
setMaxConcurrentStreams(1);
|
||||||
|
|
||||||
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
||||||
anyBoolean(), any(ChannelPromise.class))).thenAnswer(successAnswer());
|
anyBoolean(), any(Promise.class))).thenAnswer(successAnswer());
|
||||||
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
when(writer.writeHeaders(any(ChannelHandlerContext.class), anyInt(), any(Http2Headers.class), anyInt(),
|
||||||
anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(ChannelPromise.class))).thenAnswer(successAnswer());
|
anyShort(), anyBoolean(), anyInt(), anyBoolean(), any(Promise.class))).thenAnswer(successAnswer());
|
||||||
|
|
||||||
ChannelFuture f1 = encoderWriteHeaders(3, newPromise());
|
Future<Void> f1 = encoderWriteHeaders(3, newPromise());
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
ChannelFuture f2 = encoderWriteHeaders(5, newPromise());
|
Future<Void> f2 = encoderWriteHeaders(5, newPromise());
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
ChannelFuture f3 = encoderWriteHeaders(7, newPromise());
|
Future<Void> f3 = encoderWriteHeaders(7, newPromise());
|
||||||
assertEquals(2, encoder.numBufferedStreams());
|
assertEquals(2, encoder.numBufferedStreams());
|
||||||
|
|
||||||
ByteBuf empty = Unpooled.buffer(0);
|
ByteBuf empty = Unpooled.buffer(0);
|
||||||
@ -333,7 +334,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
encoderWriteHeaders(3, newPromise());
|
encoderWriteHeaders(3, newPromise());
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
|
|
||||||
ChannelPromise rstStreamPromise = newPromise();
|
Promise<Void> rstStreamPromise = newPromise();
|
||||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstStreamPromise);
|
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstStreamPromise);
|
||||||
assertTrue(rstStreamPromise.isSuccess());
|
assertTrue(rstStreamPromise.isSuccess());
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
@ -457,7 +458,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
setMaxConcurrentStreams(0);
|
setMaxConcurrentStreams(0);
|
||||||
|
|
||||||
// Simulate numeric overflow for the next stream ID.
|
// Simulate numeric overflow for the next stream ID.
|
||||||
ChannelFuture f = encoderWriteHeaders(-1, newPromise());
|
Future<Void> f = encoderWriteHeaders(-1, newPromise());
|
||||||
|
|
||||||
// Verify that the write fails.
|
// Verify that the write fails.
|
||||||
assertNotNull(f.cause());
|
assertNotNull(f.cause());
|
||||||
@ -468,15 +469,15 @@ public class StreamBufferingEncoderTest {
|
|||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx, newPromise());
|
||||||
setMaxConcurrentStreams(0);
|
setMaxConcurrentStreams(0);
|
||||||
ByteBuf data = mock(ByteBuf.class);
|
ByteBuf data = mock(ByteBuf.class);
|
||||||
ChannelFuture f1 = encoderWriteHeaders(3, newPromise());
|
Future<Void> f1 = encoderWriteHeaders(3, newPromise());
|
||||||
assertEquals(1, encoder.numBufferedStreams());
|
assertEquals(1, encoder.numBufferedStreams());
|
||||||
ChannelFuture f2 = encoder.writeData(ctx, 3, data, 0, false, newPromise());
|
Future<Void> f2 = encoder.writeData(ctx, 3, data, 0, false, newPromise());
|
||||||
|
|
||||||
ChannelPromise rstPromise = mock(ChannelPromise.class);
|
Promise<Void> rstPromise = mock(Promise.class);
|
||||||
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstPromise);
|
encoder.writeRstStream(ctx, 3, CANCEL.code(), rstPromise);
|
||||||
|
|
||||||
assertEquals(0, encoder.numBufferedStreams());
|
assertEquals(0, encoder.numBufferedStreams());
|
||||||
verify(rstPromise).setSuccess();
|
verify(rstPromise).setSuccess(null);
|
||||||
assertTrue(f1.isSuccess());
|
assertTrue(f1.isSuccess());
|
||||||
assertTrue(f2.isSuccess());
|
assertTrue(f2.isSuccess());
|
||||||
verify(data).release();
|
verify(data).release();
|
||||||
@ -487,9 +488,9 @@ public class StreamBufferingEncoderTest {
|
|||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx, newPromise());
|
||||||
connection.local().maxActiveStreams(0);
|
connection.local().maxActiveStreams(0);
|
||||||
|
|
||||||
ChannelFuture f1 = encoderWriteHeaders(3, newPromise());
|
Future<Void> f1 = encoderWriteHeaders(3, newPromise());
|
||||||
ChannelFuture f2 = encoderWriteHeaders(5, newPromise());
|
Future<Void> f2 = encoderWriteHeaders(5, newPromise());
|
||||||
ChannelFuture f3 = encoderWriteHeaders(7, newPromise());
|
Future<Void> f3 = encoderWriteHeaders(7, newPromise());
|
||||||
|
|
||||||
encoder.close();
|
encoder.close();
|
||||||
assertNotNull(f1.cause());
|
assertNotNull(f1.cause());
|
||||||
@ -502,7 +503,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
encoder.writeSettingsAck(ctx, newPromise());
|
encoder.writeSettingsAck(ctx, newPromise());
|
||||||
encoder.close();
|
encoder.close();
|
||||||
|
|
||||||
ChannelFuture f = encoderWriteHeaders(3, newPromise());
|
Future<Void> f = encoderWriteHeaders(3, newPromise());
|
||||||
assertNotNull(f.cause());
|
assertNotNull(f.cause());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,7 +517,7 @@ public class StreamBufferingEncoderTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture encoderWriteHeaders(int streamId, ChannelPromise promise) {
|
private Future<Void> encoderWriteHeaders(int streamId, Promise<Void> promise) {
|
||||||
encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT,
|
encoder.writeHeaders(ctx, streamId, new DefaultHttp2Headers(), 0, DEFAULT_PRIORITY_WEIGHT,
|
||||||
false, 0, false, promise);
|
false, 0, false, promise);
|
||||||
try {
|
try {
|
||||||
@ -530,28 +531,28 @@ public class StreamBufferingEncoderTest {
|
|||||||
private void writeVerifyWriteHeaders(VerificationMode mode, int streamId) {
|
private void writeVerifyWriteHeaders(VerificationMode mode, int streamId) {
|
||||||
verify(writer, mode).writeHeaders(eq(ctx), eq(streamId), any(Http2Headers.class), eq(0),
|
verify(writer, mode).writeHeaders(eq(ctx), eq(streamId), any(Http2Headers.class), eq(0),
|
||||||
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0),
|
eq(DEFAULT_PRIORITY_WEIGHT), eq(false), eq(0),
|
||||||
eq(false), any(ChannelPromise.class));
|
eq(false), any(Promise.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Answer<ChannelFuture> successAnswer() {
|
private static Answer<Future<Void>> successAnswer() {
|
||||||
return invocation -> {
|
return invocation -> {
|
||||||
for (Object a : invocation.getArguments()) {
|
for (Object a : invocation.getArguments()) {
|
||||||
ReferenceCountUtil.safeRelease(a);
|
ReferenceCountUtil.safeRelease(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
ChannelPromise future = newPromise();
|
Promise<Void> future = newPromise();
|
||||||
future.setSuccess();
|
future.setSuccess(null);
|
||||||
return future;
|
return future;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private Answer<ChannelFuture> noopAnswer() {
|
private static Answer<Future<Void>> noopAnswer() {
|
||||||
return new Answer<ChannelFuture>() {
|
return new Answer<Future<Void>>() {
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture answer(InvocationOnMock invocation) throws Throwable {
|
public Future<Void> answer(InvocationOnMock invocation) throws Throwable {
|
||||||
for (Object a : invocation.getArguments()) {
|
for (Object a : invocation.getArguments()) {
|
||||||
if (a instanceof ChannelPromise) {
|
if (a instanceof Promise) {
|
||||||
return (ChannelFuture) a;
|
return (Future<Void>) a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return newPromise();
|
return newPromise();
|
||||||
@ -559,8 +560,8 @@ public class StreamBufferingEncoderTest {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelPromise newPromise() {
|
private static Promise<Void> newPromise() {
|
||||||
return new DefaultChannelPromise(channel, ImmediateEventExecutor.INSTANCE);
|
return new DefaultPromise<Void>(ImmediateEventExecutor.INSTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ByteBuf data() {
|
private static ByteBuf data() {
|
||||||
|
@ -18,17 +18,17 @@ package io.netty.handler.codec.memcache.binary;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.CombinedChannelDuplexHandler;
|
import io.netty.channel.CombinedChannelDuplexHandler;
|
||||||
import io.netty.handler.codec.PrematureChannelClosureException;
|
import io.netty.handler.codec.PrematureChannelClosureException;
|
||||||
import io.netty.handler.codec.memcache.LastMemcacheContent;
|
import io.netty.handler.codec.memcache.LastMemcacheContent;
|
||||||
import io.netty.util.Attribute;
|
import io.netty.util.Attribute;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.UnstableApi;
|
import io.netty.util.internal.UnstableApi;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
@ -108,52 +108,63 @@ public final class BinaryMemcacheClientCodec extends
|
|||||||
return ctx.channel();
|
return ctx.channel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public EventExecutor executor() {
|
public EventExecutor executor() {
|
||||||
return ctx.executor();
|
return ctx.executor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
return ctx.name();
|
return ctx.name();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandler handler() {
|
public ChannelHandler handler() {
|
||||||
return ctx.handler();
|
return ctx.handler();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public boolean isRemoved() {
|
public boolean isRemoved() {
|
||||||
return ctx.isRemoved();
|
return ctx.isRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext fireChannelRegistered() {
|
public ChannelHandlerContext fireChannelRegistered() {
|
||||||
ctx.fireChannelRegistered();
|
ctx.fireChannelRegistered();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext fireChannelUnregistered() {
|
public ChannelHandlerContext fireChannelUnregistered() {
|
||||||
ctx.fireChannelUnregistered();
|
ctx.fireChannelUnregistered();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext fireChannelActive() {
|
public ChannelHandlerContext fireChannelActive() {
|
||||||
ctx.fireChannelActive();
|
ctx.fireChannelActive();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext fireChannelInactive() {
|
public ChannelHandlerContext fireChannelInactive() {
|
||||||
ctx.fireChannelInactive();
|
ctx.fireChannelInactive();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext fireExceptionCaught(Throwable cause) {
|
public ChannelHandlerContext fireExceptionCaught(Throwable cause) {
|
||||||
ctx.fireExceptionCaught(cause);
|
ctx.fireExceptionCaught(cause);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext fireUserEventTriggered(Object evt) {
|
public ChannelHandlerContext fireUserEventTriggered(Object evt) {
|
||||||
ctx.fireUserEventTriggered(evt);
|
ctx.fireUserEventTriggered(evt);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext fireChannelRead(Object msg) {
|
public ChannelHandlerContext fireChannelRead(Object msg) {
|
||||||
if (failOnMissingResponse && msg instanceof LastMemcacheContent) {
|
if (failOnMissingResponse && msg instanceof LastMemcacheContent) {
|
||||||
requestResponseCounter.decrementAndGet();
|
requestResponseCounter.decrementAndGet();
|
||||||
@ -162,128 +173,155 @@ public final class BinaryMemcacheClientCodec extends
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext fireChannelReadComplete() {
|
public ChannelHandlerContext fireChannelReadComplete() {
|
||||||
ctx.fireChannelReadComplete();
|
ctx.fireChannelReadComplete();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext fireChannelWritabilityChanged() {
|
public ChannelHandlerContext fireChannelWritabilityChanged() {
|
||||||
ctx.fireChannelWritabilityChanged();
|
ctx.fireChannelWritabilityChanged();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext read() {
|
public ChannelHandlerContext read() {
|
||||||
ctx.read();
|
ctx.read();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelHandlerContext flush() {
|
public ChannelHandlerContext flush() {
|
||||||
ctx.flush();
|
ctx.flush();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ChannelPipeline pipeline() {
|
public ChannelPipeline pipeline() {
|
||||||
return ctx.pipeline();
|
return ctx.pipeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public ByteBufAllocator alloc() {
|
public ByteBufAllocator alloc() {
|
||||||
return ctx.alloc();
|
return ctx.alloc();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public <T> Attribute<T> attr(AttributeKey<T> key) {
|
public <T> Attribute<T> attr(AttributeKey<T> key) {
|
||||||
return ctx.attr(key);
|
return ctx.attr(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public <T> boolean hasAttr(AttributeKey<T> key) {
|
public <T> boolean hasAttr(AttributeKey<T> key) {
|
||||||
return ctx.hasAttr(key);
|
return ctx.hasAttr(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture bind(SocketAddress localAddress) {
|
@Override
|
||||||
|
public Future<Void> bind(SocketAddress localAddress) {
|
||||||
return ctx.bind(localAddress);
|
return ctx.bind(localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress) {
|
@Override
|
||||||
|
public Future<Void> connect(SocketAddress remoteAddress) {
|
||||||
return ctx.connect(remoteAddress);
|
return ctx.connect(remoteAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
@Override
|
||||||
|
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
||||||
return ctx.connect(remoteAddress, localAddress);
|
return ctx.connect(remoteAddress, localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture disconnect() {
|
@Override
|
||||||
|
public Future<Void> disconnect() {
|
||||||
return ctx.disconnect();
|
return ctx.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture close() {
|
@Override
|
||||||
|
public Future<Void> close() {
|
||||||
return ctx.close();
|
return ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture deregister() {
|
@Override
|
||||||
|
public Future<Void> deregister() {
|
||||||
return ctx.deregister();
|
return ctx.deregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register() {
|
public Future<Void> register() {
|
||||||
return ctx.register();
|
return ctx.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register(ChannelPromise promise) {
|
public Future<Void> register(Promise<Void> promise) {
|
||||||
return ctx.register(promise);
|
return ctx.register(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
|
@Override
|
||||||
|
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return ctx.bind(localAddress, promise);
|
return ctx.bind(localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
|
@Override
|
||||||
|
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
|
||||||
return ctx.connect(remoteAddress, promise);
|
return ctx.connect(remoteAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture connect(
|
@Override
|
||||||
SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
|
public Future<Void> connect(
|
||||||
|
SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return ctx.connect(remoteAddress, localAddress, promise);
|
return ctx.connect(remoteAddress, localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture disconnect(ChannelPromise promise) {
|
@Override
|
||||||
|
public Future<Void> disconnect(Promise<Void> promise) {
|
||||||
return ctx.disconnect(promise);
|
return ctx.disconnect(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture close(ChannelPromise promise) {
|
@Override
|
||||||
|
public Future<Void> close(Promise<Void> promise) {
|
||||||
return ctx.close(promise);
|
return ctx.close(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture deregister(ChannelPromise promise) {
|
@Override
|
||||||
|
public Future<Void> deregister(Promise<Void> promise) {
|
||||||
return ctx.deregister(promise);
|
return ctx.deregister(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture write(Object msg) {
|
@Override
|
||||||
|
public Future<Void> write(Object msg) {
|
||||||
return ctx.write(msg);
|
return ctx.write(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture write(Object msg, ChannelPromise promise) {
|
@Override
|
||||||
|
public Future<Void> write(Object msg, Promise<Void> promise) {
|
||||||
return ctx.write(msg, promise);
|
return ctx.write(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
|
@Override
|
||||||
|
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
|
||||||
return ctx.writeAndFlush(msg, promise);
|
return ctx.writeAndFlush(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture writeAndFlush(Object msg) {
|
@Override
|
||||||
|
public Future<Void> writeAndFlush(Object msg) {
|
||||||
return ctx.writeAndFlush(msg);
|
return ctx.writeAndFlush(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelPromise newPromise() {
|
@Override
|
||||||
|
public Promise<Void> newPromise() {
|
||||||
return ctx.newPromise();
|
return ctx.newPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture newSucceededFuture() {
|
@Override
|
||||||
|
public Future<Void> newSucceededFuture() {
|
||||||
return ctx.newSucceededFuture();
|
return ctx.newSucceededFuture();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ChannelFuture newFailedFuture(Throwable cause) {
|
@Override
|
||||||
|
public Future<Void> newFailedFuture(Throwable cause) {
|
||||||
return ctx.newFailedFuture(cause);
|
return ctx.newFailedFuture(cause);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -18,7 +18,7 @@ package io.netty.handler.codec;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelHandlerAdapter;
|
import io.netty.channel.ChannelHandlerAdapter;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.TypeParameterMatcher;
|
import io.netty.util.internal.TypeParameterMatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,7 +102,7 @@ public abstract class ByteToMessageCodec<I> extends ChannelHandlerAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
encoder.write(ctx, msg, promise);
|
encoder.write(ctx, msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,31 +15,30 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec;
|
package io.netty.handler.codec;
|
||||||
|
|
||||||
import static io.netty.util.internal.ObjectUtil.checkPositive;
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBufConvertible;
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufAllocator;
|
import io.netty.buffer.ByteBufAllocator;
|
||||||
|
import io.netty.buffer.ByteBufConvertible;
|
||||||
import io.netty.buffer.CompositeByteBuf;
|
import io.netty.buffer.CompositeByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelHandlerAdapter;
|
|
||||||
import io.netty.channel.ChannelConfig;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelConfig;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
|
import io.netty.channel.ChannelHandlerAdapter;
|
||||||
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.socket.ChannelInputShutdownEvent;
|
import io.netty.channel.socket.ChannelInputShutdownEvent;
|
||||||
import io.netty.util.Attribute;
|
import io.netty.util.Attribute;
|
||||||
import io.netty.util.AttributeKey;
|
import io.netty.util.AttributeKey;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
|
import static io.netty.util.internal.ObjectUtil.checkPositive;
|
||||||
import static java.lang.Integer.MAX_VALUE;
|
import static java.lang.Integer.MAX_VALUE;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link ChannelHandler} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an
|
* {@link ChannelHandler} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an
|
||||||
@ -137,7 +136,7 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
|
|||||||
composite.capacity(composite.writerIndex());
|
composite.capacity(composite.writerIndex());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
composite = alloc.compositeBuffer(Integer.MAX_VALUE).addFlattenedComponents(true, cumulation);
|
composite = alloc.compositeBuffer(MAX_VALUE).addFlattenedComponents(true, cumulation);
|
||||||
}
|
}
|
||||||
composite.addFlattenedComponents(true, in);
|
composite.addFlattenedComponents(true, in);
|
||||||
in = null;
|
in = null;
|
||||||
@ -235,8 +234,8 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
public final void handlerAdded(ChannelHandlerContext ctx) throws Exception {
|
||||||
this.context = new ByteToMessageDecoderContext(ctx);
|
context = new ByteToMessageDecoderContext(ctx);
|
||||||
handlerAdded0(this.context);
|
handlerAdded0(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
|
protected void handlerAdded0(ChannelHandlerContext ctx) throws Exception {
|
||||||
@ -257,7 +256,7 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
|
|||||||
buf.release();
|
buf.release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handlerRemoved0(this.context);
|
handlerRemoved0(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -635,107 +634,107 @@ public abstract class ByteToMessageDecoder extends ChannelHandlerAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture bind(SocketAddress localAddress) {
|
public Future<Void> bind(SocketAddress localAddress) {
|
||||||
return ctx.bind(localAddress);
|
return ctx.bind(localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress) {
|
public Future<Void> connect(SocketAddress remoteAddress) {
|
||||||
return ctx.connect(remoteAddress);
|
return ctx.connect(remoteAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress) {
|
||||||
return ctx.connect(remoteAddress, localAddress);
|
return ctx.connect(remoteAddress, localAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture disconnect() {
|
public Future<Void> disconnect() {
|
||||||
return ctx.disconnect();
|
return ctx.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close() {
|
public Future<Void> close() {
|
||||||
return ctx.close();
|
return ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture deregister() {
|
public Future<Void> deregister() {
|
||||||
return ctx.deregister();
|
return ctx.deregister();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
|
public Future<Void> bind(SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return ctx.bind(localAddress, promise);
|
return ctx.bind(localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
|
public Future<Void> connect(SocketAddress remoteAddress, Promise<Void> promise) {
|
||||||
return ctx.connect(remoteAddress, promise);
|
return ctx.connect(remoteAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
|
public Future<Void> connect(SocketAddress remoteAddress, SocketAddress localAddress, Promise<Void> promise) {
|
||||||
return ctx.connect(remoteAddress, localAddress, promise);
|
return ctx.connect(remoteAddress, localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture disconnect(ChannelPromise promise) {
|
public Future<Void> disconnect(Promise<Void> promise) {
|
||||||
return ctx.disconnect(promise);
|
return ctx.disconnect(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close(ChannelPromise promise) {
|
public Future<Void> close(Promise<Void> promise) {
|
||||||
return ctx.close(promise);
|
return ctx.close(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register() {
|
public Future<Void> register() {
|
||||||
return ctx.register();
|
return ctx.register();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture register(ChannelPromise promise) {
|
public Future<Void> register(Promise<Void> promise) {
|
||||||
return ctx.register(promise);
|
return ctx.register(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture deregister(ChannelPromise promise) {
|
public Future<Void> deregister(Promise<Void> promise) {
|
||||||
return ctx.deregister(promise);
|
return ctx.deregister(promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture write(Object msg) {
|
public Future<Void> write(Object msg) {
|
||||||
return ctx.write(msg);
|
return ctx.write(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture write(Object msg, ChannelPromise promise) {
|
public Future<Void> write(Object msg, Promise<Void> promise) {
|
||||||
return ctx.write(msg, promise);
|
return ctx.write(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
|
public Future<Void> writeAndFlush(Object msg, Promise<Void> promise) {
|
||||||
return ctx.writeAndFlush(msg, promise);
|
return ctx.writeAndFlush(msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture writeAndFlush(Object msg) {
|
public Future<Void> writeAndFlush(Object msg) {
|
||||||
return ctx.writeAndFlush(msg);
|
return ctx.writeAndFlush(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelPromise newPromise() {
|
public Promise<Void> newPromise() {
|
||||||
return ctx.newPromise();
|
return ctx.newPromise();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture newSucceededFuture() {
|
public Future<Void> newSucceededFuture() {
|
||||||
return ctx.newSucceededFuture();
|
return ctx.newSucceededFuture();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture newFailedFuture(Throwable cause) {
|
public Future<Void> newFailedFuture(Throwable cause) {
|
||||||
return ctx.newFailedFuture(cause);
|
return ctx.newFailedFuture(cause);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,21 +15,21 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec;
|
package io.netty.handler.codec;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBufConvertible;
|
import io.netty.buffer.ByteBufConvertible;
|
||||||
import io.netty.channel.AddressedEnvelope;
|
import io.netty.channel.AddressedEnvelope;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.socket.DatagramPacket;
|
import io.netty.channel.socket.DatagramPacket;
|
||||||
import io.netty.handler.codec.protobuf.ProtobufEncoder;
|
import io.netty.handler.codec.protobuf.ProtobufEncoder;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An encoder that encodes the content in {@link AddressedEnvelope} to {@link DatagramPacket} using
|
* An encoder that encodes the content in {@link AddressedEnvelope} to {@link DatagramPacket} using
|
||||||
* the specified message encoder. E.g.,
|
* the specified message encoder. E.g.,
|
||||||
@ -91,29 +91,29 @@ public class DatagramPacketEncoder<M> extends MessageToMessageEncoder<AddressedE
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
|
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, Promise<Void> promise) {
|
||||||
encoder.bind(ctx, localAddress, promise);
|
encoder.bind(ctx, localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void connect(
|
public void connect(
|
||||||
ChannelHandlerContext ctx, SocketAddress remoteAddress,
|
ChannelHandlerContext ctx, SocketAddress remoteAddress,
|
||||||
SocketAddress localAddress, ChannelPromise promise) {
|
SocketAddress localAddress, Promise<Void> promise) {
|
||||||
encoder.connect(ctx, remoteAddress, localAddress, promise);
|
encoder.connect(ctx, remoteAddress, localAddress, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void disconnect(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
encoder.disconnect(ctx, promise);
|
encoder.disconnect(ctx, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void close(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
encoder.close(ctx, promise);
|
encoder.close(ctx, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) {
|
public void deregister(ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
encoder.deregister(ctx, promise);
|
encoder.deregister(ctx, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,12 +18,13 @@ package io.netty.handler.codec;
|
|||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.ByteBufHolder;
|
import io.netty.buffer.ByteBufHolder;
|
||||||
import io.netty.buffer.CompositeByteBuf;
|
import io.netty.buffer.CompositeByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
import io.netty.channel.ChannelFutureListeners;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.FutureContextListener;
|
||||||
|
|
||||||
import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
|
import static io.netty.buffer.Unpooled.EMPTY_BUFFER;
|
||||||
import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
|
import static io.netty.util.internal.ObjectUtil.checkPositiveOrZero;
|
||||||
@ -58,7 +59,7 @@ public abstract class MessageAggregator<I, S, C extends ByteBufHolder, O extends
|
|||||||
|
|
||||||
private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
|
private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
|
||||||
private ChannelHandlerContext ctx;
|
private ChannelHandlerContext ctx;
|
||||||
private ChannelFutureListener continueResponseWriteListener;
|
private FutureContextListener<ChannelHandlerContext, Void> continueResponseWriteListener;
|
||||||
|
|
||||||
private boolean aggregating;
|
private boolean aggregating;
|
||||||
|
|
||||||
@ -221,11 +222,11 @@ public abstract class MessageAggregator<I, S, C extends ByteBufHolder, O extends
|
|||||||
Object continueResponse = newContinueResponse(m, maxContentLength, ctx.pipeline());
|
Object continueResponse = newContinueResponse(m, maxContentLength, ctx.pipeline());
|
||||||
if (continueResponse != null) {
|
if (continueResponse != null) {
|
||||||
// Cache the write listener for reuse.
|
// Cache the write listener for reuse.
|
||||||
ChannelFutureListener listener = continueResponseWriteListener;
|
FutureContextListener<ChannelHandlerContext, Void> listener = continueResponseWriteListener;
|
||||||
if (listener == null) {
|
if (listener == null) {
|
||||||
continueResponseWriteListener = listener = future -> {
|
continueResponseWriteListener = listener = (context, future) -> {
|
||||||
if (!future.isSuccess()) {
|
if (!future.isSuccess()) {
|
||||||
ctx.fireExceptionCaught(future.cause());
|
context.fireExceptionCaught(future.cause());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -234,10 +235,10 @@ public abstract class MessageAggregator<I, S, C extends ByteBufHolder, O extends
|
|||||||
boolean closeAfterWrite = closeAfterContinueResponse(continueResponse);
|
boolean closeAfterWrite = closeAfterContinueResponse(continueResponse);
|
||||||
handlingOversizedMessage = ignoreContentAfterContinueResponse(continueResponse);
|
handlingOversizedMessage = ignoreContentAfterContinueResponse(continueResponse);
|
||||||
|
|
||||||
final ChannelFuture future = ctx.writeAndFlush(continueResponse).addListener(listener);
|
Future<Void> future = ctx.writeAndFlush(continueResponse).addListener(ctx, listener);
|
||||||
|
|
||||||
if (closeAfterWrite) {
|
if (closeAfterWrite) {
|
||||||
future.addListener(ChannelFutureListener.CLOSE);
|
future.addListener(ctx.channel(), ChannelFutureListeners.CLOSE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (handlingOversizedMessage) {
|
if (handlingOversizedMessage) {
|
||||||
|
@ -21,8 +21,8 @@ import io.netty.channel.ChannelHandler;
|
|||||||
import io.netty.channel.ChannelHandlerAdapter;
|
import io.netty.channel.ChannelHandlerAdapter;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.TypeParameterMatcher;
|
import io.netty.util.internal.TypeParameterMatcher;
|
||||||
|
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ public abstract class MessageToByteEncoder<I> extends ChannelHandlerAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
ByteBuf buf = null;
|
ByteBuf buf = null;
|
||||||
try {
|
try {
|
||||||
if (acceptOutboundMessage(msg)) {
|
if (acceptOutboundMessage(msg)) {
|
||||||
|
@ -17,8 +17,8 @@ package io.netty.handler.codec;
|
|||||||
|
|
||||||
import io.netty.channel.ChannelHandlerAdapter;
|
import io.netty.channel.ChannelHandlerAdapter;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.util.ReferenceCounted;
|
import io.netty.util.ReferenceCounted;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.internal.TypeParameterMatcher;
|
import io.netty.util.internal.TypeParameterMatcher;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -112,7 +112,7 @@ public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends Cha
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
encoder.write(ctx, msg, promise);
|
encoder.write(ctx, msg, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ import io.netty.channel.ChannelHandler;
|
|||||||
import io.netty.channel.ChannelHandlerAdapter;
|
import io.netty.channel.ChannelHandlerAdapter;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.util.ReferenceCountUtil;
|
import io.netty.util.ReferenceCountUtil;
|
||||||
import io.netty.util.ReferenceCounted;
|
import io.netty.util.ReferenceCounted;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.PromiseCombiner;
|
import io.netty.util.concurrent.PromiseCombiner;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
import io.netty.util.internal.TypeParameterMatcher;
|
import io.netty.util.internal.TypeParameterMatcher;
|
||||||
@ -78,7 +78,7 @@ public abstract class MessageToMessageEncoder<I> extends ChannelHandlerAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
CodecOutputList out = null;
|
CodecOutputList out = null;
|
||||||
try {
|
try {
|
||||||
if (acceptOutboundMessage(msg)) {
|
if (acceptOutboundMessage(msg)) {
|
||||||
@ -118,7 +118,7 @@ public abstract class MessageToMessageEncoder<I> extends ChannelHandlerAdapter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, ChannelPromise promise) {
|
private static void writePromiseCombiner(ChannelHandlerContext ctx, CodecOutputList out, Promise<Void> promise) {
|
||||||
final PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
|
final PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
|
||||||
for (int i = 0; i < out.size(); i++) {
|
for (int i = 0; i < out.size(); i++) {
|
||||||
combiner.add(ctx.write(out.getUnsafe(i)));
|
combiner.add(ctx.write(out.getUnsafe(i)));
|
||||||
|
@ -16,18 +16,22 @@
|
|||||||
package io.netty.handler.codec.compression;
|
package io.netty.handler.codec.compression;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.PromiseNotifier;
|
import io.netty.util.concurrent.PromiseNotifier;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import static io.netty.handler.codec.compression.Bzip2Constants.*;
|
import static io.netty.handler.codec.compression.Bzip2Constants.BASE_BLOCK_SIZE;
|
||||||
|
import static io.netty.handler.codec.compression.Bzip2Constants.END_OF_STREAM_MAGIC_1;
|
||||||
|
import static io.netty.handler.codec.compression.Bzip2Constants.END_OF_STREAM_MAGIC_2;
|
||||||
|
import static io.netty.handler.codec.compression.Bzip2Constants.MAGIC_NUMBER;
|
||||||
|
import static io.netty.handler.codec.compression.Bzip2Constants.MAX_BLOCK_SIZE;
|
||||||
|
import static io.netty.handler.codec.compression.Bzip2Constants.MIN_BLOCK_SIZE;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compresses a {@link ByteBuf} using the Bzip2 algorithm.
|
* Compresses a {@link ByteBuf} using the Bzip2 algorithm.
|
||||||
@ -167,25 +171,25 @@ public class Bzip2Encoder extends MessageToByteEncoder<ByteBuf> {
|
|||||||
/**
|
/**
|
||||||
* Close this {@link Bzip2Encoder} and so finish the encoding.
|
* Close this {@link Bzip2Encoder} and so finish the encoding.
|
||||||
*
|
*
|
||||||
* The returned {@link ChannelFuture} will be notified once the operation completes.
|
* The returned {@link Future} will be notified once the operation completes.
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close() {
|
public Future<Void> close() {
|
||||||
return close(ctx().newPromise());
|
return close(ctx().newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close this {@link Bzip2Encoder} and so finish the encoding.
|
* Close this {@link Bzip2Encoder} and so finish the encoding.
|
||||||
* The given {@link ChannelFuture} will be notified once the operation
|
* The given {@link Future} will be notified once the operation
|
||||||
* completes and will also be returned.
|
* completes and will also be returned.
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(final ChannelPromise promise) {
|
public Future<Void> close(Promise<Void> promise) {
|
||||||
ChannelHandlerContext ctx = ctx();
|
ChannelHandlerContext ctx = ctx();
|
||||||
EventExecutor executor = ctx.executor();
|
EventExecutor executor = ctx.executor();
|
||||||
if (executor.inEventLoop()) {
|
if (executor.inEventLoop()) {
|
||||||
return finishEncode(ctx, promise);
|
return finishEncode(ctx, promise);
|
||||||
} else {
|
} else {
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
ChannelFuture f = finishEncode(ctx(), promise);
|
Future<Void> f = finishEncode(ctx(), promise);
|
||||||
PromiseNotifier.cascade(f, promise);
|
PromiseNotifier.cascade(f, promise);
|
||||||
});
|
});
|
||||||
return promise;
|
return promise;
|
||||||
@ -193,9 +197,9 @@ public class Bzip2Encoder extends MessageToByteEncoder<ByteBuf> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) {
|
public void close(final ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ChannelFuture f = finishEncode(ctx, ctx.newPromise());
|
Future<Void> f = finishEncode(ctx, ctx.newPromise());
|
||||||
f.addListener((ChannelFutureListener) f1 -> ctx.close(promise));
|
f.addListener(f1 -> ctx.close(promise));
|
||||||
|
|
||||||
if (!f.isDone()) {
|
if (!f.isDone()) {
|
||||||
// Ensure the channel is closed even if the write operation completes in time.
|
// Ensure the channel is closed even if the write operation completes in time.
|
||||||
@ -205,9 +209,9 @@ public class Bzip2Encoder extends MessageToByteEncoder<ByteBuf> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture finishEncode(final ChannelHandlerContext ctx, ChannelPromise promise) {
|
private Future<Void> finishEncode(final ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
if (finished) {
|
if (finished) {
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
finished = true;
|
finished = true;
|
||||||
|
@ -15,14 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.compression;
|
package io.netty.handler.codec.compression;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.PromiseNotifier;
|
import io.netty.util.concurrent.PromiseNotifier;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
@ -30,6 +27,8 @@ import java.util.concurrent.TimeUnit;
|
|||||||
import java.util.zip.CRC32;
|
import java.util.zip.CRC32;
|
||||||
import java.util.zip.Deflater;
|
import java.util.zip.Deflater;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compresses a {@link ByteBuf} using the deflate algorithm.
|
* Compresses a {@link ByteBuf} using the deflate algorithm.
|
||||||
*/
|
*/
|
||||||
@ -156,20 +155,20 @@ public class JdkZlibEncoder extends ZlibEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close() {
|
public Future<Void> close() {
|
||||||
return close(ctx().newPromise());
|
return close(ctx().newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelFuture close(final ChannelPromise promise) {
|
public Future<Void> close(Promise<Void> promise) {
|
||||||
ChannelHandlerContext ctx = ctx();
|
ChannelHandlerContext ctx = ctx();
|
||||||
EventExecutor executor = ctx.executor();
|
EventExecutor executor = ctx.executor();
|
||||||
if (executor.inEventLoop()) {
|
if (executor.inEventLoop()) {
|
||||||
return finishEncode(ctx, promise);
|
return finishEncode(ctx, promise);
|
||||||
} else {
|
} else {
|
||||||
final ChannelPromise p = ctx.newPromise();
|
Promise<Void> p = ctx.newPromise();
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
ChannelFuture f = finishEncode(ctx(), p);
|
Future<Void> f = finishEncode(ctx(), p);
|
||||||
PromiseNotifier.cascade(f, promise);
|
PromiseNotifier.cascade(f, promise);
|
||||||
});
|
});
|
||||||
return p;
|
return p;
|
||||||
@ -262,9 +261,9 @@ public class JdkZlibEncoder extends ZlibEncoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) {
|
public void close(final ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ChannelFuture f = finishEncode(ctx, ctx.newPromise());
|
Future<Void> f = finishEncode(ctx, ctx.newPromise());
|
||||||
f.addListener((ChannelFutureListener) f1 -> ctx.close(promise));
|
f.addListener(f1 -> ctx.close(promise));
|
||||||
|
|
||||||
if (!f.isDone()) {
|
if (!f.isDone()) {
|
||||||
// Ensure the channel is closed even if the write operation completes in time.
|
// Ensure the channel is closed even if the write operation completes in time.
|
||||||
@ -274,9 +273,9 @@ public class JdkZlibEncoder extends ZlibEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture finishEncode(final ChannelHandlerContext ctx, ChannelPromise promise) {
|
private Future<Void> finishEncode(final ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
if (finished) {
|
if (finished) {
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,14 +18,13 @@ package io.netty.handler.codec.compression;
|
|||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPipeline;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.EncoderException;
|
import io.netty.handler.codec.EncoderException;
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
import io.netty.util.concurrent.EventExecutor;
|
import io.netty.util.concurrent.EventExecutor;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import io.netty.util.concurrent.PromiseNotifier;
|
import io.netty.util.concurrent.PromiseNotifier;
|
||||||
import io.netty.util.internal.ObjectUtil;
|
import io.netty.util.internal.ObjectUtil;
|
||||||
import net.jpountz.lz4.LZ4Compressor;
|
import net.jpountz.lz4.LZ4Compressor;
|
||||||
@ -49,7 +48,6 @@ import static io.netty.handler.codec.compression.Lz4Constants.MAGIC_NUMBER;
|
|||||||
import static io.netty.handler.codec.compression.Lz4Constants.MAX_BLOCK_SIZE;
|
import static io.netty.handler.codec.compression.Lz4Constants.MAX_BLOCK_SIZE;
|
||||||
import static io.netty.handler.codec.compression.Lz4Constants.MIN_BLOCK_SIZE;
|
import static io.netty.handler.codec.compression.Lz4Constants.MIN_BLOCK_SIZE;
|
||||||
import static io.netty.handler.codec.compression.Lz4Constants.TOKEN_OFFSET;
|
import static io.netty.handler.codec.compression.Lz4Constants.TOKEN_OFFSET;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -306,9 +304,9 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
|
|||||||
ctx.flush();
|
ctx.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChannelFuture finishEncode(final ChannelHandlerContext ctx, ChannelPromise promise) {
|
private Future<Void> finishEncode(final ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
if (finished) {
|
if (finished) {
|
||||||
promise.setSuccess();
|
promise.setSuccess(null);
|
||||||
return promise;
|
return promise;
|
||||||
}
|
}
|
||||||
finished = true;
|
finished = true;
|
||||||
@ -340,25 +338,25 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
|
|||||||
/**
|
/**
|
||||||
* Close this {@link Lz4FrameEncoder} and so finish the encoding.
|
* Close this {@link Lz4FrameEncoder} and so finish the encoding.
|
||||||
*
|
*
|
||||||
* The returned {@link ChannelFuture} will be notified once the operation completes.
|
* The returned {@link java.util.concurrent.Future} will be notified once the operation completes.
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close() {
|
public Future<Void> close() {
|
||||||
return close(ctx().newPromise());
|
return close(ctx().newPromise());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close this {@link Lz4FrameEncoder} and so finish the encoding.
|
* Close this {@link Lz4FrameEncoder} and so finish the encoding.
|
||||||
* The given {@link ChannelFuture} will be notified once the operation
|
* The given {@link java.util.concurrent.Future} will be notified once the operation
|
||||||
* completes and will also be returned.
|
* completes and will also be returned.
|
||||||
*/
|
*/
|
||||||
public ChannelFuture close(final ChannelPromise promise) {
|
public Future<Void> close(Promise<Void> promise) {
|
||||||
ChannelHandlerContext ctx = ctx();
|
ChannelHandlerContext ctx = ctx();
|
||||||
EventExecutor executor = ctx.executor();
|
EventExecutor executor = ctx.executor();
|
||||||
if (executor.inEventLoop()) {
|
if (executor.inEventLoop()) {
|
||||||
return finishEncode(ctx, promise);
|
return finishEncode(ctx, promise);
|
||||||
} else {
|
} else {
|
||||||
executor.execute(() -> {
|
executor.execute(() -> {
|
||||||
ChannelFuture f = finishEncode(ctx(), promise);
|
Future<Void> f = finishEncode(ctx(), promise);
|
||||||
PromiseNotifier.cascade(f, promise);
|
PromiseNotifier.cascade(f, promise);
|
||||||
});
|
});
|
||||||
return promise;
|
return promise;
|
||||||
@ -366,9 +364,9 @@ public class Lz4FrameEncoder extends MessageToByteEncoder<ByteBuf> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close(final ChannelHandlerContext ctx, final ChannelPromise promise) {
|
public void close(final ChannelHandlerContext ctx, Promise<Void> promise) {
|
||||||
ChannelFuture f = finishEncode(ctx, ctx.newPromise());
|
Future<Void> f = finishEncode(ctx, ctx.newPromise());
|
||||||
f.addListener((ChannelFutureListener) f1 -> ctx.close(promise));
|
f.addListener(f1 -> ctx.close(promise));
|
||||||
|
|
||||||
if (!f.isDone()) {
|
if (!f.isDone()) {
|
||||||
// Ensure the channel is closed even if the write operation completes in time.
|
// Ensure the channel is closed even if the write operation completes in time.
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
package io.netty.handler.codec.compression;
|
package io.netty.handler.codec.compression;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compresses a {@link ByteBuf} using the deflate algorithm.
|
* Compresses a {@link ByteBuf} using the deflate algorithm.
|
||||||
@ -42,16 +42,16 @@ public abstract class ZlibEncoder extends MessageToByteEncoder<ByteBuf> {
|
|||||||
/**
|
/**
|
||||||
* Close this {@link ZlibEncoder} and so finish the encoding.
|
* Close this {@link ZlibEncoder} and so finish the encoding.
|
||||||
*
|
*
|
||||||
* The returned {@link ChannelFuture} will be notified once the
|
* The returned {@link Future} will be notified once the
|
||||||
* operation completes.
|
* operation completes.
|
||||||
*/
|
*/
|
||||||
public abstract ChannelFuture close();
|
public abstract Future<Void> close();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Close this {@link ZlibEncoder} and so finish the encoding.
|
* Close this {@link ZlibEncoder} and so finish the encoding.
|
||||||
* The given {@link ChannelFuture} will be notified once the operation
|
* The given {@link Future} will be notified once the operation
|
||||||
* completes and will also be returned.
|
* completes and will also be returned.
|
||||||
*/
|
*/
|
||||||
public abstract ChannelFuture close(ChannelPromise promise);
|
public abstract Future<Void> close(Promise<Void> promise);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,19 +15,19 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec;
|
package io.netty.handler.codec;
|
||||||
|
|
||||||
import io.netty.channel.ChannelFuture;
|
|
||||||
import io.netty.channel.ChannelHandler;
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelPromise;
|
|
||||||
import io.netty.channel.embedded.EmbeddedChannel;
|
import io.netty.channel.embedded.EmbeddedChannel;
|
||||||
|
import io.netty.util.concurrent.Future;
|
||||||
|
import io.netty.util.concurrent.Promise;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class MessageToMessageEncoderTest {
|
public class MessageToMessageEncoderTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -59,7 +59,7 @@ public class MessageToMessageEncoderTest {
|
|||||||
ChannelHandler writeThrower = new ChannelHandler() {
|
ChannelHandler writeThrower = new ChannelHandler() {
|
||||||
private boolean firstWritten;
|
private boolean firstWritten;
|
||||||
@Override
|
@Override
|
||||||
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
|
public void write(ChannelHandlerContext ctx, Object msg, Promise<Void> promise) {
|
||||||
if (firstWritten) {
|
if (firstWritten) {
|
||||||
ctx.write(msg, promise);
|
ctx.write(msg, promise);
|
||||||
} else {
|
} else {
|
||||||
@ -71,7 +71,7 @@ public class MessageToMessageEncoderTest {
|
|||||||
|
|
||||||
EmbeddedChannel channel = new EmbeddedChannel(writeThrower, encoder);
|
EmbeddedChannel channel = new EmbeddedChannel(writeThrower, encoder);
|
||||||
Object msg = new Object();
|
Object msg = new Object();
|
||||||
ChannelFuture write = channel.writeAndFlush(msg);
|
Future<Void> write = channel.writeAndFlush(msg);
|
||||||
assertSame(firstWriteException, write.cause());
|
assertSame(firstWriteException, write.cause());
|
||||||
assertSame(msg, channel.readOutbound());
|
assertSame(msg, channel.readOutbound());
|
||||||
assertFalse(channel.finish());
|
assertFalse(channel.finish());
|
||||||
|
@ -22,7 +22,6 @@ import io.netty.buffer.ByteBufAllocator;
|
|||||||
import io.netty.buffer.ByteBufInputStream;
|
import io.netty.buffer.ByteBufInputStream;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelFutureListener;
|
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.channel.EventLoopGroup;
|
import io.netty.channel.EventLoopGroup;
|
||||||
@ -278,13 +277,13 @@ public class Lz4FrameEncoderTest extends AbstractEncoderTest {
|
|||||||
final int size = 27;
|
final int size = 27;
|
||||||
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(size, size);
|
ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(size, size);
|
||||||
finalClientChannel.writeAndFlush(buf.writerIndex(buf.writerIndex() + size))
|
finalClientChannel.writeAndFlush(buf.writerIndex(buf.writerIndex() + size))
|
||||||
.addListener((ChannelFutureListener) future -> {
|
.addListener(future -> {
|
||||||
try {
|
try {
|
||||||
writeFailCauseRef.set(future.cause());
|
writeFailCauseRef.set(future.cause());
|
||||||
} finally {
|
} finally {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
latch.await();
|
latch.await();
|
||||||
Throwable writeFailCause = writeFailCauseRef.get();
|
Throwable writeFailCause = writeFailCauseRef.get();
|
||||||
|
@ -82,12 +82,12 @@ public abstract class AbstractEventExecutor extends AbstractExecutorService impl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> Future<V> newSucceededFuture(V result) {
|
public <V> Future<V> newSucceededFuture(V result) {
|
||||||
return new SucceededFuture<>(this, result);
|
return DefaultPromise.newSuccessfulPromise(this, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <V> Future<V> newFailedFuture(Throwable cause) {
|
public <V> Future<V> newFailedFuture(Throwable cause) {
|
||||||
return new FailedFuture<>(this, cause);
|
return DefaultPromise.newFailedPromise(this, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -107,12 +107,12 @@ public abstract class AbstractEventExecutor extends AbstractExecutorService impl
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
|
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
|
||||||
return newRunnableFuture(this.newPromise(), runnable, value);
|
return newRunnableFuture(newPromise(), runnable, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
|
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
|
||||||
return newRunnableFuture(this.newPromise(), callable);
|
return newRunnableFuture(newPromise(), callable);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,6 +38,8 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
|
|||||||
|
|
||||||
private static final Comparator<RunnableScheduledFutureNode<?>> SCHEDULED_FUTURE_TASK_COMPARATOR =
|
private static final Comparator<RunnableScheduledFutureNode<?>> SCHEDULED_FUTURE_TASK_COMPARATOR =
|
||||||
Comparable::compareTo;
|
Comparable::compareTo;
|
||||||
|
private static final RunnableScheduledFutureNode<?>[]
|
||||||
|
EMPTY_RUNNABLE_SCHEDULED_FUTURE_NODES = new RunnableScheduledFutureNode<?>[0];
|
||||||
|
|
||||||
private PriorityQueue<RunnableScheduledFutureNode<?>> scheduledTaskQueue;
|
private PriorityQueue<RunnableScheduledFutureNode<?>> scheduledTaskQueue;
|
||||||
|
|
||||||
@ -88,7 +90,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
|
|||||||
}
|
}
|
||||||
|
|
||||||
final RunnableScheduledFutureNode<?>[] scheduledTasks =
|
final RunnableScheduledFutureNode<?>[] scheduledTasks =
|
||||||
scheduledTaskQueue.toArray(new RunnableScheduledFutureNode<?>[0]);
|
scheduledTaskQueue.toArray(EMPTY_RUNNABLE_SCHEDULED_FUTURE_NODES);
|
||||||
|
|
||||||
for (RunnableScheduledFutureNode<?> task: scheduledTasks) {
|
for (RunnableScheduledFutureNode<?> task: scheduledTasks) {
|
||||||
task.cancel(false);
|
task.cancel(false);
|
||||||
@ -145,11 +147,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
|
|||||||
if (scheduledTaskQueue == null) {
|
if (scheduledTaskQueue == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
RunnableScheduledFutureNode<?> node = scheduledTaskQueue.peek();
|
return scheduledTaskQueue.peek();
|
||||||
if (node == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -236,9 +234,9 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
|
|||||||
}
|
}
|
||||||
|
|
||||||
private <V> void add0(RunnableScheduledFuture<V> task) {
|
private <V> void add0(RunnableScheduledFuture<V> task) {
|
||||||
final RunnableScheduledFutureNode node;
|
final RunnableScheduledFutureNode<V> node;
|
||||||
if (task instanceof RunnableScheduledFutureNode) {
|
if (task instanceof RunnableScheduledFutureNode) {
|
||||||
node = (RunnableScheduledFutureNode) task;
|
node = (RunnableScheduledFutureNode<V>) task;
|
||||||
} else {
|
} else {
|
||||||
node = new DefaultRunnableScheduledFutureNode<V>(task);
|
node = new DefaultRunnableScheduledFutureNode<V>(task);
|
||||||
}
|
}
|
||||||
@ -270,7 +268,7 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
|
|||||||
*/
|
*/
|
||||||
protected <V> RunnableScheduledFuture<V> newScheduledTaskFor(
|
protected <V> RunnableScheduledFuture<V> newScheduledTaskFor(
|
||||||
Callable<V> callable, long deadlineNanos, long period) {
|
Callable<V> callable, long deadlineNanos, long period) {
|
||||||
return newRunnableScheduledFuture(this, this.newPromise(), callable, deadlineNanos, period);
|
return newRunnableScheduledFuture(this, newPromise(), callable, deadlineNanos, period);
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RunnableScheduledFutureNode<V> extends PriorityQueueNode, RunnableScheduledFuture<V> { }
|
interface RunnableScheduledFutureNode<V> extends PriorityQueueNode, RunnableScheduledFuture<V> { }
|
||||||
@ -304,12 +302,18 @@ public abstract class AbstractScheduledEventExecutor extends AbstractEventExecut
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RunnableScheduledFuture<V> addListener(
|
public RunnableScheduledFuture<V> addListener(FutureListener<? super V> listener) {
|
||||||
GenericFutureListener<? extends Future<? super V>> listener) {
|
|
||||||
future.addListener(listener);
|
future.addListener(listener);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <C> RunnableScheduledFuture<V> addListener(C context,
|
||||||
|
FutureContextListener<? super C, ? super V> listener) {
|
||||||
|
future.addListener(context, listener);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPeriodic() {
|
public boolean isPeriodic() {
|
||||||
return future.isPeriodic();
|
return future.isPeriodic();
|
||||||
|
@ -1,140 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 The Netty Project
|
|
||||||
*
|
|
||||||
* The Netty Project licenses this file to you under the Apache License,
|
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at:
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.netty.util.concurrent;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A skeletal {@link Future} implementation which represents a {@link Future} which has been completed already.
|
|
||||||
*/
|
|
||||||
public abstract class CompleteFuture<V> implements Future<V> {
|
|
||||||
|
|
||||||
private final EventExecutor executor;
|
|
||||||
|
|
||||||
// It is fine to not make this volatile as even if we override the value in there it does not matter as
|
|
||||||
// DefaultFutureCompletionStage has no state itself and is just a wrapper around this CompletableFuture instance.
|
|
||||||
private DefaultFutureCompletionStage<V> stage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param executor the {@link EventExecutor} associated with this future
|
|
||||||
*/
|
|
||||||
protected CompleteFuture(EventExecutor executor) {
|
|
||||||
this.executor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the {@link EventExecutor} which is used by this {@link CompleteFuture}.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EventExecutor executor() {
|
|
||||||
return executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
|
|
||||||
requireNonNull(listener, "listener");
|
|
||||||
DefaultPromise.safeExecute(executor(), () -> DefaultPromise.notifyListener0(this, listener));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<V> await() throws InterruptedException {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<V> sync() throws InterruptedException {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<V> syncUninterruptibly() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean await(long timeoutMillis) throws InterruptedException {
|
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<V> awaitUninterruptibly() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean awaitUninterruptibly(long timeout, TimeUnit unit) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean awaitUninterruptibly(long timeoutMillis) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isDone() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCancellable() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCancelled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*
|
|
||||||
* @param mayInterruptIfRunning this value has no effect in this implementation.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FutureCompletionStage<V> asStage() {
|
|
||||||
DefaultFutureCompletionStage<V> stageAdapter = stage;
|
|
||||||
if (stageAdapter == null) {
|
|
||||||
stage = stageAdapter = new DefaultFutureCompletionStage<>(this);
|
|
||||||
}
|
|
||||||
return stageAdapter;
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,7 +28,7 @@ import java.util.function.Function;
|
|||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a {@link io.netty.util.concurrent.Future} and provides a {@link FutureCompletionStage} implementation
|
* Wraps a {@link Future} and provides a {@link FutureCompletionStage} implementation
|
||||||
* on top of it.
|
* on top of it.
|
||||||
*
|
*
|
||||||
* @param <V> the value type.
|
* @param <V> the value type.
|
||||||
@ -41,7 +41,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
|
|
||||||
// Just a marker
|
// Just a marker
|
||||||
private static final Executor SAME_AS_FUTURE = task -> {
|
private static final Executor SAME_AS_FUTURE = task -> {
|
||||||
throw new UnsupportedOperationException("Only a marker, should never been called!");
|
throw new UnsupportedOperationException("Marker executor. Should never be called!");
|
||||||
};
|
};
|
||||||
|
|
||||||
private final Future<V> future;
|
private final Future<V> future;
|
||||||
@ -191,7 +191,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
future.addListener(future -> {
|
future.addListener(future -> {
|
||||||
Throwable cause = future.cause();
|
Throwable cause = future.cause();
|
||||||
if (cause == null) {
|
if (cause == null) {
|
||||||
@SuppressWarnings("unchecked") V value = (V) future.getNow();
|
V value = future.getNow();
|
||||||
if (executeDirectly(executor)) {
|
if (executeDirectly(executor)) {
|
||||||
thenApplyAsync0(promise, value, fn);
|
thenApplyAsync0(promise, value, fn);
|
||||||
} else {
|
} else {
|
||||||
@ -224,7 +224,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
future.addListener(future -> {
|
future.addListener(future -> {
|
||||||
Throwable cause = future.cause();
|
Throwable cause = future.cause();
|
||||||
if (cause == null) {
|
if (cause == null) {
|
||||||
@SuppressWarnings("unchecked") V value = (V) future.getNow();
|
V value = future.getNow();
|
||||||
if (executeDirectly(executor)) {
|
if (executeDirectly(executor)) {
|
||||||
thenAcceptAsync0(promise, value, action);
|
thenAcceptAsync0(promise, value, action);
|
||||||
} else {
|
} else {
|
||||||
@ -333,6 +333,8 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
|
|
||||||
Promise<U> promise = executor().newPromise();
|
Promise<U> promise = executor().newPromise();
|
||||||
BiConsumer<V, Throwable> consumer = new AtomicBiConsumer<V, U>(promise) {
|
BiConsumer<V, Throwable> consumer = new AtomicBiConsumer<V, U>(promise) {
|
||||||
|
private static final long serialVersionUID = -8454630185124276599L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected U apply(V value) {
|
protected U apply(V value) {
|
||||||
return fn.apply(value);
|
return fn.apply(value);
|
||||||
@ -351,6 +353,8 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
|
|
||||||
Promise<Void> promise = executor().newPromise();
|
Promise<Void> promise = executor().newPromise();
|
||||||
BiConsumer<V, Throwable> consumer = new AtomicBiConsumer<V, Void>(promise) {
|
BiConsumer<V, Throwable> consumer = new AtomicBiConsumer<V, Void>(promise) {
|
||||||
|
private static final long serialVersionUID = -8429618092318150682L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void apply(V value) {
|
protected Void apply(V value) {
|
||||||
action.accept(value);
|
action.accept(value);
|
||||||
@ -370,6 +374,8 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
|
|
||||||
Promise<Void> promise = executor().newPromise();
|
Promise<Void> promise = executor().newPromise();
|
||||||
BiConsumer<Object, Throwable> consumer = new AtomicBiConsumer<Object, Void>(promise) {
|
BiConsumer<Object, Throwable> consumer = new AtomicBiConsumer<Object, Void>(promise) {
|
||||||
|
private static final long serialVersionUID = 5994110691767731494L;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void apply(Object value) {
|
protected Void apply(Object value) {
|
||||||
action.run();
|
action.run();
|
||||||
@ -391,7 +397,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
future.addListener(f -> {
|
future.addListener(f -> {
|
||||||
Throwable cause = f.cause();
|
Throwable cause = f.cause();
|
||||||
if (cause == null) {
|
if (cause == null) {
|
||||||
@SuppressWarnings("unchecked") V value = (V) f.getNow();
|
V value = f.getNow();
|
||||||
if (executeDirectly(executor)) {
|
if (executeDirectly(executor)) {
|
||||||
thenComposeAsync0(promise, fn, value);
|
thenComposeAsync0(promise, fn, value);
|
||||||
} else {
|
} else {
|
||||||
@ -430,7 +436,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
future.addListener(f -> {
|
future.addListener(f -> {
|
||||||
Throwable error = f.cause();
|
Throwable error = f.cause();
|
||||||
if (error == null) {
|
if (error == null) {
|
||||||
@SuppressWarnings("unchecked") V value = (V) f.getNow();
|
V value = f.getNow();
|
||||||
promise.setSuccess(value);
|
promise.setSuccess(value);
|
||||||
} else {
|
} else {
|
||||||
final V result;
|
final V result;
|
||||||
@ -464,10 +470,9 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static <V> void whenCompleteAsync0(
|
private static <V> void whenCompleteAsync0(
|
||||||
Promise<V> promise, Future<? super V> f, BiConsumer<? super V, ? super Throwable> action) {
|
Promise<V> promise, Future<? extends V> f, BiConsumer<? super V, ? super Throwable> action) {
|
||||||
Throwable cause = f.cause();
|
Throwable cause = f.cause();
|
||||||
|
V value = cause == null ? f.getNow() : null;
|
||||||
@SuppressWarnings("unchecked") V value = cause == null ? (V) f.getNow() : null;
|
|
||||||
try {
|
try {
|
||||||
action.accept(value, cause);
|
action.accept(value, cause);
|
||||||
} catch (Throwable error) {
|
} catch (Throwable error) {
|
||||||
@ -532,6 +537,7 @@ final class DefaultFutureCompletionStage<V> implements FutureCompletionStage<V>
|
|||||||
|
|
||||||
private abstract static class AtomicBiConsumer<V, U> extends AtomicReference<Object>
|
private abstract static class AtomicBiConsumer<V, U> extends AtomicReference<Object>
|
||||||
implements BiConsumer<V, Throwable> {
|
implements BiConsumer<V, Throwable> {
|
||||||
|
private static final long serialVersionUID = 880039612531973027L;
|
||||||
|
|
||||||
private final Promise<U> promise;
|
private final Promise<U> promise;
|
||||||
|
|
||||||
|
@ -15,53 +15,77 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.util.concurrent;
|
package io.netty.util.concurrent;
|
||||||
|
|
||||||
|
import io.netty.util.internal.logging.InternalLogger;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
final class DefaultFutureListeners {
|
final class DefaultFutureListeners {
|
||||||
|
private Object[] listeners;
|
||||||
private GenericFutureListener<? extends Future<?>>[] listeners;
|
|
||||||
private int size;
|
private int size;
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
DefaultFutureListeners() {
|
||||||
DefaultFutureListeners(
|
listeners = new Object[4];
|
||||||
GenericFutureListener<? extends Future<?>> first, GenericFutureListener<? extends Future<?>> second) {
|
|
||||||
listeners = new GenericFutureListener[2];
|
|
||||||
listeners[0] = first;
|
|
||||||
listeners[1] = second;
|
|
||||||
size = 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(GenericFutureListener<? extends Future<?>> l) {
|
public void add(Object listener, Object context) {
|
||||||
GenericFutureListener<? extends Future<?>>[] listeners = this.listeners;
|
Object[] listeners = this.listeners;
|
||||||
final int size = this.size;
|
int index = size << 1;
|
||||||
if (size == listeners.length) {
|
if (index == listeners.length) {
|
||||||
this.listeners = listeners = Arrays.copyOf(listeners, size << 1);
|
this.listeners = listeners = Arrays.copyOf(listeners, listeners.length << 1);
|
||||||
}
|
}
|
||||||
listeners[size] = l;
|
listeners[index] = listener;
|
||||||
this.size = size + 1;
|
listeners[index + 1] = context;
|
||||||
|
size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(GenericFutureListener<? extends Future<?>> l) {
|
public void remove(EventListener listener) {
|
||||||
final GenericFutureListener<? extends Future<?>>[] listeners = this.listeners;
|
final Object[] listeners = this.listeners;
|
||||||
int size = this.size;
|
for (int i = 0, len = listeners.length; i < len; i += 2) {
|
||||||
for (int i = 0; i < size; i ++) {
|
Object candidateListener = listeners[i]; // With associated context at listeners[i + 1]
|
||||||
if (listeners[i] == l) {
|
if (candidateListener == listener) {
|
||||||
int listenersToMove = size - i - 1;
|
int listenersToMove = len - i - 2;
|
||||||
if (listenersToMove > 0) {
|
if (listenersToMove > 0) {
|
||||||
System.arraycopy(listeners, i + 1, listeners, i, listenersToMove);
|
System.arraycopy(listeners, i + 2, listeners, i, listenersToMove);
|
||||||
}
|
}
|
||||||
listeners[-- size] = null;
|
listeners[len - 2] = null;
|
||||||
this.size = size;
|
listeners[len - 1] = null;
|
||||||
|
size--;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public GenericFutureListener<? extends Future<?>>[] listeners() {
|
@SuppressWarnings("unchecked")
|
||||||
return listeners;
|
public <V> void notifyListeners(DefaultPromise<V> promise, InternalLogger logger) {
|
||||||
}
|
int size = this.size;
|
||||||
|
Object[] listeners = this.listeners;
|
||||||
public int size() {
|
for (int i = 0, len = size << 1; i < len; i += 2) {
|
||||||
return size;
|
Object listener = listeners[i];
|
||||||
|
Object context = listeners[i + 1];
|
||||||
|
try {
|
||||||
|
// Since a listener could in theory be both a FutureListener and a FutureContextListener,
|
||||||
|
// we use the presence of a context object to determine which one was meant when the listener
|
||||||
|
// was added. The context reference will never be null if the FutureContextListener was intended,
|
||||||
|
// even if the context passed was null. In that case, the reference will point to the
|
||||||
|
// NULL_CONTEXT, and we have to convert it back to null here.
|
||||||
|
if (context != null) {
|
||||||
|
FutureContextListener<Object, V> fcl = (FutureContextListener<Object, V>) listener;
|
||||||
|
fcl.operationComplete(context == DefaultPromise.NULL_CONTEXT ? null : context, promise);
|
||||||
|
} else if (listener instanceof FutureListener) {
|
||||||
|
FutureListener<V> fl = (FutureListener<V>) listener;
|
||||||
|
fl.operationComplete(promise);
|
||||||
|
} else if (listener != null) {
|
||||||
|
logger.warn("Unknown future listener type: {} of type {}", listener, listener.getClass());
|
||||||
|
} else {
|
||||||
|
break; // Listeners are always densely packed in the array, so finding a null means we're done.
|
||||||
|
}
|
||||||
|
} catch (Throwable t) {
|
||||||
|
if (logger.isWarnEnabled()) {
|
||||||
|
String className = listener.getClass().getName();
|
||||||
|
logger.warn("An exception was thrown by " + className + ".operationComplete()", t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
private static final CauseHolder CANCELLATION_CAUSE_HOLDER = new CauseHolder(
|
private static final CauseHolder CANCELLATION_CAUSE_HOLDER = new CauseHolder(
|
||||||
StacklessCancellationException.newInstance(DefaultPromise.class, "cancel(...)"));
|
StacklessCancellationException.newInstance(DefaultPromise.class, "cancel(...)"));
|
||||||
private static final StackTraceElement[] CANCELLATION_STACK = CANCELLATION_CAUSE_HOLDER.cause.getStackTrace();
|
private static final StackTraceElement[] CANCELLATION_STACK = CANCELLATION_CAUSE_HOLDER.cause.getStackTrace();
|
||||||
|
static final Object NULL_CONTEXT = new Object();
|
||||||
|
|
||||||
private volatile Object result;
|
private volatile Object result;
|
||||||
private final EventExecutor executor;
|
private final EventExecutor executor;
|
||||||
@ -51,9 +52,12 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
private DefaultFutureCompletionStage<V> stage;
|
private DefaultFutureCompletionStage<V> stage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* One or more listeners. Can be a {@link GenericFutureListener} or a {@link DefaultFutureListeners}.
|
* One or more listeners. Can be a {@link FutureListener} or a {@link DefaultFutureListeners}.
|
||||||
* If {@code null}, it means either 1) no listeners were added yet or 2) all listeners were notified.
|
* If {@code null}, it means either 1) no listeners were added yet or 2) all listeners were notified.
|
||||||
*
|
* <p>
|
||||||
|
* Note that if a {@link FutureContextListener} is added, we immediately upgrade to a {@link DefaultFutureListeners}
|
||||||
|
* as we otherwise wouldn't have room to store the associated context object.
|
||||||
|
* <p>
|
||||||
* Threading - synchronized(this). We must support adding listeners when there is no EventExecutor.
|
* Threading - synchronized(this). We must support adding listeners when there is no EventExecutor.
|
||||||
*/
|
*/
|
||||||
private Object listeners;
|
private Object listeners;
|
||||||
@ -63,22 +67,61 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
private short waiters;
|
private short waiters;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new unfulfilled promise.
|
||||||
*
|
*
|
||||||
* It is preferable to use {@link EventExecutor#newPromise()} to create a new promise
|
* It is preferable to use {@link EventExecutor#newPromise()} to create a new promise
|
||||||
*
|
*
|
||||||
* @param executor
|
* @param executor
|
||||||
* the {@link EventExecutor} which is used to notify the promise once it is complete.
|
* The {@link EventExecutor} which is used to notify the promise once it is complete.
|
||||||
* It is assumed this executor will protect against {@link StackOverflowError} exceptions.
|
* It is assumed this executor will protect against {@link StackOverflowError} exceptions.
|
||||||
* The executor may be used to avoid {@link StackOverflowError} by executing a {@link Runnable} if the stack
|
* The executor may be used to avoid {@link StackOverflowError} by executing a {@link Runnable} if the stack
|
||||||
* depth exceeds a threshold.
|
* depth exceeds a threshold.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public DefaultPromise(EventExecutor executor) {
|
public DefaultPromise(EventExecutor executor) {
|
||||||
this.executor = requireNonNull(executor, "executor");
|
this.executor = requireNonNull(executor, "executor");
|
||||||
stage = new DefaultFutureCompletionStage<>(this);
|
stage = new DefaultFutureCompletionStage<>(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new promise that has already been completed successfully.
|
||||||
|
*
|
||||||
|
* @param executor
|
||||||
|
* The {@link EventExecutor} which is used to notify the promise once it is complete.
|
||||||
|
* It is assumed this executor will protect against {@link StackOverflowError} exceptions.
|
||||||
|
* The executor may be used to avoid {@link StackOverflowError} by executing a {@link Runnable} if the stack
|
||||||
|
* depth exceeds a threshold.
|
||||||
|
* @param result The result of the successful promise.
|
||||||
|
*/
|
||||||
|
public static <V> Promise<V> newSuccessfulPromise(EventExecutor executor, V result) {
|
||||||
|
return new DefaultPromise<>(executor, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new promise that has already failed.
|
||||||
|
*
|
||||||
|
* @param executor
|
||||||
|
* The {@link EventExecutor} which is used to notify the promise once it is complete.
|
||||||
|
* It is assumed this executor will protect against {@link StackOverflowError} exceptions.
|
||||||
|
* The executor may be used to avoid {@link StackOverflowError} by executing a {@link Runnable} if the stack
|
||||||
|
* depth exceeds a threshold.
|
||||||
|
* @param cause The {@link Throwable} that caused the failure of the returned promise.
|
||||||
|
*/
|
||||||
|
public static <V> Promise<V> newFailedPromise(EventExecutor executor, Throwable cause) {
|
||||||
|
return new DefaultPromise<>(cause, executor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefaultPromise(EventExecutor executor, Object result) {
|
||||||
|
this.executor = requireNonNull(executor, "executor");
|
||||||
|
this.result = result == null? SUCCESS : result;
|
||||||
|
stage = new DefaultFutureCompletionStage<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DefaultPromise(Throwable cause, EventExecutor executor) {
|
||||||
|
this.executor = requireNonNull(executor, "executor");
|
||||||
|
result = new CauseHolder(requireNonNull(cause, "cause"));
|
||||||
|
stage = new DefaultFutureCompletionStage<>(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Promise<V> setSuccess(V result) {
|
public Promise<V> setSuccess(V result) {
|
||||||
if (setSuccess0(result)) {
|
if (setSuccess0(result)) {
|
||||||
@ -161,13 +204,10 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
|
public Promise<V> addListener(FutureListener<? super V> listener) {
|
||||||
requireNonNull(listener, "listener");
|
requireNonNull(listener, "listener");
|
||||||
|
|
||||||
synchronized (this) {
|
addListener0(listener, null);
|
||||||
addListener0(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDone()) {
|
if (isDone()) {
|
||||||
notifyListeners();
|
notifyListeners();
|
||||||
}
|
}
|
||||||
@ -176,7 +216,19 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Promise<V> await() throws InterruptedException {
|
public <C> Promise<V> addListener(C context, FutureContextListener<? super C, ? super V> listener) {
|
||||||
|
requireNonNull(listener, "listener");
|
||||||
|
|
||||||
|
addListener0(listener, context == null ? NULL_CONTEXT : context);
|
||||||
|
if (isDone()) {
|
||||||
|
notifyListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Future<V> await() throws InterruptedException {
|
||||||
if (isDone()) {
|
if (isDone()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -201,7 +253,7 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Promise<V> awaitUninterruptibly() {
|
public Future<V> awaitUninterruptibly() {
|
||||||
if (isDone()) {
|
if (isDone()) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@ -339,14 +391,14 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Promise<V> sync() throws InterruptedException {
|
public Future<V> sync() throws InterruptedException {
|
||||||
await();
|
await();
|
||||||
rethrowIfFailed();
|
rethrowIfFailed();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Promise<V> syncUninterruptibly() {
|
public Future<V> syncUninterruptibly() {
|
||||||
awaitUninterruptibly();
|
awaitUninterruptibly();
|
||||||
rethrowIfFailed();
|
rethrowIfFailed();
|
||||||
return this;
|
return this;
|
||||||
@ -410,6 +462,7 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
safeExecute(executor(), this::notifyListenersNow);
|
safeExecute(executor(), this::notifyListenersNow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
private void notifyListenersNow() {
|
private void notifyListenersNow() {
|
||||||
Object listeners;
|
Object listeners;
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
@ -424,7 +477,7 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
if (listeners instanceof DefaultFutureListeners) {
|
if (listeners instanceof DefaultFutureListeners) {
|
||||||
notifyListeners0((DefaultFutureListeners) listeners);
|
notifyListeners0((DefaultFutureListeners) listeners);
|
||||||
} else {
|
} else {
|
||||||
notifyListener0(this, (GenericFutureListener<?>) listeners);
|
notifyListener0(this, (FutureListener<V>) listeners);
|
||||||
}
|
}
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
if (this.listeners == null) {
|
if (this.listeners == null) {
|
||||||
@ -437,15 +490,10 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void notifyListeners0(DefaultFutureListeners listeners) {
|
private void notifyListeners0(DefaultFutureListeners listeners) {
|
||||||
GenericFutureListener<?>[] a = listeners.listeners();
|
listeners.notifyListeners(this, logger);
|
||||||
int size = listeners.size();
|
|
||||||
for (int i = 0; i < size; i ++) {
|
|
||||||
notifyListener0(this, a[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
static <V> void notifyListener0(Future<V> future, FutureListener<? super V> l) {
|
||||||
static void notifyListener0(Future future, GenericFutureListener l) {
|
|
||||||
try {
|
try {
|
||||||
l.operationComplete(future);
|
l.operationComplete(future);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
@ -455,13 +503,18 @@ public class DefaultPromise<V> implements Promise<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addListener0(GenericFutureListener<? extends Future<? super V>> listener) {
|
private synchronized void addListener0(Object listener, Object context) {
|
||||||
if (listeners == null) {
|
if (listeners == null && context == null) {
|
||||||
listeners = listener;
|
listeners = listener;
|
||||||
} else if (listeners instanceof DefaultFutureListeners) {
|
} else if (listeners instanceof DefaultFutureListeners) {
|
||||||
((DefaultFutureListeners) listeners).add(listener);
|
((DefaultFutureListeners) listeners).add(listener, context);
|
||||||
} else {
|
} else {
|
||||||
listeners = new DefaultFutureListeners((GenericFutureListener<?>) listeners, listener);
|
DefaultFutureListeners listeners = new DefaultFutureListeners();
|
||||||
|
if (this.listeners != null) {
|
||||||
|
listeners.add(this.listeners, null);
|
||||||
|
}
|
||||||
|
listeners.add(listener, context);
|
||||||
|
this.listeners = listeners;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2012 The Netty Project
|
|
||||||
*
|
|
||||||
* The Netty Project licenses this file to you under the Apache License,
|
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at:
|
|
||||||
*
|
|
||||||
* https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
* License for the specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
package io.netty.util.concurrent;
|
|
||||||
|
|
||||||
import io.netty.util.internal.PlatformDependent;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The {@link CompleteFuture} which is failed already. It is
|
|
||||||
* recommended to use {@link EventExecutor#newFailedFuture(Throwable)}
|
|
||||||
* instead of calling the constructor of this future.
|
|
||||||
*/
|
|
||||||
public final class FailedFuture<V> extends CompleteFuture<V> {
|
|
||||||
|
|
||||||
private final Throwable cause;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance.
|
|
||||||
*
|
|
||||||
* @param executor the {@link EventExecutor} associated with this future
|
|
||||||
* @param cause the cause of failure
|
|
||||||
*/
|
|
||||||
public FailedFuture(EventExecutor executor, Throwable cause) {
|
|
||||||
super(executor);
|
|
||||||
requireNonNull(cause, "cause");
|
|
||||||
this.cause = cause;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Throwable cause() {
|
|
||||||
return cause;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isSuccess() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<V> sync() {
|
|
||||||
PlatformDependent.throwException(cause);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Future<V> syncUninterruptibly() {
|
|
||||||
PlatformDependent.throwException(cause);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public V getNow() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,10 +23,150 @@ import java.util.concurrent.TimeoutException;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The result of an asynchronous operation.
|
* The result of an asynchronous operation.
|
||||||
|
* <p>
|
||||||
|
* An asynchronous operation is one that might be completed outside a given
|
||||||
|
* thread of execution. The operation can either be performing computation,
|
||||||
|
* or I/O, or both.
|
||||||
|
* <p>
|
||||||
|
* All I/O operations in Netty are asynchronous. It means any I/O calls will
|
||||||
|
* return immediately with no guarantee that the requested I/O operation has
|
||||||
|
* been completed at the end of the call. Instead, you will be returned with
|
||||||
|
* a {@link Future} instance which gives you the information about the
|
||||||
|
* result or status of the I/O operation.
|
||||||
|
* <p>
|
||||||
|
* A {@link Future} is either <em>uncompleted</em> or <em>completed</em>.
|
||||||
|
* When an I/O operation begins, a new future object is created. The new future
|
||||||
|
* is uncompleted initially - it is neither succeeded, failed, nor cancelled
|
||||||
|
* because the I/O operation is not finished yet. If the I/O operation is
|
||||||
|
* finished either successfully, with failure, or by cancellation, the future is
|
||||||
|
* marked as completed with more specific information, such as the cause of the
|
||||||
|
* failure. Please note that even failure and cancellation belong to the
|
||||||
|
* completed state.
|
||||||
|
* <pre>
|
||||||
|
* +---------------------------+
|
||||||
|
* | Completed successfully |
|
||||||
|
* +---------------------------+
|
||||||
|
* +----> isDone() = true |
|
||||||
|
* +--------------------------+ | | isSuccess() = true |
|
||||||
|
* | Uncompleted | | +===========================+
|
||||||
|
* +--------------------------+ | | Completed with failure |
|
||||||
|
* | isDone() = false | | +---------------------------+
|
||||||
|
* | isSuccess() = false |----+----> isDone() = true |
|
||||||
|
* | isCancelled() = false | | | cause() = non-null |
|
||||||
|
* | cause() = null | | +===========================+
|
||||||
|
* +--------------------------+ | | Completed by cancellation |
|
||||||
|
* | +---------------------------+
|
||||||
|
* +----> isDone() = true |
|
||||||
|
* | isCancelled() = true |
|
||||||
|
* +---------------------------+
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Various methods are provided to let you check if the I/O operation has been
|
||||||
|
* completed, wait for the completion, and retrieve the result of the I/O
|
||||||
|
* operation. It also allows you to add {@link FutureListener}s so you
|
||||||
|
* can get notified when the I/O operation is completed.
|
||||||
|
*
|
||||||
|
* <h3>Prefer {@link #addListener(FutureListener)} to {@link #await()}</h3>
|
||||||
|
*
|
||||||
|
* It is recommended to prefer {@link #addListener(FutureListener)}, or
|
||||||
|
* {@link #addListener(Object, FutureContextListener)}, to {@link #await()}
|
||||||
|
* wherever possible to get notified when an I/O operation is done and to
|
||||||
|
* do any follow-up tasks.
|
||||||
|
* <p>
|
||||||
|
* The {@link #addListener(FutureListener)} method is non-blocking. It simply adds
|
||||||
|
* the specified {@link FutureListener} to the {@link Future}, and the I/O thread
|
||||||
|
* will notify the listeners when the I/O operation associated with the future is
|
||||||
|
* done. The {@link FutureListener} and {@link FutureContextListener} callbacks
|
||||||
|
* yield the best performance and resource utilization because it does not block at
|
||||||
|
* all, but it could be tricky to implement a sequential logic if you are not used to
|
||||||
|
* event-driven programming.
|
||||||
|
* <p>
|
||||||
|
* By contrast, {@link #await()} is a blocking operation. Once called, the
|
||||||
|
* caller thread blocks until the operation is done. It is easier to implement
|
||||||
|
* a sequential logic with {@link #await()}, but the caller thread blocks
|
||||||
|
* unnecessarily until the I/O operation is done and there's relatively
|
||||||
|
* expensive cost of inter-thread notification. Moreover, there's a chance of
|
||||||
|
* dead-lock in a particular circumstance, which is described below.
|
||||||
|
*
|
||||||
|
* <h3>Do not call {@link #await()} inside a {@link io.netty.channel.ChannelHandler}</h3>
|
||||||
|
* <p>
|
||||||
|
* The event handler methods in {@link io.netty.channel.ChannelHandler} are usually
|
||||||
|
* called by an I/O thread. If {@link #await()} is called by an event handler method,
|
||||||
|
* which is called by the I/O thread, the I/O operation it is waiting for might never
|
||||||
|
* complete because {@link #await()} can block the I/O operation it is waiting for,
|
||||||
|
* which is a dead-lock.
|
||||||
|
* <pre>
|
||||||
|
* // BAD - NEVER DO THIS
|
||||||
|
* {@code @Override}
|
||||||
|
* public void channelRead({@link io.netty.channel.ChannelHandlerContext} ctx, Object msg) {
|
||||||
|
* {@link Future} future = ctx.channel().close();
|
||||||
|
* future.awaitUninterruptibly();
|
||||||
|
* // Perform post-closure operation
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // GOOD
|
||||||
|
* {@code @Override}
|
||||||
|
* public void channelRead({@link io.netty.channel.ChannelHandlerContext} ctx, Object msg) {
|
||||||
|
* {@link Future} future = ctx.channel().close();
|
||||||
|
* future.addListener(new {@link FutureListener}() {
|
||||||
|
* public void operationComplete({@link Future} future) {
|
||||||
|
* // Perform post-closure operation
|
||||||
|
* // ...
|
||||||
|
* }
|
||||||
|
* });
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* <p>
|
||||||
|
* In spite of the disadvantages mentioned above, there are certainly the cases
|
||||||
|
* where it is more convenient to call {@link #await()}. In such a case, please
|
||||||
|
* make sure you do not call {@link #await()} in an I/O thread. Otherwise,
|
||||||
|
* {@link BlockingOperationException} will be raised to prevent a dead-lock.
|
||||||
|
*
|
||||||
|
* <h3>Do not confuse I/O timeout and await timeout</h3>
|
||||||
|
*
|
||||||
|
* The timeout value you specify with {@link #await(long)},
|
||||||
|
* {@link #await(long, TimeUnit)}, {@link #awaitUninterruptibly(long)}, or
|
||||||
|
* {@link #awaitUninterruptibly(long, TimeUnit)} are not related with I/O
|
||||||
|
* timeout at all. If an I/O operation times out, the future will be marked as
|
||||||
|
* 'completed with failure,' as depicted in the diagram above. For example,
|
||||||
|
* connect timeout should be configured via a transport-specific option:
|
||||||
|
* <pre>
|
||||||
|
* // BAD - NEVER DO THIS
|
||||||
|
* {@link io.netty.bootstrap.Bootstrap} b = ...;
|
||||||
|
* {@link Future} f = b.connect(...);
|
||||||
|
* f.awaitUninterruptibly(10, TimeUnit.SECONDS);
|
||||||
|
* if (f.isCancelled()) {
|
||||||
|
* // Connection attempt cancelled by user
|
||||||
|
* } else if (!f.isSuccess()) {
|
||||||
|
* // You might get a NullPointerException here because the future
|
||||||
|
* // might not be completed yet.
|
||||||
|
* f.cause().printStackTrace();
|
||||||
|
* } else {
|
||||||
|
* // Connection established successfully
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // GOOD
|
||||||
|
* {@link io.netty.bootstrap.Bootstrap} b = ...;
|
||||||
|
* // Configure the connect timeout option.
|
||||||
|
* <b>b.option({@link io.netty.channel.ChannelOption}.CONNECT_TIMEOUT_MILLIS, 10000);</b>
|
||||||
|
* {@link Future} f = b.connect(...);
|
||||||
|
* f.awaitUninterruptibly();
|
||||||
|
*
|
||||||
|
* // Now we are sure the future is completed.
|
||||||
|
* assert f.isDone();
|
||||||
|
*
|
||||||
|
* if (f.isCancelled()) {
|
||||||
|
* // Connection attempt cancelled by user
|
||||||
|
* } else if (!f.isSuccess()) {
|
||||||
|
* f.cause().printStackTrace();
|
||||||
|
* } else {
|
||||||
|
* // Connection established successfully
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("ClassNameSameAsAncestorName")
|
@SuppressWarnings("ClassNameSameAsAncestorName")
|
||||||
public interface Future<V> extends java.util.concurrent.Future<V> {
|
public interface Future<V> extends java.util.concurrent.Future<V> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns {@code true} if and only if the I/O operation was completed
|
* Returns {@code true} if and only if the I/O operation was completed
|
||||||
* successfully.
|
* successfully.
|
||||||
@ -49,12 +189,27 @@ public interface Future<V> extends java.util.concurrent.Future<V> {
|
|||||||
Throwable cause();
|
Throwable cause();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the specified listener to this future. The
|
* Adds the specified listener to this future.
|
||||||
* specified listener is notified when this future is
|
* The specified listener is notified when this future is {@linkplain #isDone() done}.
|
||||||
* {@linkplain #isDone() done}. If this future is already
|
* If this future is already completed, the specified listener is notified immediately.
|
||||||
* completed, the specified listener is notified immediately.
|
*
|
||||||
|
* @param listener The listener to be called when this future completes.
|
||||||
|
* The listener will be passed this future as an argument.
|
||||||
|
* @return this future object.
|
||||||
*/
|
*/
|
||||||
Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
|
Future<V> addListener(FutureListener<? super V> listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified listener to this future.
|
||||||
|
* The specified listener is notified when this future is {@linkplain #isDone() done}.
|
||||||
|
* If this future is already completed, the specified listener is notified immediately.
|
||||||
|
*
|
||||||
|
* @param context The context object that will be passed to the listener when this future completes.
|
||||||
|
* @param listener The listener to be called when this future completes.
|
||||||
|
* The listener will be passed the given context, and this future.
|
||||||
|
* @return this future object.
|
||||||
|
*/
|
||||||
|
<C> Future<V> addListener(C context, FutureContextListener<? super C, ? super V> listener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Waits for this future until it is done, and rethrows the cause of the failure if this future
|
* Waits for this future until it is done, and rethrows the cause of the failure if this future
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2013 The Netty Project
|
* Copyright 2021 The Netty Project
|
||||||
*
|
*
|
||||||
* The Netty Project licenses this file to you under the Apache License,
|
* The Netty Project licenses this file to you under the Apache License,
|
||||||
* version 2.0 (the "License"); you may not use this file except in compliance
|
* version 2.0 (the "License"); you may not use this file except in compliance
|
||||||
@ -19,14 +19,18 @@ import java.util.EventListener;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Listens to the result of a {@link Future}. The result of the asynchronous operation is notified once this listener
|
* Listens to the result of a {@link Future}. The result of the asynchronous operation is notified once this listener
|
||||||
* is added by calling {@link Future#addListener(GenericFutureListener)}.
|
* is added by calling {@link Future#addListener(Object, FutureContextListener)}.
|
||||||
|
* <pre>
|
||||||
|
* Future f = new DefaultPromise(..);
|
||||||
|
* f.addListener(context, (context, future) -> { .. });
|
||||||
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public interface GenericFutureListener<F extends Future<?>> extends EventListener {
|
@FunctionalInterface
|
||||||
|
public interface FutureContextListener<C, V> {
|
||||||
/**
|
/**
|
||||||
* Invoked when the operation associated with the {@link Future} has been completed.
|
* Invoked when the operation associated with the {@link Future} has been completed.
|
||||||
*
|
*
|
||||||
* @param future the source {@link Future} which called this callback
|
* @param future the source {@link Future} which called this callback
|
||||||
*/
|
*/
|
||||||
void operationComplete(F future) throws Exception;
|
void operationComplete(C context, Future<? extends V> future) throws Exception;
|
||||||
}
|
}
|
@ -16,13 +16,22 @@
|
|||||||
|
|
||||||
package io.netty.util.concurrent;
|
package io.netty.util.concurrent;
|
||||||
|
|
||||||
|
import java.util.EventListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A subtype of {@link GenericFutureListener} that hides type parameter for convenience.
|
* Listens to the result of a {@link Future}. The result of the asynchronous operation is notified once this listener
|
||||||
|
* is added by calling {@link Future#addListener(FutureListener)}.
|
||||||
* <pre>
|
* <pre>
|
||||||
* Future f = new DefaultPromise(..);
|
* Future f = new DefaultPromise(..);
|
||||||
* f.addListener(new FutureListener() {
|
* f.addListener(future -> { .. });
|
||||||
* public void operationComplete(Future f) { .. }
|
|
||||||
* });
|
|
||||||
* </pre>
|
* </pre>
|
||||||
*/
|
*/
|
||||||
public interface FutureListener<V> extends GenericFutureListener<Future<V>> { }
|
@FunctionalInterface
|
||||||
|
public interface FutureListener<V> {
|
||||||
|
/**
|
||||||
|
* Invoked when the operation associated with the {@link Future} has been completed.
|
||||||
|
*
|
||||||
|
* @param future the source {@link Future} which called this callback
|
||||||
|
*/
|
||||||
|
void operationComplete(Future<? extends V> future) throws Exception;
|
||||||
|
}
|
||||||
|
@ -67,7 +67,8 @@ public final class GlobalEventExecutor extends AbstractScheduledEventExecutor im
|
|||||||
private final AtomicBoolean started = new AtomicBoolean();
|
private final AtomicBoolean started = new AtomicBoolean();
|
||||||
volatile Thread thread;
|
volatile Thread thread;
|
||||||
|
|
||||||
private final Future<?> terminationFuture = new FailedFuture<>(this, new UnsupportedOperationException());
|
private final Future<?> terminationFuture = DefaultPromise.newFailedPromise(
|
||||||
|
this, new UnsupportedOperationException());
|
||||||
|
|
||||||
private GlobalEventExecutor() {
|
private GlobalEventExecutor() {
|
||||||
threadFactory = ThreadExecutorMap.apply(new DefaultThreadFactory(
|
threadFactory = ThreadExecutorMap.apply(new DefaultThreadFactory(
|
||||||
|
@ -54,7 +54,7 @@ public final class ImmediateEventExecutor extends AbstractEventExecutor {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private final Future<?> terminationFuture = new FailedFuture<>(
|
private final Future<?> terminationFuture = DefaultPromise.newFailedPromise(
|
||||||
GlobalEventExecutor.INSTANCE, new UnsupportedOperationException());
|
GlobalEventExecutor.INSTANCE, new UnsupportedOperationException());
|
||||||
|
|
||||||
private ImmediateEventExecutor() { }
|
private ImmediateEventExecutor() { }
|
||||||
|
@ -15,67 +15,53 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.util.concurrent;
|
package io.netty.util.concurrent;
|
||||||
|
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Special {@link Future} which is writable.
|
* Special {@link Future} which is writable.
|
||||||
*/
|
*/
|
||||||
public interface Promise<V> extends Future<V> {
|
public interface Promise<V> extends Future<V> {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks this future as a success and notifies all
|
* Marks this future as a success and notifies all listeners.
|
||||||
* listeners.
|
* <p>
|
||||||
*
|
|
||||||
* If it is success or failed already it will throw an {@link IllegalStateException}.
|
* If it is success or failed already it will throw an {@link IllegalStateException}.
|
||||||
*/
|
*/
|
||||||
Promise<V> setSuccess(V result);
|
Promise<V> setSuccess(V result);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks this future as a success and notifies all
|
* Marks this future as a success and notifies all listeners.
|
||||||
* listeners.
|
|
||||||
*
|
*
|
||||||
* @return {@code true} if and only if successfully marked this future as
|
* @return {@code true} if and only if successfully marked this future as a success. Otherwise {@code false} because
|
||||||
* a success. Otherwise {@code false} because this future is
|
* this future is already marked as either a success or a failure.
|
||||||
* already marked as either a success or a failure.
|
|
||||||
*/
|
*/
|
||||||
boolean trySuccess(V result);
|
boolean trySuccess(V result);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks this future as a failure and notifies all
|
* Marks this future as a failure and notifies all listeners.
|
||||||
* listeners.
|
* <p>
|
||||||
*
|
|
||||||
* If it is success or failed already it will throw an {@link IllegalStateException}.
|
* If it is success or failed already it will throw an {@link IllegalStateException}.
|
||||||
*/
|
*/
|
||||||
Promise<V> setFailure(Throwable cause);
|
Promise<V> setFailure(Throwable cause);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Marks this future as a failure and notifies all
|
* Marks this future as a failure and notifies all listeners.
|
||||||
* listeners.
|
|
||||||
*
|
*
|
||||||
* @return {@code true} if and only if successfully marked this future as
|
* @return {@code true} if and only if successfully marked this future as a failure. Otherwise {@code false} because
|
||||||
* a failure. Otherwise {@code false} because this future is
|
* this future is already marked as either a success or a failure.
|
||||||
* already marked as either a success or a failure.
|
|
||||||
*/
|
*/
|
||||||
boolean tryFailure(Throwable cause);
|
boolean tryFailure(Throwable cause);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make this future impossible to cancel.
|
* Make this future impossible to cancel.
|
||||||
*
|
*
|
||||||
* @return {@code true} if and only if successfully marked this future as uncancellable or it is already done
|
* @return {@code true} if and only if successfully marked this future as uncancellable, or it is already done
|
||||||
* without being cancelled. {@code false} if this future has been cancelled already.
|
* without being cancelled. Otherwise {@code false} if this future has been cancelled already.
|
||||||
*/
|
*/
|
||||||
boolean setUncancellable();
|
boolean setUncancellable();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
|
Promise<V> addListener(FutureListener<? super V> listener);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
Promise<V> await() throws InterruptedException;
|
<C> Promise<V> addListener(C context, FutureContextListener<? super C, ? super V> listener);
|
||||||
|
|
||||||
@Override
|
|
||||||
Promise<V> awaitUninterruptibly();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Promise<V> sync() throws InterruptedException;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Promise<V> syncUninterruptibly();
|
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ public final class PromiseCombiner {
|
|||||||
private int doneCount;
|
private int doneCount;
|
||||||
private Promise<Void> aggregatePromise;
|
private Promise<Void> aggregatePromise;
|
||||||
private Throwable cause;
|
private Throwable cause;
|
||||||
private final GenericFutureListener<Future<?>> listener = new GenericFutureListener<Future<?>>() {
|
private final FutureListener<Object> listener = new FutureListener<>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(final Future<?> future) {
|
public void operationComplete(final Future<?> future) {
|
||||||
if (executor.inEventLoop()) {
|
if (executor.inEventLoop()) {
|
||||||
@ -62,14 +62,6 @@ public final class PromiseCombiner {
|
|||||||
|
|
||||||
private final EventExecutor executor;
|
private final EventExecutor executor;
|
||||||
|
|
||||||
/**
|
|
||||||
* Deprecated use {@link PromiseCombiner#PromiseCombiner(EventExecutor)}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public PromiseCombiner() {
|
|
||||||
this(ImmediateEventExecutor.INSTANCE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The {@link EventExecutor} to use for notifications. You must call {@link #add(Future)}, {@link #addAll(Future[])}
|
* The {@link EventExecutor} to use for notifications. You must call {@link #add(Future)}, {@link #addAll(Future[])}
|
||||||
* and {@link #finish(Promise)} from within the {@link EventExecutor} thread.
|
* and {@link #finish(Promise)} from within the {@link EventExecutor} thread.
|
||||||
@ -80,56 +72,28 @@ public final class PromiseCombiner {
|
|||||||
this.executor = requireNonNull(executor, "executor");
|
this.executor = requireNonNull(executor, "executor");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a new promise to be combined. New promises may be added until an aggregate promise is added via the
|
|
||||||
* {@link PromiseCombiner#finish(Promise)} method.
|
|
||||||
*
|
|
||||||
* @param promise the promise to add to this promise combiner
|
|
||||||
*
|
|
||||||
* @deprecated Replaced by {@link PromiseCombiner#add(Future)}.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void add(Promise promise) {
|
|
||||||
add((Future) promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a new future to be combined. New futures may be added until an aggregate promise is added via the
|
* Adds a new future to be combined. New futures may be added until an aggregate promise is added via the
|
||||||
* {@link PromiseCombiner#finish(Promise)} method.
|
* {@link PromiseCombiner#finish(Promise)} method.
|
||||||
*
|
*
|
||||||
* @param future the future to add to this promise combiner
|
* @param future the future to add to this promise combiner
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
public void add(Future<?> future) {
|
||||||
public void add(Future future) {
|
|
||||||
checkAddAllowed();
|
checkAddAllowed();
|
||||||
checkInEventLoop();
|
checkInEventLoop();
|
||||||
++expectedCount;
|
++expectedCount;
|
||||||
future.addListener(listener);
|
future.addListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds new promises to be combined. New promises may be added until an aggregate promise is added via the
|
|
||||||
* {@link PromiseCombiner#finish(Promise)} method.
|
|
||||||
*
|
|
||||||
* @param promises the promises to add to this promise combiner
|
|
||||||
*
|
|
||||||
* @deprecated Replaced by {@link PromiseCombiner#addAll(Future[])}
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void addAll(Promise... promises) {
|
|
||||||
addAll((Future[]) promises);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds new futures to be combined. New futures may be added until an aggregate promise is added via the
|
* Adds new futures to be combined. New futures may be added until an aggregate promise is added via the
|
||||||
* {@link PromiseCombiner#finish(Promise)} method.
|
* {@link PromiseCombiner#finish(Promise)} method.
|
||||||
*
|
*
|
||||||
* @param futures the futures to add to this promise combiner
|
* @param futures the futures to add to this promise combiner
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
public void addAll(Future<?>... futures) {
|
||||||
public void addAll(Future... futures) {
|
for (Future<?> future : futures) {
|
||||||
for (Future future : futures) {
|
add(future);
|
||||||
this.add(future);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +127,7 @@ public final class PromiseCombiner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean tryPromise() {
|
private boolean tryPromise() {
|
||||||
return (cause == null) ? aggregatePromise.trySuccess(null) : aggregatePromise.tryFailure(cause);
|
return cause == null? aggregatePromise.trySuccess(null) : aggregatePromise.tryFailure(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAddAllowed() {
|
private void checkAddAllowed() {
|
||||||
|
@ -23,13 +23,12 @@ import static io.netty.util.internal.ObjectUtil.checkNotNullWithIAE;
|
|||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@link GenericFutureListener} implementation which takes other {@link Promise}s
|
* A {@link FutureListener} implementation which takes other {@link Promise}s
|
||||||
* and notifies them on completion.
|
* and notifies them on completion.
|
||||||
*
|
*
|
||||||
* @param <V> the type of value returned by the future
|
* @param <V> the type of value returned by the future
|
||||||
* @param <F> the type of future
|
|
||||||
*/
|
*/
|
||||||
public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureListener<F> {
|
public class PromiseNotifier<V> implements FutureListener<V> {
|
||||||
|
|
||||||
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PromiseNotifier.class);
|
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PromiseNotifier.class);
|
||||||
private final Promise<? super V>[] promises;
|
private final Promise<? super V>[] promises;
|
||||||
@ -38,7 +37,7 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
|
|||||||
/**
|
/**
|
||||||
* Create a new instance.
|
* Create a new instance.
|
||||||
*
|
*
|
||||||
* @param promises the {@link Promise}s to notify once this {@link GenericFutureListener} is notified.
|
* @param promises the {@link Promise}s to notify once this {@link FutureListener} is notified.
|
||||||
*/
|
*/
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public PromiseNotifier(Promise<? super V>... promises) {
|
public PromiseNotifier(Promise<? super V>... promises) {
|
||||||
@ -49,7 +48,7 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
|
|||||||
* Create a new instance.
|
* Create a new instance.
|
||||||
*
|
*
|
||||||
* @param logNotifyFailure {@code true} if logging should be done in case notification fails.
|
* @param logNotifyFailure {@code true} if logging should be done in case notification fails.
|
||||||
* @param promises the {@link Promise}s to notify once this {@link GenericFutureListener} is notified.
|
* @param promises the {@link Promise}s to notify once this {@link FutureListener} is notified.
|
||||||
*/
|
*/
|
||||||
@SafeVarargs
|
@SafeVarargs
|
||||||
public PromiseNotifier(boolean logNotifyFailure, Promise<? super V>... promises) {
|
public PromiseNotifier(boolean logNotifyFailure, Promise<? super V>... promises) {
|
||||||
@ -69,10 +68,9 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
|
|||||||
* @param future the {@link Future} which will be used to listen to for notifying the {@link Promise}.
|
* @param future the {@link Future} which will be used to listen to for notifying the {@link Promise}.
|
||||||
* @param promise the {@link Promise} which will be notified
|
* @param promise the {@link Promise} which will be notified
|
||||||
* @param <V> the type of the value.
|
* @param <V> the type of the value.
|
||||||
* @param <F> the type of the {@link Future}
|
|
||||||
* @return the passed in {@link Future}
|
* @return the passed in {@link Future}
|
||||||
*/
|
*/
|
||||||
public static <V, F extends Future<V>> F cascade(final F future, final Promise<? super V> promise) {
|
public static <V> Future<V> cascade(final Future<V> future, final Promise<? super V> promise) {
|
||||||
return cascade(true, future, promise);
|
return cascade(true, future, promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,29 +83,12 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
|
|||||||
* @param future the {@link Future} which will be used to listen to for notifying the {@link Promise}.
|
* @param future the {@link Future} which will be used to listen to for notifying the {@link Promise}.
|
||||||
* @param promise the {@link Promise} which will be notified
|
* @param promise the {@link Promise} which will be notified
|
||||||
* @param <V> the type of the value.
|
* @param <V> the type of the value.
|
||||||
* @param <F> the type of the {@link Future}
|
|
||||||
* @return the passed in {@link Future}
|
* @return the passed in {@link Future}
|
||||||
*/
|
*/
|
||||||
public static <V, F extends Future<V>> F cascade(boolean logNotifyFailure, final F future,
|
public static <V> Future<V> cascade(boolean logNotifyFailure, final Future<V> future,
|
||||||
final Promise<? super V> promise) {
|
final Promise<? super V> promise) {
|
||||||
promise.addListener(new FutureListener<Object>() {
|
promise.addListener(future, PromiseNotifier::propagateCancel);
|
||||||
@Override
|
future.addListener(new PromiseNotifier<V>(logNotifyFailure, promise), PromiseNotifier::propagateComplete);
|
||||||
public void operationComplete(Future<Object> f) {
|
|
||||||
if (f.isCancelled()) {
|
|
||||||
future.cancel(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
future.addListener(new PromiseNotifier<V, F>(logNotifyFailure, promise) {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(F f) throws Exception {
|
|
||||||
if (promise.isCancelled() && f.isCancelled()) {
|
|
||||||
// Just return if we propagate a cancel from the promise to the future and both are notified already
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.operationComplete(future);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,19 +104,12 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
|
|||||||
* @param successResult the result that will be propagated to the promise on success
|
* @param successResult the result that will be propagated to the promise on success
|
||||||
* @return the passed in {@link Future}
|
* @return the passed in {@link Future}
|
||||||
*/
|
*/
|
||||||
public static <R, F extends Future<Void>> F cascade(boolean logNotifyFailure, F future,
|
public static <R> Future<Void> cascade(boolean logNotifyFailure, Future<Void> future,
|
||||||
Promise<R> promise, R successResult) {
|
Promise<R> promise, R successResult) {
|
||||||
promise.addListener(new FutureListener<Object>() {
|
promise.addListener(future, PromiseNotifier::propagateCancel);
|
||||||
|
future.addListener(new FutureListener<Void>() {
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(Future<Object> f) {
|
public void operationComplete(Future<? extends Void> f) throws Exception {
|
||||||
if (f.isCancelled()) {
|
|
||||||
future.cancel(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
future.addListener(new GenericFutureListener<F>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(F f) throws Exception {
|
|
||||||
if (promise.isCancelled() && f.isCancelled()) {
|
if (promise.isCancelled() && f.isCancelled()) {
|
||||||
// Just return if we propagate a cancel from the promise to the future and both are notified already
|
// Just return if we propagate a cancel from the promise to the future and both are notified already
|
||||||
return;
|
return;
|
||||||
@ -157,8 +131,26 @@ public class PromiseNotifier<V, F extends Future<V>> implements GenericFutureLis
|
|||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static <V, F extends Future<?>> void propagateCancel(F target, Future<? extends V> source) {
|
||||||
|
if (source.isCancelled()) {
|
||||||
|
target.cancel(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V> void propagateComplete(PromiseNotifier<V> target, Future<? extends V> source) throws Exception {
|
||||||
|
boolean allCancelled = target.promises.length > 0;
|
||||||
|
for (Promise<? super V> promise : target.promises) {
|
||||||
|
allCancelled &= promise.isCancelled();
|
||||||
|
}
|
||||||
|
if (allCancelled && source.isCancelled()) {
|
||||||
|
// Just return if we propagate a cancel from the promise to the future and both are notified already
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
target.operationComplete(source);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void operationComplete(F future) throws Exception {
|
public void operationComplete(Future<? extends V> future) throws Exception {
|
||||||
InternalLogger internalLogger = logNotifyFailure ? logger : null;
|
InternalLogger internalLogger = logNotifyFailure ? logger : null;
|
||||||
if (future.isSuccess()) {
|
if (future.isSuccess()) {
|
||||||
V result = future.get();
|
V result = future.get();
|
||||||
|
@ -112,10 +112,6 @@ class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final boolean trySuccessInternal(V result) {
|
|
||||||
return super.trySuccess(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public final boolean setUncancellable() {
|
public final boolean setUncancellable() {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
@ -125,11 +121,6 @@ class PromiseTask<V> extends DefaultPromise<V> implements RunnableFuture<V> {
|
|||||||
return super.setUncancellable();
|
return super.setUncancellable();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
|
|
||||||
return super.addListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected StringBuilder toStringBuilder() {
|
protected StringBuilder toStringBuilder() {
|
||||||
StringBuilder buf = super.toStringBuilder();
|
StringBuilder buf = super.toStringBuilder();
|
||||||
|
@ -22,7 +22,10 @@ package io.netty.util.concurrent;
|
|||||||
public interface RunnableFuture<V> extends java.util.concurrent.RunnableFuture<V>, Future<V> {
|
public interface RunnableFuture<V> extends java.util.concurrent.RunnableFuture<V>, Future<V> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
RunnableFuture<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
|
RunnableFuture<V> addListener(FutureListener<? super V> listener);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
<C> RunnableFuture<V> addListener(C context, FutureContextListener<? super C, ? super V> listener);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
RunnableFuture<V> sync() throws InterruptedException;
|
RunnableFuture<V> sync() throws InterruptedException;
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user